]> git.lizzy.rs Git - rust.git/commitdiff
:arrow_up: rust-analyzer
authorLaurențiu Nicola <lnicola@dend.ro>
Tue, 13 Sep 2022 12:38:11 +0000 (15:38 +0300)
committerLaurențiu Nicola <lnicola@dend.ro>
Tue, 13 Sep 2022 12:38:11 +0000 (15:38 +0300)
25 files changed:
1  2 
src/tools/rust-analyzer/.github/workflows/release.yaml
src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.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-completion/src/completions/mod_.rs
src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs
src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
src/tools/rust-analyzer/crates/ide/src/hover/render.rs
src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
src/tools/rust-analyzer/crates/ide/src/lib.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs

index 303a10615bb7b7e33e5beb616a27059321cfff58,0000000000000000000000000000000000000000..f4d472e3d5c1dc5f6382685f66d8a35193d288b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,261 -1,0 +1,271 @@@
 +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@v1
 +        with:
 +          node-version: 16.x
 +
 +      - 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@v1
 +        with:
 +          node-version: 16.x
 +
 +      - 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
 +        # token from https://dev.azure.com/rust-analyzer/
 +        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
 +
 +      - 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 || true
index a9c124b42dc2c112b3a04718f4c0d161fcb00afc,0000000000000000000000000000000000000000..4a5533c6487e48361dd1a94943f7e917c384821e
mode 100644,000000..100644
--- /dev/null
@@@ -1,358 -1,0 +1,360 @@@
 +//! Various extensions traits for Chalk types.
 +
 +use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy};
 +use hir_def::{
 +    builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
 +    generics::TypeOrConstParamData,
 +    type_ref::Rawness,
 +    FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
 +};
 +use syntax::SmolStr;
 +
 +use crate::{
 +    db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
 +    from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
 +    CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
 +    Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
 +};
 +
 +pub trait TyExt {
 +    fn is_unit(&self) -> bool;
 +    fn is_never(&self) -> bool;
 +    fn is_unknown(&self) -> bool;
 +    fn is_ty_var(&self) -> bool;
 +
 +    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
 +    fn as_builtin(&self) -> Option<BuiltinType>;
 +    fn as_tuple(&self) -> Option<&Substitution>;
 +    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
 +    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
 +    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
 +    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
 +
 +    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
 +    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
 +
 +    fn strip_references(&self) -> &Ty;
 +    fn strip_reference(&self) -> &Ty;
 +
 +    /// If this is a `dyn Trait`, returns that trait.
 +    fn dyn_trait(&self) -> Option<TraitId>;
 +
 +    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
 +    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
 +
 +    /// FIXME: Get rid of this, it's not a good abstraction
 +    fn equals_ctor(&self, other: &Ty) -> bool;
 +}
 +
 +impl TyExt for Ty {
 +    fn is_unit(&self) -> bool {
 +        matches!(self.kind(Interner), TyKind::Tuple(0, _))
 +    }
 +
 +    fn is_never(&self) -> bool {
 +        matches!(self.kind(Interner), TyKind::Never)
 +    }
 +
 +    fn is_unknown(&self) -> bool {
 +        matches!(self.kind(Interner), TyKind::Error)
 +    }
 +
 +    fn is_ty_var(&self) -> bool {
 +        matches!(self.kind(Interner), TyKind::InferenceVar(_, _))
 +    }
 +
 +    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
 +        match self.kind(Interner) {
 +            TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
 +            _ => None,
 +        }
 +    }
 +
 +    fn as_builtin(&self) -> Option<BuiltinType> {
 +        match self.kind(Interner) {
 +            TyKind::Str => Some(BuiltinType::Str),
 +            TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool),
 +            TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
 +            TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
 +                FloatTy::F64 => BuiltinFloat::F64,
 +                FloatTy::F32 => BuiltinFloat::F32,
 +            })),
 +            TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
 +                IntTy::Isize => BuiltinInt::Isize,
 +                IntTy::I8 => BuiltinInt::I8,
 +                IntTy::I16 => BuiltinInt::I16,
 +                IntTy::I32 => BuiltinInt::I32,
 +                IntTy::I64 => BuiltinInt::I64,
 +                IntTy::I128 => BuiltinInt::I128,
 +            })),
 +            TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity {
 +                UintTy::Usize => BuiltinUint::Usize,
 +                UintTy::U8 => BuiltinUint::U8,
 +                UintTy::U16 => BuiltinUint::U16,
 +                UintTy::U32 => BuiltinUint::U32,
 +                UintTy::U64 => BuiltinUint::U64,
 +                UintTy::U128 => BuiltinUint::U128,
 +            })),
 +            _ => None,
 +        }
 +    }
 +
 +    fn as_tuple(&self) -> Option<&Substitution> {
 +        match self.kind(Interner) {
 +            TyKind::Tuple(_, substs) => Some(substs),
 +            _ => None,
 +        }
 +    }
 +
 +    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
 +        match self.callable_def(db) {
 +            Some(CallableDefId::FunctionId(func)) => Some(func),
 +            Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
 +        }
 +    }
 +    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
 +        match self.kind(Interner) {
 +            TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
 +            _ => None,
 +        }
 +    }
 +
 +    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
 +        match self.kind(Interner) {
 +            TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
 +            TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
 +            _ => None,
 +        }
 +    }
 +
 +    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
 +        match *self.kind(Interner) {
 +            TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
 +            TyKind::FnDef(callable, ..) => {
 +                Some(db.lookup_intern_callable_def(callable.into()).into())
 +            }
 +            TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
 +            TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
 +            _ => None,
 +        }
 +    }
 +
 +    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
 +        match self.kind(Interner) {
 +            &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
 +            _ => None,
 +        }
 +    }
 +
 +    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
 +        match self.kind(Interner) {
 +            TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
 +            TyKind::FnDef(def, parameters) => {
 +                let callable_def = db.lookup_intern_callable_def((*def).into());
 +                let sig = db.callable_item_signature(callable_def);
 +                Some(sig.substitute(Interner, &parameters))
 +            }
 +            TyKind::Closure(.., substs) => {
 +                let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
 +                sig_param.callable_sig(db)
 +            }
 +            _ => None,
 +        }
 +    }
 +
 +    fn dyn_trait(&self) -> Option<TraitId> {
 +        let trait_ref = match self.kind(Interner) {
++            // The principal trait bound should be the first element of the bounds. This is an
++            // invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
 +            TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
 +                match b.skip_binders() {
 +                    WhereClause::Implemented(trait_ref) => Some(trait_ref),
 +                    _ => None,
 +                }
 +            }),
 +            _ => None,
 +        }?;
 +        Some(from_chalk_trait_id(trait_ref.trait_id))
 +    }
 +
 +    fn strip_references(&self) -> &Ty {
 +        let mut t: &Ty = self;
 +        while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) {
 +            t = ty;
 +        }
 +        t
 +    }
 +
 +    fn strip_reference(&self) -> &Ty {
 +        self.as_reference().map_or(self, |(ty, _, _)| ty)
 +    }
 +
 +    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
 +        match self.kind(Interner) {
 +            TyKind::OpaqueType(opaque_ty_id, subst) => {
 +                match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
 +                    ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
 +                        let krate = def.module(db.upcast()).krate();
 +                        if let Some(future_trait) = db
 +                            .lang_item(krate, SmolStr::new_inline("future_trait"))
 +                            .and_then(|item| item.as_trait())
 +                        {
 +                            // This is only used by type walking.
 +                            // Parameters will be walked outside, and projection predicate is not used.
 +                            // So just provide the Future trait.
 +                            let impl_bound = Binders::empty(
 +                                Interner,
 +                                WhereClause::Implemented(TraitRef {
 +                                    trait_id: to_chalk_trait_id(future_trait),
 +                                    substitution: Substitution::empty(Interner),
 +                                }),
 +                            );
 +                            Some(vec![impl_bound])
 +                        } else {
 +                            None
 +                        }
 +                    }
 +                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
 +                        db.return_type_impl_traits(func).map(|it| {
 +                            let data = (*it)
 +                                .as_ref()
 +                                .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
 +                            data.substitute(Interner, &subst).into_value_and_skipped_binders().0
 +                        })
 +                    }
 +                }
 +            }
 +            TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
 +                let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
 +                {
 +                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
 +                        db.return_type_impl_traits(func).map(|it| {
 +                            let data = (*it)
 +                                .as_ref()
 +                                .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
 +                            data.substitute(Interner, &opaque_ty.substitution)
 +                        })
 +                    }
 +                    // It always has an parameter for Future::Output type.
 +                    ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
 +                };
 +
 +                predicates.map(|it| it.into_value_and_skipped_binders().0)
 +            }
 +            TyKind::Placeholder(idx) => {
 +                let id = from_placeholder_idx(db, *idx);
 +                let generic_params = db.generic_params(id.parent);
 +                let param_data = &generic_params.type_or_consts[id.local_id];
 +                match param_data {
 +                    TypeOrConstParamData::TypeParamData(p) => match p.provenance {
 +                        hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
 +                            let substs = TyBuilder::placeholder_subst(db, id.parent);
 +                            let predicates = 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(Interner) == self,
 +                                    _ => false,
 +                                })
 +                                .collect::<Vec<_>>();
 +
 +                            Some(predicates)
 +                        }
 +                        _ => None,
 +                    },
 +                    _ => None,
 +                }
 +            }
 +            _ => None,
 +        }
 +    }
 +
 +    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
 +        match self.kind(Interner) {
 +            TyKind::AssociatedType(id, ..) => {
 +                match from_assoc_type_id(*id).lookup(db.upcast()).container {
 +                    ItemContainerId::TraitId(trait_id) => Some(trait_id),
 +                    _ => None,
 +                }
 +            }
 +            TyKind::Alias(AliasTy::Projection(projection_ty)) => {
 +                match from_assoc_type_id(projection_ty.associated_ty_id)
 +                    .lookup(db.upcast())
 +                    .container
 +                {
 +                    ItemContainerId::TraitId(trait_id) => Some(trait_id),
 +                    _ => None,
 +                }
 +            }
 +            _ => None,
 +        }
 +    }
 +
 +    fn equals_ctor(&self, other: &Ty) -> bool {
 +        match (self.kind(Interner), other.kind(Interner)) {
 +            (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
 +            (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => {
 +                true
 +            }
 +            (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
 +            (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
 +            (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
 +                ty_id == ty_id2
 +            }
 +            (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
 +            (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
 +            (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
 +            | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
 +                mutability == mutability2
 +            }
 +            (
 +                TyKind::Function(FnPointer { num_binders, sig, .. }),
 +                TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }),
 +            ) => num_binders == num_binders2 && sig == sig2,
 +            (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
 +                cardinality == cardinality2
 +            }
 +            (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
 +            (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
 +            _ => false,
 +        }
 +    }
 +}
 +
 +pub trait ProjectionTyExt {
 +    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
 +    fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
 +}
 +
 +impl ProjectionTyExt for ProjectionTy {
 +    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
 +        TraitRef {
 +            trait_id: to_chalk_trait_id(self.trait_(db)),
 +            substitution: self.substitution.clone(),
 +        }
 +    }
 +
 +    fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
 +        match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
 +            ItemContainerId::TraitId(it) => it,
 +            _ => panic!("projection ty without parent trait"),
 +        }
 +    }
 +}
 +
 +pub trait TraitRefExt {
 +    fn hir_trait_id(&self) -> TraitId;
 +}
 +
 +impl TraitRefExt for TraitRef {
 +    fn hir_trait_id(&self) -> TraitId {
 +        from_chalk_trait_id(self.trait_id)
 +    }
 +}
index 4a37a7945330cc2a216a3eb6582ca7bbd0db0287,0000000000000000000000000000000000000000..532544fee595c51eee1680627a70cbfe327a7a01
mode 100644,000000..100644
--- /dev/null
@@@ -1,1813 -1,0 +1,1842 @@@
-             let bounds =
-                 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false));
 +//! 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),
 +            TypeRef::Fn(params, is_varargs) => {
 +                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
 +                    sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
 +                    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 + parent_params + 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 handle type parameters on the segment
 +                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
 +                                    associated_ty_id: to_assoc_type_id(associated_ty),
 +                                    substitution: trait_ref.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 generics = generics(self.db.upcast(), impl_id.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.impl_self_ty(impl_id).substitute(Interner, &substs)
 +            }
 +            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 {
 +                    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(),
 +                                self.resolver
 +                                    .generic_def()
 +                                    .expect("there should be generics if there's a generic param"),
 +                            );
 +                            let s = generics.placeholder_subst(self.db);
 +                            s.apply(t.substitution.clone(), Interner)
 +                        }
 +                        ParamLoweringMode::Variable => t.substitution.clone(),
 +                    };
 +                    // 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);
 +                    // FIXME handle type parameters on the segment
 +                    Some(
 +                        TyKind::Alias(AliasTy::Projection(ProjectionTy {
 +                            associated_ty_id: to_assoc_type_id(associated_ty),
 +                            substitution: substs,
 +                        }))
 +                        .intern(Interner),
 +                    )
 +                } else {
 +                    None
 +                }
 +            },
 +        );
 +
 +        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_generic: Option<GenericDefId>,
 +        infer_args: bool,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> Substitution {
 +        let mut substs = Vec::new();
 +        let def_generics = if let Some(def) = def_generic {
 +            generics(self.db.upcast(), def)
 +        } else {
 +            return Substitution::empty(Interner);
 +        };
 +        let (parent_params, self_params, type_params, const_params, impl_trait_params) =
 +            def_generics.provenance_split();
 +        let total_len =
 +            parent_params + self_params + type_params + const_params + impl_trait_params;
 +
 +        let ty_error = GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner);
 +
 +        let mut def_generic_iter = def_generics.iter_id();
 +
 +        for _ in 0..parent_params {
 +            if let Some(eid) = def_generic_iter.next() {
 +                match eid {
 +                    Either::Left(_) => substs.push(ty_error.clone()),
 +                    Either::Right(x) => {
 +                        substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
 +                    }
 +                }
 +            }
 +        }
 +
 +        let fill_self_params = || {
 +            for x in explicit_self_ty
 +                .into_iter()
 +                .map(|x| GenericArgData::Ty(x).intern(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();
 +        }
 +
 +        // handle defaults. In expression or pattern path segments without
 +        // explicitly specified type arguments, missing type arguments are inferred
 +        // (i.e. defaults aren't used).
 +        if !infer_args || had_explicit_args {
 +            if let Some(def_generic) = def_generic {
 +                let defaults = self.db.generic_defaults(def_generic);
 +                assert_eq!(total_len, defaults.len());
 +
 +                for default_ty in defaults.iter().skip(substs.len()) {
 +                    // each default can depend on the previous parameters
 +                    let substs_so_far = Substitution::from_iter(Interner, substs.clone());
 +                    if let Some(_id) = def_generic_iter.next() {
 +                        substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
 +                    }
 +                }
 +            }
 +        }
 +
 +        // add placeholders for args that were not provided
 +        // FIXME: emit diagnostics in contexts where this is not allowed
 +        for eid in def_generic_iter {
 +            match eid {
 +                Either::Left(_) => substs.push(ty_error.clone()),
 +                Either::Right(x) => {
 +                    substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
 +                }
 +            }
 +        }
 +        // If this assert fails, it means you pushed into subst but didn't call .next() of def_generic_iter
 +        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,
 +                };
 +                let projection_ty = ProjectionTy {
 +                    associated_ty_id: to_assoc_type_id(associated_ty),
 +                    substitution: super_trait_ref.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 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.
++        // This invariant is used by `TyExt::dyn_trait()` and chalk.
 +        let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-             let mut auto_traits = SmallVec::<[_; 8]>::new();
-             let mut regular_traits = SmallVec::<[_; 2]>::new();
-             let mut other_bounds = SmallVec::<[_; 8]>::new();
-             for bound in bounds {
-                 if let Some(id) = bound.trait_id() {
-                     if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto {
-                         auto_traits.push(bound);
-                     } else {
-                         regular_traits.push(bound);
++            let mut bounds: Vec<_> = bounds
++                .iter()
++                .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
++                .collect();
 +
-                 } else {
-                     other_bounds.push(bound);
++            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 regular_traits.len() > 1 {
++            });
 +
-             auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap());
-             auto_traits.dedup();
++            if multiple_regular_traits || multiple_same_projection {
 +                return None;
 +            }
 +
-             Some(QuantifiedWhereClauses::from_iter(
-                 Interner,
-                 regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
-             ))
++            // 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();
 +
-             // FIXME: report error (additional non-auto traits)
++            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 or associated type rebound)
 +            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) => search(
 +            // we're _in_ the impl -- the binders get added back later. Correct,
 +            // but it would be nice to make this more explicit
 +            db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
 +        ),
 +        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 generics = generics(db.upcast(), trait_id.into());
 +                if generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
 +                    let trait_ref = TyBuilder::trait_ref(db, trait_id)
 +                        .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
 +                        .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 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 crate::make_binders_with_count(db, idx, &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. This is forbidden (FIXME: report
 +            // diagnostic)
 +            ty = fallback_bound_vars(ty, idx);
 +            let val = GenericArgData::Ty(ty).intern(Interner);
 +            crate::make_binders_with_count(db, idx, &generic_params, val)
 +        })
 +        .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()
 +        .enumerate()
 +        .map(|(count, 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_with_count(db, count, &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());
 +    let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs());
 +    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))
 +}
 +
 +/// 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))
 +}
 +
 +/// 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);
 +
 +/// 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))
 +        }
 +    }
 +}
 +
 +/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
 +/// num_vars_to_keep) by `TyKind::Unknown`.
 +fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
 +    s: T,
 +    num_vars_to_keep: usize,
 +) -> T {
 +    crate::fold_free_vars(
 +        s,
 +        |bound, binders| {
 +            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
 +                TyKind::Error.intern(Interner)
 +            } else {
 +                bound.shifted_in_from(binders).to_ty(Interner)
 +            }
 +        },
 +        |ty, bound, binders| {
 +            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
 +                unknown_const(ty.clone())
 +            } else {
 +                bound.shifted_in_from(binders).to_const(Interner, ty)
 +            }
 +        },
 +    )
 +}
index e67c27aa2db97fb4cff6fd19475fa448aa422cc9,0000000000000000000000000000000000000000..21a86319763fc1d78212c1aef02e9f9e22878918
mode 100644,000000..100644
--- /dev/null
@@@ -1,3927 -1,0 +1,3955 @@@
 +use cov_mark::check;
 +use expect_test::expect;
 +
 +use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
 +
 +#[test]
 +fn infer_await() {
 +    check_types(
 +        r#"
 +//- minicore: future
 +struct IntFuture;
 +
 +impl core::future::Future for IntFuture {
 +    type Output = u64;
 +}
 +
 +fn test() {
 +    let r = IntFuture;
 +    let v = r.await;
 +    v;
 +} //^ u64
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_async() {
 +    check_types(
 +        r#"
 +//- minicore: future
 +async fn foo() -> u64 { 128 }
 +
 +fn test() {
 +    let r = foo();
 +    let v = r.await;
 +    v;
 +} //^ u64
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_desugar_async() {
 +    check_types(
 +        r#"
 +//- minicore: future, sized
 +async fn foo() -> u64 { 128 }
 +
 +fn test() {
 +    let r = foo();
 +    r;
 +} //^ impl Future<Output = u64>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_async_block() {
 +    check_types(
 +        r#"
 +//- minicore: future, option
 +async fn test() {
 +    let a = async { 42 };
 +    a;
 +//  ^ impl Future<Output = i32>
 +    let x = a.await;
 +    x;
 +//  ^ i32
 +    let b = async {}.await;
 +    b;
 +//  ^ ()
 +    let c = async {
 +        let y = None;
 +        y
 +    //  ^ Option<u64>
 +    };
 +    let _: Option<u64> = c.await;
 +    c;
 +//  ^ impl Future<Output = Option<u64>>
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn auto_sized_async_block() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: future, sized
 +
 +use core::future::Future;
 +struct MyFut<Fut>(Fut);
 +
 +impl<Fut> Future for MyFut<Fut>
 +where Fut: Future
 +{
 +    type Output = Fut::Output;
 +}
 +async fn reproduction() -> usize {
 +    let f = async {999usize};
 +    MyFut(f).await
 +}
 +    "#,
 +    );
 +    check_no_mismatches(
 +        r#"
 +//- minicore: future
 +//#11815
 +#[lang = "sized"]
 +pub trait Sized {}
 +
 +#[lang = "unsize"]
 +pub trait Unsize<T: ?Sized> {}
 +
 +#[lang = "coerce_unsized"]
 +pub trait CoerceUnsized<T> {}
 +
 +pub unsafe trait Allocator {}
 +
 +pub struct Global;
 +unsafe impl Allocator for Global {}
 +
 +#[lang = "owned_box"]
 +#[fundamental]
 +pub struct Box<T: ?Sized, A: Allocator = Global>;
 +
 +impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
 +
 +fn send() ->  Box<dyn Future<Output = ()> + Send + 'static>{
 +    box async move {}
 +}
 +
 +fn not_send() -> Box<dyn Future<Output = ()> + 'static> {
 +    box async move {}
 +}
 +    "#,
 +    );
 +}
 +
 +#[test]
 +fn into_future_trait() {
 +    check_types(
 +        r#"
 +//- minicore: future
 +struct Futurable;
 +impl core::future::IntoFuture for Futurable {
 +    type Output = u64;
 +    type IntoFuture = IntFuture;
 +}
 +
 +struct IntFuture;
 +impl core::future::Future for IntFuture {
 +    type Output = u64;
 +}
 +
 +fn test() {
 +    let r = Futurable;
 +    let v = r.await;
 +    v;
 +} //^ u64
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_try() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:core
 +fn test() {
 +    let r: Result<i32, u64> = Result::Ok(1);
 +    let v = r?;
 +    v;
 +} //^ i32
 +
 +//- /core.rs crate:core
 +pub mod ops {
 +    pub trait Try {
 +        type Ok;
 +        type Error;
 +    }
 +}
 +
 +pub mod result {
 +    pub enum Result<O, E> {
 +        Ok(O),
 +        Err(E)
 +    }
 +
 +    impl<O, E> crate::ops::Try for Result<O, E> {
 +        type Ok = O;
 +        type Error = E;
 +    }
 +}
 +
 +pub mod prelude {
 +    pub mod rust_2018 {
 +        pub use crate::{result::*, ops::*};
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_try_trait_v2() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:core
 +fn test() {
 +    let r: Result<i32, u64> = Result::Ok(1);
 +    let v = r?;
 +    v;
 +} //^ i32
 +
 +//- /core.rs crate:core
 +mod ops {
 +    mod try_trait {
 +        pub trait Try: FromResidual {
 +            type Output;
 +            type Residual;
 +        }
 +        pub trait FromResidual<R = <Self as Try>::Residual> {}
 +    }
 +
 +    pub use self::try_trait::FromResidual;
 +    pub use self::try_trait::Try;
 +}
 +
 +mod convert {
 +    pub trait From<T> {}
 +    impl<T> From<T> for T {}
 +}
 +
 +pub mod result {
 +    use crate::convert::From;
 +    use crate::ops::{Try, FromResidual};
 +
 +    pub enum Infallible {}
 +    pub enum Result<O, E> {
 +        Ok(O),
 +        Err(E)
 +    }
 +
 +    impl<O, E> Try for Result<O, E> {
 +        type Output = O;
 +        type Error = Result<Infallible, E>;
 +    }
 +
 +    impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
 +}
 +
 +pub mod prelude {
 +    pub mod rust_2018 {
 +        pub use crate::result::*;
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_for_loop() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:core,alloc
 +#![no_std]
 +use alloc::collections::Vec;
 +
 +fn test() {
 +    let v = Vec::new();
 +    v.push("foo");
 +    for x in v {
 +        x;
 +    } //^ &str
 +}
 +
 +//- /core.rs crate:core
 +pub mod iter {
 +    pub trait IntoIterator {
 +        type Item;
 +    }
 +}
 +pub mod prelude {
 +    pub mod rust_2018 {
 +        pub use crate::iter::*;
 +    }
 +}
 +
 +//- /alloc.rs crate:alloc deps:core
 +#![no_std]
 +pub mod collections {
 +    pub struct Vec<T> {}
 +    impl<T> Vec<T> {
 +        pub fn new() -> Self { Vec {} }
 +        pub fn push(&mut self, t: T) { }
 +    }
 +
 +    impl<T> IntoIterator for Vec<T> {
 +        type Item=T;
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_neg() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:std
 +struct Bar;
 +struct Foo;
 +
 +impl std::ops::Neg for Bar {
 +    type Output = Foo;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = -a;
 +    b;
 +} //^ Foo
 +
 +//- /std.rs crate:std
 +#[prelude_import] use ops::*;
 +mod ops {
 +    #[lang = "neg"]
 +    pub trait Neg {
 +        type Output;
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_not() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:std
 +struct Bar;
 +struct Foo;
 +
 +impl std::ops::Not for Bar {
 +    type Output = Foo;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = !a;
 +    b;
 +} //^ Foo
 +
 +//- /std.rs crate:std
 +#[prelude_import] use ops::*;
 +mod ops {
 +    #[lang = "not"]
 +    pub trait Not {
 +        type Output;
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_from_bound_1() {
 +    check_types(
 +        r#"
 +trait Trait<T> {}
 +struct S<T>(T);
 +impl<U> Trait<U> for S<U> {}
 +fn foo<T: Trait<u32>>(t: T) {}
 +fn test() {
 +    let s = S(unknown);
 +           // ^^^^^^^ u32
 +    foo(s);
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_from_bound_2() {
 +    check_types(
 +        r#"
 +trait Trait<T> {}
 +struct S<T>(T);
 +impl<U> Trait<U> for S<U> {}
 +fn foo<U, T: Trait<U>>(t: T) -> U { loop {} }
 +fn test() {
 +    let s = S(unknown);
 +           // ^^^^^^^ u32
 +    let x: u32 = foo(s);
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn trait_default_method_self_bound_implements_trait() {
 +    cov_mark::check!(trait_self_implements_self);
 +    check(
 +        r#"
 +trait Trait {
 +    fn foo(&self) -> i64;
 +    fn bar(&self) -> () {
 +        self.foo();
 +     // ^^^^^^^^^^ type: i64
 +    }
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn trait_default_method_self_bound_implements_super_trait() {
 +    check(
 +        r#"
 +trait SuperTrait {
 +    fn foo(&self) -> i64;
 +}
 +trait Trait: SuperTrait {
 +    fn bar(&self) -> () {
 +        self.foo();
 +     // ^^^^^^^^^^ type: i64
 +    }
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_project_associated_type() {
 +    check_types(
 +        r#"
 +trait Iterable {
 +    type Item;
 +}
 +struct S;
 +impl Iterable for S { type Item = u32; }
 +fn test<T: Iterable>() {
 +    let x: <S as Iterable>::Item = 1;
 +                                // ^ u32
 +    let y: <T as Iterable>::Item = u;
 +                                // ^ Iterable::Item<T>
 +    let z: T::Item = u;
 +                  // ^ Iterable::Item<T>
 +    let a: <T>::Item = u;
 +                    // ^ Iterable::Item<T>
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_return_associated_type() {
 +    check_types(
 +        r#"
 +trait Iterable {
 +    type Item;
 +}
 +struct S;
 +impl Iterable for S { type Item = u32; }
 +fn foo1<T: Iterable>(t: T) -> T::Item { loop {} }
 +fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item { loop {} }
 +fn foo3<T: Iterable>(t: T) -> <T>::Item { loop {} }
 +fn test() {
 +    foo1(S);
 + // ^^^^^^^ u32
 +    foo2(S);
 + // ^^^^^^^ u32
 +    foo3(S);
 + // ^^^^^^^ u32
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn associated_type_shorthand_from_method_bound() {
 +    check_types(
 +        r#"
 +trait Iterable {
 +    type Item;
 +}
 +struct S<T>;
 +impl<T> S<T> {
 +    fn foo(self) -> T::Item where T: Iterable { loop {} }
 +}
 +fn test<T: Iterable>() {
 +    let s: S<T>;
 +    s.foo();
 + // ^^^^^^^ Iterable::Item<T>
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn associated_type_shorthand_from_self_issue_12484() {
 +    check_types(
 +        r#"
 +trait Bar {
 +    type A;
 +}
 +trait Foo {
 +    type A;
 +    fn test(a: Self::A, _: impl Bar) {
 +        a;
 +      //^ Foo::A<Self>
 +    }
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_associated_type_bound() {
 +    check_types(
 +        r#"
 +trait Iterable {
 +    type Item;
 +}
 +fn test<T: Iterable<Item=u32>>() {
 +    let y: T::Item = unknown;
 +                  // ^^^^^^^ u32
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_const_body() {
 +    // FIXME make check_types work with other bodies
 +    check_infer(
 +        r#"
 +const A: u32 = 1 + 1;
 +static B: u64 = { let x = 1; x };
 +"#,
 +        expect![[r#"
 +            15..16 '1': u32
 +            15..20 '1 + 1': u32
 +            19..20 '1': u32
 +            38..54 '{ let ...1; x }': u64
 +            44..45 'x': u64
 +            48..49 '1': u64
 +            51..52 'x': u64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn tuple_struct_fields() {
 +    check_infer(
 +        r#"
 +struct S(i32, u64);
 +fn test() -> u64 {
 +    let a = S(4, 6);
 +    let b = a.0;
 +    a.1
 +}"#,
 +        expect![[r#"
 +            37..86 '{     ... a.1 }': u64
 +            47..48 'a': S
 +            51..52 'S': S(i32, u64) -> S
 +            51..58 'S(4, 6)': S
 +            53..54 '4': i32
 +            56..57 '6': u64
 +            68..69 'b': i32
 +            72..73 'a': S
 +            72..75 'a.0': i32
 +            81..82 'a': S
 +            81..84 'a.1': u64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn tuple_struct_with_fn() {
 +    check_infer(
 +        r#"
 +struct S(fn(u32) -> u64);
 +fn test() -> u64 {
 +    let a = S(|i| 2*i);
 +    let b = a.0(4);
 +    a.0(2)
 +}"#,
 +        expect![[r#"
 +            43..101 '{     ...0(2) }': u64
 +            53..54 'a': S
 +            57..58 'S': S(fn(u32) -> u64) -> S
 +            57..67 'S(|i| 2*i)': S
 +            59..66 '|i| 2*i': |u32| -> u64
 +            60..61 'i': u32
 +            63..64 '2': u32
 +            63..66 '2*i': u32
 +            65..66 'i': u32
 +            77..78 'b': u64
 +            81..82 'a': S
 +            81..84 'a.0': fn(u32) -> u64
 +            81..87 'a.0(4)': u64
 +            85..86 '4': u32
 +            93..94 'a': S
 +            93..96 'a.0': fn(u32) -> u64
 +            93..99 'a.0(2)': u64
 +            97..98 '2': u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn indexing_arrays() {
 +    check_infer(
 +        "fn main() { &mut [9][2]; }",
 +        expect![[r#"
 +            10..26 '{ &mut...[2]; }': ()
 +            12..23 '&mut [9][2]': &mut {unknown}
 +            17..20 '[9]': [i32; 1]
 +            17..23 '[9][2]': {unknown}
 +            18..19 '9': i32
 +            21..22 '2': i32
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn infer_ops_index() {
 +    check_types(
 +        r#"
 +//- minicore: index
 +struct Bar;
 +struct Foo;
 +
 +impl core::ops::Index<u32> for Bar {
 +    type Output = Foo;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = a[1u32];
 +    b;
 +} //^ Foo
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_index_field() {
 +    check_types(
 +        r#"
 +//- minicore: index
 +struct Bar;
 +struct Foo {
 +    field: u32;
 +}
 +
 +impl core::ops::Index<u32> for Bar {
 +    type Output = Foo;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = a[1u32].field;
 +    b;
 +} //^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_index_field_autoderef() {
 +    check_types(
 +        r#"
 +//- minicore: index
 +struct Bar;
 +struct Foo {
 +    field: u32;
 +}
 +
 +impl core::ops::Index<u32> for Bar {
 +    type Output = Foo;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = (&a[1u32]).field;
 +    b;
 +} //^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_index_int() {
 +    check_types(
 +        r#"
 +//- minicore: index
 +struct Bar;
 +struct Foo;
 +
 +impl core::ops::Index<u32> for Bar {
 +    type Output = Foo;
 +}
 +
 +struct Range;
 +impl core::ops::Index<Range> for Bar {
 +    type Output = Bar;
 +}
 +
 +fn test() {
 +    let a = Bar;
 +    let b = a[1];
 +    b;
 +  //^ Foo
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_ops_index_autoderef() {
 +    check_types(
 +        r#"
 +//- minicore: index, slice
 +fn test() {
 +    let a = &[1u32, 2, 3];
 +    let b = a[1];
 +    b;
 +} //^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn deref_trait() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct Arc<T: ?Sized>;
 +impl<T: ?Sized> core::ops::Deref for Arc<T> {
 +    type Target = T;
 +}
 +
 +struct S;
 +impl S {
 +    fn foo(&self) -> u128 { 0 }
 +}
 +
 +fn test(s: Arc<S>) {
 +    (*s, s.foo());
 +} //^^^^^^^^^^^^^ (S, u128)
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn deref_trait_with_inference_var() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct Arc<T: ?Sized>;
 +fn new_arc<T: ?Sized>() -> Arc<T> { Arc }
 +impl<T: ?Sized> core::ops::Deref for Arc<T> {
 +    type Target = T;
 +}
 +
 +struct S;
 +fn foo(a: Arc<S>) {}
 +
 +fn test() {
 +    let a = new_arc();
 +    let b = *a;
 +          //^^ S
 +    foo(a);
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn deref_trait_infinite_recursion() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct S;
 +
 +impl core::ops::Deref for S {
 +    type Target = S;
 +}
 +
 +fn test(s: S) {
 +    s.foo();
 +} //^^^^^^^ {unknown}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn deref_trait_with_question_mark_size() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct Arc<T: ?Sized>;
 +impl<T: ?Sized> core::ops::Deref for Arc<T> {
 +    type Target = T;
 +}
 +
 +struct S;
 +impl S {
 +    fn foo(&self) -> u128 { 0 }
 +}
 +
 +fn test(s: Arc<S>) {
 +    (*s, s.foo());
 +} //^^^^^^^^^^^^^ (S, u128)
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn deref_trait_with_implicit_sized_requirement_on_inference_var() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct Foo<T>;
 +impl<T> core::ops::Deref for Foo<T> {
 +    type Target = ();
 +}
 +fn test() {
 +    let foo = Foo;
 +    *foo;
 +  //^^^^ ()
 +    let _: Foo<u8> = foo;
 +}
 +"#,
 +    )
 +}
 +
 +#[test]
 +fn obligation_from_function_clause() {
 +    check_types(
 +        r#"
 +struct S;
 +
 +trait Trait<T> {}
 +impl Trait<u32> for S {}
 +
 +fn foo<T: Trait<U>, U>(t: T) -> U { loop {} }
 +
 +fn test(s: S) {
 +    foo(s);
 +} //^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn obligation_from_method_clause() {
 +    check_types(
 +        r#"
 +//- /main.rs
 +struct S;
 +
 +trait Trait<T> {}
 +impl Trait<isize> for S {}
 +
 +struct O;
 +impl O {
 +    fn foo<T: Trait<U>, U>(&self, t: T) -> U { loop {} }
 +}
 +
 +fn test() {
 +    O.foo(S);
 +} //^^^^^^^^ isize
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn obligation_from_self_method_clause() {
 +    check_types(
 +        r#"
 +struct S;
 +
 +trait Trait<T> {}
 +impl Trait<i64> for S {}
 +
 +impl S {
 +    fn foo<U>(&self) -> U where Self: Trait<U> { loop {} }
 +}
 +
 +fn test() {
 +    S.foo();
 +} //^^^^^^^ i64
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn obligation_from_impl_clause() {
 +    check_types(
 +        r#"
 +struct S;
 +
 +trait Trait<T> {}
 +impl Trait<&str> for S {}
 +
 +struct O<T>;
 +impl<U, T: Trait<U>> O<T> {
 +    fn foo(&self) -> U { loop {} }
 +}
 +
 +fn test(o: O<S>) {
 +    o.foo();
 +} //^^^^^^^ &str
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn generic_param_env_1() {
 +    check_types(
 +        r#"
 +trait Clone {}
 +trait Trait { fn foo(self) -> u128; }
 +struct S;
 +impl Clone for S {}
 +impl<T> Trait for T where T: Clone {}
 +fn test<T: Clone>(t: T) { t.foo(); }
 +                        //^^^^^^^ u128
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn generic_param_env_1_not_met() {
 +    check_types(
 +        r#"
 +//- /main.rs
 +trait Clone {}
 +trait Trait { fn foo(self) -> u128; }
 +struct S;
 +impl Clone for S {}
 +impl<T> Trait for T where T: Clone {}
 +fn test<T>(t: T) { t.foo(); }
 +                 //^^^^^^^ {unknown}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn generic_param_env_2() {
 +    check_types(
 +        r#"
 +trait Trait { fn foo(self) -> u128; }
 +struct S;
 +impl Trait for S {}
 +fn test<T: Trait>(t: T) { t.foo(); }
 +                        //^^^^^^^ u128
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn generic_param_env_2_not_met() {
 +    check_types(
 +        r#"
 +trait Trait { fn foo(self) -> u128; }
 +struct S;
 +impl Trait for S {}
 +fn test<T>(t: T) { t.foo(); }
 +                 //^^^^^^^ {unknown}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn generic_param_env_deref() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +trait Trait {}
 +impl<T> core::ops::Deref for T where T: Trait {
 +    type Target = i128;
 +}
 +fn test<T: Trait>(t: T) { *t; }
 +                        //^^ i128
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn associated_type_placeholder() {
 +    // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
 +    check_types(
 +        r#"
 +pub trait ApplyL {
 +    type Out;
 +}
 +
 +pub struct RefMutL<T>;
 +
 +impl<T> ApplyL for RefMutL<T> {
 +    type Out = <T as ApplyL>::Out;
 +}
 +
 +fn test<T: ApplyL>() {
 +    let y: <RefMutL<T> as ApplyL>::Out = no_matter;
 +    y;
 +} //^ ApplyL::Out<T>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn associated_type_placeholder_2() {
 +    check_types(
 +        r#"
 +pub trait ApplyL {
 +    type Out;
 +}
 +fn foo<T: ApplyL>(t: T) -> <T as ApplyL>::Out;
 +
 +fn test<T: ApplyL>(t: T) {
 +    let y = foo(t);
 +    y;
 +} //^ ApplyL::Out<T>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn argument_impl_trait() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +    fn foo2(&self) -> i64;
 +}
 +fn bar(x: impl Trait<u16>) {}
 +struct S<T>(T);
 +impl<T> Trait<T> for S<T> {}
 +
 +fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
 +    x;
 +    y;
 +    let z = S(1);
 +    bar(z);
 +    x.foo();
 +    y.foo();
 +    z.foo();
 +    x.foo2();
 +    y.foo2();
 +    z.foo2();
 +}"#,
 +        expect![[r#"
 +            29..33 'self': &Self
 +            54..58 'self': &Self
 +            77..78 'x': impl Trait<u16>
 +            97..99 '{}': ()
 +            154..155 'x': impl Trait<u64>
 +            174..175 'y': &impl Trait<u32>
 +            195..323 '{     ...2(); }': ()
 +            201..202 'x': impl Trait<u64>
 +            208..209 'y': &impl Trait<u32>
 +            219..220 'z': S<u16>
 +            223..224 'S': S<u16>(u16) -> S<u16>
 +            223..227 'S(1)': S<u16>
 +            225..226 '1': u16
 +            233..236 'bar': fn bar(S<u16>)
 +            233..239 'bar(z)': ()
 +            237..238 'z': S<u16>
 +            245..246 'x': impl Trait<u64>
 +            245..252 'x.foo()': u64
 +            258..259 'y': &impl Trait<u32>
 +            258..265 'y.foo()': u32
 +            271..272 'z': S<u16>
 +            271..278 'z.foo()': u16
 +            284..285 'x': impl Trait<u64>
 +            284..292 'x.foo2()': i64
 +            298..299 'y': &impl Trait<u32>
 +            298..306 'y.foo2()': i64
 +            312..313 'z': S<u16>
 +            312..320 'z.foo2()': i64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn argument_impl_trait_type_args_1() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +trait Foo {
 +    // this function has an implicit Self param, an explicit type param,
 +    // and an implicit impl Trait param!
 +    fn bar<T>(x: impl Trait) -> T { loop {} }
 +}
 +fn foo<T>(x: impl Trait) -> T { loop {} }
 +struct S;
 +impl Trait for S {}
 +struct F;
 +impl Foo for F {}
 +
 +fn test() {
 +    Foo::bar(S);
 +    <F as Foo>::bar(S);
 +    F::bar(S);
 +    Foo::bar::<u32>(S);
 +    <F as Foo>::bar::<u32>(S);
 +
 +    foo(S);
 +    foo::<u32>(S);
 +    foo::<u32, i32>(S); // we should ignore the extraneous i32
 +}"#,
 +        expect![[r#"
 +            155..156 'x': impl Trait
 +            175..186 '{ loop {} }': T
 +            177..184 'loop {}': !
 +            182..184 '{}': ()
 +            199..200 'x': impl Trait
 +            219..230 '{ loop {} }': T
 +            221..228 'loop {}': !
 +            226..228 '{}': ()
 +            300..509 '{     ... i32 }': ()
 +            306..314 'Foo::bar': fn bar<{unknown}, {unknown}>(S) -> {unknown}
 +            306..317 'Foo::bar(S)': {unknown}
 +            315..316 'S': S
 +            323..338 '<F as Foo>::bar': fn bar<F, {unknown}>(S) -> {unknown}
 +            323..341 '<F as ...bar(S)': {unknown}
 +            339..340 'S': S
 +            347..353 'F::bar': fn bar<F, {unknown}>(S) -> {unknown}
 +            347..356 'F::bar(S)': {unknown}
 +            354..355 'S': S
 +            362..377 'Foo::bar::<u32>': fn bar<{unknown}, u32>(S) -> u32
 +            362..380 'Foo::b...32>(S)': u32
 +            378..379 'S': S
 +            386..408 '<F as ...:<u32>': fn bar<F, u32>(S) -> u32
 +            386..411 '<F as ...32>(S)': u32
 +            409..410 'S': S
 +            418..421 'foo': fn foo<{unknown}>(S) -> {unknown}
 +            418..424 'foo(S)': {unknown}
 +            422..423 'S': S
 +            430..440 'foo::<u32>': fn foo<u32>(S) -> u32
 +            430..443 'foo::<u32>(S)': u32
 +            441..442 'S': S
 +            449..464 'foo::<u32, i32>': fn foo<u32>(S) -> u32
 +            449..467 'foo::<...32>(S)': u32
 +            465..466 'S': S
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn argument_impl_trait_type_args_2() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +struct S;
 +impl Trait for S {}
 +struct F<T>;
 +impl<T> F<T> {
 +    fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} }
 +}
 +
 +fn test() {
 +    F.foo(S);
 +    F::<u32>.foo(S);
 +    F::<u32>.foo::<i32>(S);
 +    F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored
 +}"#,
 +        expect![[r#"
 +            87..91 'self': F<T>
 +            93..94 'x': impl Trait
 +            118..129 '{ loop {} }': (T, U)
 +            120..127 'loop {}': !
 +            125..127 '{}': ()
 +            143..283 '{     ...ored }': ()
 +            149..150 'F': F<{unknown}>
 +            149..157 'F.foo(S)': ({unknown}, {unknown})
 +            155..156 'S': S
 +            163..171 'F::<u32>': F<u32>
 +            163..178 'F::<u32>.foo(S)': (u32, {unknown})
 +            176..177 'S': S
 +            184..192 'F::<u32>': F<u32>
 +            184..206 'F::<u3...32>(S)': (u32, i32)
 +            204..205 'S': S
 +            212..220 'F::<u32>': F<u32>
 +            212..239 'F::<u3...32>(S)': (u32, i32)
 +            237..238 'S': S
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn argument_impl_trait_to_fn_pointer() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +fn foo(x: impl Trait) { loop {} }
 +struct S;
 +impl Trait for S {}
 +
 +fn test() {
 +    let f: fn(S) -> () = foo;
 +}"#,
 +        expect![[r#"
 +            22..23 'x': impl Trait
 +            37..48 '{ loop {} }': ()
 +            39..46 'loop {}': !
 +            44..46 '{}': ()
 +            90..123 '{     ...foo; }': ()
 +            100..101 'f': fn(S)
 +            117..120 'foo': fn foo(S)
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn impl_trait() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +    fn foo2(&self) -> i64;
 +}
 +fn bar() -> impl Trait<u64> {}
 +
 +fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
 +    x;
 +    y;
 +    let z = bar();
 +    x.foo();
 +    y.foo();
 +    z.foo();
 +    x.foo2();
 +    y.foo2();
 +    z.foo2();
 +}"#,
 +        expect![[r#"
 +            29..33 'self': &Self
 +            54..58 'self': &Self
 +            98..100 '{}': ()
 +            110..111 'x': impl Trait<u64>
 +            130..131 'y': &impl Trait<u64>
 +            151..268 '{     ...2(); }': ()
 +            157..158 'x': impl Trait<u64>
 +            164..165 'y': &impl Trait<u64>
 +            175..176 'z': impl Trait<u64>
 +            179..182 'bar': fn bar() -> impl Trait<u64>
 +            179..184 'bar()': impl Trait<u64>
 +            190..191 'x': impl Trait<u64>
 +            190..197 'x.foo()': u64
 +            203..204 'y': &impl Trait<u64>
 +            203..210 'y.foo()': u64
 +            216..217 'z': impl Trait<u64>
 +            216..223 'z.foo()': u64
 +            229..230 'x': impl Trait<u64>
 +            229..237 'x.foo2()': i64
 +            243..244 'y': &impl Trait<u64>
 +            243..251 'y.foo2()': i64
 +            257..258 'z': impl Trait<u64>
 +            257..265 'z.foo2()': i64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn simple_return_pos_impl_trait() {
 +    cov_mark::check!(lower_rpit);
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +}
 +fn bar() -> impl Trait<u64> { loop {} }
 +
 +fn test() {
 +    let a = bar();
 +    a.foo();
 +}"#,
 +        expect![[r#"
 +            29..33 'self': &Self
 +            71..82 '{ loop {} }': !
 +            73..80 'loop {}': !
 +            78..80 '{}': ()
 +            94..129 '{     ...o(); }': ()
 +            104..105 'a': impl Trait<u64>
 +            108..111 'bar': fn bar() -> impl Trait<u64>
 +            108..113 'bar()': impl Trait<u64>
 +            119..120 'a': impl Trait<u64>
 +            119..126 'a.foo()': u64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn more_return_pos_impl_trait() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Iterator {
 +    type Item;
 +    fn next(&mut self) -> Self::Item;
 +}
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +}
 +fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
 +fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
 +
 +fn test() {
 +    let (a, b) = bar();
 +    a.next().foo();
 +    b.foo();
 +    let (c, d) = baz(1u128);
 +    c.next().foo();
 +    d.foo();
 +}"#,
 +        expect![[r#"
 +            49..53 'self': &mut Self
 +            101..105 'self': &Self
 +            184..195 '{ loop {} }': ({unknown}, {unknown})
 +            186..193 'loop {}': !
 +            191..193 '{}': ()
 +            206..207 't': T
 +            268..279 '{ loop {} }': ({unknown}, {unknown})
 +            270..277 'loop {}': !
 +            275..277 '{}': ()
 +            291..413 '{     ...o(); }': ()
 +            301..307 '(a, b)': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
 +            302..303 'a': impl Iterator<Item = impl Trait<u32>>
 +            305..306 'b': impl Trait<u64>
 +            310..313 'bar': fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
 +            310..315 'bar()': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
 +            321..322 'a': impl Iterator<Item = impl Trait<u32>>
 +            321..329 'a.next()': impl Trait<u32>
 +            321..335 'a.next().foo()': u32
 +            341..342 'b': impl Trait<u64>
 +            341..348 'b.foo()': u64
 +            358..364 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
 +            359..360 'c': impl Iterator<Item = impl Trait<u128>>
 +            362..363 'd': impl Trait<u128>
 +            367..370 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
 +            367..377 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
 +            371..376 '1u128': u128
 +            383..384 'c': impl Iterator<Item = impl Trait<u128>>
 +            383..391 'c.next()': impl Trait<u128>
 +            383..397 'c.next().foo()': u128
 +            403..404 'd': impl Trait<u128>
 +            403..410 'd.foo()': u128
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn infer_from_return_pos_impl_trait() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn, sized
 +trait Trait<T> {}
 +struct Bar<T>(T);
 +impl<T> Trait<T> for Bar<T> {}
 +fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
 +    (|input, t| {}, Bar(C))
 +}
 +"#,
 +        expect![[r#"
 +            134..165 '{     ...(C)) }': (|&str, T| -> (), Bar<u8>)
 +            140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
 +            141..154 '|input, t| {}': |&str, T| -> ()
 +            142..147 'input': &str
 +            149..150 't': T
 +            152..154 '{}': ()
 +            156..159 'Bar': Bar<u8>(u8) -> Bar<u8>
 +            156..162 'Bar(C)': Bar<u8>
 +            160..161 'C': u8
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn dyn_trait() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +    fn foo2(&self) -> i64;
 +}
 +fn bar() -> dyn Trait<u64> {}
 +
 +fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
 +    x;
 +    y;
 +    let z = bar();
 +    x.foo();
 +    y.foo();
 +    z.foo();
 +    x.foo2();
 +    y.foo2();
 +    z.foo2();
 +}"#,
 +        expect![[r#"
 +            29..33 'self': &Self
 +            54..58 'self': &Self
 +            97..99 '{}': dyn Trait<u64>
 +            109..110 'x': dyn Trait<u64>
 +            128..129 'y': &dyn Trait<u64>
 +            148..265 '{     ...2(); }': ()
 +            154..155 'x': dyn Trait<u64>
 +            161..162 'y': &dyn Trait<u64>
 +            172..173 'z': dyn Trait<u64>
 +            176..179 'bar': fn bar() -> dyn Trait<u64>
 +            176..181 'bar()': dyn Trait<u64>
 +            187..188 'x': dyn Trait<u64>
 +            187..194 'x.foo()': u64
 +            200..201 'y': &dyn Trait<u64>
 +            200..207 'y.foo()': u64
 +            213..214 'z': dyn Trait<u64>
 +            213..220 'z.foo()': u64
 +            226..227 'x': dyn Trait<u64>
 +            226..234 'x.foo2()': i64
 +            240..241 'y': &dyn Trait<u64>
 +            240..248 'y.foo2()': i64
 +            254..255 'z': dyn Trait<u64>
 +            254..262 'z.foo2()': i64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn dyn_trait_in_impl() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait<T, U> {
 +    fn foo(&self) -> (T, U);
 +}
 +struct S<T, U> {}
 +impl<T, U> S<T, U> {
 +    fn bar(&self) -> &dyn Trait<T, U> { loop {} }
 +}
 +trait Trait2<T, U> {
 +    fn baz(&self) -> (T, U);
 +}
 +impl<T, U> Trait2<T, U> for dyn Trait<T, U> { }
 +
 +fn test(s: S<u32, i32>) {
 +    s.bar().baz();
 +}"#,
 +        expect![[r#"
 +            32..36 'self': &Self
 +            102..106 'self': &S<T, U>
 +            128..139 '{ loop {} }': &dyn Trait<T, U>
 +            130..137 'loop {}': !
 +            135..137 '{}': ()
 +            175..179 'self': &Self
 +            251..252 's': S<u32, i32>
 +            267..289 '{     ...z(); }': ()
 +            273..274 's': S<u32, i32>
 +            273..280 's.bar()': &dyn Trait<u32, i32>
 +            273..286 's.bar().baz()': (u32, i32)
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn dyn_trait_bare() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait {
 +    fn foo(&self) -> u64;
 +}
 +fn bar() -> Trait {}
 +
 +fn test(x: Trait, y: &Trait) -> u64 {
 +    x;
 +    y;
 +    let z = bar();
 +    x.foo();
 +    y.foo();
 +    z.foo();
 +}"#,
 +        expect![[r#"
 +            26..30 'self': &Self
 +            60..62 '{}': dyn Trait
 +            72..73 'x': dyn Trait
 +            82..83 'y': &dyn Trait
 +            100..175 '{     ...o(); }': u64
 +            106..107 'x': dyn Trait
 +            113..114 'y': &dyn Trait
 +            124..125 'z': dyn Trait
 +            128..131 'bar': fn bar() -> dyn Trait
 +            128..133 'bar()': dyn Trait
 +            139..140 'x': dyn Trait
 +            139..146 'x.foo()': u64
 +            152..153 'y': &dyn Trait
 +            152..159 'y.foo()': u64
 +            165..166 'z': dyn Trait
 +            165..172 'z.foo()': u64
 +        "#]],
 +    );
 +
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn, coerce_unsized
 +struct S;
 +impl S {
 +    fn foo(&self) {}
 +}
 +fn f(_: &Fn(S)) {}
 +fn main() {
 +    f(&|number| number.foo());
 +}
 +        "#,
 +        expect![[r#"
 +            31..35 'self': &S
 +            37..39 '{}': ()
 +            47..48 '_': &dyn Fn(S)
 +            58..60 '{}': ()
 +            71..105 '{     ...()); }': ()
 +            77..78 'f': fn f(&dyn Fn(S))
 +            77..102 'f(&|nu...foo())': ()
 +            79..101 '&|numb....foo()': &|S| -> ()
 +            80..101 '|numbe....foo()': |S| -> ()
 +            81..87 'number': S
 +            89..95 'number': S
 +            89..101 'number.foo()': ()
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn weird_bounds() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +fn test(
 +    a: impl Trait + 'lifetime,
 +    b: impl 'lifetime,
 +    c: impl (Trait),
 +    d: impl ('lifetime),
 +    e: impl ?Sized,
 +    f: impl Trait + ?Sized
 +) {}
 +"#,
 +        expect![[r#"
 +            28..29 'a': impl Trait
 +            59..60 'b': impl Sized
 +            82..83 'c': impl Trait
 +            103..104 'd': impl Sized
 +            128..129 'e': impl ?Sized
 +            148..149 'f': impl Trait + ?Sized
 +            173..175 '{}': ()
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn error_bound_chalk() {
 +    check_types(
 +        r#"
 +trait Trait {
 +    fn foo(&self) -> u32 { 0 }
 +}
 +
 +fn test(x: (impl Trait + UnknownTrait)) {
 +    x.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn assoc_type_bindings() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +trait Trait {
 +    type Type;
 +}
 +
 +fn get<T: Trait>(t: T) -> <T as Trait>::Type {}
 +fn get2<U, T: Trait<Type = U>>(t: T) -> U {}
 +fn set<T: Trait<Type = u64>>(t: T) -> T {t}
 +
 +struct S<T>;
 +impl<T> Trait for S<T> { type Type = T; }
 +
 +fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
 +    get(x);
 +    get2(x);
 +    get(y);
 +    get2(y);
 +    get(set(S));
 +    get2(set(S));
 +    get2(S::<str>);
 +}"#,
 +        expect![[r#"
 +            49..50 't': T
 +            77..79 '{}': Trait::Type<T>
 +            111..112 't': T
 +            122..124 '{}': U
 +            154..155 't': T
 +            165..168 '{t}': T
 +            166..167 't': T
 +            256..257 'x': T
 +            262..263 'y': impl Trait<Type = i64>
 +            289..397 '{     ...r>); }': ()
 +            295..298 'get': fn get<T>(T) -> <T as Trait>::Type
 +            295..301 'get(x)': u32
 +            299..300 'x': T
 +            307..311 'get2': fn get2<u32, T>(T) -> u32
 +            307..314 'get2(x)': u32
 +            312..313 'x': T
 +            320..323 'get': fn get<impl Trait<Type = i64>>(impl Trait<Type = i64>) -> <impl Trait<Type = i64> as Trait>::Type
 +            320..326 'get(y)': i64
 +            324..325 'y': impl Trait<Type = i64>
 +            332..336 'get2': fn get2<i64, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> i64
 +            332..339 'get2(y)': i64
 +            337..338 'y': impl Trait<Type = i64>
 +            345..348 'get': fn get<S<u64>>(S<u64>) -> <S<u64> as Trait>::Type
 +            345..356 'get(set(S))': u64
 +            349..352 'set': fn set<S<u64>>(S<u64>) -> S<u64>
 +            349..355 'set(S)': S<u64>
 +            353..354 'S': S<u64>
 +            362..366 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64
 +            362..374 'get2(set(S))': u64
 +            367..370 'set': fn set<S<u64>>(S<u64>) -> S<u64>
 +            367..373 'set(S)': S<u64>
 +            371..372 'S': S<u64>
 +            380..384 'get2': fn get2<str, S<str>>(S<str>) -> str
 +            380..394 'get2(S::<str>)': str
 +            385..393 'S::<str>': S<str>
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn impl_trait_assoc_binding_projection_bug() {
 +    check_types(
 +        r#"
 +//- minicore: iterator
 +pub trait Language {
 +    type Kind;
 +}
 +pub enum RustLanguage {}
 +impl Language for RustLanguage {
 +    type Kind = SyntaxKind;
 +}
 +struct SyntaxNode<L> {}
 +fn foo() -> impl Iterator<Item = SyntaxNode<RustLanguage>> {}
 +
 +trait Clone {
 +    fn clone(&self) -> Self;
 +}
 +
 +fn api_walkthrough() {
 +    for node in foo() {
 +        node.clone();
 +    } //^^^^^^^^^^^^ {unknown}
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn projection_eq_within_chalk() {
 +    check_infer(
 +        r#"
 +trait Trait1 {
 +    type Type;
 +}
 +trait Trait2<T> {
 +    fn foo(self) -> T;
 +}
 +impl<T, U> Trait2<T> for U where U: Trait1<Type = T> {}
 +
 +fn test<T: Trait1<Type = u32>>(x: T) {
 +    x.foo();
 +}"#,
 +        expect![[r#"
 +            61..65 'self': Self
 +            163..164 'x': T
 +            169..185 '{     ...o(); }': ()
 +            175..176 'x': T
 +            175..182 'x.foo()': u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn where_clause_trait_in_scope_for_method_resolution() {
 +    check_types(
 +        r#"
 +mod foo {
 +    trait Trait {
 +        fn foo(&self) -> u32 { 0 }
 +    }
 +}
 +
 +fn test<T: foo::Trait>(x: T) {
 +    x.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn super_trait_method_resolution() {
 +    check_infer(
 +        r#"
 +mod foo {
 +    trait SuperTrait {
 +        fn foo(&self) -> u32 {}
 +    }
 +}
 +trait Trait1: foo::SuperTrait {}
 +trait Trait2 where Self: foo::SuperTrait {}
 +
 +fn test<T: Trait1, U: Trait2>(x: T, y: U) {
 +    x.foo();
 +    y.foo();
 +}"#,
 +        expect![[r#"
 +            49..53 'self': &Self
 +            62..64 '{}': u32
 +            181..182 'x': T
 +            187..188 'y': U
 +            193..222 '{     ...o(); }': ()
 +            199..200 'x': T
 +            199..206 'x.foo()': u32
 +            212..213 'y': U
 +            212..219 'y.foo()': u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn super_trait_impl_trait_method_resolution() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +mod foo {
 +    trait SuperTrait {
 +        fn foo(&self) -> u32 {}
 +    }
 +}
 +trait Trait1: foo::SuperTrait {}
 +
 +fn test(x: &impl Trait1) {
 +    x.foo();
 +}"#,
 +        expect![[r#"
 +            49..53 'self': &Self
 +            62..64 '{}': u32
 +            115..116 'x': &impl Trait1
 +            132..148 '{     ...o(); }': ()
 +            138..139 'x': &impl Trait1
 +            138..145 'x.foo()': u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn super_trait_cycle() {
 +    // This just needs to not crash
 +    check_infer(
 +        r#"
 +        trait A: B {}
 +        trait B: A {}
 +
 +        fn test<T: A>(x: T) {
 +            x.foo();
 +        }
 +        "#,
 +        expect![[r#"
 +            43..44 'x': T
 +            49..65 '{     ...o(); }': ()
 +            55..56 'x': T
 +            55..62 'x.foo()': {unknown}
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn super_trait_assoc_type_bounds() {
 +    check_infer(
 +        r#"
 +trait SuperTrait { type Type; }
 +trait Trait where Self: SuperTrait {}
 +
 +fn get2<U, T: Trait<Type = U>>(t: T) -> U {}
 +fn set<T: Trait<Type = u64>>(t: T) -> T {t}
 +
 +struct S<T>;
 +impl<T> SuperTrait for S<T> { type Type = T; }
 +impl<T> Trait for S<T> {}
 +
 +fn test() {
 +    get2(set(S));
 +}"#,
 +        expect![[r#"
 +            102..103 't': T
 +            113..115 '{}': U
 +            145..146 't': T
 +            156..159 '{t}': T
 +            157..158 't': T
 +            258..279 '{     ...S)); }': ()
 +            264..268 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64
 +            264..276 'get2(set(S))': u64
 +            269..272 'set': fn set<S<u64>>(S<u64>) -> S<u64>
 +            269..275 'set(S)': S<u64>
 +            273..274 'S': S<u64>
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn fn_trait() {
 +    check_infer_with_mismatches(
 +        r#"
 +trait FnOnce<Args> {
 +    type Output;
 +
 +    fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output;
 +}
 +
 +fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
 +    f.call_once((1, 2));
 +}"#,
 +        expect![[r#"
 +            56..60 'self': Self
 +            62..66 'args': Args
 +            149..150 'f': F
 +            155..183 '{     ...2)); }': ()
 +            161..162 'f': F
 +            161..180 'f.call...1, 2))': u128
 +            173..179 '(1, 2)': (u32, u64)
 +            174..175 '1': u32
 +            177..178 '2': u64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn fn_ptr_and_item() {
 +    check_infer_with_mismatches(
 +        r#"
 +#[lang="fn_once"]
 +trait FnOnce<Args> {
 +    type Output;
 +
 +    fn call_once(self, args: Args) -> Self::Output;
 +}
 +
 +trait Foo<T> {
 +    fn foo(&self) -> T;
 +}
 +
 +struct Bar<T>(T);
 +
 +impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> {
 +    fn foo(&self) -> (A1, R) { loop {} }
 +}
 +
 +enum Opt<T> { None, Some(T) }
 +impl<T> Opt<T> {
 +    fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> { loop {} }
 +}
 +
 +fn test() {
 +    let bar: Bar<fn(u8) -> u32>;
 +    bar.foo();
 +
 +    let opt: Opt<u8>;
 +    let f: fn(u8) -> u32;
 +    opt.map(f);
 +}"#,
 +        expect![[r#"
 +            74..78 'self': Self
 +            80..84 'args': Args
 +            139..143 'self': &Self
 +            243..247 'self': &Bar<F>
 +            260..271 '{ loop {} }': (A1, R)
 +            262..269 'loop {}': !
 +            267..269 '{}': ()
 +            355..359 'self': Opt<T>
 +            361..362 'f': F
 +            377..388 '{ loop {} }': Opt<U>
 +            379..386 'loop {}': !
 +            384..386 '{}': ()
 +            402..518 '{     ...(f); }': ()
 +            412..415 'bar': Bar<fn(u8) -> u32>
 +            441..444 'bar': Bar<fn(u8) -> u32>
 +            441..450 'bar.foo()': (u8, u32)
 +            461..464 'opt': Opt<u8>
 +            483..484 'f': fn(u8) -> u32
 +            505..508 'opt': Opt<u8>
 +            505..515 'opt.map(f)': Opt<u32>
 +            513..514 'f': fn(u8) -> u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn fn_trait_deref_with_ty_default() {
 +    check_infer(
 +        r#"
 +//- minicore: deref, fn
 +struct Foo;
 +
 +impl Foo {
 +    fn foo(&self) -> usize {}
 +}
 +
 +struct Lazy<T, F = fn() -> T>(F);
 +
 +impl<T, F> Lazy<T, F> {
 +    pub fn new(f: F) -> Lazy<T, F> {}
 +}
 +
 +impl<T, F: FnOnce() -> T> core::ops::Deref for Lazy<T, F> {
 +    type Target = T;
 +}
 +
 +fn test() {
 +    let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo);
 +    let r1 = lazy1.foo();
 +
 +    fn make_foo_fn() -> Foo {}
 +    let make_foo_fn_ptr: fn() -> Foo = make_foo_fn;
 +    let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr);
 +    let r2 = lazy2.foo();
 +}"#,
 +        expect![[r#"
 +            36..40 'self': &Foo
 +            51..53 '{}': usize
 +            131..132 'f': F
 +            151..153 '{}': Lazy<T, F>
 +            251..497 '{     ...o(); }': ()
 +            261..266 'lazy1': Lazy<Foo, || -> Foo>
 +            283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
 +            283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
 +            293..299 '|| Foo': || -> Foo
 +            296..299 'Foo': Foo
 +            310..312 'r1': usize
 +            315..320 'lazy1': Lazy<Foo, || -> Foo>
 +            315..326 'lazy1.foo()': usize
 +            368..383 'make_foo_fn_ptr': fn() -> Foo
 +            399..410 'make_foo_fn': fn make_foo_fn() -> Foo
 +            420..425 'lazy2': Lazy<Foo, fn() -> Foo>
 +            442..451 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
 +            442..468 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
 +            452..467 'make_foo_fn_ptr': fn() -> Foo
 +            478..480 'r2': usize
 +            483..488 'lazy2': Lazy<Foo, fn() -> Foo>
 +            483..494 'lazy2.foo()': usize
 +            357..359 '{}': Foo
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn closure_1() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn
 +enum Option<T> { Some(T), None }
 +impl<T> Option<T> {
 +    fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} }
 +}
 +
 +fn test() {
 +    let x = Option::Some(1u32);
 +    x.map(|v| v + 1);
 +    x.map(|_v| 1u64);
 +    let y: Option<i64> = x.map(|_v| 1);
 +}"#,
 +        expect![[r#"
 +            86..90 'self': Option<T>
 +            92..93 'f': F
 +            111..122 '{ loop {} }': Option<U>
 +            113..120 'loop {}': !
 +            118..120 '{}': ()
 +            136..255 '{     ... 1); }': ()
 +            146..147 'x': Option<u32>
 +            150..162 'Option::Some': Some<u32>(u32) -> Option<u32>
 +            150..168 'Option...(1u32)': Option<u32>
 +            163..167 '1u32': u32
 +            174..175 'x': Option<u32>
 +            174..190 'x.map(...v + 1)': Option<u32>
 +            180..189 '|v| v + 1': |u32| -> u32
 +            181..182 'v': u32
 +            184..185 'v': u32
 +            184..189 'v + 1': u32
 +            188..189 '1': u32
 +            196..197 'x': Option<u32>
 +            196..212 'x.map(... 1u64)': Option<u64>
 +            202..211 '|_v| 1u64': |u32| -> u64
 +            203..205 '_v': u32
 +            207..211 '1u64': u64
 +            222..223 'y': Option<i64>
 +            239..240 'x': Option<u32>
 +            239..252 'x.map(|_v| 1)': Option<i64>
 +            245..251 '|_v| 1': |u32| -> i64
 +            246..248 '_v': u32
 +            250..251 '1': i64
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn closure_2() {
 +    check_types(
 +        r#"
 +//- minicore: add, fn
 +
 +impl core::ops::Add for u64 {
 +    type Output = Self;
 +    fn add(self, rhs: u64) -> Self::Output {0}
 +}
 +
 +impl core::ops::Add for u128 {
 +    type Output = Self;
 +    fn add(self, rhs: u128) -> Self::Output {0}
 +}
 +
 +fn test<F: FnOnce(u32) -> u64>(f: F) {
 +    f(1);
 +  //  ^ u32
 +  //^^^^ u64
 +    let g = |v| v + 1;
 +              //^^^^^ u64
 +          //^^^^^^^^^ |u64| -> u64
 +    g(1u64);
 +  //^^^^^^^ u64
 +    let h = |v| 1u128 + v;
 +          //^^^^^^^^^^^^^ |u128| -> u128
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn closure_as_argument_inference_order() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn
 +fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} }
 +fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} }
 +
 +struct S;
 +impl S {
 +    fn method(self) -> u64;
 +
 +    fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U { loop {} }
 +    fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U { loop {} }
 +}
 +
 +fn test() {
 +    let x1 = foo1(S, |s| s.method());
 +    let x2 = foo2(|s| s.method(), S);
 +    let x3 = S.foo1(S, |s| s.method());
 +    let x4 = S.foo2(|s| s.method(), S);
 +}"#,
 +        expect![[r#"
 +            33..34 'x': T
 +            39..40 'f': F
 +            50..61 '{ loop {} }': U
 +            52..59 'loop {}': !
 +            57..59 '{}': ()
 +            95..96 'f': F
 +            101..102 'x': T
 +            112..123 '{ loop {} }': U
 +            114..121 'loop {}': !
 +            119..121 '{}': ()
 +            158..162 'self': S
 +            210..214 'self': S
 +            216..217 'x': T
 +            222..223 'f': F
 +            233..244 '{ loop {} }': U
 +            235..242 'loop {}': !
 +            240..242 '{}': ()
 +            282..286 'self': S
 +            288..289 'f': F
 +            294..295 'x': T
 +            305..316 '{ loop {} }': U
 +            307..314 'loop {}': !
 +            312..314 '{}': ()
 +            330..489 '{     ... S); }': ()
 +            340..342 'x1': u64
 +            345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
 +            345..368 'foo1(S...hod())': u64
 +            350..351 'S': S
 +            353..367 '|s| s.method()': |S| -> u64
 +            354..355 's': S
 +            357..358 's': S
 +            357..367 's.method()': u64
 +            378..380 'x2': u64
 +            383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
 +            383..406 'foo2(|...(), S)': u64
 +            388..402 '|s| s.method()': |S| -> u64
 +            389..390 's': S
 +            392..393 's': S
 +            392..402 's.method()': u64
 +            404..405 'S': S
 +            416..418 'x3': u64
 +            421..422 'S': S
 +            421..446 'S.foo1...hod())': u64
 +            428..429 'S': S
 +            431..445 '|s| s.method()': |S| -> u64
 +            432..433 's': S
 +            435..436 's': S
 +            435..445 's.method()': u64
 +            456..458 'x4': u64
 +            461..462 'S': S
 +            461..486 'S.foo2...(), S)': u64
 +            468..482 '|s| s.method()': |S| -> u64
 +            469..470 's': S
 +            472..473 's': S
 +            472..482 's.method()': u64
 +            484..485 'S': S
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn fn_item_fn_trait() {
 +    check_types(
 +        r#"
 +//- minicore: fn
 +struct S;
 +
 +fn foo() -> S { S }
 +
 +fn takes_closure<U, F: FnOnce() -> U>(f: F) -> U { f() }
 +
 +fn test() {
 +    takes_closure(foo);
 +} //^^^^^^^^^^^^^^^^^^ S
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_1() {
 +    check_types(
 +        r#"
 +//- /main.rs
 +trait Trait {
 +    type Item;
 +}
 +
 +trait Trait2 {
 +    fn foo(&self) -> u32;
 +}
 +
 +fn test<T: Trait>() where T::Item: Trait2 {
 +    let x: T::Item = no_matter;
 +    x.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_2() {
 +    check_types(
 +        r#"
 +trait Trait<T> {
 +    type Item;
 +}
 +
 +trait Trait2 {
 +    fn foo(&self) -> u32;
 +}
 +
 +fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
 +    let x: T::Item = no_matter;
 +    x.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_on_impl_self() {
 +    check_infer(
 +        r#"
 +//- /main.rs
 +trait Trait {
 +    type Item;
 +
 +    fn f(&self, x: Self::Item);
 +}
 +
 +struct S;
 +
 +impl Trait for S {
 +    type Item = u32;
 +    fn f(&self, x: Self::Item) { let y = x; }
 +}
 +
 +struct S2;
 +
 +impl Trait for S2 {
 +    type Item = i32;
 +    fn f(&self, x: <Self>::Item) { let y = x; }
 +}"#,
 +        expect![[r#"
 +            40..44 'self': &Self
 +            46..47 'x': Trait::Item<Self>
 +            126..130 'self': &S
 +            132..133 'x': u32
 +            147..161 '{ let y = x; }': ()
 +            153..154 'y': u32
 +            157..158 'x': u32
 +            228..232 'self': &S2
 +            234..235 'x': i32
 +            251..265 '{ let y = x; }': ()
 +            257..258 'y': i32
 +            261..262 'x': i32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_on_trait_self() {
 +    check_types(
 +        r#"
 +trait Trait {
 +    type Item;
 +
 +    fn f(&self) -> Self::Item { loop {} }
 +}
 +
 +struct S;
 +impl Trait for S {
 +    type Item = u32;
 +}
 +
 +fn test() {
 +    S.f();
 +} //^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_chalk_fold() {
 +    check_types(
 +        r#"
 +trait Interner {}
 +trait Fold<I: Interner, TI = I> {
 +    type Result;
 +}
 +
 +struct Ty<I: Interner> {}
 +impl<I: Interner, TI: Interner> Fold<I, TI> for Ty<I> {
 +    type Result = Ty<TI>;
 +}
 +
 +fn fold<I: Interner, T>(interner: &I, t: T) -> T::Result
 +where
 +    T: Fold<I, I>,
 +{
 +    loop {}
 +}
 +
 +fn foo<I: Interner>(interner: &I, t: Ty<I>) {
 +    fold(interner, t);
 +} //^^^^^^^^^^^^^^^^^ Ty<I>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn trait_impl_self_ty() {
 +    check_types(
 +        r#"
 +trait Trait<T> {
 +   fn foo(&self);
 +}
 +
 +struct S;
 +
 +impl Trait<Self> for S {}
 +
 +fn test() {
 +    S.foo();
 +} //^^^^^^^ ()
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn trait_impl_self_ty_cycle() {
 +    check_types(
 +        r#"
 +trait Trait {
 +   fn foo(&self);
 +}
 +
 +struct S<T>;
 +
 +impl Trait for S<Self> {}
 +
 +fn test() {
 +    S.foo();
 +} //^^^^^^^ {unknown}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_cycle_1() {
 +    // This is not a cycle, because the `T: Trait2<T::Item>` bound depends only on the `T: Trait`
 +    // bound, not on itself (since only `Trait` can define `Item`).
 +    check_types(
 +        r#"
 +trait Trait {
 +    type Item;
 +}
 +
 +trait Trait2<T> {}
 +
 +fn test<T: Trait>() where T: Trait2<T::Item> {
 +    let x: T::Item = no_matter;
 +}                  //^^^^^^^^^ Trait::Item<T>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_cycle_2() {
 +    // this is a legitimate cycle
 +    check_types(
 +        r#"
 +//- /main.rs
 +trait Trait<T> {
 +    type Item;
 +}
 +
 +fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
 +    let x: T::Item = no_matter;
 +}                  //^^^^^^^^^ {unknown}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_cycle_3() {
 +    // this is a cycle for rustc; we currently accept it
 +    check_types(
 +        r#"
 +//- /main.rs
 +trait Trait {
 +    type Item;
 +    type OtherItem;
 +}
 +
 +fn test<T>() where T: Trait<OtherItem = T::Item> {
 +    let x: T::Item = no_matter;
 +}                  //^^^^^^^^^ Trait::Item<T>
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn unselected_projection_in_trait_env_no_cycle() {
 +    // this is not a cycle
 +    check_types(
 +        r#"
 +//- /main.rs
 +trait Index {
 +    type Output;
 +}
 +
 +type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key;
 +
 +pub trait UnificationStoreBase: Index<Output = Key<Self>> {
 +    type Key;
 +
 +    fn len(&self) -> usize;
 +}
 +
 +pub trait UnificationStoreMut: UnificationStoreBase {
 +    fn push(&mut self, value: Self::Key);
 +}
 +
 +fn test<T>(t: T) where T: UnificationStoreMut {
 +    let x;
 +    t.push(x);
 +    let y: Key<T>;
 +    (x, y);
 +} //^^^^^^ (UnificationStoreBase::Key<T>, UnificationStoreBase::Key<T>)
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn inline_assoc_type_bounds_1() {
 +    check_types(
 +        r#"
 +trait Iterator {
 +    type Item;
 +}
 +trait OtherTrait<T> {
 +    fn foo(&self) -> T;
 +}
 +
 +// workaround for Chalk assoc type normalization problems
 +pub struct S<T>;
 +impl<T: Iterator> Iterator for S<T> {
 +    type Item = <T as Iterator>::Item;
 +}
 +
 +fn test<I: Iterator<Item: OtherTrait<u32>>>() {
 +    let x: <S<I> as Iterator>::Item;
 +    x.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn inline_assoc_type_bounds_2() {
 +    check_types(
 +        r#"
 +trait Iterator {
 +    type Item;
 +}
 +
 +fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
 +    let x: <<I as Iterator>::Item as Iterator>::Item;
 +    x;
 +} //^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn proc_macro_server_types() {
 +    check_infer(
 +        r#"
 +macro_rules! with_api {
 +    ($S:ident, $self:ident, $m:ident) => {
 +        $m! {
 +            TokenStream {
 +                fn new() -> $S::TokenStream;
 +            },
 +            Group {
 +            },
 +        }
 +    };
 +}
 +macro_rules! associated_item {
 +    (type TokenStream) =>
 +        (type TokenStream: 'static;);
 +    (type Group) =>
 +        (type Group: 'static;);
 +    ($($item:tt)*) => ($($item)*;)
 +}
 +macro_rules! declare_server_traits {
 +    ($($name:ident {
 +        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
 +    }),* $(,)?) => {
 +        pub trait Types {
 +            $(associated_item!(type $name);)*
 +        }
 +
 +        $(pub trait $name: Types {
 +            $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)*
 +        })*
 +
 +        pub trait Server: Types $(+ $name)* {}
 +        impl<S: Types $(+ $name)*> Server for S {}
 +    }
 +}
 +
 +with_api!(Self, self_, declare_server_traits);
 +struct G {}
 +struct T {}
 +struct RustAnalyzer;
 +impl Types for RustAnalyzer {
 +    type TokenStream = T;
 +    type Group = G;
 +}
 +
 +fn make<T>() -> T { loop {} }
 +impl TokenStream for RustAnalyzer {
 +    fn new() -> Self::TokenStream {
 +        let group: Self::Group = make();
 +        make()
 +    }
 +}"#,
 +        expect![[r#"
 +            1075..1086 '{ loop {} }': T
 +            1077..1084 'loop {}': !
 +            1082..1084 '{}': ()
 +            1157..1220 '{     ...     }': T
 +            1171..1176 'group': G
 +            1192..1196 'make': fn make<G>() -> G
 +            1192..1198 'make()': G
 +            1208..1212 'make': fn make<T>() -> T
 +            1208..1214 'make()': T
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn unify_impl_trait() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Trait<T> {}
 +
 +fn foo(x: impl Trait<u32>) { loop {} }
 +fn bar<T>(x: impl Trait<T>) -> T { loop {} }
 +
 +struct S<T>(T);
 +impl<T> Trait<T> for S<T> {}
 +
 +fn default<T>() -> T { loop {} }
 +
 +fn test() -> impl Trait<i32> {
 +    let s1 = S(default());
 +    foo(s1);
 +    let x: i32 = bar(S(default()));
 +    S(default())
 +}"#,
 +        expect![[r#"
 +            26..27 'x': impl Trait<u32>
 +            46..57 '{ loop {} }': ()
 +            48..55 'loop {}': !
 +            53..55 '{}': ()
 +            68..69 'x': impl Trait<T>
 +            91..102 '{ loop {} }': T
 +            93..100 'loop {}': !
 +            98..100 '{}': ()
 +            171..182 '{ loop {} }': T
 +            173..180 'loop {}': !
 +            178..180 '{}': ()
 +            213..309 '{     ...t()) }': S<i32>
 +            223..225 's1': S<u32>
 +            228..229 'S': S<u32>(u32) -> S<u32>
 +            228..240 'S(default())': S<u32>
 +            230..237 'default': fn default<u32>() -> u32
 +            230..239 'default()': u32
 +            246..249 'foo': fn foo(S<u32>)
 +            246..253 'foo(s1)': ()
 +            250..252 's1': S<u32>
 +            263..264 'x': i32
 +            272..275 'bar': fn bar<i32>(S<i32>) -> i32
 +            272..289 'bar(S(...lt()))': i32
 +            276..277 'S': S<i32>(i32) -> S<i32>
 +            276..288 'S(default())': S<i32>
 +            278..285 'default': fn default<i32>() -> i32
 +            278..287 'default()': i32
 +            295..296 'S': S<i32>(i32) -> S<i32>
 +            295..307 'S(default())': S<i32>
 +            297..304 'default': fn default<i32>() -> i32
 +            297..306 'default()': i32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn assoc_types_from_bounds() {
 +    check_infer(
 +        r#"
 +//- minicore: fn
 +trait T {
 +    type O;
 +}
 +
 +impl T for () {
 +    type O = ();
 +}
 +
 +fn f<X, F>(_v: F)
 +where
 +    X: T,
 +    F: FnOnce(&X::O),
 +{ }
 +
 +fn main() {
 +    f::<(), _>(|z| { z; });
 +}"#,
 +        expect![[r#"
 +            72..74 '_v': F
 +            117..120 '{ }': ()
 +            132..163 '{     ... }); }': ()
 +            138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
 +            138..160 'f::<()... z; })': ()
 +            149..159 '|z| { z; }': |&()| -> ()
 +            150..151 'z': &()
 +            153..159 '{ z; }': ()
 +            155..156 'z': &()
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn associated_type_bound() {
 +    check_types(
 +        r#"
 +pub trait Trait {
 +    type Item: OtherTrait<u32>;
 +}
 +pub trait OtherTrait<T> {
 +    fn foo(&self) -> T;
 +}
 +
 +// this is just a workaround for chalk#234
 +pub struct S<T>;
 +impl<T: Trait> Trait for S<T> {
 +    type Item = <T as Trait>::Item;
 +}
 +
 +fn test<T: Trait>() {
 +    let y: <S<T> as Trait>::Item = no_matter;
 +    y.foo();
 +} //^^^^^^^ u32
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn dyn_trait_through_chalk() {
 +    check_types(
 +        r#"
 +//- minicore: deref
 +struct Box<T: ?Sized> {}
 +impl<T: ?Sized> core::ops::Deref for Box<T> {
 +    type Target = T;
 +}
 +trait Trait {
 +    fn foo(&self);
 +}
 +
 +fn test(x: Box<dyn Trait>) {
 +    x.foo();
 +} //^^^^^^^ ()
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn string_to_owned() {
 +    check_types(
 +        r#"
 +struct String {}
 +pub trait ToOwned {
 +    type Owned;
 +    fn to_owned(&self) -> Self::Owned;
 +}
 +impl ToOwned for str {
 +    type Owned = String;
 +}
 +fn test() {
 +    "foo".to_owned();
 +} //^^^^^^^^^^^^^^^^ String
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn iterator_chain() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn, option
 +pub trait Iterator {
 +    type Item;
 +
 +    fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
 +    where
 +        F: FnMut(Self::Item) -> Option<B>,
 +    { loop {} }
 +
 +    fn for_each<F>(self, f: F)
 +    where
 +        F: FnMut(Self::Item),
 +    { loop {} }
 +}
 +
 +pub trait IntoIterator {
 +    type Item;
 +    type IntoIter: Iterator<Item = Self::Item>;
 +    fn into_iter(self) -> Self::IntoIter;
 +}
 +
 +pub struct FilterMap<I, F> { }
 +impl<B, I: Iterator, F> Iterator for FilterMap<I, F>
 +where
 +    F: FnMut(I::Item) -> Option<B>,
 +{
 +    type Item = B;
 +}
 +
 +#[stable(feature = "rust1", since = "1.0.0")]
 +impl<I: Iterator> IntoIterator for I {
 +    type Item = I::Item;
 +    type IntoIter = I;
 +
 +    fn into_iter(self) -> I {
 +        self
 +    }
 +}
 +
 +struct Vec<T> {}
 +impl<T> Vec<T> {
 +    fn new() -> Self { loop {} }
 +}
 +
 +impl<T> IntoIterator for Vec<T> {
 +    type Item = T;
 +    type IntoIter = IntoIter<T>;
 +}
 +
 +pub struct IntoIter<T> { }
 +impl<T> Iterator for IntoIter<T> {
 +    type Item = T;
 +}
 +
 +fn main() {
 +    Vec::<i32>::new().into_iter()
 +    .filter_map(|x| if x > 0 { Some(x as u32) } else { None })
 +    .for_each(|y| { y; });
 +}"#,
 +        expect![[r#"
 +            61..65 'self': Self
 +            67..68 'f': F
 +            152..163 '{ loop {} }': FilterMap<Self, F>
 +            154..161 'loop {}': !
 +            159..161 '{}': ()
 +            184..188 'self': Self
 +            190..191 'f': F
 +            240..251 '{ loop {} }': ()
 +            242..249 'loop {}': !
 +            247..249 '{}': ()
 +            360..364 'self': Self
 +            689..693 'self': I
 +            700..720 '{     ...     }': I
 +            710..714 'self': I
 +            779..790 '{ loop {} }': Vec<T>
 +            781..788 'loop {}': !
 +            786..788 '{}': ()
 +            977..1104 '{     ... }); }': ()
 +            983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
 +            983..1000 'Vec::<...:new()': Vec<i32>
 +            983..1012 'Vec::<...iter()': IntoIter<i32>
 +            983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
 +            983..1101 'Vec::<... y; })': ()
 +            1029..1074 '|x| if...None }': |i32| -> Option<u32>
 +            1030..1031 'x': i32
 +            1033..1074 'if x >...None }': Option<u32>
 +            1036..1037 'x': i32
 +            1036..1041 'x > 0': bool
 +            1040..1041 '0': i32
 +            1042..1060 '{ Some...u32) }': Option<u32>
 +            1044..1048 'Some': Some<u32>(u32) -> Option<u32>
 +            1044..1058 'Some(x as u32)': Option<u32>
 +            1049..1050 'x': i32
 +            1049..1057 'x as u32': u32
 +            1066..1074 '{ None }': Option<u32>
 +            1068..1072 'None': Option<u32>
 +            1090..1100 '|y| { y; }': |u32| -> ()
 +            1091..1092 'y': u32
 +            1094..1100 '{ y; }': ()
 +            1096..1097 'y': u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn nested_assoc() {
 +    check_types(
 +        r#"
 +struct Bar;
 +struct Foo;
 +
 +trait A {
 +    type OutputA;
 +}
 +
 +impl A for Bar {
 +    type OutputA = Foo;
 +}
 +
 +trait B {
 +    type Output;
 +    fn foo() -> Self::Output;
 +}
 +
 +impl<T:A> B for T {
 +    type Output = T::OutputA;
 +    fn foo() -> Self::Output { loop {} }
 +}
 +
 +fn main() {
 +    Bar::foo();
 +} //^^^^^^^^^^ Foo
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn trait_object_no_coercion() {
 +    check_infer_with_mismatches(
 +        r#"
 +trait Foo {}
 +
 +fn foo(x: &dyn Foo) {}
 +
 +fn test(x: &dyn Foo) {
 +    foo(x);
 +}"#,
 +        expect![[r#"
 +            21..22 'x': &dyn Foo
 +            34..36 '{}': ()
 +            46..47 'x': &dyn Foo
 +            59..74 '{     foo(x); }': ()
 +            65..68 'foo': fn foo(&dyn Foo)
 +            65..71 'foo(x)': ()
 +            69..70 'x': &dyn Foo
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn builtin_copy() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: copy
 +struct IsCopy;
 +impl Copy for IsCopy {}
 +struct NotCopy;
 +
 +trait Test { fn test(&self) -> bool; }
 +impl<T: Copy> Test for T {}
 +
 +fn test() {
 +    IsCopy.test();
 +    NotCopy.test();
 +    (IsCopy, IsCopy).test();
 +    (IsCopy, NotCopy).test();
 +}"#,
 +        expect![[r#"
 +            78..82 'self': &Self
 +            134..235 '{     ...t(); }': ()
 +            140..146 'IsCopy': IsCopy
 +            140..153 'IsCopy.test()': bool
 +            159..166 'NotCopy': NotCopy
 +            159..173 'NotCopy.test()': {unknown}
 +            179..195 '(IsCop...sCopy)': (IsCopy, IsCopy)
 +            179..202 '(IsCop...test()': bool
 +            180..186 'IsCopy': IsCopy
 +            188..194 'IsCopy': IsCopy
 +            208..225 '(IsCop...tCopy)': (IsCopy, NotCopy)
 +            208..232 '(IsCop...test()': {unknown}
 +            209..215 'IsCopy': IsCopy
 +            217..224 'NotCopy': NotCopy
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn builtin_fn_def_copy() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: copy
 +fn foo() {}
 +fn bar<T: Copy>(T) -> T {}
 +struct Struct(usize);
 +enum Enum { Variant(usize) }
 +
 +trait Test { fn test(&self) -> bool; }
 +impl<T: Copy> Test for T {}
 +
 +fn test() {
 +    foo.test();
 +    bar.test();
 +    Struct.test();
 +    Enum::Variant.test();
 +}"#,
 +        expect![[r#"
 +            9..11 '{}': ()
 +            28..29 'T': {unknown}
 +            36..38 '{}': T
 +            36..38: expected T, got ()
 +            113..117 'self': &Self
 +            169..249 '{     ...t(); }': ()
 +            175..178 'foo': fn foo()
 +            175..185 'foo.test()': bool
 +            191..194 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
 +            191..201 'bar.test()': bool
 +            207..213 'Struct': Struct(usize) -> Struct
 +            207..220 'Struct.test()': bool
 +            226..239 'Enum::Variant': Variant(usize) -> Enum
 +            226..246 'Enum::...test()': bool
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn builtin_fn_ptr_copy() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: copy
 +trait Test { fn test(&self) -> bool; }
 +impl<T: Copy> Test for T {}
 +
 +fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
 +    f1.test();
 +    f2.test();
 +    f3.test();
 +}"#,
 +        expect![[r#"
 +            22..26 'self': &Self
 +            76..78 'f1': fn()
 +            86..88 'f2': fn(usize) -> u8
 +            107..109 'f3': fn(u8, u8) -> &u8
 +            130..178 '{     ...t(); }': ()
 +            136..138 'f1': fn()
 +            136..145 'f1.test()': bool
 +            151..153 'f2': fn(usize) -> u8
 +            151..160 'f2.test()': bool
 +            166..168 'f3': fn(u8, u8) -> &u8
 +            166..175 'f3.test()': bool
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn builtin_sized() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: sized
 +trait Test { fn test(&self) -> bool; }
 +impl<T: Sized> Test for T {}
 +
 +fn test() {
 +    1u8.test();
 +    (*"foo").test(); // not Sized
 +    (1u8, 1u8).test();
 +    (1u8, *"foo").test(); // not Sized
 +}"#,
 +        expect![[r#"
 +            22..26 'self': &Self
 +            79..194 '{     ...ized }': ()
 +            85..88 '1u8': u8
 +            85..95 '1u8.test()': bool
 +            101..116 '(*"foo").test()': {unknown}
 +            102..108 '*"foo"': str
 +            103..108 '"foo"': &str
 +            135..145 '(1u8, 1u8)': (u8, u8)
 +            135..152 '(1u8, ...test()': bool
 +            136..139 '1u8': u8
 +            141..144 '1u8': u8
 +            158..171 '(1u8, *"foo")': (u8, str)
 +            158..178 '(1u8, ...test()': {unknown}
 +            159..162 '1u8': u8
 +            164..170 '*"foo"': str
 +            165..170 '"foo"': &str
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn integer_range_iterate() {
 +    check_types(
 +        r#"
 +//- /main.rs crate:main deps:core
 +fn test() {
 +    for x in 0..100 { x; }
 +}                   //^ i32
 +
 +//- /core.rs crate:core
 +pub mod ops {
 +    pub struct Range<Idx> {
 +        pub start: Idx,
 +        pub end: Idx,
 +    }
 +}
 +
 +pub mod iter {
 +    pub trait Iterator {
 +        type Item;
 +    }
 +
 +    pub trait IntoIterator {
 +        type Item;
 +        type IntoIter: Iterator<Item = Self::Item>;
 +    }
 +
 +    impl<T> IntoIterator for T where T: Iterator {
 +        type Item = <T as Iterator>::Item;
 +        type IntoIter = Self;
 +    }
 +}
 +
 +trait Step {}
 +impl Step for i32 {}
 +impl Step for i64 {}
 +
 +impl<A: Step> iter::Iterator for ops::Range<A> {
 +    type Item = A;
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_closure_arg() {
 +    check_infer(
 +        r#"
 +//- /lib.rs
 +
 +enum Option<T> {
 +    None,
 +    Some(T)
 +}
 +
 +fn foo() {
 +    let s = Option::None;
 +    let f = |x: Option<i32>| {};
 +    (&f)(s)
 +}"#,
 +        expect![[r#"
 +            52..126 '{     ...)(s) }': ()
 +            62..63 's': Option<i32>
 +            66..78 'Option::None': Option<i32>
 +            88..89 'f': |Option<i32>| -> ()
 +            92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
 +            93..94 'x': Option<i32>
 +            109..111 '{}': ()
 +            117..124 '(&f)(s)': ()
 +            118..120 '&f': &|Option<i32>| -> ()
 +            119..120 'f': |Option<i32>| -> ()
 +            122..123 's': Option<i32>
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn dyn_fn_param_informs_call_site_closure_signature() {
 +    cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature);
 +    check_types(
 +        r#"
 +//- minicore: fn, coerce_unsized
 +struct S;
 +impl S {
 +    fn inherent(&self) -> u8 { 0 }
 +}
 +fn take_dyn_fn(f: &dyn Fn(S)) {}
 +
 +fn f() {
 +    take_dyn_fn(&|x| { x.inherent(); });
 +                     //^^^^^^^^^^^^ u8
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn infer_fn_trait_arg() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn, option
 +fn foo<F, T>(f: F) -> T
 +where
 +    F: Fn(Option<i32>) -> T,
 +{
 +    let s = None;
 +    f(s)
 +}
 +"#,
 +        expect![[r#"
 +            13..14 'f': F
 +            59..89 '{     ...f(s) }': T
 +            69..70 's': Option<i32>
 +            73..77 'None': Option<i32>
 +            83..84 'f': F
 +            83..87 'f(s)': T
 +            85..86 's': Option<i32>
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn infer_box_fn_arg() {
 +    // The type mismatch is because we don't define Unsize and CoerceUnsized
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn, deref, option
 +#[lang = "owned_box"]
 +pub struct Box<T: ?Sized> {
 +    inner: *mut T,
 +}
 +
 +impl<T: ?Sized> core::ops::Deref for Box<T> {
 +    type Target = T;
 +
 +    fn deref(&self) -> &T {
 +        &self.inner
 +    }
 +}
 +
 +fn foo() {
 +    let s = None;
 +    let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
 +    f(&s);
 +}"#,
 +        expect![[r#"
 +            154..158 'self': &Box<T>
 +            166..193 '{     ...     }': &T
 +            176..187 '&self.inner': &*mut T
 +            177..181 'self': &Box<T>
 +            177..187 'self.inner': *mut T
 +            206..296 '{     ...&s); }': ()
 +            216..217 's': Option<i32>
 +            220..224 'None': Option<i32>
 +            234..235 'f': Box<dyn FnOnce(&Option<i32>)>
 +            269..282 'box (|ps| {})': Box<|&Option<i32>| -> ()>
 +            274..281 '|ps| {}': |&Option<i32>| -> ()
 +            275..277 'ps': &Option<i32>
 +            279..281 '{}': ()
 +            288..289 'f': Box<dyn FnOnce(&Option<i32>)>
 +            288..293 'f(&s)': ()
 +            290..292 '&s': &Option<i32>
 +            291..292 's': Option<i32>
 +            269..282: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn infer_dyn_fn_output() {
 +    check_types(
 +        r#"
 +//- minicore: fn
 +fn foo() {
 +    let f: &dyn Fn() -> i32;
 +    f();
 +  //^^^ i32
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn infer_dyn_fn_once_output() {
 +    check_types(
 +        r#"
 +//- minicore: fn
 +fn foo() {
 +    let f: dyn FnOnce() -> i32;
 +    f();
 +  //^^^ i32
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn variable_kinds_1() {
 +    check_types(
 +        r#"
 +trait Trait<T> { fn get(self, t: T) -> T; }
 +struct S;
 +impl Trait<u128> for S {}
 +impl Trait<f32> for S {}
 +fn test() {
 +    S.get(1);
 +  //^^^^^^^^ u128
 +    S.get(1.);
 +  //^^^^^^^^^ f32
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn variable_kinds_2() {
 +    check_types(
 +        r#"
 +trait Trait { fn get(self) -> Self; }
 +impl Trait for u128 {}
 +impl Trait for f32 {}
 +fn test() {
 +    1.get();
 +  //^^^^^^^ u128
 +    (1.).get();
 +  //^^^^^^^^^^ f32
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn underscore_import() {
 +    check_types(
 +        r#"
 +mod tr {
 +    pub trait Tr {
 +        fn method(&self) -> u8 { 0 }
 +    }
 +}
 +
 +struct Tr;
 +impl crate::tr::Tr for Tr {}
 +
 +use crate::tr::Tr as _;
 +fn test() {
 +    Tr.method();
 +  //^^^^^^^^^^^ u8
 +}
 +    "#,
 +    );
 +}
 +
 +#[test]
 +fn inner_use() {
 +    check_types(
 +        r#"
 +mod m {
 +    pub trait Tr {
 +        fn method(&self) -> u8 { 0 }
 +    }
 +
 +    impl Tr for () {}
 +}
 +
 +fn f() {
 +    use m::Tr;
 +
 +    ().method();
 +  //^^^^^^^^^^^ u8
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn trait_in_scope_with_inner_item() {
 +    check_infer(
 +        r#"
 +mod m {
 +    pub trait Tr {
 +        fn method(&self) -> u8 { 0 }
 +    }
 +
 +    impl Tr for () {}
 +}
 +
 +use m::Tr;
 +
 +fn f() {
 +    fn inner() {
 +        ().method();
 +      //^^^^^^^^^^^ u8
 +    }
 +}"#,
 +        expect![[r#"
 +            46..50 'self': &Self
 +            58..63 '{ 0 }': u8
 +            60..61 '0': u8
 +            115..185 '{     ...   } }': ()
 +            132..183 '{     ...     }': ()
 +            142..144 '()': ()
 +            142..153 '().method()': u8
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn inner_use_in_block() {
 +    check_types(
 +        r#"
 +mod m {
 +    pub trait Tr {
 +        fn method(&self) -> u8 { 0 }
 +    }
 +
 +    impl Tr for () {}
 +}
 +
 +fn f() {
 +    {
 +        use m::Tr;
 +
 +        ().method();
 +      //^^^^^^^^^^^ u8
 +    }
 +
 +    {
 +        ().method();
 +      //^^^^^^^^^^^ {unknown}
 +    }
 +
 +    ().method();
 +  //^^^^^^^^^^^ {unknown}
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn nested_inner_function_calling_self() {
 +    check_infer(
 +        r#"
 +struct S;
 +fn f() {
 +    fn inner() -> S {
 +        let s = inner();
 +    }
 +}"#,
 +        expect![[r#"
 +            17..73 '{     ...   } }': ()
 +            39..71 '{     ...     }': S
 +            53..54 's': S
 +            57..62 'inner': fn inner() -> S
 +            57..64 'inner()': S
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn infer_default_trait_type_parameter() {
 +    check_infer(
 +        r#"
 +struct A;
 +
 +trait Op<RHS=Self> {
 +    type Output;
 +
 +    fn do_op(self, rhs: RHS) -> Self::Output;
 +}
 +
 +impl Op for A {
 +    type Output = bool;
 +
 +    fn do_op(self, rhs: Self) -> Self::Output {
 +        true
 +    }
 +}
 +
 +fn test() {
 +    let x = A;
 +    let y = A;
 +    let r = x.do_op(y);
 +}"#,
 +        expect![[r#"
 +            63..67 'self': Self
 +            69..72 'rhs': RHS
 +            153..157 'self': A
 +            159..162 'rhs': A
 +            186..206 '{     ...     }': bool
 +            196..200 'true': bool
 +            220..277 '{     ...(y); }': ()
 +            230..231 'x': A
 +            234..235 'A': A
 +            245..246 'y': A
 +            249..250 'A': A
 +            260..261 'r': bool
 +            264..265 'x': A
 +            264..274 'x.do_op(y)': bool
 +            272..273 'y': A
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn qualified_path_as_qualified_trait() {
 +    check_infer(
 +        r#"
 +mod foo {
 +
 +    pub trait Foo {
 +        type Target;
 +    }
 +    pub trait Bar {
 +        type Output;
 +        fn boo() -> Self::Output {
 +            loop {}
 +        }
 +    }
 +}
 +
 +struct F;
 +impl foo::Foo for F {
 +    type Target = ();
 +}
 +impl foo::Bar for F {
 +    type Output = <F as foo::Foo>::Target;
 +}
 +
 +fn foo() {
 +    use foo::Bar;
 +    let x = <F as Bar>::boo();
 +}"#,
 +        expect![[r#"
 +            132..163 '{     ...     }': Bar::Output<Self>
 +            146..153 'loop {}': !
 +            151..153 '{}': ()
 +            306..358 '{     ...o(); }': ()
 +            334..335 'x': ()
 +            338..353 '<F as Bar>::boo': fn boo<F>() -> <F as Bar>::Output
 +            338..355 '<F as ...:boo()': ()
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn renamed_extern_crate_in_block() {
 +    check_types(
 +        r#"
 +//- /lib.rs crate:lib deps:serde
 +use serde::Deserialize;
 +
 +struct Foo {}
 +
 +const _ : () = {
 +    extern crate serde as _serde;
 +    impl _serde::Deserialize for Foo {
 +        fn deserialize() -> u8 { 0 }
 +    }
 +};
 +
 +fn foo() {
 +    Foo::deserialize();
 +  //^^^^^^^^^^^^^^^^^^ u8
 +}
 +
 +//- /serde.rs crate:serde
 +
 +pub trait Deserialize {
 +    fn deserialize() -> u8;
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn bin_op_with_rhs_is_self_for_assoc_bound() {
 +    check_no_mismatches(
 +        r#"//- minicore: eq
 +        fn repro<T>(t: T) -> bool
 +where
 +    T: Request,
 +    T::Output: Convertable,
 +{
 +    let a = execute(&t).convert();
 +    let b = execute(&t).convert();
 +    a.eq(&b);
 +    let a = execute(&t).convert2();
 +    let b = execute(&t).convert2();
 +    a.eq(&b)
 +}
 +fn execute<T>(t: &T) -> T::Output
 +where
 +    T: Request,
 +{
 +    <T as Request>::output()
 +}
 +trait Convertable {
 +    type TraitSelf: PartialEq<Self::TraitSelf>;
 +    type AssocAsDefaultSelf: PartialEq;
 +    fn convert(self) -> Self::AssocAsDefaultSelf;
 +    fn convert2(self) -> Self::TraitSelf;
 +}
 +trait Request {
 +    type Output;
 +    fn output() -> Self::Output;
 +}
 +     "#,
 +    );
 +}
 +
 +#[test]
 +fn bin_op_adt_with_rhs_primitive() {
 +    check_infer_with_mismatches(
 +        r#"
 +#[lang = "add"]
 +pub trait Add<Rhs = Self> {
 +    type Output;
 +    fn add(self, rhs: Rhs) -> Self::Output;
 +}
 +
 +struct Wrapper(u32);
 +impl Add<u32> for Wrapper {
 +    type Output = Self;
 +    fn add(self, rhs: u32) -> Wrapper {
 +        Wrapper(rhs)
 +    }
 +}
 +fn main(){
 +    let wrapped = Wrapper(10);
 +    let num: u32 = 2;
 +    let res = wrapped + num;
 +
 +}"#,
 +        expect![[r#"
 +            72..76 'self': Self
 +            78..81 'rhs': Rhs
 +            192..196 'self': Wrapper
 +            198..201 'rhs': u32
 +            219..247 '{     ...     }': Wrapper
 +            229..236 'Wrapper': Wrapper(u32) -> Wrapper
 +            229..241 'Wrapper(rhs)': Wrapper
 +            237..240 'rhs': u32
 +            259..345 '{     ...um;  }': ()
 +            269..276 'wrapped': Wrapper
 +            279..286 'Wrapper': Wrapper(u32) -> Wrapper
 +            279..290 'Wrapper(10)': Wrapper
 +            287..289 '10': u32
 +            300..303 'num': u32
 +            311..312 '2': u32
 +            322..325 'res': Wrapper
 +            328..335 'wrapped': Wrapper
 +            328..341 'wrapped + num': Wrapper
 +            338..341 'num': u32
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn array_length() {
 +    check_infer(
 +        r#"
 +trait T {
 +    type Output;
 +    fn do_thing(&self) -> Self::Output;
 +}
 +
 +impl T for [u8; 4] {
 +    type Output = usize;
 +    fn do_thing(&self) -> Self::Output {
 +        2
 +    }
 +}
 +
 +impl T for [u8; 2] {
 +    type Output = u8;
 +    fn do_thing(&self) -> Self::Output {
 +        2
 +    }
 +}
 +
 +fn main() {
 +    let v = [0u8; 2];
 +    let v2 = v.do_thing();
 +    let v3 = [0u8; 4];
 +    let v4 = v3.do_thing();
 +}
 +"#,
 +        expect![[r#"
 +            44..48 'self': &Self
 +            133..137 'self': &[u8; 4]
 +            155..172 '{     ...     }': usize
 +            165..166 '2': usize
 +            236..240 'self': &[u8; 2]
 +            258..275 '{     ...     }': u8
 +            268..269 '2': u8
 +            289..392 '{     ...g(); }': ()
 +            299..300 'v': [u8; 2]
 +            303..311 '[0u8; 2]': [u8; 2]
 +            304..307 '0u8': u8
 +            309..310 '2': usize
 +            321..323 'v2': u8
 +            326..327 'v': [u8; 2]
 +            326..338 'v.do_thing()': u8
 +            348..350 'v3': [u8; 4]
 +            353..361 '[0u8; 4]': [u8; 4]
 +            354..357 '0u8': u8
 +            359..360 '4': usize
 +            371..373 'v4': usize
 +            376..378 'v3': [u8; 4]
 +            376..389 'v3.do_thing()': usize
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn const_generics() {
 +    check_infer(
 +        r#"
 +trait T {
 +    type Output;
 +    fn do_thing(&self) -> Self::Output;
 +}
 +
 +impl<const L: usize> T for [u8; L] {
 +    type Output = [u8; L];
 +    fn do_thing(&self) -> Self::Output {
 +        *self
 +    }
 +}
 +
 +fn main() {
 +    let v = [0u8; 2];
 +    let v2 = v.do_thing();
 +}
 +"#,
 +        expect![[r#"
 +            44..48 'self': &Self
 +            151..155 'self': &[u8; L]
 +            173..194 '{     ...     }': [u8; L]
 +            183..188 '*self': [u8; L]
 +            184..188 'self': &[u8; L]
 +            208..260 '{     ...g(); }': ()
 +            218..219 'v': [u8; 2]
 +            222..230 '[0u8; 2]': [u8; 2]
 +            223..226 '0u8': u8
 +            228..229 '2': usize
 +            240..242 'v2': [u8; 2]
 +            245..246 'v': [u8; 2]
 +            245..257 'v.do_thing()': [u8; 2]
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn fn_returning_unit() {
 +    check_infer_with_mismatches(
 +        r#"
 +//- minicore: fn
 +fn test<F: FnOnce()>(f: F) {
 +    let _: () = f();
 +}"#,
 +        expect![[r#"
 +            21..22 'f': F
 +            27..51 '{     ...f(); }': ()
 +            37..38 '_': ()
 +            45..46 'f': F
 +            45..48 'f()': ()
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn trait_in_scope_of_trait_impl() {
 +    check_infer(
 +        r#"
 +mod foo {
 +    pub trait Foo {
 +        fn foo(self);
 +        fn bar(self) -> usize { 0 }
 +    }
 +}
 +impl foo::Foo for u32 {
 +    fn foo(self) {
 +        let _x = self.bar();
 +    }
 +}
 +    "#,
 +        expect![[r#"
 +            45..49 'self': Self
 +            67..71 'self': Self
 +            82..87 '{ 0 }': usize
 +            84..85 '0': usize
 +            131..135 'self': u32
 +            137..173 '{     ...     }': ()
 +            151..153 '_x': usize
 +            156..160 'self': u32
 +            156..166 'self.bar()': usize
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn infer_async_ret_type() {
 +    check_types(
 +        r#"
 +//- minicore: future, result
 +struct Fooey;
 +
 +impl Fooey {
 +    fn collect<B: Convert>(self) -> B {
 +        B::new()
 +    }
 +}
 +
 +trait Convert {
 +    fn new() -> Self;
 +}
 +impl Convert for u32 {
 +    fn new() -> Self { 0 }
 +}
 +
 +async fn get_accounts() -> Result<u32, ()> {
 +    let ret = Fooey.collect();
 +    //        ^^^^^^^^^^^^^^^ u32
 +    Ok(ret)
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn local_impl_1() {
 +    check!(block_local_impls);
 +    check_types(
 +        r#"
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +}
 +
 +fn test() {
 +    struct S;
 +    impl Trait<u32> for S {
 +        fn foo(&self) -> u32 { 0 }
 +    }
 +
 +    S.foo();
 + // ^^^^^^^ u32
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn local_impl_2() {
 +    check!(block_local_impls);
 +    check_types(
 +        r#"
 +struct S;
 +
 +fn test() {
 +    trait Trait<T> {
 +        fn foo(&self) -> T;
 +    }
 +    impl Trait<u32> for S {
 +        fn foo(&self) -> u32 { 0 }
 +    }
 +
 +    S.foo();
 + // ^^^^^^^ u32
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn local_impl_3() {
 +    check!(block_local_impls);
 +    check_types(
 +        r#"
 +trait Trait<T> {
 +    fn foo(&self) -> T;
 +}
 +
 +fn test() {
 +    struct S1;
 +    {
 +        struct S2;
 +
 +        impl Trait<S1> for S2 {
 +            fn foo(&self) -> S1 { S1 }
 +        }
 +
 +        S2.foo();
 +     // ^^^^^^^^ S1
 +    }
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn associated_type_sized_bounds() {
 +    check_infer(
 +        r#"
 +//- minicore: sized
 +struct Yes;
 +trait IsSized { const IS_SIZED: Yes; }
 +impl<T: Sized> IsSized for T { const IS_SIZED: Yes = Yes; }
 +
 +trait Foo {
 +    type Explicit: Sized;
 +    type Implicit;
 +    type Relaxed: ?Sized;
 +}
 +fn f<F: Foo>() {
 +    F::Explicit::IS_SIZED;
 +    F::Implicit::IS_SIZED;
 +    F::Relaxed::IS_SIZED;
 +}
 +"#,
 +        expect![[r#"
 +            104..107 'Yes': Yes
 +            212..295 '{     ...ZED; }': ()
 +            218..239 'F::Exp..._SIZED': Yes
 +            245..266 'F::Imp..._SIZED': Yes
 +            272..292 'F::Rel..._SIZED': {unknown}
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn dyn_map() {
 +    check_types(
 +        r#"
 +pub struct Key<K, V, P = (K, V)> {}
 +
 +pub trait Policy {
 +    type K;
 +    type V;
 +}
 +
 +impl<K, V> Policy for (K, V) {
 +    type K = K;
 +    type V = V;
 +}
 +
 +pub struct KeyMap<KEY> {}
 +
 +impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
 +    pub fn get(&self, key: &P::K) -> P::V {
 +        loop {}
 +    }
 +}
 +
 +struct Fn {}
 +struct FunctionId {}
 +
 +fn test() {
 +    let key_map: &KeyMap<Key<Fn, FunctionId>> = loop {};
 +    let key;
 +    let result = key_map.get(key);
 +      //^^^^^^ FunctionId
 +}
 +"#,
 +    )
 +}
 +
 +#[test]
 +fn dyn_multiple_auto_traits_in_different_order() {
 +    check_no_mismatches(
 +        r#"
 +auto trait Send {}
 +auto trait Sync {}
 +
 +fn f(t: &(dyn Sync + Send)) {}
 +fn g(t: &(dyn Send + Sync)) {
 +    f(t);
 +}
 +        "#,
 +    );
 +
 +    check_no_mismatches(
 +        r#"
 +auto trait Send {}
 +auto trait Sync {}
 +trait T {}
 +
 +fn f(t: &(dyn T + Send + Sync)) {}
 +fn g(t: &(dyn Sync + T + Send)) {
 +    f(t);
 +}
 +        "#,
 +    );
 +
 +    check_infer_with_mismatches(
 +        r#"
 +auto trait Send {}
 +auto trait Sync {}
 +trait T1 {}
 +trait T2 {}
 +
 +fn f(t: &(dyn T1 + T2 + Send + Sync)) {}
 +fn g(t: &(dyn Sync + T2 + T1 + Send)) {
 +    f(t);
 +}
 +        "#,
 +        expect![[r#"
 +            68..69 't': &{unknown}
 +            101..103 '{}': ()
 +            109..110 't': &{unknown}
 +            142..155 '{     f(t); }': ()
 +            148..149 'f': fn f(&{unknown})
 +            148..152 'f(t)': ()
 +            150..151 't': &{unknown}
 +        "#]],
 +    );
 +
 +    check_no_mismatches(
 +        r#"
 +auto trait Send {}
 +auto trait Sync {}
 +trait T {
 +    type Proj: Send + Sync;
 +}
 +
 +fn f(t: &(dyn T<Proj = ()>  + Send + Sync)) {}
 +fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
 +    f(t);
 +}
 +        "#,
 +    );
 +}
 +
++#[test]
++fn dyn_multiple_projection_bounds() {
++    check_no_mismatches(
++        r#"
++trait Trait {
++    type T;
++    type U;
++}
++
++fn f(t: &dyn Trait<T = (), U = ()>) {}
++fn g(t: &dyn Trait<U = (), T = ()>) {
++    f(t);
++}
++        "#,
++    );
++
++    check_types(
++        r#"
++trait Trait {
++    type T;
++}
++
++fn f(t: &dyn Trait<T = (), T = ()>) {}
++   //^&{unknown}
++        "#,
++    );
++}
++
 +#[test]
 +fn dyn_duplicate_auto_trait() {
 +    check_no_mismatches(
 +        r#"
 +auto trait Send {}
 +
 +fn f(t: &(dyn Send + Send)) {}
 +fn g(t: &(dyn Send)) {
 +    f(t);
 +}
 +        "#,
 +    );
 +
 +    check_no_mismatches(
 +        r#"
 +auto trait Send {}
 +trait T {}
 +
 +fn f(t: &(dyn T + Send + Send)) {}
 +fn g(t: &(dyn T + Send)) {
 +    f(t);
 +}
 +        "#,
 +    );
 +}
index b16f6fe03ae8e12716352b22dc7c514a189e0eea,0000000000000000000000000000000000000000..1a7919a5a104d64ca22c3e6b09392ea5670587be
mode 100644,000000..100644
--- /dev/null
@@@ -1,1709 -1,0 +1,1710 @@@
 +use std::iter::{self, Peekable};
 +
 +use either::Either;
 +use hir::{Adt, Crate, HasAttrs, HasSource, ModuleDef, Semantics};
 +use ide_db::RootDatabase;
 +use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
 +use itertools::Itertools;
++use syntax::ast::edit_in_place::Removable;
 +use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
 +
 +use crate::{
 +    utils::{self, render_snippet, Cursor},
 +    AssistContext, AssistId, AssistKind, Assists,
 +};
 +
 +// Assist: add_missing_match_arms
 +//
 +// Adds missing clauses to a `match` expression.
 +//
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         $0
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         $0Action::Move { distance } => todo!(),
 +//         Action::Stop => todo!(),
 +//     }
 +// }
 +// ```
 +pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
 +    let match_arm_list = match_expr.match_arm_list()?;
 +    let target_range = ctx.sema.original_range(match_expr.syntax()).range;
 +
 +    if let None = cursor_at_trivial_match_arm_list(ctx, &match_expr, &match_arm_list) {
 +        let arm_list_range = ctx.sema.original_range(match_arm_list.syntax()).range;
 +        let cursor_in_range = arm_list_range.contains_range(ctx.selection_trimmed());
 +        if cursor_in_range {
 +            cov_mark::hit!(not_applicable_outside_of_range_right);
 +            return None;
 +        }
 +    }
 +
 +    let expr = match_expr.expr()?;
 +
 +    let mut has_catch_all_arm = false;
 +
 +    let top_lvl_pats: Vec<_> = match_arm_list
 +        .arms()
 +        .filter_map(|arm| Some((arm.pat()?, arm.guard().is_some())))
 +        .flat_map(|(pat, has_guard)| {
 +            match pat {
 +                // Special case OrPat as separate top-level pats
 +                Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
 +                _ => Either::Right(iter::once(pat)),
 +            }
 +            .map(move |pat| (pat, has_guard))
 +        })
 +        .map(|(pat, has_guard)| {
 +            has_catch_all_arm |= !has_guard && matches!(pat, Pat::WildcardPat(_));
 +            pat
 +        })
 +        // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
 +        .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
 +        .collect();
 +
 +    let module = ctx.sema.scope(expr.syntax())?.module();
 +    let (mut missing_pats, is_non_exhaustive): (
 +        Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
 +        bool,
 +    ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
 +        let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
 +
 +        let variants = enum_def.variants(ctx.db());
 +
 +        let missing_pats = variants
 +            .into_iter()
 +            .filter_map(|variant| {
 +                Some((
 +                    build_pat(ctx.db(), module, variant)?,
 +                    variant.should_be_hidden(ctx.db(), module.krate()),
 +                ))
 +            })
 +            .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
 +
 +        let option_enum = FamousDefs(&ctx.sema, module.krate()).core_option_Option().map(lift_enum);
 +        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
 +            // Match `Some` variant first.
 +            cov_mark::hit!(option_order);
 +            Box::new(missing_pats.rev())
 +        } else {
 +            Box::new(missing_pats)
 +        };
 +        (missing_pats.peekable(), is_non_exhaustive)
 +    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
 +        let is_non_exhaustive =
 +            enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate()));
 +
 +        let mut n_arms = 1;
 +        let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
 +            .into_iter()
 +            .map(|enum_def| enum_def.variants(ctx.db()))
 +            .inspect(|variants| n_arms *= variants.len())
 +            .collect();
 +
 +        // When calculating the match arms for a tuple of enums, we want
 +        // to create a match arm for each possible combination of enum
 +        // values. The `multi_cartesian_product` method transforms
 +        // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
 +        // where each tuple represents a proposed match arm.
 +
 +        // A number of arms grows very fast on even a small tuple of large enums.
 +        // We skip the assist beyond an arbitrary threshold.
 +        if n_arms > 256 {
 +            return None;
 +        }
 +        let missing_pats = variants_of_enums
 +            .into_iter()
 +            .multi_cartesian_product()
 +            .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation))
 +            .map(|variants| {
 +                let is_hidden = variants
 +                    .iter()
 +                    .any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
 +                let patterns =
 +                    variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
 +
 +                (ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
 +            })
 +            .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
 +        ((Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), is_non_exhaustive)
 +    } else {
 +        return None;
 +    };
 +
 +    let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
 +
 +    if !needs_catch_all_arm && missing_pats.peek().is_none() {
 +        return None;
 +    }
 +
 +    acc.add(
 +        AssistId("add_missing_match_arms", AssistKind::QuickFix),
 +        "Fill match arms",
 +        target_range,
 +        |builder| {
 +            let new_match_arm_list = match_arm_list.clone_for_update();
 +            let missing_arms = missing_pats
 +                .map(|(pat, hidden)| {
 +                    (make::match_arm(iter::once(pat), None, make::ext::expr_todo()), hidden)
 +                })
 +                .map(|(it, hidden)| (it.clone_for_update(), hidden));
 +
 +            let catch_all_arm = new_match_arm_list
 +                .arms()
 +                .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
 +            if let Some(arm) = catch_all_arm {
 +                let is_empty_expr = arm.expr().map_or(true, |e| match e {
 +                    ast::Expr::BlockExpr(b) => {
 +                        b.statements().next().is_none() && b.tail_expr().is_none()
 +                    }
 +                    ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
 +                    _ => false,
 +                });
 +                if is_empty_expr {
 +                    arm.remove();
 +                } else {
 +                    cov_mark::hit!(add_missing_match_arms_empty_expr);
 +                }
 +            }
 +            let mut first_new_arm = None;
 +            for (arm, hidden) in missing_arms {
 +                if hidden {
 +                    needs_catch_all_arm = !has_catch_all_arm;
 +                } else {
 +                    first_new_arm.get_or_insert_with(|| arm.clone());
 +                    new_match_arm_list.add_arm(arm);
 +                }
 +            }
 +            if needs_catch_all_arm && !has_catch_all_arm {
 +                cov_mark::hit!(added_wildcard_pattern);
 +                let arm = make::match_arm(
 +                    iter::once(make::wildcard_pat().into()),
 +                    None,
 +                    make::ext::expr_todo(),
 +                )
 +                .clone_for_update();
 +                first_new_arm.get_or_insert_with(|| arm.clone());
 +                new_match_arm_list.add_arm(arm);
 +            }
 +
 +            let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
 +            match (first_new_arm, ctx.config.snippet_cap) {
 +                (Some(first_new_arm), Some(cap)) => {
 +                    let extend_lifetime;
 +                    let cursor =
 +                        match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
 +                        {
 +                            Some(it) => {
 +                                extend_lifetime = it.syntax().clone();
 +                                Cursor::Replace(&extend_lifetime)
 +                            }
 +                            None => Cursor::Before(first_new_arm.syntax()),
 +                        };
 +                    let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
 +                    builder.replace_snippet(cap, old_range, snippet);
 +                }
 +                _ => builder.replace(old_range, new_match_arm_list.to_string()),
 +            }
 +        },
 +    )
 +}
 +
 +fn cursor_at_trivial_match_arm_list(
 +    ctx: &AssistContext<'_>,
 +    match_expr: &MatchExpr,
 +    match_arm_list: &MatchArmList,
 +) -> Option<()> {
 +    // match x { $0 }
 +    if match_arm_list.arms().next() == None {
 +        cov_mark::hit!(add_missing_match_arms_empty_body);
 +        return Some(());
 +    }
 +
 +    // match x {
 +    //     bar => baz,
 +    //     $0
 +    // }
 +    if let Some(last_arm) = match_arm_list.arms().last() {
 +        let last_arm_range = last_arm.syntax().text_range();
 +        let match_expr_range = match_expr.syntax().text_range();
 +        if last_arm_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() {
 +            cov_mark::hit!(add_missing_match_arms_end_of_last_arm);
 +            return Some(());
 +        }
 +    }
 +
 +    // match { _$0 => {...} }
 +    let wild_pat = ctx.find_node_at_offset_with_descend::<ast::WildcardPat>()?;
 +    let arm = wild_pat.syntax().parent().and_then(ast::MatchArm::cast)?;
 +    let arm_match_expr = arm.syntax().ancestors().nth(2).and_then(ast::MatchExpr::cast)?;
 +    if arm_match_expr == *match_expr {
 +        cov_mark::hit!(add_missing_match_arms_trivial_arm);
 +        return Some(());
 +    }
 +
 +    None
 +}
 +
 +fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
 +    !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
 +}
 +
 +// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
 +fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
 +    match (pat, var) {
 +        (Pat::WildcardPat(_), _) => true,
 +        (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
 +            tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
 +        }
 +        _ => utils::does_pat_match_variant(pat, var),
 +    }
 +}
 +
 +#[derive(Eq, PartialEq, Clone, Copy)]
 +enum ExtendedEnum {
 +    Bool,
 +    Enum(hir::Enum),
 +}
 +
 +#[derive(Eq, PartialEq, Clone, Copy)]
 +enum ExtendedVariant {
 +    True,
 +    False,
 +    Variant(hir::Variant),
 +}
 +
 +impl ExtendedVariant {
 +    fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool {
 +        match self {
 +            ExtendedVariant::Variant(var) => {
 +                var.attrs(db).has_doc_hidden() && var.module(db).krate() != krate
 +            }
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn lift_enum(e: hir::Enum) -> ExtendedEnum {
 +    ExtendedEnum::Enum(e)
 +}
 +
 +impl ExtendedEnum {
 +    fn is_non_exhaustive(self, db: &RootDatabase, krate: Crate) -> bool {
 +        match self {
 +            ExtendedEnum::Enum(e) => {
 +                e.attrs(db).by_key("non_exhaustive").exists() && e.module(db).krate() != krate
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
 +        match self {
 +            ExtendedEnum::Enum(e) => {
 +                e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
 +            }
 +            ExtendedEnum::Bool => {
 +                Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
 +            }
 +        }
 +    }
 +}
 +
 +fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
 +    sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
 +        Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
 +        _ => ty.is_bool().then(|| ExtendedEnum::Bool),
 +    })
 +}
 +
 +fn resolve_tuple_of_enum_def(
 +    sema: &Semantics<'_, RootDatabase>,
 +    expr: &ast::Expr,
 +) -> Option<Vec<ExtendedEnum>> {
 +    sema.type_of_expr(expr)?
 +        .adjusted()
 +        .tuple_fields(sema.db)
 +        .iter()
 +        .map(|ty| {
 +            ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
 +                Some(Adt::Enum(e)) => Some(lift_enum(e)),
 +                // For now we only handle expansion for a tuple of enums. Here
 +                // we map non-enum items to None and rely on `collect` to
 +                // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
 +                _ => ty.is_bool().then(|| ExtendedEnum::Bool),
 +            })
 +        })
 +        .collect()
 +}
 +
 +fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
 +    match var {
 +        ExtendedVariant::Variant(var) => {
 +            let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
 +
 +            // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
 +            let pat: ast::Pat = match var.source(db)?.value.kind() {
 +                ast::StructKind::Tuple(field_list) => {
 +                    let pats =
 +                        iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
 +                    make::tuple_struct_pat(path, pats).into()
 +                }
 +                ast::StructKind::Record(field_list) => {
 +                    let pats = field_list
 +                        .fields()
 +                        .map(|f| make::ext::simple_ident_pat(f.name().unwrap()).into());
 +                    make::record_pat(path, pats).into()
 +                }
 +                ast::StructKind::Unit => make::path_pat(path),
 +            };
 +
 +            Some(pat)
 +        }
 +        ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
 +        ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{
 +        check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
 +    };
 +
 +    use super::add_missing_match_arms;
 +
 +    #[test]
 +    fn all_match_arms_provided() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +enum A {
 +    As,
 +    Bs{x:i32, y:Option<i32>},
 +    Cs(i32, Option<i32>),
 +}
 +fn main() {
 +    match A::As$0 {
 +        A::As,
 +        A::Bs{x,y:Some(_)} => {}
 +        A::Cs(_, Some(_)) => {}
 +    }
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_outside_of_range_left() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +enum A { X, Y }
 +
 +fn foo(a: A) {
 +    $0 match a {
 +        A::X => { }
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_outside_of_range_right() {
 +        cov_mark::check!(not_applicable_outside_of_range_right);
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +enum A { X, Y }
 +
 +fn foo(a: A) {
 +    match a {$0
 +        A::X => { }
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn all_boolean_match_arms_provided() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match a$0 {
 +        true => {}
 +        false => {}
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn tuple_of_non_enum() {
 +        // for now this case is not handled, although it potentially could be
 +        // in the future
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +fn main() {
 +    match (0, false)$0 {
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_boolean() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match a$0 {
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: bool) {
 +    match a {
 +        $0true => todo!(),
 +        false => todo!(),
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn partial_fill_boolean() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match a$0 {
 +        true => {}
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: bool) {
 +    match a {
 +        true => {}
 +        $0false => todo!(),
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn all_boolean_tuple_arms_provided() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match (a, a)$0 {
 +        (true, true) => {}
 +        (true, false) => {}
 +        (false, true) => {}
 +        (false, false) => {}
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn fill_boolean_tuple() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match (a, a)$0 {
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: bool) {
 +    match (a, a) {
 +        $0(true, true) => todo!(),
 +        (true, false) => todo!(),
 +        (false, true) => todo!(),
 +        (false, false) => todo!(),
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn partial_fill_boolean_tuple() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(a: bool) {
 +    match (a, a)$0 {
 +        (false, true) => {}
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: bool) {
 +    match (a, a) {
 +        (false, true) => {}
 +        $0(true, true) => todo!(),
 +        (true, false) => todo!(),
 +        (false, false) => todo!(),
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn partial_fill_record_tuple() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A {
 +    As,
 +    Bs { x: i32, y: Option<i32> },
 +    Cs(i32, Option<i32>),
 +}
 +fn main() {
 +    match A::As$0 {
 +        A::Bs { x, y: Some(_) } => {}
 +        A::Cs(_, Some(_)) => {}
 +    }
 +}
 +"#,
 +            r#"
 +enum A {
 +    As,
 +    Bs { x: i32, y: Option<i32> },
 +    Cs(i32, Option<i32>),
 +}
 +fn main() {
 +    match A::As {
 +        A::Bs { x, y: Some(_) } => {}
 +        A::Cs(_, Some(_)) => {}
 +        $0A::As => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn partial_fill_option() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- minicore: option
 +fn main() {
 +    match None$0 {
 +        None => {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match None {
 +        None => {}
 +        Some(${0:_}) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn partial_fill_or_pat() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { As, Bs, Cs(Option<i32>) }
 +fn main() {
 +    match A::As$0 {
 +        A::Cs(_) | A::Bs => {}
 +    }
 +}
 +"#,
 +            r#"
 +enum A { As, Bs, Cs(Option<i32>) }
 +fn main() {
 +    match A::As {
 +        A::Cs(_) | A::Bs => {}
 +        $0A::As => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn partial_fill() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { As, Bs, Cs, Ds(String), Es(B) }
 +enum B { Xs, Ys }
 +fn main() {
 +    match A::As$0 {
 +        A::Bs if 0 < 1 => {}
 +        A::Ds(_value) => { let x = 1; }
 +        A::Es(B::Xs) => (),
 +    }
 +}
 +"#,
 +            r#"
 +enum A { As, Bs, Cs, Ds(String), Es(B) }
 +enum B { Xs, Ys }
 +fn main() {
 +    match A::As {
 +        A::Bs if 0 < 1 => {}
 +        A::Ds(_value) => { let x = 1; }
 +        A::Es(B::Xs) => (),
 +        $0A::As => todo!(),
 +        A::Cs => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn partial_fill_bind_pat() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { As, Bs, Cs(Option<i32>) }
 +fn main() {
 +    match A::As$0 {
 +        A::As(_) => {}
 +        a @ A::Bs(_) => {}
 +    }
 +}
 +"#,
 +            r#"
 +enum A { As, Bs, Cs(Option<i32>) }
 +fn main() {
 +    match A::As {
 +        A::As(_) => {}
 +        a @ A::Bs(_) => {}
 +        A::Cs(${0:_}) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_empty_body() {
 +        cov_mark::check!(add_missing_match_arms_empty_body);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
 +
 +fn main() {
 +    let a = A::As;
 +    match a {$0}
 +}
 +"#,
 +            r#"
 +enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
 +
 +fn main() {
 +    let a = A::As;
 +    match a {
 +        $0A::As => todo!(),
 +        A::Bs => todo!(),
 +        A::Cs(_) => todo!(),
 +        A::Ds(_, _) => todo!(),
 +        A::Es { x, y } => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_end_of_last_arm() {
 +        cov_mark::check!(add_missing_match_arms_end_of_last_arm);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a, b) {
 +        (A::Two, B::One) => {},$0
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a, b) {
 +        (A::Two, B::One) => {},
 +        $0(A::One, B::One) => todo!(),
 +        (A::One, B::Two) => todo!(),
 +        (A::Two, B::Two) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_tuple_of_enum() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a$0, b) {}
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a, b) {
 +        $0(A::One, B::One) => todo!(),
 +        (A::One, B::Two) => todo!(),
 +        (A::Two, B::One) => todo!(),
 +        (A::Two, B::Two) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_tuple_of_enum_ref() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (&a$0, &b) {}
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (&a, &b) {
 +        $0(A::One, B::One) => todo!(),
 +        (A::One, B::Two) => todo!(),
 +        (A::Two, B::One) => todo!(),
 +        (A::Two, B::Two) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_tuple_of_enum_partial() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a$0, b) {
 +        (A::Two, B::One) => {}
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a, b) {
 +        (A::Two, B::One) => {}
 +        $0(A::One, B::One) => todo!(),
 +        (A::One, B::Two) => todo!(),
 +        (A::Two, B::Two) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_tuple_of_enum_partial_with_wildcards() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- minicore: option
 +fn main() {
 +    let a = Some(1);
 +    let b = Some(());
 +    match (a$0, b) {
 +        (Some(_), _) => {}
 +        (None, Some(_)) => {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let a = Some(1);
 +    let b = Some(());
 +    match (a, b) {
 +        (Some(_), _) => {}
 +        (None, Some(_)) => {}
 +        $0(None, None) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_partial_with_deep_pattern() {
 +        // Fixme: cannot handle deep patterns
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +//- minicore: option
 +fn main() {
 +    match $0Some(true) {
 +        Some(true) => {}
 +        None => {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_tuple_of_enum_not_applicable() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +enum B { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    let b = B::One;
 +    match (a$0, b) {
 +        (A::Two, B::One) => {}
 +        (A::One, B::One) => {}
 +        (A::One, B::Two) => {}
 +        (A::Two, B::Two) => {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_single_element_tuple_of_enum() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    match (a$0, ) {
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +
 +fn main() {
 +    let a = A::One;
 +    match (a, ) {
 +        $0(A::One,) => todo!(),
 +        (A::Two,) => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fill_match_arm_refs() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { As }
 +
 +fn foo(a: &A) {
 +    match a$0 {
 +    }
 +}
 +"#,
 +            r#"
 +enum A { As }
 +
 +fn foo(a: &A) {
 +    match a {
 +        $0A::As => todo!(),
 +    }
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A {
 +    Es { x: usize, y: usize }
 +}
 +
 +fn foo(a: &mut A) {
 +    match a$0 {
 +    }
 +}
 +"#,
 +            r#"
 +enum A {
 +    Es { x: usize, y: usize }
 +}
 +
 +fn foo(a: &mut A) {
 +    match a {
 +        $0A::Es { x, y } => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_target_simple() {
 +        check_assist_target(
 +            add_missing_match_arms,
 +            r#"
 +enum E { X, Y }
 +
 +fn main() {
 +    match E::X$0 {}
 +}
 +"#,
 +            "match E::X {}",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_target_complex() {
 +        check_assist_target(
 +            add_missing_match_arms,
 +            r#"
 +enum E { X, Y }
 +
 +fn main() {
 +    match E::X$0 {
 +        E::X => {}
 +    }
 +}
 +"#,
 +            "match E::X {
 +        E::X => {}
 +    }",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_trivial_arm() {
 +        cov_mark::check!(add_missing_match_arms_trivial_arm);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum E { X, Y }
 +
 +fn main() {
 +    match E::X {
 +        $0_ => {}
 +    }
 +}
 +"#,
 +            r#"
 +enum E { X, Y }
 +
 +fn main() {
 +    match E::X {
 +        $0E::X => todo!(),
 +        E::Y => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wildcard_inside_expression_not_applicable() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +enum E { X, Y }
 +
 +fn foo(e : E) {
 +    match e {
 +        _ => {
 +            println!("1");$0
 +            println!("2");
 +        }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_qualifies_path() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +mod foo { pub enum E { X, Y } }
 +use foo::E::X;
 +
 +fn main() {
 +    match X {
 +        $0
 +    }
 +}
 +"#,
 +            r#"
 +mod foo { pub enum E { X, Y } }
 +use foo::E::X;
 +
 +fn main() {
 +    match X {
 +        $0X => todo!(),
 +        foo::E::Y => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_preserves_comments() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +fn foo(a: A) {
 +    match a $0 {
 +        // foo bar baz
 +        A::One => {}
 +        // This is where the rest should be
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +fn foo(a: A) {
 +    match a  {
 +        // foo bar baz
 +        A::One => {}
 +        $0A::Two => todo!(),
 +        // This is where the rest should be
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_preserves_comments_empty() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two }
 +fn foo(a: A) {
 +    match a {
 +        // foo bar baz$0
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two }
 +fn foo(a: A) {
 +    match a {
 +        $0A::One => todo!(),
 +        A::Two => todo!(),
 +        // foo bar baz
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_missing_match_arms_placeholder() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two, }
 +fn foo(a: A) {
 +    match a$0 {
 +        _ => (),
 +    }
 +}
 +"#,
 +            r#"
 +enum A { One, Two, }
 +fn foo(a: A) {
 +    match a {
 +        $0A::One => todo!(),
 +        A::Two => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn option_order() {
 +        cov_mark::check!(option_order);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- minicore: option
 +fn foo(opt: Option<i32>) {
 +    match opt$0 {
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(opt: Option<i32>) {
 +    match opt {
 +        Some(${0:_}) => todo!(),
 +        None => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_inside_macro_call() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +macro_rules! m { ($expr:expr) => {$expr}}
 +enum Test {
 +    A,
 +    B,
 +    C,
 +}
 +
 +fn foo(t: Test) {
 +    m!(match t$0 {});
 +}"#,
 +            r#"
 +macro_rules! m { ($expr:expr) => {$expr}}
 +enum Test {
 +    A,
 +    B,
 +    C,
 +}
 +
 +fn foo(t: Test) {
 +    m!(match t {
 +    $0Test::A => todo!(),
 +    Test::B => todo!(),
 +    Test::C => todo!(),
 +});
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn lazy_computation() {
 +        // Computing a single missing arm is enough to determine applicability of the assist.
 +        cov_mark::check_count!(add_missing_match_arms_lazy_computation, 1);
 +        check_assist_unresolved(
 +            add_missing_match_arms,
 +            r#"
 +enum A { One, Two, }
 +fn foo(tuple: (A, A)) {
 +    match $0tuple {};
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn adds_comma_before_new_arms() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(t: bool) {
 +    match $0t {
 +        true => 1 + 2
 +    }
 +}"#,
 +            r#"
 +fn foo(t: bool) {
 +    match t {
 +        true => 1 + 2,
 +        $0false => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_add_extra_comma() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(t: bool) {
 +    match $0t {
 +        true => 1 + 2,
 +    }
 +}"#,
 +            r#"
 +fn foo(t: bool) {
 +    match t {
 +        true => 1 + 2,
 +        $0false => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_remove_catch_all_with_non_empty_expr() {
 +        cov_mark::check!(add_missing_match_arms_empty_expr);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +fn foo(t: bool) {
 +    match $0t {
 +        _ => 1 + 2,
 +    }
 +}"#,
 +            r#"
 +fn foo(t: bool) {
 +    match t {
 +        _ => 1 + 2,
 +        $0true => todo!(),
 +        false => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_fill_hidden_variants() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { A, #[doc(hidden)] B, }
 +"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        $0e::E::A => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_fill_hidden_variants_tuple() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: (bool, ::e::E)) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { A, #[doc(hidden)] B, }
 +"#,
 +            r#"
 +fn foo(t: (bool, ::e::E)) {
 +    match t {
 +        $0(true, e::E::A) => todo!(),
 +        (false, e::E::A) => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fills_wildcard_with_only_hidden_variants() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { #[doc(hidden)] A, }
 +"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        ${0:_} => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_fill_wildcard_when_hidden_variants_are_explicit() {
 +        check_assist_not_applicable(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +        e::E::A => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { #[doc(hidden)] A, }
 +"#,
 +        );
 +    }
 +
 +    // FIXME: I don't think the assist should be applicable in this case
 +    #[test]
 +    fn does_not_fill_wildcard_with_wildcard() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +        _ => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { #[doc(hidden)] A, }
 +"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fills_wildcard_on_non_exhaustive_with_explicit_matches() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +        e::E::A => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +#[non_exhaustive]
 +pub enum E { A, }
 +"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        e::E::A => todo!(),
 +        ${0:_} => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fills_wildcard_on_non_exhaustive_without_matches() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +#[non_exhaustive]
 +pub enum E { A, }
 +"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        $0e::E::A => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fills_wildcard_on_non_exhaustive_with_doc_hidden() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +#[non_exhaustive]
 +pub enum E { A, #[doc(hidden)] B }"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        $0e::E::A => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fills_wildcard_on_non_exhaustive_with_doc_hidden_with_explicit_arms() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +        e::E::A => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +#[non_exhaustive]
 +pub enum E { A, #[doc(hidden)] B }"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        e::E::A => todo!(),
 +        ${0:_} => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fill_wildcard_with_partial_wildcard() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E, b: bool) {
 +    match $0t {
 +        _ if b => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { #[doc(hidden)] A, }"#,
 +            r#"
 +fn foo(t: ::e::E, b: bool) {
 +    match t {
 +        _ if b => todo!(),
 +        ${0:_} => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E, b: bool) {
 +    match $0t {
 +        _ if b => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +//- /e.rs crate:e
 +pub enum E { #[doc(hidden)] A, }"#,
 +            r#"
 +fn foo(t: ::e::E, b: bool) {
 +    match t {
 +        _ if b => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn non_exhaustive_doc_hidden_tuple_fills_wildcard() {
 +        cov_mark::check!(added_wildcard_pattern);
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +//- /main.rs crate:main deps:e
 +fn foo(t: ::e::E) {
 +    match $0t {
 +    }
 +}
 +//- /e.rs crate:e
 +#[non_exhaustive]
 +pub enum E { A, #[doc(hidden)] B, }"#,
 +            r#"
 +fn foo(t: ::e::E) {
 +    match t {
 +        $0e::E::A => todo!(),
 +        _ => todo!(),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignores_doc_hidden_for_crate_local_enums() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +enum E { A, #[doc(hidden)] B, }
 +
 +fn foo(t: E) {
 +    match $0t {
 +    }
 +}"#,
 +            r#"
 +enum E { A, #[doc(hidden)] B, }
 +
 +fn foo(t: E) {
 +    match t {
 +        $0E::A => todo!(),
 +        E::B => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignores_non_exhaustive_for_crate_local_enums() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +#[non_exhaustive]
 +enum E { A, B, }
 +
 +fn foo(t: E) {
 +    match $0t {
 +    }
 +}"#,
 +            r#"
 +#[non_exhaustive]
 +enum E { A, B, }
 +
 +fn foo(t: E) {
 +    match t {
 +        $0E::A => todo!(),
 +        E::B => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignores_doc_hidden_and_non_exhaustive_for_crate_local_enums() {
 +        check_assist(
 +            add_missing_match_arms,
 +            r#"
 +#[non_exhaustive]
 +enum E { A, #[doc(hidden)] B, }
 +
 +fn foo(t: E) {
 +    match $0t {
 +    }
 +}"#,
 +            r#"
 +#[non_exhaustive]
 +enum E { A, #[doc(hidden)] B, }
 +
 +fn foo(t: E) {
 +    match t {
 +        $0E::A => todo!(),
 +        E::B => todo!(),
 +    }
 +}"#,
 +        );
 +    }
 +}
index 96890ad51a6f941e3399fea772058ab19d653af9,0000000000000000000000000000000000000000..9f51cdaf8b1eb0d7eccc6ae154ec038f5118504c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1256 -1,0 +1,1259 @@@
-                 let (name_refs, name_refs_use): (Vec<_>, Vec<_>) = refs
-                     .into_iter()
-                     .filter_map(|file_ref| match file_ref.name {
-                         ast::NameLike::NameRef(name_ref) => Some(name_ref),
-                         _ => None,
-                     })
-                     .partition_map(|name_ref| {
-                         match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
-                             Some(use_tree) => Either::Right(builder.make_mut(use_tree)),
-                             None => Either::Left(name_ref),
-                         }
-                     });
 +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 🙅‍♀️🙅‍♂️
-                     name_refs_use.into_iter().for_each(|use_tree| {
-                         if let Some(path) = use_tree.path() {
-                             remove_path_if_in_use_stmt(&path);
-                         }
-                     })
++                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,
 +            };
 +            (function, format!("Inline `{}`", path))
 +        }
 +        ast::CallableExpr::MethodCall(call) => {
 +            (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());
 +                })
 +        }
 +    }
 +    // 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() {
 +        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());
 +            }
 +        };
 +        // 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;
 +        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
 +            _ => {
 +                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(),
 +                    )
 +                }
 +            }
 +        }
 +    }
 +    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);
 +    }
 +}
 +"#,
 +        )
 +    }
 +}
index 9adf6381c1cbcf752b41b1852706043d71150050,0000000000000000000000000000000000000000..353d467ed19f3bb9c49d1b45e6b5a6fdfb8fcd3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1001 -1,0 +1,1020 @@@
- use ide_db::{defs::Definition, search::FileReference};
 +// Some ideas for future improvements:
 +// - Support replacing aliases which are used in expressions, e.g. `A::new()`.
 +// - Remove unused aliases if there are no longer any users, see inline_call.rs.
 +
 +use hir::{HasSource, PathResolution};
- // type A = i32;
++use ide_db::{
++    defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
++    search::FileReference,
++};
 +use itertools::Itertools;
 +use std::collections::HashMap;
 +use syntax::{
 +    ast::{self, make, HasGenericParams, HasName},
 +    ted, AstNode, NodeOrToken, SyntaxNode,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
++use super::inline_call::split_refs_and_uses;
++
 +// Assist: inline_type_alias_uses
 +//
 +// Inline a type alias into all of its uses where possible.
 +//
 +// ```
 +// type $0A = i32;
 +// fn id(x: A) -> A {
 +//     x
 +// };
 +// fn foo() {
 +//     let _: A = 3;
 +// }
 +// ```
 +// ->
 +// ```
-                 let path_types: Vec<ast::PathType> = refs
-                     .into_iter()
-                     .filter_map(|file_ref| match file_ref.name {
-                         ast::NameLike::NameRef(path_type) => {
-                             path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
-                         }
-                         _ => None,
-                     })
-                     .collect();
++//
 +// fn id(x: i32) -> i32 {
 +//     x
 +// };
 +// fn foo() {
 +//     let _: i32 = 3;
 +// }
 +pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let name = ctx.find_node_at_offset::<ast::Name>()?;
 +    let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?;
 +
 +    let hir_alias = ctx.sema.to_def(&ast_alias)?;
 +    let concrete_type = ast_alias.ty()?;
 +
 +    let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema);
 +    if !usages.at_least_one() {
 +        return None;
 +    }
 +
 +    // until this is ok
 +
 +    acc.add(
 +        AssistId("inline_type_alias_uses", AssistKind::RefactorInline),
 +        "Inline type alias into all uses",
 +        name.syntax().text_range(),
 +        |builder| {
 +            let usages = usages.all();
++            let mut definition_deleted = false;
 +
 +            let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
 +                builder.edit_file(file_id);
 +
- type A = u32;
++                let (path_types, path_type_uses) =
++                    split_refs_and_uses(builder, refs, |path_type| {
++                        path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
++                    });
 +
++                path_type_uses
++                    .iter()
++                    .flat_map(ast_to_remove_for_path_in_use_stmt)
++                    .for_each(|x| builder.delete(x.syntax().text_range()));
 +                for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
 +                    let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type);
 +                    let target = path_type.syntax().text_range();
 +                    Some((target, replacement))
 +                }) {
 +                    builder.replace(target, replacement);
 +                }
++
++                if file_id == ctx.file_id() {
++                    builder.delete(ast_alias.syntax().text_range());
++                    definition_deleted = true;
++                }
 +            };
 +
 +            for (file_id, refs) in usages.into_iter() {
 +                inline_refs_for_file(file_id, refs);
 +            }
++            if !definition_deleted {
++                builder.edit_file(ctx.file_id());
++                builder.delete(ast_alias.syntax().text_range());
++            }
 +        },
 +    )
 +}
 +
 +// Assist: inline_type_alias
 +//
 +// Replace a type alias with its concrete type.
 +//
 +// ```
 +// type A<T = u32> = Vec<T>;
 +//
 +// fn main() {
 +//     let a: $0A;
 +// }
 +// ```
 +// ->
 +// ```
 +// type A<T = u32> = Vec<T>;
 +//
 +// fn main() {
 +//     let a: Vec<u32>;
 +// }
 +// ```
 +pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?;
 +    let concrete_type;
 +    let replacement;
 +    match alias_instance.path()?.as_single_name_ref() {
 +        Some(nameref) if nameref.Self_token().is_some() => {
 +            match ctx.sema.resolve_path(&alias_instance.path()?)? {
 +                PathResolution::SelfType(imp) => {
 +                    concrete_type = imp.source(ctx.db())?.value.self_ty()?;
 +                }
 +                // FIXME: should also work in ADT definitions
 +                _ => return None,
 +            }
 +
 +            replacement = Replacement::Plain;
 +        }
 +        _ => {
 +            let alias = get_type_alias(&ctx, &alias_instance)?;
 +            concrete_type = alias.ty()?;
 +            replacement = inline(&alias, &alias_instance)?;
 +        }
 +    }
 +
 +    let target = alias_instance.syntax().text_range();
 +
 +    acc.add(
 +        AssistId("inline_type_alias", AssistKind::RefactorInline),
 +        "Inline type alias",
 +        target,
 +        |builder| builder.replace(target, replacement.to_text(&concrete_type)),
 +    )
 +}
 +
 +impl Replacement {
 +    fn to_text(&self, concrete_type: &ast::Type) -> String {
 +        match self {
 +            Replacement::Generic { lifetime_map, const_and_type_map } => {
 +                create_replacement(&lifetime_map, &const_and_type_map, &concrete_type)
 +            }
 +            Replacement::Plain => concrete_type.to_string(),
 +        }
 +    }
 +}
 +
 +enum Replacement {
 +    Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
 +    Plain,
 +}
 +
 +fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option<Replacement> {
 +    let repl = if let Some(alias_generics) = alias_def.generic_param_list() {
 +        if alias_generics.generic_params().next().is_none() {
 +            cov_mark::hit!(no_generics_params);
 +            return None;
 +        }
 +        let instance_args =
 +            alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
 +
 +        Replacement::Generic {
 +            lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
 +            const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
 +        }
 +    } else {
 +        Replacement::Plain
 +    };
 +    Some(repl)
 +}
 +
 +struct LifetimeMap(HashMap<String, ast::Lifetime>);
 +
 +impl LifetimeMap {
 +    fn new(
 +        instance_args: &Option<ast::GenericArgList>,
 +        alias_generics: &ast::GenericParamList,
 +    ) -> Option<Self> {
 +        let mut inner = HashMap::new();
 +
 +        let wildcard_lifetime = make::lifetime("'_");
 +        let lifetimes = alias_generics
 +            .lifetime_params()
 +            .filter_map(|lp| lp.lifetime())
 +            .map(|l| l.to_string())
 +            .collect_vec();
 +
 +        for lifetime in &lifetimes {
 +            inner.insert(lifetime.to_string(), wildcard_lifetime.clone());
 +        }
 +
 +        if let Some(instance_generic_args_list) = &instance_args {
 +            for (index, lifetime) in instance_generic_args_list
 +                .lifetime_args()
 +                .filter_map(|arg| arg.lifetime())
 +                .enumerate()
 +            {
 +                let key = match lifetimes.get(index) {
 +                    Some(key) => key,
 +                    None => {
 +                        cov_mark::hit!(too_many_lifetimes);
 +                        return None;
 +                    }
 +                };
 +
 +                inner.insert(key.clone(), lifetime);
 +            }
 +        }
 +
 +        Some(Self(inner))
 +    }
 +}
 +
 +struct ConstAndTypeMap(HashMap<String, SyntaxNode>);
 +
 +impl ConstAndTypeMap {
 +    fn new(
 +        instance_args: &Option<ast::GenericArgList>,
 +        alias_generics: &ast::GenericParamList,
 +    ) -> Option<Self> {
 +        let mut inner = HashMap::new();
 +        let instance_generics = generic_args_to_const_and_type_generics(instance_args);
 +        let alias_generics = generic_param_list_to_const_and_type_generics(&alias_generics);
 +
 +        if instance_generics.len() > alias_generics.len() {
 +            cov_mark::hit!(too_many_generic_args);
 +            return None;
 +        }
 +
 +        // Any declaration generics that don't have a default value must have one
 +        // provided by the instance.
 +        for (i, declaration_generic) in alias_generics.iter().enumerate() {
 +            let key = declaration_generic.replacement_key()?;
 +
 +            if let Some(instance_generic) = instance_generics.get(i) {
 +                inner.insert(key, instance_generic.replacement_value()?);
 +            } else if let Some(value) = declaration_generic.replacement_value() {
 +                inner.insert(key, value);
 +            } else {
 +                cov_mark::hit!(missing_replacement_param);
 +                return None;
 +            }
 +        }
 +
 +        Some(Self(inner))
 +    }
 +}
 +
 +/// This doesn't attempt to ensure specified generics are compatible with those
 +/// required by the type alias, other than lifetimes which must either all be
 +/// specified or all omitted. It will replace TypeArgs with ConstArgs and vice
 +/// versa if they're in the wrong position. It supports partially specified
 +/// generics.
 +///
 +/// 1. Map the provided instance's generic args to the type alias's generic
 +///    params:
 +///
 +///    ```
 +///    type A<'a, const N: usize, T = u64> = &'a [T; N];
 +///          ^ alias generic params
 +///    let a: A<100>;
 +///            ^ instance generic args
 +///    ```
 +///
 +///    generic['a] = '_ due to omission
 +///    generic[N] = 100 due to the instance arg
 +///    generic[T] = u64 due to the default param
 +///
 +/// 2. Copy the concrete type and substitute in each found mapping:
 +///
 +///    &'_ [u64; 100]
 +///
 +/// 3. Remove wildcard lifetimes entirely:
 +///
 +///    &[u64; 100]
 +fn create_replacement(
 +    lifetime_map: &LifetimeMap,
 +    const_and_type_map: &ConstAndTypeMap,
 +    concrete_type: &ast::Type,
 +) -> String {
 +    let updated_concrete_type = concrete_type.clone_for_update();
 +    let mut replacements = Vec::new();
 +    let mut removals = Vec::new();
 +
 +    for syntax in updated_concrete_type.syntax().descendants() {
 +        let syntax_string = syntax.to_string();
 +        let syntax_str = syntax_string.as_str();
 +
 +        if let Some(old_lifetime) = ast::Lifetime::cast(syntax.clone()) {
 +            if let Some(new_lifetime) = lifetime_map.0.get(&old_lifetime.to_string()) {
 +                if new_lifetime.text() == "'_" {
 +                    removals.push(NodeOrToken::Node(syntax.clone()));
 +
 +                    if let Some(ws) = syntax.next_sibling_or_token() {
 +                        removals.push(ws.clone());
 +                    }
 +
 +                    continue;
 +                }
 +
 +                replacements.push((syntax.clone(), new_lifetime.syntax().clone_for_update()));
 +            }
 +        } else if let Some(replacement_syntax) = const_and_type_map.0.get(syntax_str) {
 +            let new_string = replacement_syntax.to_string();
 +            let new = if new_string == "_" {
 +                make::wildcard_pat().syntax().clone_for_update()
 +            } else {
 +                replacement_syntax.clone_for_update()
 +            };
 +
 +            replacements.push((syntax.clone(), new));
 +        }
 +    }
 +
 +    for (old, new) in replacements {
 +        ted::replace(old, new);
 +    }
 +
 +    for syntax in removals {
 +        ted::remove(syntax);
 +    }
 +
 +    updated_concrete_type.to_string()
 +}
 +
 +fn get_type_alias(ctx: &AssistContext<'_>, path: &ast::PathType) -> Option<ast::TypeAlias> {
 +    let resolved_path = ctx.sema.resolve_path(&path.path()?)?;
 +
 +    // We need the generics in the correct order to be able to map any provided
 +    // instance generics to declaration generics. The `hir::TypeAlias` doesn't
 +    // keep the order, so we must get the `ast::TypeAlias` from the hir
 +    // definition.
 +    if let PathResolution::Def(hir::ModuleDef::TypeAlias(ta)) = resolved_path {
 +        Some(ctx.sema.source(ta)?.value)
 +    } else {
 +        None
 +    }
 +}
 +
 +enum ConstOrTypeGeneric {
 +    ConstArg(ast::ConstArg),
 +    TypeArg(ast::TypeArg),
 +    ConstParam(ast::ConstParam),
 +    TypeParam(ast::TypeParam),
 +}
 +
 +impl ConstOrTypeGeneric {
 +    fn replacement_key(&self) -> Option<String> {
 +        // Only params are used as replacement keys.
 +        match self {
 +            ConstOrTypeGeneric::ConstParam(cp) => Some(cp.name()?.to_string()),
 +            ConstOrTypeGeneric::TypeParam(tp) => Some(tp.name()?.to_string()),
 +            _ => None,
 +        }
 +    }
 +
 +    fn replacement_value(&self) -> Option<SyntaxNode> {
 +        Some(match self {
 +            ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(),
 +            ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(),
 +            ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(),
 +            ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(),
 +        })
 +    }
 +}
 +
 +fn generic_param_list_to_const_and_type_generics(
 +    generics: &ast::GenericParamList,
 +) -> Vec<ConstOrTypeGeneric> {
 +    let mut others = Vec::new();
 +
 +    for param in generics.generic_params() {
 +        match param {
 +            ast::GenericParam::LifetimeParam(_) => {}
 +            ast::GenericParam::ConstParam(cp) => {
 +                others.push(ConstOrTypeGeneric::ConstParam(cp));
 +            }
 +            ast::GenericParam::TypeParam(tp) => others.push(ConstOrTypeGeneric::TypeParam(tp)),
 +        }
 +    }
 +
 +    others
 +}
 +
 +fn generic_args_to_const_and_type_generics(
 +    generics: &Option<ast::GenericArgList>,
 +) -> Vec<ConstOrTypeGeneric> {
 +    let mut others = Vec::new();
 +
 +    // It's fine for there to be no instance generics because the declaration
 +    // might have default values or they might be inferred.
 +    if let Some(generics) = generics {
 +        for arg in generics.generic_args() {
 +            match arg {
 +                ast::GenericArg::TypeArg(ta) => {
 +                    others.push(ConstOrTypeGeneric::TypeArg(ta));
 +                }
 +                ast::GenericArg::ConstArg(ca) => {
 +                    others.push(ConstOrTypeGeneric::ConstArg(ca));
 +                }
 +                _ => {}
 +            }
 +        }
 +    }
 +
 +    others
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    #[test]
 +    fn empty_generic_params() {
 +        cov_mark::check!(no_generics_params);
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +type A<> = T;
 +fn main() {
 +    let a: $0A<u32>;
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn too_many_generic_args() {
 +        cov_mark::check!(too_many_generic_args);
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +type A<T> = T;
 +fn main() {
 +    let a: $0A<u32, u64>;
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn too_many_lifetimes() {
 +        cov_mark::check!(too_many_lifetimes);
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +type A<'a> = &'a &'b u32;
 +fn f<'a>() {
 +    let a: $0A<'a, 'b> = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    // This must be supported in order to support "inline_alias_to_users" or
 +    // whatever it will be called.
 +    #[test]
 +    fn alias_as_expression_ignored() {
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +type A = Vec<u32>;
 +fn main() {
 +    let a: A = $0A::new();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn primitive_arg() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<T> = T;
 +fn main() {
 +    let a: $0A<u32> = 0;
 +}
 +"#,
 +            r#"
 +type A<T> = T;
 +fn main() {
 +    let a: u32 = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_generic_replacements() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A = Vec<u32>;
 +fn main() {
 +    let a: $0A;
 +}
 +"#,
 +            r#"
 +type A = Vec<u32>;
 +fn main() {
 +    let a: Vec<u32>;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_expression() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<const N: usize = { 1 }> = [u32; N];
 +fn main() {
 +    let a: $0A;
 +}
 +"#,
 +            r#"
 +type A<const N: usize = { 1 }> = [u32; N];
 +fn main() {
 +    let a: [u32; { 1 }];
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_default_value() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<const N: usize = 1> = [u32; N];
 +fn main() {
 +    let a: $0A;
 +}
 +"#,
 +            r#"
 +type A<const N: usize = 1> = [u32; N];
 +fn main() {
 +    let a: [u32; 1];
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn all_param_types() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +struct Struct<const C: usize>;
 +type A<'inner1, 'outer1, Outer1, const INNER1: usize, Inner1: Clone, const OUTER1: usize> = (Struct<INNER1>, Struct<OUTER1>, Outer1, &'inner1 (), Inner1, &'outer1 ());
 +fn foo<'inner2, 'outer2, Outer2, const INNER2: usize, Inner2, const OUTER2: usize>() {
 +    let a: $0A<'inner2, 'outer2, Outer2, INNER2, Inner2, OUTER2>;
 +}
 +"#,
 +            r#"
 +struct Struct<const C: usize>;
 +type A<'inner1, 'outer1, Outer1, const INNER1: usize, Inner1: Clone, const OUTER1: usize> = (Struct<INNER1>, Struct<OUTER1>, Outer1, &'inner1 (), Inner1, &'outer1 ());
 +fn foo<'inner2, 'outer2, Outer2, const INNER2: usize, Inner2, const OUTER2: usize>() {
 +    let a: (Struct<INNER2>, Struct<OUTER2>, Outer2, &'inner2 (), Inner2, &'outer2 ());
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn omitted_lifetimes() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<'l, 'r> = &'l &'r u32;
 +fn main() {
 +    let a: $0A;
 +}
 +"#,
 +            r#"
 +type A<'l, 'r> = &'l &'r u32;
 +fn main() {
 +    let a: &&u32;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn omitted_type() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
 +fn main() {
 +    let a: $0A<'_, '_>;
 +}
 +"#,
 +            r#"
 +type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
 +fn main() {
 +    let a: &std::collections::HashMap<&str, u32>;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn omitted_everything() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
 +fn main() {
 +    let v = std::collections::HashMap<&str, u32>;
 +    let a: $0A = &v;
 +}
 +"#,
 +            r#"
 +type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
 +fn main() {
 +    let v = std::collections::HashMap<&str, u32>;
 +    let a: &std::collections::HashMap<&str, u32> = &v;
 +}
 +"#,
 +        );
 +    }
 +
 +    // This doesn't actually cause the GenericArgsList to contain a AssocTypeArg.
 +    #[test]
 +    fn arg_associated_type() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +trait Tra { type Assoc; fn a(); }
 +struct Str {}
 +impl Tra for Str {
 +    type Assoc = u32;
 +    fn a() {
 +        type A<T> = Vec<T>;
 +        let a: $0A<Self::Assoc>;
 +    }
 +}
 +"#,
 +            r#"
 +trait Tra { type Assoc; fn a(); }
 +struct Str {}
 +impl Tra for Str {
 +    type Assoc = u32;
 +    fn a() {
 +        type A<T> = Vec<T>;
 +        let a: Vec<Self::Assoc>;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_default_associated_type() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +trait Tra { type Assoc; fn a() }
 +struct Str {}
 +impl Tra for Str {
 +    type Assoc = u32;
 +    fn a() {
 +        type A<T = Self::Assoc> = Vec<T>;
 +        let a: $0A;
 +    }
 +}
 +"#,
 +            r#"
 +trait Tra { type Assoc; fn a() }
 +struct Str {}
 +impl Tra for Str {
 +    type Assoc = u32;
 +    fn a() {
 +        type A<T = Self::Assoc> = Vec<T>;
 +        let a: Vec<Self::Assoc>;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_pointer() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A = fn(u32);
 +fn foo(a: u32) {}
 +fn main() {
 +    let a: $0A = foo;
 +}
 +"#,
 +            r#"
 +type A = fn(u32);
 +fn foo(a: u32) {}
 +fn main() {
 +    let a: fn(u32) = foo;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn closure() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A = Box<dyn FnOnce(u32) -> u32>;
 +fn main() {
 +    let a: $0A = Box::new(|_| 0);
 +}
 +"#,
 +            r#"
 +type A = Box<dyn FnOnce(u32) -> u32>;
 +fn main() {
 +    let a: Box<dyn FnOnce(u32) -> u32> = Box::new(|_| 0);
 +}
 +"#,
 +        );
 +    }
 +
 +    // Type aliases can't be used in traits, but someone might use the assist to
 +    // fix the error.
 +    #[test]
 +    fn bounds() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"type A = std::io::Write; fn f<T>() where T: $0A {}"#,
 +            r#"type A = std::io::Write; fn f<T>() where T: std::io::Write {}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_parameter() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A = std::io::Write;
 +fn f(a: impl $0A) {}
 +"#,
 +            r#"
 +type A = std::io::Write;
 +fn f(a: impl std::io::Write) {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn arg_expression() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<const N: usize> = [u32; N];
 +fn main() {
 +    let a: $0A<{ 1 + 1 }>;
 +}
 +"#,
 +            r#"
 +type A<const N: usize> = [u32; N];
 +fn main() {
 +    let a: [u32; { 1 + 1 }];
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn alias_instance_generic_path() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A<const N: usize> = [u32; N];
 +fn main() {
 +    let a: $0A<u32::MAX>;
 +}
 +"#,
 +            r#"
 +type A<const N: usize> = [u32; N];
 +fn main() {
 +    let a: [u32; u32::MAX];
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn generic_type() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +type A = String;
 +fn f(a: Vec<$0A>) {}
 +"#,
 +            r#"
 +type A = String;
 +fn f(a: Vec<String>) {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn missing_replacement_param() {
 +        cov_mark::check!(missing_replacement_param);
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +type A<U> = Vec<T>;
 +fn main() {
 +    let a: $0A;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn full_path_type_is_replaced() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +mod foo {
 +    pub type A = String;
 +}
 +fn main() {
 +    let a: foo::$0A;
 +}
 +"#,
 +            r#"
 +mod foo {
 +    pub type A = String;
 +}
 +fn main() {
 +    let a: String;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_self_type() {
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +struct Strukt;
 +
 +impl Strukt {
 +    fn new() -> Self$0 {}
 +}
 +"#,
 +            r#"
 +struct Strukt;
 +
 +impl Strukt {
 +    fn new() -> Strukt {}
 +}
 +"#,
 +        );
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +struct Strukt<'a, T, const C: usize>(&'a [T; C]);
 +
 +impl<T, const C: usize> Strukt<'_, T, C> {
 +    fn new() -> Self$0 {}
 +}
 +"#,
 +            r#"
 +struct Strukt<'a, T, const C: usize>(&'a [T; C]);
 +
 +impl<T, const C: usize> Strukt<'_, T, C> {
 +    fn new() -> Strukt<'_, T, C> {}
 +}
 +"#,
 +        );
 +        check_assist(
 +            inline_type_alias,
 +            r#"
 +struct Strukt<'a, T, const C: usize>(&'a [T; C]);
 +
 +trait Tr<'b, T> {}
 +
 +impl<T, const C: usize> Tr<'static, u8> for Strukt<'_, T, C> {
 +    fn new() -> Self$0 {}
 +}
 +"#,
 +            r#"
 +struct Strukt<'a, T, const C: usize>(&'a [T; C]);
 +
 +trait Tr<'b, T> {}
 +
 +impl<T, const C: usize> Tr<'static, u8> for Strukt<'_, T, C> {
 +    fn new() -> Strukt<'_, T, C> {}
 +}
 +"#,
 +        );
 +
 +        check_assist_not_applicable(
 +            inline_type_alias,
 +            r#"
 +trait Tr {
 +    fn new() -> Self$0;
 +}
 +"#,
 +        );
 +    }
 +
 +    mod inline_type_alias_uses {
 +        use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist};
 +
 +        #[test]
 +        fn inline_uses() {
 +            check_assist(
 +                inline_type_alias_uses,
 +                r#"
 +type $0A = u32;
 +
 +fn foo() {
 +    let _: A = 3;
 +    let _: A = 4;
 +}
 +"#,
 +                r#"
- type T<E> = Vec<E>;
++
 +
 +fn foo() {
 +    let _: u32 = 3;
 +    let _: u32 = 4;
 +}
 +"#,
 +            );
 +        }
 +
 +        #[test]
 +        fn inline_uses_across_files() {
 +            check_assist(
 +                inline_type_alias_uses,
 +                r#"
 +//- /lib.rs
 +mod foo;
 +type $0T<E> = Vec<E>;
 +fn f() -> T<&str> {
 +    vec!["hello"]
 +}
 +
 +//- /foo.rs
 +use super::T;
 +fn foo() {
 +    let _: T<i8> = Vec::new();
 +}
 +"#,
 +                r#"
 +//- /lib.rs
 +mod foo;
- use super::T;
++
 +fn f() -> Vec<&str> {
 +    vec!["hello"]
 +}
 +
 +//- /foo.rs
- use super::I;
++
 +fn foo() {
 +    let _: Vec<i8> = Vec::new();
 +}
 +"#,
 +            );
 +        }
 +
 +        #[test]
 +        fn inline_uses_across_files_2() {
 +            check_assist(
 +                inline_type_alias_uses,
 +                r#"
 +//- /lib.rs
 +mod foo;
 +type $0I = i32;
 +
 +//- /foo.rs
 +use super::I;
 +fn foo() {
 +    let _: I = 0;
 +}
 +"#,
 +                r#"
++//- /lib.rs
++mod foo;
++
++
++//- /foo.rs
++
 +fn foo() {
 +    let _: i32 = 0;
 +}
 +"#,
 +            );
 +        }
 +    }
 +}
index 7e102ceba891ae2f9d3d2f5132b8a2fff678a9d9,0000000000000000000000000000000000000000..2bdbec93b1f960a211e657f43a5948eca0796b86
mode 100644,000000..100644
--- /dev/null
@@@ -1,570 -1,0 +1,574 @@@
- use syntax::{algo::neighbor, ast, match_ast, ted, AstNode, SyntaxElement, SyntaxNode};
 +use either::Either;
 +use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
-                     Remove(it) => it.as_ref().either(ast::Use::remove, ast::UseTree::remove),
++use syntax::{
++    algo::neighbor,
++    ast::{self, edit_in_place::Removable},
++    match_ast, ted, AstNode, SyntaxElement, SyntaxNode,
++};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    utils::next_prev,
 +    AssistId, AssistKind,
 +};
 +
 +use Edit::*;
 +
 +// Assist: merge_imports
 +//
 +// Merges two imports with a common prefix.
 +//
 +// ```
 +// use std::$0fmt::Formatter;
 +// use std::io;
 +// ```
 +// ->
 +// ```
 +// use std::{fmt::Formatter, io};
 +// ```
 +pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let (target, edits) = if ctx.has_empty_selection() {
 +        // Merge a neighbor
 +        let tree: ast::UseTree = ctx.find_node_at_offset()?;
 +        let target = tree.syntax().text_range();
 +
 +        let edits = if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
 +            let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
 +            use_item.try_merge_from(&mut neighbor)
 +        } else {
 +            let mut neighbor = next_prev().find_map(|dir| neighbor(&tree, dir)).into_iter();
 +            tree.try_merge_from(&mut neighbor)
 +        };
 +        (target, edits?)
 +    } else {
 +        // Merge selected
 +        let selection_range = ctx.selection_trimmed();
 +        let parent_node = match ctx.covering_element() {
 +            SyntaxElement::Node(n) => n,
 +            SyntaxElement::Token(t) => t.parent()?,
 +        };
 +        let mut selected_nodes =
 +            parent_node.children().filter(|it| selection_range.contains_range(it.text_range()));
 +
 +        let first_selected = selected_nodes.next()?;
 +        let edits = match_ast! {
 +            match first_selected {
 +                ast::Use(use_item) => {
 +                    use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast))
 +                },
 +                ast::UseTree(use_tree) => {
 +                    use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast))
 +                },
 +                _ => return None,
 +            }
 +        };
 +        (selection_range, edits?)
 +    };
 +
 +    acc.add(
 +        AssistId("merge_imports", AssistKind::RefactorRewrite),
 +        "Merge imports",
 +        target,
 +        |builder| {
 +            let edits_mut: Vec<Edit> = edits
 +                .into_iter()
 +                .map(|it| match it {
 +                    Remove(Either::Left(it)) => Remove(Either::Left(builder.make_mut(it))),
 +                    Remove(Either::Right(it)) => Remove(Either::Right(builder.make_mut(it))),
 +                    Replace(old, new) => Replace(builder.make_syntax_mut(old), new),
 +                })
 +                .collect();
 +            for edit in edits_mut {
 +                match edit {
++                    Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
 +                    Replace(old, new) => ted::replace(old, new),
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +trait Merge: AstNode + Clone {
 +    fn try_merge_from(self, items: &mut dyn Iterator<Item = Self>) -> Option<Vec<Edit>> {
 +        let mut edits = Vec::new();
 +        let mut merged = self.clone();
 +        while let Some(item) = items.next() {
 +            merged = merged.try_merge(&item)?;
 +            edits.push(Edit::Remove(item.into_either()));
 +        }
 +        if !edits.is_empty() {
 +            edits.push(Edit::replace(self, merged));
 +            Some(edits)
 +        } else {
 +            None
 +        }
 +    }
 +    fn try_merge(&self, other: &Self) -> Option<Self>;
 +    fn into_either(self) -> Either<ast::Use, ast::UseTree>;
 +}
 +
 +impl Merge for ast::Use {
 +    fn try_merge(&self, other: &Self) -> Option<Self> {
 +        try_merge_imports(self, other, MergeBehavior::Crate)
 +    }
 +    fn into_either(self) -> Either<ast::Use, ast::UseTree> {
 +        Either::Left(self)
 +    }
 +}
 +
 +impl Merge for ast::UseTree {
 +    fn try_merge(&self, other: &Self) -> Option<Self> {
 +        try_merge_trees(self, other, MergeBehavior::Crate)
 +    }
 +    fn into_either(self) -> Either<ast::Use, ast::UseTree> {
 +        Either::Right(self)
 +    }
 +}
 +
 +enum Edit {
 +    Remove(Either<ast::Use, ast::UseTree>),
 +    Replace(SyntaxNode, SyntaxNode),
 +}
 +
 +impl Edit {
 +    fn replace(old: impl AstNode, new: impl AstNode) -> Self {
 +        Edit::Replace(old.syntax().clone(), new.syntax().clone())
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_merge_equal() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::fmt$0::{Display, Debug};
 +use std::fmt::{Display, Debug};
 +",
 +            r"
 +use std::fmt::{Display, Debug};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_first() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::fmt$0::Debug;
 +use std::fmt::Display;
 +",
 +            r"
 +use std::fmt::{Debug, Display};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_second() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::fmt::Debug;
 +use std::fmt$0::Display;
 +",
 +            r"
 +use std::fmt::{Display, Debug};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_self1() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::fmt$0;
 +use std::fmt::Display;
 +",
 +            r"
 +use std::fmt::{self, Display};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_self2() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::{fmt, $0fmt::Display};
 +",
 +            r"
 +use std::{fmt::{Display, self}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_pub1() {
 +        check_assist_not_applicable(
 +            merge_imports,
 +            r"
 +pub use std::fmt$0::Debug;
 +use std::fmt::Display;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_pub_last() {
 +        check_assist_not_applicable(
 +            merge_imports,
 +            r"
 +use std::fmt$0::Debug;
 +pub use std::fmt::Display;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_pub_crate_pub() {
 +        check_assist_not_applicable(
 +            merge_imports,
 +            r"
 +pub(crate) use std::fmt$0::Debug;
 +pub use std::fmt::Display;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_pub_pub_crate() {
 +        check_assist_not_applicable(
 +            merge_imports,
 +            r"
 +pub use std::fmt$0::Debug;
 +pub(crate) use std::fmt::Display;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_pub() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +pub use std::fmt$0::Debug;
 +pub use std::fmt::Display;
 +",
 +            r"
 +pub use std::fmt::{Debug, Display};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_pub_crate() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +pub(crate) use std::fmt$0::Debug;
 +pub(crate) use std::fmt::Display;
 +",
 +            r"
 +pub(crate) use std::fmt::{Debug, Display};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_pub_in_path_crate() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +pub(in this::path) use std::fmt$0::Debug;
 +pub(in this::path) use std::fmt::Display;
 +",
 +            r"
 +pub(in this::path) use std::fmt::{Debug, Display};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_nested() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::{fmt$0::Debug, fmt::Display};
 +",
 +            r"
 +use std::{fmt::{Debug, Display}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_merge_nested2() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::{fmt::Debug, fmt$0::Display};
 +",
 +            r"
 +use std::{fmt::{Display, Debug}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_merge_with_nested_self_item() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std$0::{fmt::{Write, Display}};
 +use std::{fmt::{self, Debug}};
 +",
 +            r"
 +use std::{fmt::{Write, Display, self, Debug}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_merge_with_nested_self_item2() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std$0::{fmt::{self, Debug}};
 +use std::{fmt::{Write, Display}};
 +",
 +            r"
 +use std::{fmt::{self, Debug, Write, Display}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_merge_self_with_nested_self_item() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::{fmt$0::{self, Debug}, fmt::{Write, Display}};
 +",
 +            r"
 +use std::{fmt::{self, Debug, Write, Display}};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_merge_nested_self_and_empty() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use foo::$0{bar::{self}};
 +use foo::{bar};
 +",
 +            r"
 +use foo::{bar::{self}};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_nested_empty_and_self() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use foo::$0{bar};
 +use foo::{bar::{self}};
 +",
 +            r"
 +use foo::{bar::{self}};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_nested_list_self_and_glob() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std$0::{fmt::*};
 +use std::{fmt::{self, Display}};
 +",
 +            r"
 +use std::{fmt::{*, self, Display}};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_single_wildcard_diff_prefixes() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std$0::cell::*;
 +use std::str;
 +",
 +            r"
 +use std::{cell::*, str};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_merge_both_wildcard_diff_prefixes() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std$0::cell::*;
 +use std::str::*;
 +",
 +            r"
 +use std::{cell::*, str::*};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn removes_just_enough_whitespace() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use foo$0::bar;
 +use foo::baz;
 +
 +/// Doc comment
 +",
 +            r"
 +use foo::{bar, baz};
 +
 +/// Doc comment
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn works_with_trailing_comma() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use {
 +    foo$0::bar,
 +    foo::baz,
 +};
 +",
 +            r"
 +use {
 +    foo::{bar, baz},
 +};
 +",
 +        );
 +        check_assist(
 +            merge_imports,
 +            r"
 +use {
 +    foo::baz,
 +    foo$0::bar,
 +};
 +",
 +            r"
 +use {
 +    foo::{bar, baz},
 +};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_double_comma() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use foo::bar::baz;
 +use foo::$0{
 +    FooBar,
 +};
 +",
 +            r"
 +use foo::{
 +    FooBar, bar::baz,
 +};
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_empty_use() {
 +        check_assist_not_applicable(
 +            merge_imports,
 +            r"
 +use std::$0
 +fn main() {}",
 +        );
 +    }
 +
 +    #[test]
 +    fn split_glob() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use foo::$0*;
 +use foo::bar::Baz;
 +",
 +            r"
 +use foo::{*, bar::Baz};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_selection_uses() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::fmt::Error;
 +$0use std::fmt::Display;
 +use std::fmt::Debug;
 +use std::fmt::Write;
 +$0use std::fmt::Result;
 +",
 +            r"
 +use std::fmt::Error;
 +use std::fmt::{Display, Debug, Write};
 +use std::fmt::Result;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_selection_use_trees() {
 +        check_assist(
 +            merge_imports,
 +            r"
 +use std::{
 +    fmt::Error,
 +    $0fmt::Display,
 +    fmt::Debug,
 +    fmt::Write,$0
 +    fmt::Result,
 +};",
 +            r"
 +use std::{
 +    fmt::Error,
 +    fmt::{Display, Debug, Write},
 +    fmt::Result,
 +};",
 +        );
 +        // FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
 +        check_assist(
 +            merge_imports,
 +            r"use std::$0{fmt::Display, fmt::Debug}$0;",
 +            r"use std::{fmt::{Display, Debug}};",
 +        );
 +    }
 +}
index 176a3bf5803fcdddd52d2baa1f17ba78bbe95066,0000000000000000000000000000000000000000..1dd376ac3fd5350db8fc7fd5b696c27b968db081
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,126 @@@
-     ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasName, HasTypeBounds},
 +use syntax::{
++    ast::{
++        self,
++        edit_in_place::{GenericParamsOwnerEdit, Removable},
++        make, AstNode, HasName, HasTypeBounds,
++    },
 +    match_ast,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: move_bounds_to_where_clause
 +//
 +// Moves inline type bounds to a where clause.
 +//
 +// ```
 +// fn apply<T, U, $0F: FnOnce(T) -> U>(f: F, x: T) -> U {
 +//     f(x)
 +// }
 +// ```
 +// ->
 +// ```
 +// fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
 +//     f(x)
 +// }
 +// ```
 +pub(crate) fn move_bounds_to_where_clause(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?;
 +
 +    let mut type_params = type_param_list.type_or_const_params();
 +    if type_params.all(|p| match p {
 +        ast::TypeOrConstParam::Type(t) => t.type_bound_list().is_none(),
 +        ast::TypeOrConstParam::Const(_) => true,
 +    }) {
 +        return None;
 +    }
 +
 +    let parent = type_param_list.syntax().parent()?;
 +
 +    let target = type_param_list.syntax().text_range();
 +    acc.add(
 +        AssistId("move_bounds_to_where_clause", AssistKind::RefactorRewrite),
 +        "Move to where clause",
 +        target,
 +        |edit| {
 +            let type_param_list = edit.make_mut(type_param_list);
 +            let parent = edit.make_syntax_mut(parent);
 +
 +            let where_clause: ast::WhereClause = match_ast! {
 +                match parent {
 +                    ast::Fn(it) => it.get_or_create_where_clause(),
 +                    ast::Trait(it) => it.get_or_create_where_clause(),
 +                    ast::Impl(it) => it.get_or_create_where_clause(),
 +                    ast::Enum(it) => it.get_or_create_where_clause(),
 +                    ast::Struct(it) => it.get_or_create_where_clause(),
 +                    _ => return,
 +                }
 +            };
 +
 +            for toc_param in type_param_list.type_or_const_params() {
 +                let type_param = match toc_param {
 +                    ast::TypeOrConstParam::Type(x) => x,
 +                    ast::TypeOrConstParam::Const(_) => continue,
 +                };
 +                if let Some(tbl) = type_param.type_bound_list() {
 +                    if let Some(predicate) = build_predicate(type_param) {
 +                        where_clause.add_predicate(predicate)
 +                    }
 +                    tbl.remove()
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
 +    let path = make::ext::ident_path(&param.name()?.syntax().to_string());
 +    let predicate = make::where_pred(path, param.type_bound_list()?.bounds());
 +    Some(predicate.clone_for_update())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::check_assist;
 +
 +    #[test]
 +    fn move_bounds_to_where_clause_fn() {
 +        check_assist(
 +            move_bounds_to_where_clause,
 +            r#"fn foo<T: u32, $0F: FnOnce(T) -> T>() {}"#,
 +            r#"fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_bounds_to_where_clause_impl() {
 +        check_assist(
 +            move_bounds_to_where_clause,
 +            r#"impl<U: u32, $0T> A<U, T> {}"#,
 +            r#"impl<U, T> A<U, T> where U: u32 {}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_bounds_to_where_clause_struct() {
 +        check_assist(
 +            move_bounds_to_where_clause,
 +            r#"struct A<$0T: Iterator<Item = u32>> {}"#,
 +            r#"struct A<T> where T: Iterator<Item = u32> {}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_bounds_to_where_clause_tuple_struct() {
 +        check_assist(
 +            move_bounds_to_where_clause,
 +            r#"struct Pair<$0T: u32>(T, T);"#,
 +            r#"struct Pair<T>(T, T) where T: u32;"#,
 +        );
 +    }
 +}
index 3ce028e9306597b87fa117b36cdbfc428c8a2f4a,0000000000000000000000000000000000000000..dac216b69b727a62646ec192031fa83aa50793b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,237 @@@
-     ast::{self, make, HasVisibility},
 +use syntax::{
++    ast::{self, edit_in_place::Removable, make, HasVisibility},
 +    ted::{self, Position},
 +    AstNode, SyntaxKind,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: unmerge_use
 +//
 +// Extracts single use item from use list.
 +//
 +// ```
 +// use std::fmt::{Debug, Display$0};
 +// ```
 +// ->
 +// ```
 +// use std::fmt::{Debug};
 +// use std::fmt::Display;
 +// ```
 +pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let tree: ast::UseTree = ctx.find_node_at_offset::<ast::UseTree>()?.clone_for_update();
 +
 +    let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
 +    if tree_list.use_trees().count() < 2 {
 +        cov_mark::hit!(skip_single_use_item);
 +        return None;
 +    }
 +
 +    let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
 +    let path = resolve_full_path(&tree)?;
 +
 +    let old_parent_range = use_.syntax().parent()?.text_range();
 +    let new_parent = use_.syntax().parent()?;
 +
 +    let target = tree.syntax().text_range();
 +    acc.add(
 +        AssistId("unmerge_use", AssistKind::RefactorRewrite),
 +        "Unmerge use",
 +        target,
 +        |builder| {
 +            let new_use = make::use_(
 +                use_.visibility(),
 +                make::use_tree(
 +                    path,
 +                    tree.use_tree_list(),
 +                    tree.rename(),
 +                    tree.star_token().is_some(),
 +                ),
 +            )
 +            .clone_for_update();
 +
 +            tree.remove();
 +            ted::insert(Position::after(use_.syntax()), new_use.syntax());
 +
 +            builder.replace(old_parent_range, new_parent.to_string());
 +        },
 +    )
 +}
 +
 +fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
 +    let paths = tree
 +        .syntax()
 +        .ancestors()
 +        .take_while(|n| n.kind() != SyntaxKind::USE)
 +        .filter_map(ast::UseTree::cast)
 +        .filter_map(|t| t.path());
 +
 +    let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?;
 +    if final_path.segment().map_or(false, |it| it.self_token().is_some()) {
 +        final_path.qualifier()
 +    } else {
 +        Some(final_path)
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn skip_single_use_item() {
 +        cov_mark::check!(skip_single_use_item);
 +        check_assist_not_applicable(
 +            unmerge_use,
 +            r"
 +use std::fmt::Debug$0;
 +",
 +        );
 +        check_assist_not_applicable(
 +            unmerge_use,
 +            r"
 +use std::fmt::{Debug$0};
 +",
 +        );
 +        check_assist_not_applicable(
 +            unmerge_use,
 +            r"
 +use std::fmt::Debug as Dbg$0;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_single_glob_import() {
 +        check_assist_not_applicable(
 +            unmerge_use,
 +            r"
 +use std::fmt::*$0;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_use_item() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use std::fmt::{Debug, Display$0};
 +",
 +            r"
 +use std::fmt::{Debug};
 +use std::fmt::Display;
 +",
 +        );
 +
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use std::fmt::{Debug, format$0, Display};
 +",
 +            r"
 +use std::fmt::{Debug, Display};
 +use std::fmt::format;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_glob_import() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use std::fmt::{*$0, Display};
 +",
 +            r"
 +use std::fmt::{Display};
 +use std::fmt::*;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_renamed_use_item() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use std::fmt::{Debug, Display as Disp$0};
 +",
 +            r"
 +use std::fmt::{Debug};
 +use std::fmt::Display as Disp;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_indented_use_item() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +mod format {
 +    use std::fmt::{Debug, Display$0 as Disp, format};
 +}
 +",
 +            r"
 +mod format {
 +    use std::fmt::{Debug, format};
 +    use std::fmt::Display as Disp;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_nested_use_item() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use foo::bar::{baz::{qux$0, foobar}, barbaz};
 +",
 +            r"
 +use foo::bar::{baz::{foobar}, barbaz};
 +use foo::bar::baz::qux;
 +",
 +        );
 +        check_assist(
 +            unmerge_use,
 +            r"
 +use foo::bar::{baz$0::{qux, foobar}, barbaz};
 +",
 +            r"
 +use foo::bar::{barbaz};
 +use foo::bar::baz::{qux, foobar};
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_use_item_with_visibility() {
 +        check_assist(
 +            unmerge_use,
 +            r"
 +pub use std::fmt::{Debug, Display$0};
 +",
 +            r"
 +pub use std::fmt::{Debug};
 +pub use std::fmt::Display;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn unmerge_use_item_on_self() {
 +        check_assist(
 +            unmerge_use,
 +            r"use std::process::{Command, self$0};",
 +            r"use std::process::{Command};
 +use std::process;",
 +        );
 +    }
 +}
index a8c8622c1c1d89cdcab5caf4d489f96f454112f2,0000000000000000000000000000000000000000..227e2300f92a0b258275512adca9fcadfc51f23a
mode 100644,000000..100644
--- /dev/null
@@@ -1,2370 -1,0 +1,2370 @@@
- type A = i32;
 +//! 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_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_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_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 103e3259fa2ef9f047d4cf7431b08a519e877b9e,0000000000000000000000000000000000000000..4ab6e2627fa75413837b55be578c1a237a4147c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,703 -1,0 +1,703 @@@
-         edit_in_place::AttrsOwnerEdit,
 +//! Assorted functions shared by several assists.
 +
 +use std::ops;
 +
 +use itertools::Itertools;
 +
 +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, SmolStr, 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 {
 +        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_*`
 +pub(crate) fn find_struct_impl(
 +    ctx: &AssistContext<'_>,
 +    adt: &ast::Adt,
 +    name: &str,
 +) -> 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_fn(impl_blk, name) {
 +            return None;
 +        }
 +    }
 +
 +    Some(block)
 +}
 +
 +fn has_fn(imp: &ast::Impl, rhs_name: &str) -> 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 name.text().eq_ignore_ascii_case(rhs_name) {
 +                        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 {
 +    let generic_params = adt.generic_param_list();
 +    let mut buf = String::with_capacity(code.len());
 +    buf.push_str("\n\n");
 +    adt.attrs()
 +        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
 +        .for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
 +    buf.push_str("impl");
 +    if let Some(generic_params) = &generic_params {
 +        let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
 +        let toc_params = generic_params.type_or_const_params().map(|toc_param| {
 +            let type_param = match toc_param {
 +                ast::TypeOrConstParam::Type(x) => x,
 +                ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
 +            };
 +            let mut buf = String::new();
 +            if let Some(it) = type_param.name() {
 +                format_to!(buf, "{}", it.syntax());
 +            }
 +            if let Some(it) = type_param.colon_token() {
 +                format_to!(buf, "{} ", it);
 +            }
 +            if let Some(it) = type_param.type_bound_list() {
 +                format_to!(buf, "{}", it.syntax());
 +            }
 +            buf
 +        });
 +        let generics = lifetimes.chain(toc_params).format(", ");
 +        format_to!(buf, "<{}>", generics);
 +    }
 +    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 {
 +        let lifetime_params = generic_params
 +            .lifetime_params()
 +            .filter_map(|it| it.lifetime())
 +            .map(|it| SmolStr::from(it.text()));
 +        let toc_params = generic_params
 +            .type_or_const_params()
 +            .filter_map(|it| it.name())
 +            .map(|it| SmolStr::from(it.text()));
 +        format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
 +    }
 +
 +    match adt.where_clause() {
 +        Some(where_clause) => {
 +            format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
 +        }
 +        None => {
 +            format_to!(buf, " {{\n{}\n}}", code);
 +        }
 +    }
 +
 +    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)
 +            }
 +            ReferenceConversionType::Dereferenced => {
 +                let type_argument_name =
 +                    self.ty.type_arguments().next().unwrap().display(db).to_string();
 +                format!("&{}", type_argument_name)
 +            }
 +            ReferenceConversionType::Option => {
 +                let type_argument_name =
 +                    self.ty.type_arguments().next().unwrap().display(db).to_string();
 +                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();
 +                format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn getter(&self, field_name: String) -> String {
 +        match self.conversion {
 +            ReferenceConversionType::Copy => format!("self.{}", field_name),
 +            ReferenceConversionType::AsRefStr
 +            | ReferenceConversionType::AsRefSlice
 +            | ReferenceConversionType::Dereferenced
 +            | ReferenceConversionType::Option
 +            | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
 +        }
 +    }
 +}
 +
 +// 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 9c975b92953377b2e476edb8065a77a35652f57f,0000000000000000000000000000000000000000..950731eb4ca8c880dc1c211d8da76aef60b80383
mode 100644,000000..100644
--- /dev/null
@@@ -1,354 -1,0 +1,374 @@@
 +//! Completes mod declarations.
 +
 +use std::iter;
 +
 +use hir::{Module, ModuleSource};
 +use ide_db::{
 +    base_db::{SourceDatabaseExt, VfsPath},
 +    FxHashSet, RootDatabase, SymbolKind,
 +};
 +use syntax::{ast, AstNode, SyntaxKind};
 +
 +use crate::{context::CompletionContext, CompletionItem, Completions};
 +
 +/// Complete mod declaration, i.e. `mod $0;`
 +pub(crate) fn complete_mod(
 +    acc: &mut Completions,
 +    ctx: &CompletionContext<'_>,
 +    mod_under_caret: &ast::Module,
 +) -> Option<()> {
 +    if mod_under_caret.item_list().is_some() {
 +        return None;
 +    }
 +
 +    let _p = profile::span("completion::complete_mod");
 +
 +    let mut current_module = ctx.module;
 +    // For `mod $0`, `ctx.module` is its parent, but for `mod f$0`, it's `mod f` itself, but we're
 +    // interested in its parent.
 +    if ctx.original_token.kind() == SyntaxKind::IDENT {
 +        if let Some(module) =
 +            ctx.original_token.parent_ancestors().nth(1).and_then(ast::Module::cast)
 +        {
 +            match ctx.sema.to_def(&module) {
 +                Some(module) if module == current_module => {
 +                    if let Some(parent) = current_module.parent(ctx.db) {
 +                        current_module = parent;
 +                    }
 +                }
 +                _ => {}
 +            }
 +        }
 +    }
 +
 +    let module_definition_file =
 +        current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
 +    let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
 +    let directory_to_look_for_submodules = directory_to_look_for_submodules(
 +        current_module,
 +        ctx.db,
 +        source_root.path_for_file(&module_definition_file)?,
 +    )?;
 +
 +    let existing_mod_declarations = current_module
 +        .children(ctx.db)
 +        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
++        .filter(|module| module != ctx.original_token.text())
 +        .collect::<FxHashSet<_>>();
 +
 +    let module_declaration_file =
 +        current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
 +            module_declaration_source_file.file_id.original_file(ctx.db)
 +        });
 +
 +    source_root
 +        .iter()
 +        .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
 +        .filter(|submodule_candidate_file| {
 +            Some(submodule_candidate_file) != module_declaration_file.as_ref()
 +        })
 +        .filter_map(|submodule_file| {
 +            let submodule_path = source_root.path_for_file(&submodule_file)?;
 +            let directory_with_submodule = submodule_path.parent()?;
 +            let (name, ext) = submodule_path.name_and_extension()?;
 +            if ext != Some("rs") {
 +                return None;
 +            }
 +            match name {
 +                "lib" | "main" => None,
 +                "mod" => {
 +                    if directory_with_submodule.parent()? == directory_to_look_for_submodules {
 +                        match directory_with_submodule.name_and_extension()? {
 +                            (directory_name, None) => Some(directory_name.to_owned()),
 +                            _ => None,
 +                        }
 +                    } else {
 +                        None
 +                    }
 +                }
 +                file_name if directory_with_submodule == directory_to_look_for_submodules => {
 +                    Some(file_name.to_owned())
 +                }
 +                _ => None,
 +            }
 +        })
 +        .filter(|name| !existing_mod_declarations.contains(name))
 +        .for_each(|submodule_name| {
 +            let mut label = submodule_name;
 +            if mod_under_caret.semicolon_token().is_none() {
 +                label.push(';');
 +            }
 +            let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
 +            item.add_to(acc)
 +        });
 +
 +    Some(())
 +}
 +
 +fn directory_to_look_for_submodules(
 +    module: Module,
 +    db: &RootDatabase,
 +    module_file_path: &VfsPath,
 +) -> Option<VfsPath> {
 +    let directory_with_module_path = module_file_path.parent()?;
 +    let (name, ext) = module_file_path.name_and_extension()?;
 +    if ext != Some("rs") {
 +        return None;
 +    }
 +    let base_directory = match name {
 +        "mod" | "lib" | "main" => Some(directory_with_module_path),
 +        regular_rust_file_name => {
 +            if matches!(
 +                (
 +                    directory_with_module_path
 +                        .parent()
 +                        .as_ref()
 +                        .and_then(|path| path.name_and_extension()),
 +                    directory_with_module_path.name_and_extension(),
 +                ),
 +                (Some(("src", None)), Some(("bin", None)))
 +            ) {
 +                // files in /src/bin/ can import each other directly
 +                Some(directory_with_module_path)
 +            } else {
 +                directory_with_module_path.join(regular_rust_file_name)
 +            }
 +        }
 +    }?;
 +
 +    module_chain_to_containing_module_file(module, db)
 +        .into_iter()
 +        .filter_map(|module| module.name(db))
 +        .try_fold(base_directory, |path, name| path.join(&name.to_smol_str()))
 +}
 +
 +fn module_chain_to_containing_module_file(
 +    current_module: Module,
 +    db: &RootDatabase,
 +) -> Vec<Module> {
 +    let mut path =
 +        iter::successors(Some(current_module), |current_module| current_module.parent(db))
 +            .take_while(|current_module| {
 +                matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
 +            })
 +            .collect::<Vec<_>>();
 +    path.reverse();
 +    path
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use expect_test::{expect, Expect};
 +
 +    use crate::tests::completion_list;
 +
 +    fn check(ra_fixture: &str, expect: Expect) {
 +        let actual = completion_list(ra_fixture);
 +        expect.assert_eq(&actual);
 +    }
 +
 +    #[test]
 +    fn lib_module_completion() {
 +        check(
 +            r#"
 +//- /lib.rs
 +mod $0
 +//- /foo.rs
 +fn foo() {}
 +//- /foo/ignored_foo.rs
 +fn ignored_foo() {}
 +//- /bar/mod.rs
 +fn bar() {}
 +//- /bar/ignored_bar.rs
 +fn ignored_bar() {}
 +"#,
 +            expect![[r#"
 +                md bar;
 +                md foo;
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn no_module_completion_with_module_body() {
 +        check(
 +            r#"
 +//- /lib.rs
 +mod $0 {
 +
 +}
 +//- /foo.rs
 +fn foo() {}
 +"#,
 +            expect![[r#""#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn main_module_completion() {
 +        check(
 +            r#"
 +//- /main.rs
 +mod $0
 +//- /foo.rs
 +fn foo() {}
 +//- /foo/ignored_foo.rs
 +fn ignored_foo() {}
 +//- /bar/mod.rs
 +fn bar() {}
 +//- /bar/ignored_bar.rs
 +fn ignored_bar() {}
 +"#,
 +            expect![[r#"
 +                md bar;
 +                md foo;
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn main_test_module_completion() {
 +        check(
 +            r#"
 +//- /main.rs
 +mod tests {
 +    mod $0;
 +}
 +//- /tests/foo.rs
 +fn foo() {}
 +"#,
 +            expect![[r#"
 +                md foo
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn directly_nested_module_completion() {
 +        check(
 +            r#"
 +//- /lib.rs
 +mod foo;
 +//- /foo.rs
 +mod $0;
 +//- /foo/bar.rs
 +fn bar() {}
 +//- /foo/bar/ignored_bar.rs
 +fn ignored_bar() {}
 +//- /foo/baz/mod.rs
 +fn baz() {}
 +//- /foo/moar/ignored_moar.rs
 +fn ignored_moar() {}
 +"#,
 +            expect![[r#"
 +                md bar
 +                md baz
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn nested_in_source_module_completion() {
 +        check(
 +            r#"
 +//- /lib.rs
 +mod foo;
 +//- /foo.rs
 +mod bar {
 +    mod $0
 +}
 +//- /foo/bar/baz.rs
 +fn baz() {}
 +"#,
 +            expect![[r#"
 +                md baz;
 +            "#]],
 +        );
 +    }
 +
 +    // FIXME binary modules are not supported in tests properly
 +    // Binary modules are a bit special, they allow importing the modules from `/src/bin`
 +    // and that's why are good to test two things:
 +    // * no cycles are allowed in mod declarations
 +    // * no modules from the parent directory are proposed
 +    // Unfortunately, binary modules support is in cargo not rustc,
 +    // hence the test does not work now
 +    //
 +    // #[test]
 +    // fn regular_bin_module_completion() {
 +    //     check(
 +    //         r#"
 +    //         //- /src/bin.rs
 +    //         fn main() {}
 +    //         //- /src/bin/foo.rs
 +    //         mod $0
 +    //         //- /src/bin/bar.rs
 +    //         fn bar() {}
 +    //         //- /src/bin/bar/bar_ignored.rs
 +    //         fn bar_ignored() {}
 +    //     "#,
 +    //         expect![[r#"
 +    //             md bar;
 +    //         "#]],foo
 +    //     );
 +    // }
 +
 +    #[test]
 +    fn already_declared_bin_module_completion_omitted() {
 +        check(
 +            r#"
 +//- /src/bin.rs crate:main
 +fn main() {}
 +//- /src/bin/foo.rs
 +mod $0
 +//- /src/bin/bar.rs
 +mod foo;
 +fn bar() {}
 +//- /src/bin/bar/bar_ignored.rs
 +fn bar_ignored() {}
 +"#,
 +            expect![[r#""#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn name_partially_typed() {
 +        check(
 +            r#"
 +//- /lib.rs
 +mod f$0
 +//- /foo.rs
 +fn foo() {}
 +//- /foo/ignored_foo.rs
 +fn ignored_foo() {}
 +//- /bar/mod.rs
 +fn bar() {}
 +//- /bar/ignored_bar.rs
 +fn ignored_bar() {}
 +"#,
 +            expect![[r#"
 +                md bar;
 +                md foo;
 +            "#]],
 +        );
 +    }
++
++    #[test]
++    fn semi_colon_completion() {
++        check(
++            r#"
++//- /lib.rs
++mod foo;
++//- /foo.rs
++mod bar {
++    mod baz$0
++}
++//- /foo/bar/baz.rs
++fn baz() {}
++"#,
++            expect![[r#"
++                md baz;
++            "#]],
++        );
++    }
 +}
index 38e24ebc732d497aa4250f6fe23d06a2c2614f61,0000000000000000000000000000000000000000..8e26d889f9b63c8ce28ffe210da9a433aa6c126a
mode 100644,000000..100644
--- /dev/null
@@@ -1,708 -1,0 +1,747 @@@
 +//! Completion tests for expressions.
 +use expect_test::{expect, Expect};
 +
 +use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
 +
 +fn check(ra_fixture: &str, expect: Expect) {
 +    let actual = completion_list(&format!("{}{}", BASE_ITEMS_FIXTURE, ra_fixture));
 +    expect.assert_eq(&actual)
 +}
 +
 +fn check_empty(ra_fixture: &str, expect: Expect) {
 +    let actual = completion_list(ra_fixture);
 +    expect.assert_eq(&actual);
 +}
 +
 +#[test]
 +fn complete_literal_struct_with_a_private_field() {
 +    // `FooDesc.bar` is private, the completion should not be triggered.
 +    check(
 +        r#"
 +mod _69latrick {
 +    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, bar: bool }
 +    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
 +}
 +
 +fn baz() {
 +    use _69latrick::*;
 +
 +    let foo = create_foo(&$0);
 +}
 +            "#,
 +        // This should not contain `FooDesc {…}`.
 +        expect![[r#"
 +            ct CONST
 +            en Enum
 +            fn baz()         fn()
 +            fn create_foo(…) fn(&FooDesc)
 +            fn function()    fn()
 +            ma makro!(…)     macro_rules! makro
 +            md _69latrick
 +            md module
 +            sc STATIC
 +            st FooDesc
 +            st Record
 +            st Tuple
 +            st Unit
 +            un Union
 +            ev TupleV(…)     TupleV(u32)
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw mut
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn completes_various_bindings() {
 +    check_empty(
 +        r#"
 +fn func(param0 @ (param1, param2): (i32, i32)) {
 +    let letlocal = 92;
 +    if let ifletlocal = 100 {
 +        match 0 {
 +            matcharm => 1 + $0,
 +            otherwise => (),
 +        }
 +    }
 +    let letlocal2 = 44;
 +}
 +"#,
 +        expect![[r#"
 +            fn func(…)    fn((i32, i32))
 +            lc ifletlocal i32
 +            lc letlocal   i32
 +            lc matcharm   i32
 +            lc param0     (i32, i32)
 +            lc param1     i32
 +            lc param2     i32
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn completes_all_the_things_in_fn_body() {
 +    check(
 +        r#"
 +use non_existant::Unresolved;
 +mod qualified { pub enum Enum { Variant } }
 +
 +impl Unit {
 +    fn foo<'lifetime, TypeParam, const CONST_PARAM: usize>(self) {
 +        fn local_func() {}
 +        $0
 +    }
 +}
 +"#,
 +        // `self` is in here twice, once as the module, once as the local
 +        expect![[r#"
 +            ct CONST
 +            cp CONST_PARAM
 +            en Enum
 +            fn function()   fn()
 +            fn local_func() fn()
 +            lc self         Unit
 +            ma makro!(…)    macro_rules! makro
 +            md module
 +            md qualified
 +            sp Self
 +            sc STATIC
 +            st Record
 +            st Tuple
 +            st Unit
 +            tp TypeParam
 +            un Union
 +            ev TupleV(…)    TupleV(u32)
 +            bt u32
 +            kw const
 +            kw crate::
 +            kw enum
 +            kw extern
 +            kw false
 +            kw fn
 +            kw for
 +            kw if
 +            kw if let
 +            kw impl
 +            kw let
 +            kw loop
 +            kw match
 +            kw mod
 +            kw return
 +            kw self::
 +            kw static
 +            kw struct
 +            kw trait
 +            kw true
 +            kw type
 +            kw union
 +            kw unsafe
 +            kw use
 +            kw while
 +            kw while let
 +            me self.foo()   fn(self)
 +            sn macro_rules
 +            sn pd
 +            sn ppd
 +            ?? Unresolved
 +        "#]],
 +    );
 +    check(
 +        r#"
 +use non_existant::Unresolved;
 +mod qualified { pub enum Enum { Variant } }
 +
 +impl Unit {
 +    fn foo<'lifetime, TypeParam, const CONST_PARAM: usize>(self) {
 +        fn local_func() {}
 +        self::$0
 +    }
 +}
 +"#,
 +        expect![[r#"
 +            ct CONST
 +            en Enum
 +            fn function() fn()
 +            ma makro!(…)  macro_rules! makro
 +            md module
 +            md qualified
 +            sc STATIC
 +            st Record
 +            st Tuple
 +            st Unit
 +            tt Trait
 +            un Union
 +            ev TupleV(…)  TupleV(u32)
 +            ?? Unresolved
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn complete_in_block() {
 +    check_empty(
 +        r#"
 +    fn foo() {
 +        if true {
 +            $0
 +        }
 +    }
 +"#,
 +        expect![[r#"
 +            fn foo()       fn()
 +            bt u32
 +            kw const
 +            kw crate::
 +            kw enum
 +            kw extern
 +            kw false
 +            kw fn
 +            kw for
 +            kw if
 +            kw if let
 +            kw impl
 +            kw let
 +            kw loop
 +            kw match
 +            kw mod
 +            kw return
 +            kw self::
 +            kw static
 +            kw struct
 +            kw trait
 +            kw true
 +            kw type
 +            kw union
 +            kw unsafe
 +            kw use
 +            kw while
 +            kw while let
 +            sn macro_rules
 +            sn pd
 +            sn ppd
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn complete_after_if_expr() {
 +    check_empty(
 +        r#"
 +    fn foo() {
 +        if true {}
 +        $0
 +    }
 +"#,
 +        expect![[r#"
 +            fn foo()       fn()
 +            bt u32
 +            kw const
 +            kw crate::
 +            kw else
 +            kw else if
 +            kw enum
 +            kw extern
 +            kw false
 +            kw fn
 +            kw for
 +            kw if
 +            kw if let
 +            kw impl
 +            kw let
 +            kw loop
 +            kw match
 +            kw mod
 +            kw return
 +            kw self::
 +            kw static
 +            kw struct
 +            kw trait
 +            kw true
 +            kw type
 +            kw union
 +            kw unsafe
 +            kw use
 +            kw while
 +            kw while let
 +            sn macro_rules
 +            sn pd
 +            sn ppd
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn complete_in_match_arm() {
 +    check_empty(
 +        r#"
 +    fn foo() {
 +        match () {
 +            () => $0
 +        }
 +    }
 +"#,
 +        expect![[r#"
 +            fn foo()     fn()
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn completes_in_loop_ctx() {
 +    check_empty(
 +        r"fn my() { loop { $0 } }",
 +        expect![[r#"
 +            fn my()        fn()
 +            bt u32
 +            kw break
 +            kw const
 +            kw continue
 +            kw crate::
 +            kw enum
 +            kw extern
 +            kw false
 +            kw fn
 +            kw for
 +            kw if
 +            kw if let
 +            kw impl
 +            kw let
 +            kw loop
 +            kw match
 +            kw mod
 +            kw return
 +            kw self::
 +            kw static
 +            kw struct
 +            kw trait
 +            kw true
 +            kw type
 +            kw union
 +            kw unsafe
 +            kw use
 +            kw while
 +            kw while let
 +            sn macro_rules
 +            sn pd
 +            sn ppd
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn completes_in_let_initializer() {
 +    check_empty(
 +        r#"fn main() { let _ = $0 }"#,
 +        expect![[r#"
 +            fn main()    fn()
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn struct_initializer_field_expr() {
 +    check_empty(
 +        r#"
 +struct Foo {
 +    pub f: i32,
 +}
 +fn foo() {
 +    Foo {
 +        f: $0
 +    }
 +}
 +"#,
 +        expect![[r#"
 +            fn foo()     fn()
 +            st Foo
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn shadowing_shows_single_completion() {
 +    cov_mark::check!(shadowing_shows_single_completion);
 +
 +    check_empty(
 +        r#"
 +fn foo() {
 +    let bar = 92;
 +    {
 +        let bar = 62;
 +        drop($0)
 +    }
 +}
 +"#,
 +        expect![[r#"
 +            fn foo()     fn()
 +            lc bar       i32
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn in_macro_expr_frag() {
 +    check_empty(
 +        r#"
 +macro_rules! m { ($e:expr) => { $e } }
 +fn quux(x: i32) {
 +    m!($0);
 +}
 +"#,
 +        expect![[r#"
 +            fn quux(…)   fn(i32)
 +            lc x         i32
 +            ma m!(…)     macro_rules! m
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    );
 +    check_empty(
 +        r"
 +macro_rules! m { ($e:expr) => { $e } }
 +fn quux(x: i32) {
 +    m!(x$0);
 +}
 +",
 +        expect![[r#"
 +            fn quux(…)   fn(i32)
 +            lc x         i32
 +            ma m!(…)     macro_rules! m
 +            bt u32
 +            kw crate::
 +            kw false
 +            kw for
 +            kw if
 +            kw if let
 +            kw loop
 +            kw match
 +            kw return
 +            kw self::
 +            kw true
 +            kw unsafe
 +            kw while
 +            kw while let
 +        "#]],
 +    );
 +    check_empty(
 +        r#"
 +macro_rules! m { ($e:expr) => { $e } }
 +fn quux(x: i32) {
 +    let y = 92;
 +    m!(x$0
 +}
 +"#,
 +        expect![[r#""#]],
 +    );
 +}
 +
 +#[test]
 +fn enum_qualified() {
 +    check(
 +        r#"
 +impl Enum {
 +    type AssocType = ();
 +    const ASSOC_CONST: () = ();
 +    fn assoc_fn() {}
 +}
 +fn func() {
 +    Enum::$0
 +}
 +"#,
 +        expect![[r#"
 +            ct ASSOC_CONST const ASSOC_CONST: ()
 +            fn assoc_fn()  fn()
 +            ta AssocType   type AssocType = ()
 +            ev RecordV {…} RecordV { field: u32 }
 +            ev TupleV(…)   TupleV(u32)
 +            ev UnitV       UnitV
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn ty_qualified_no_drop() {
 +    check_empty(
 +        r#"
 +//- minicore: drop
 +struct Foo;
 +impl Drop for Foo {
 +    fn drop(&mut self) {}
 +}
 +fn func() {
 +    Foo::$0
 +}
 +"#,
 +        expect![[r#""#]],
 +    );
 +}
 +
 +#[test]
 +fn with_parens() {
 +    check_empty(
 +        r#"
 +enum Enum {
 +    Variant()
 +}
 +impl Enum {
 +    fn variant() -> Self { Enum::Variant() }
 +}
 +fn func() {
 +    Enum::$0()
 +}
 +"#,
 +        expect![[r#"
 +            fn variant fn() -> Enum
 +            ev Variant Variant
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn detail_impl_trait_in_return_position() {
 +    check_empty(
 +        r"
 +//- minicore: sized
 +trait Trait<T> {}
 +fn foo<U>() -> impl Trait<U> {}
 +fn main() {
 +    self::$0
 +}
 +",
 +        expect![[r#"
 +            fn foo()  fn() -> impl Trait<U>
 +            fn main() fn()
 +            tt Trait
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn detail_async_fn() {
 +    check_empty(
 +        r#"
 +//- minicore: future, sized
 +trait Trait<T> {}
 +async fn foo() -> u8 {}
 +async fn bar<U>() -> impl Trait<U> {}
 +fn main() {
 +    self::$0
 +}
 +"#,
 +        expect![[r#"
 +            fn bar()  async fn() -> impl Trait<U>
 +            fn foo()  async fn() -> u8
 +            fn main() fn()
 +            tt Trait
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn detail_impl_trait_in_argument_position() {
 +    check_empty(
 +        r"
 +//- minicore: sized
 +trait Trait<T> {}
 +struct Foo;
 +impl Foo {
 +    fn bar<U>(_: impl Trait<U>) {}
 +}
 +fn main() {
 +    Foo::$0
 +}
 +",
 +        expect![[r"
 +            fn bar(…) fn(impl Trait<U>)
 +        "]],
 +    );
 +}
 +
 +#[test]
 +fn complete_record_expr_path() {
 +    check(
 +        r#"
 +struct Zulu;
 +impl Zulu {
 +    fn test() -> Self { }
 +}
 +fn boi(val: Zulu) { }
 +fn main() {
 +    boi(Zulu:: $0 {});
 +}
 +"#,
 +        expect![[r#"
 +            fn test() fn() -> Zulu
 +        "#]],
 +    );
 +}
 +
++#[test]
++fn varaiant_with_struct() {
++    check_empty(
++        r#"
++pub struct YoloVariant {
++    pub f: usize
++}
++
++pub enum HH {
++    Yolo(YoloVariant),
++}
++
++fn brr() {
++    let t = HH::Yolo(Y$0);
++}
++"#,
++        expect![[r#"
++            en HH
++            fn brr()           fn()
++            st YoloVariant
++            st YoloVariant {…} YoloVariant { f: usize }
++            bt u32
++            kw crate::
++            kw false
++            kw for
++            kw if
++            kw if let
++            kw loop
++            kw match
++            kw return
++            kw self::
++            kw true
++            kw unsafe
++            kw while
++            kw while let
++        "#]],
++    );
++}
++
 +#[test]
 +fn return_unit_block() {
 +    cov_mark::check!(return_unit_block);
 +    check_edit("return", r#"fn f() { if true { $0 } }"#, r#"fn f() { if true { return; } }"#);
 +}
 +
 +#[test]
 +fn return_unit_no_block() {
 +    cov_mark::check!(return_unit_no_block);
 +    check_edit(
 +        "return",
 +        r#"fn f() { match () { () => $0 } }"#,
 +        r#"fn f() { match () { () => return } }"#,
 +    );
 +}
 +
 +#[test]
 +fn return_value_block() {
 +    cov_mark::check!(return_value_block);
 +    check_edit(
 +        "return",
 +        r#"fn f() -> i32 { if true { $0 } }"#,
 +        r#"fn f() -> i32 { if true { return $0; } }"#,
 +    );
 +}
 +
 +#[test]
 +fn return_value_no_block() {
 +    cov_mark::check!(return_value_no_block);
 +    check_edit(
 +        "return",
 +        r#"fn f() -> i32 { match () { () => $0 } }"#,
 +        r#"fn f() -> i32 { match () { () => return $0 } }"#,
 +    );
 +}
index 7303ef8b7bb588d4b525e889d68519bdc6ec3565,0000000000000000000000000000000000000000..7109c6fd188f58df18f7d283b02af6a7b3bbe140
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,78 @@@
-     pub pat: Either<ast::SelfParam, ast::Pat>,
 +//! This module provides functionality for querying callable information about a token.
 +
 +use either::Either;
 +use hir::{Semantics, Type};
 +use syntax::{
 +    ast::{self, HasArgList, HasName},
 +    AstNode, SyntaxToken,
 +};
 +
 +use crate::RootDatabase;
 +
 +#[derive(Debug)]
 +pub struct ActiveParameter {
 +    pub ty: Type,
-         pat.map(|pat| ActiveParameter { ty, pat })
++    pub pat: Option<Either<ast::SelfParam, ast::Pat>>,
 +}
 +
 +impl ActiveParameter {
 +    /// Returns information about the call argument this token is part of.
 +    pub fn at_token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Self> {
 +        let (signature, active_parameter) = callable_for_token(sema, token)?;
 +
 +        let idx = active_parameter?;
 +        let mut params = signature.params(sema.db);
 +        if !(idx < params.len()) {
 +            cov_mark::hit!(too_many_arguments);
 +            return None;
 +        }
 +        let (pat, ty) = params.swap_remove(idx);
-         self.pat.as_ref().right().and_then(|param| match param {
-             ast::Pat::IdentPat(ident) => ident.name(),
++        Some(ActiveParameter { ty, pat })
 +    }
 +
 +    pub fn ident(&self) -> Option<ast::Name> {
++        self.pat.as_ref().and_then(|param| match param {
++            Either::Right(ast::Pat::IdentPat(ident)) => ident.name(),
 +            _ => None,
 +        })
 +    }
 +}
 +
 +/// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
 +pub fn callable_for_token(
 +    sema: &Semantics<'_, RootDatabase>,
 +    token: SyntaxToken,
 +) -> Option<(hir::Callable, Option<usize>)> {
 +    // Find the calling expression and its NameRef
 +    let parent = token.parent()?;
 +    let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
 +        it.arg_list()
 +            .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
 +    })?;
 +
 +    callable_for_node(sema, &calling_node, &token)
 +}
 +
 +pub fn callable_for_node(
 +    sema: &Semantics<'_, RootDatabase>,
 +    calling_node: &ast::CallableExpr,
 +    token: &SyntaxToken,
 +) -> Option<(hir::Callable, Option<usize>)> {
 +    let callable = match &calling_node {
 +        ast::CallableExpr::Call(call) => {
 +            let expr = call.expr()?;
 +            sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
 +        }
 +        ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
 +    }?;
 +    let active_param = if let Some(arg_list) = calling_node.arg_list() {
 +        let param = arg_list
 +            .args()
 +            .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
 +            .count();
 +        Some(param)
 +    } else {
 +        None
 +    };
 +    Some((callable, active_param))
 +}
index c14182279d05eabe404b8b8f2131d6bcc1070fb5,0000000000000000000000000000000000000000..9be1d36634934a6405856ce820dc46989a85b13b
mode 100644,000000..100644
--- /dev/null
@@@ -1,446 -1,0 +1,453 @@@
-     ast::{self, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind},
 +//! Handle syntactic aspects of inserting a new `use` item.
 +#[cfg(test)]
 +mod tests;
 +
 +use std::cmp::Ordering;
 +
 +use hir::Semantics;
 +use syntax::{
 +    algo,
- pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
++    ast::{
++        self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility,
++        PathSegmentKind,
++    },
 +    ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode,
 +};
 +
 +use crate::{
 +    imports::merge_imports::{
 +        common_prefix, eq_attrs, eq_visibility, try_merge_imports, use_tree_path_cmp, MergeBehavior,
 +    },
 +    RootDatabase,
 +};
 +
 +pub use hir::PrefixKind;
 +
 +/// How imports should be grouped into use statements.
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +pub enum ImportGranularity {
 +    /// Do not change the granularity of any imports and preserve the original structure written by the developer.
 +    Preserve,
 +    /// Merge imports from the same crate into a single use statement.
 +    Crate,
 +    /// Merge imports from the same module into a single use statement.
 +    Module,
 +    /// Flatten imports so that each has its own use statement.
 +    Item,
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 +pub struct InsertUseConfig {
 +    pub granularity: ImportGranularity,
 +    pub enforce_granularity: bool,
 +    pub prefix_kind: PrefixKind,
 +    pub group: bool,
 +    pub skip_glob_imports: bool,
 +}
 +
 +#[derive(Debug, Clone)]
 +pub enum ImportScope {
 +    File(ast::SourceFile),
 +    Module(ast::ItemList),
 +    Block(ast::StmtList),
 +}
 +
 +impl ImportScope {
 +    // FIXME: Remove this?
 +    #[cfg(test)]
 +    fn from(syntax: SyntaxNode) -> Option<Self> {
 +        use syntax::match_ast;
 +        fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool {
 +            attrs
 +                .attrs()
 +                .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg"))
 +        }
 +        match_ast! {
 +            match syntax {
 +                ast::Module(module) => module.item_list().map(ImportScope::Module),
 +                ast::SourceFile(file) => Some(ImportScope::File(file)),
 +                ast::Fn(func) => contains_cfg_attr(&func).then(|| func.body().and_then(|it| it.stmt_list().map(ImportScope::Block))).flatten(),
 +                ast::Const(konst) => contains_cfg_attr(&konst).then(|| match konst.body()? {
 +                    ast::Expr::BlockExpr(block) => Some(block),
 +                    _ => None,
 +                }).flatten().and_then(|it| it.stmt_list().map(ImportScope::Block)),
 +                ast::Static(statik) => contains_cfg_attr(&statik).then(|| match statik.body()? {
 +                    ast::Expr::BlockExpr(block) => Some(block),
 +                    _ => None,
 +                }).flatten().and_then(|it| it.stmt_list().map(ImportScope::Block)),
 +                _ => None,
 +
 +            }
 +        }
 +    }
 +
 +    /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
 +    /// Returns the original source node inside attributes.
 +    pub fn find_insert_use_container(
 +        position: &SyntaxNode,
 +        sema: &Semantics<'_, RootDatabase>,
 +    ) -> Option<Self> {
 +        fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool {
 +            attrs
 +                .attrs()
 +                .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg"))
 +        }
 +
 +        // Walk up the ancestor tree searching for a suitable node to do insertions on
 +        // with special handling on cfg-gated items, in which case we want to insert imports locally
 +        // or FIXME: annotate inserted imports with the same cfg
 +        for syntax in sema.ancestors_with_macros(position.clone()) {
 +            if let Some(file) = ast::SourceFile::cast(syntax.clone()) {
 +                return Some(ImportScope::File(file));
 +            } else if let Some(item) = ast::Item::cast(syntax) {
 +                return match item {
 +                    ast::Item::Const(konst) if contains_cfg_attr(&konst) => {
 +                        // FIXME: Instead of bailing out with None, we should note down that
 +                        // this import needs an attribute added
 +                        match sema.original_ast_node(konst)?.body()? {
 +                            ast::Expr::BlockExpr(block) => block,
 +                            _ => return None,
 +                        }
 +                        .stmt_list()
 +                        .map(ImportScope::Block)
 +                    }
 +                    ast::Item::Fn(func) if contains_cfg_attr(&func) => {
 +                        // FIXME: Instead of bailing out with None, we should note down that
 +                        // this import needs an attribute added
 +                        sema.original_ast_node(func)?.body()?.stmt_list().map(ImportScope::Block)
 +                    }
 +                    ast::Item::Static(statik) if contains_cfg_attr(&statik) => {
 +                        // FIXME: Instead of bailing out with None, we should note down that
 +                        // this import needs an attribute added
 +                        match sema.original_ast_node(statik)?.body()? {
 +                            ast::Expr::BlockExpr(block) => block,
 +                            _ => return None,
 +                        }
 +                        .stmt_list()
 +                        .map(ImportScope::Block)
 +                    }
 +                    ast::Item::Module(module) => {
 +                        // early return is important here, if we can't find the original module
 +                        // in the input there is no way for us to insert an import anywhere.
 +                        sema.original_ast_node(module)?.item_list().map(ImportScope::Module)
 +                    }
 +                    _ => continue,
 +                };
 +            }
 +        }
 +        None
 +    }
 +
 +    pub fn as_syntax_node(&self) -> &SyntaxNode {
 +        match self {
 +            ImportScope::File(file) => file.syntax(),
 +            ImportScope::Module(item_list) => item_list.syntax(),
 +            ImportScope::Block(block) => block.syntax(),
 +        }
 +    }
 +
 +    pub fn clone_for_update(&self) -> Self {
 +        match self {
 +            ImportScope::File(file) => ImportScope::File(file.clone_for_update()),
 +            ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()),
 +            ImportScope::Block(block) => ImportScope::Block(block.clone_for_update()),
 +        }
 +    }
 +}
 +
 +/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
 +pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
 +    let _p = profile::span("insert_use");
 +    let mut mb = match cfg.granularity {
 +        ImportGranularity::Crate => Some(MergeBehavior::Crate),
 +        ImportGranularity::Module => Some(MergeBehavior::Module),
 +        ImportGranularity::Item | ImportGranularity::Preserve => None,
 +    };
 +    if !cfg.enforce_granularity {
 +        let file_granularity = guess_granularity_from_scope(scope);
 +        mb = match file_granularity {
 +            ImportGranularityGuess::Unknown => mb,
 +            ImportGranularityGuess::Item => None,
 +            ImportGranularityGuess::Module => Some(MergeBehavior::Module),
 +            ImportGranularityGuess::ModuleOrItem => mb.and(Some(MergeBehavior::Module)),
 +            ImportGranularityGuess::Crate => Some(MergeBehavior::Crate),
 +            ImportGranularityGuess::CrateOrModule => mb.or(Some(MergeBehavior::Crate)),
 +        };
 +    }
 +
 +    let use_item =
 +        make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update();
 +    // merge into existing imports if possible
 +    if let Some(mb) = mb {
 +        let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it));
 +        for existing_use in
 +            scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter)
 +        {
 +            if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
 +                ted::replace(existing_use.syntax(), merged.syntax());
 +                return;
 +            }
 +        }
 +    }
 +
 +    // either we weren't allowed to merge or there is no import that fits the merge conditions
 +    // so look for the place we have to insert to
 +    insert_use_(scope, &path, cfg.group, use_item);
 +}
 +
-         return;
++pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> {
 +    // FIXME: improve this
 +    if path.parent_path().is_some() {
-     if let Some(use_tree) = path.syntax().parent().and_then(ast::UseTree::cast) {
-         if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
-             return;
-         }
-         if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
-             use_.remove();
-             return;
-         }
-         use_tree.remove();
++        return None;
 +    }
++    let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?;
++    if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
++        return None;
++    }
++    if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
++        return Some(Box::new(use_));
++    }
++    Some(Box::new(use_tree))
++}
++
++pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
++    if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) {
++        node.remove();
 +    }
 +}
 +
 +#[derive(Eq, PartialEq, PartialOrd, Ord)]
 +enum ImportGroup {
 +    // the order here defines the order of new group inserts
 +    Std,
 +    ExternCrate,
 +    ThisCrate,
 +    ThisModule,
 +    SuperModule,
 +}
 +
 +impl ImportGroup {
 +    fn new(path: &ast::Path) -> ImportGroup {
 +        let default = ImportGroup::ExternCrate;
 +
 +        let first_segment = match path.first_segment() {
 +            Some(it) => it,
 +            None => return default,
 +        };
 +
 +        let kind = first_segment.kind().unwrap_or(PathSegmentKind::SelfKw);
 +        match kind {
 +            PathSegmentKind::SelfKw => ImportGroup::ThisModule,
 +            PathSegmentKind::SuperKw => ImportGroup::SuperModule,
 +            PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
 +            PathSegmentKind::Name(name) => match name.text().as_str() {
 +                "std" => ImportGroup::Std,
 +                "core" => ImportGroup::Std,
 +                _ => ImportGroup::ExternCrate,
 +            },
 +            // these aren't valid use paths, so fall back to something random
 +            PathSegmentKind::SelfTypeKw => ImportGroup::ExternCrate,
 +            PathSegmentKind::Type { .. } => ImportGroup::ExternCrate,
 +        }
 +    }
 +}
 +
 +#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
 +enum ImportGranularityGuess {
 +    Unknown,
 +    Item,
 +    Module,
 +    ModuleOrItem,
 +    Crate,
 +    CrateOrModule,
 +}
 +
 +fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
 +    // The idea is simple, just check each import as well as the import and its precedent together for
 +    // whether they fulfill a granularity criteria.
 +    let use_stmt = |item| match item {
 +        ast::Item::Use(use_) => {
 +            let use_tree = use_.use_tree()?;
 +            Some((use_tree, use_.visibility(), use_.attrs()))
 +        }
 +        _ => None,
 +    };
 +    let mut use_stmts = match scope {
 +        ImportScope::File(f) => f.items(),
 +        ImportScope::Module(m) => m.items(),
 +        ImportScope::Block(b) => b.items(),
 +    }
 +    .filter_map(use_stmt);
 +    let mut res = ImportGranularityGuess::Unknown;
 +    let (mut prev, mut prev_vis, mut prev_attrs) = match use_stmts.next() {
 +        Some(it) => it,
 +        None => return res,
 +    };
 +    loop {
 +        if let Some(use_tree_list) = prev.use_tree_list() {
 +            if use_tree_list.use_trees().any(|tree| tree.use_tree_list().is_some()) {
 +                // Nested tree lists can only occur in crate style, or with no proper style being enforced in the file.
 +                break ImportGranularityGuess::Crate;
 +            } else {
 +                // Could still be crate-style so continue looking.
 +                res = ImportGranularityGuess::CrateOrModule;
 +            }
 +        }
 +
 +        let (curr, curr_vis, curr_attrs) = match use_stmts.next() {
 +            Some(it) => it,
 +            None => break res,
 +        };
 +        if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) {
 +            if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) {
 +                if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) {
 +                    if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() {
 +                        let prefix_c = prev_prefix.qualifiers().count();
 +                        let curr_c = curr_path.qualifiers().count() - prefix_c;
 +                        let prev_c = prev_path.qualifiers().count() - prefix_c;
 +                        if curr_c == 1 && prev_c == 1 {
 +                            // Same prefix, only differing in the last segment and no use tree lists so this has to be of item style.
 +                            break ImportGranularityGuess::Item;
 +                        } else {
 +                            // Same prefix and no use tree list but differs in more than one segment at the end. This might be module style still.
 +                            res = ImportGranularityGuess::ModuleOrItem;
 +                        }
 +                    } else {
 +                        // Same prefix with item tree lists, has to be module style as it
 +                        // can't be crate style since the trees wouldn't share a prefix then.
 +                        break ImportGranularityGuess::Module;
 +                    }
 +                }
 +            }
 +        }
 +        prev = curr;
 +        prev_vis = curr_vis;
 +        prev_attrs = curr_attrs;
 +    }
 +}
 +
 +fn insert_use_(
 +    scope: &ImportScope,
 +    insert_path: &ast::Path,
 +    group_imports: bool,
 +    use_item: ast::Use,
 +) {
 +    let scope_syntax = scope.as_syntax_node();
 +    let group = ImportGroup::new(insert_path);
 +    let path_node_iter = scope_syntax
 +        .children()
 +        .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node)))
 +        .flat_map(|(use_, node)| {
 +            let tree = use_.use_tree()?;
 +            let path = tree.path()?;
 +            let has_tl = tree.use_tree_list().is_some();
 +            Some((path, has_tl, node))
 +        });
 +
 +    if group_imports {
 +        // Iterator that discards anything thats not in the required grouping
 +        // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
 +        let group_iter = path_node_iter
 +            .clone()
 +            .skip_while(|(path, ..)| ImportGroup::new(path) != group)
 +            .take_while(|(path, ..)| ImportGroup::new(path) == group);
 +
 +        // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
 +        let mut last = None;
 +        // find the element that would come directly after our new import
 +        let post_insert: Option<(_, _, SyntaxNode)> = group_iter
 +            .inspect(|(.., node)| last = Some(node.clone()))
 +            .find(|&(ref path, has_tl, _)| {
 +                use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater
 +            });
 +
 +        if let Some((.., node)) = post_insert {
 +            cov_mark::hit!(insert_group);
 +            // insert our import before that element
 +            return ted::insert(ted::Position::before(node), use_item.syntax());
 +        }
 +        if let Some(node) = last {
 +            cov_mark::hit!(insert_group_last);
 +            // there is no element after our new import, so append it to the end of the group
 +            return ted::insert(ted::Position::after(node), use_item.syntax());
 +        }
 +
 +        // the group we were looking for actually doesn't exist, so insert
 +
 +        let mut last = None;
 +        // find the group that comes after where we want to insert
 +        let post_group = path_node_iter
 +            .inspect(|(.., node)| last = Some(node.clone()))
 +            .find(|(p, ..)| ImportGroup::new(p) > group);
 +        if let Some((.., node)) = post_group {
 +            cov_mark::hit!(insert_group_new_group);
 +            ted::insert(ted::Position::before(&node), use_item.syntax());
 +            if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
 +                ted::insert(ted::Position::after(node), make::tokens::single_newline());
 +            }
 +            return;
 +        }
 +        // there is no such group, so append after the last one
 +        if let Some(node) = last {
 +            cov_mark::hit!(insert_group_no_group);
 +            ted::insert(ted::Position::after(&node), use_item.syntax());
 +            ted::insert(ted::Position::after(node), make::tokens::single_newline());
 +            return;
 +        }
 +    } else {
 +        // There exists a group, so append to the end of it
 +        if let Some((_, _, node)) = path_node_iter.last() {
 +            cov_mark::hit!(insert_no_grouping_last);
 +            ted::insert(ted::Position::after(node), use_item.syntax());
 +            return;
 +        }
 +    }
 +
 +    let l_curly = match scope {
 +        ImportScope::File(_) => None,
 +        // don't insert the imports before the item list/block expr's opening curly brace
 +        ImportScope::Module(item_list) => item_list.l_curly_token(),
 +        // don't insert the imports before the item list's opening curly brace
 +        ImportScope::Block(block) => block.l_curly_token(),
 +    };
 +    // there are no imports in this file at all
 +    // so put the import after all inner module attributes and possible license header comments
 +    if let Some(last_inner_element) = scope_syntax
 +        .children_with_tokens()
 +        // skip the curly brace
 +        .skip(l_curly.is_some() as usize)
 +        .take_while(|child| match child {
 +            NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
 +            NodeOrToken::Token(token) => {
 +                [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG]
 +                    .contains(&token.kind())
 +            }
 +        })
 +        .filter(|child| child.as_token().map_or(true, |t| t.kind() != SyntaxKind::WHITESPACE))
 +        .last()
 +    {
 +        cov_mark::hit!(insert_empty_inner_attr);
 +        ted::insert(ted::Position::after(&last_inner_element), use_item.syntax());
 +        ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline());
 +    } else {
 +        match l_curly {
 +            Some(b) => {
 +                cov_mark::hit!(insert_empty_module);
 +                ted::insert(ted::Position::after(&b), make::tokens::single_newline());
 +                ted::insert(ted::Position::after(&b), use_item.syntax());
 +            }
 +            None => {
 +                cov_mark::hit!(insert_empty_file);
 +                ted::insert(
 +                    ted::Position::first_child_of(scope_syntax),
 +                    make::tokens::blank_line(),
 +                );
 +                ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
 +            }
 +        }
 +    }
 +}
 +
 +fn is_inner_attribute(node: SyntaxNode) -> bool {
 +    ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
 +}
index f54ae6c9202294fa9a4a93aabccb1faecbbc1f11,0000000000000000000000000000000000000000..8bc093a85a21bc6fa38211eab6ff2ae109121c04
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,136 @@@
-             T![;] => {
 +//! Utilities for formatting macro expanded nodes until we get a proper formatter.
 +use syntax::{
 +    ast::make,
 +    ted::{self, Position},
 +    NodeOrToken,
 +    SyntaxKind::{self, *},
 +    SyntaxNode, SyntaxToken, WalkEvent, T,
 +};
 +
 +// FIXME: It would also be cool to share logic here and in the mbe tests,
 +// which are pretty unreadable at the moment.
 +/// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them.
 +pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
 +    let mut indent = 0;
 +    let mut last: Option<SyntaxKind> = None;
 +    let mut mods = Vec::new();
 +    let syn = syn.clone_subtree().clone_for_update();
 +
 +    let before = Position::before;
 +    let after = Position::after;
 +
 +    let do_indent = |pos: fn(_) -> Position, token: &SyntaxToken, indent| {
 +        (pos(token.clone()), make::tokens::whitespace(&" ".repeat(2 * indent)))
 +    };
 +    let do_ws = |pos: fn(_) -> Position, token: &SyntaxToken| {
 +        (pos(token.clone()), make::tokens::single_space())
 +    };
 +    let do_nl = |pos: fn(_) -> Position, token: &SyntaxToken| {
 +        (pos(token.clone()), make::tokens::single_newline())
 +    };
 +
 +    for event in syn.preorder_with_tokens() {
 +        let token = match event {
 +            WalkEvent::Enter(NodeOrToken::Token(token)) => token,
 +            WalkEvent::Leave(NodeOrToken::Node(node))
 +                if matches!(
 +                    node.kind(),
 +                    ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES
 +                ) =>
 +            {
 +                if indent > 0 {
 +                    mods.push((
 +                        Position::after(node.clone()),
 +                        make::tokens::whitespace(&" ".repeat(2 * indent)),
 +                    ));
 +                }
 +                if node.parent().is_some() {
 +                    mods.push((Position::after(node), make::tokens::single_newline()));
 +                }
 +                continue;
 +            }
 +            _ => continue,
 +        };
 +        let tok = &token;
 +
 +        let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
 +            tok.next_token().map(|it| f(it.kind())).unwrap_or(default)
 +        };
 +        let is_last =
 +            |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
 +
 +        match tok.kind() {
 +            k if is_text(k) && is_next(|it| !it.is_punct() || it == UNDERSCORE, false) => {
 +                mods.push(do_ws(after, tok));
 +            }
 +            L_CURLY if is_next(|it| it != R_CURLY, true) => {
 +                indent += 1;
 +                if is_last(is_text, false) {
 +                    mods.push(do_ws(before, tok));
 +                }
 +
 +                mods.push(do_indent(after, tok, indent));
 +                mods.push(do_nl(after, tok));
 +            }
 +            R_CURLY if is_last(|it| it != L_CURLY, true) => {
 +                indent = indent.saturating_sub(1);
 +
 +                if indent > 0 {
 +                    mods.push(do_indent(before, tok, indent));
 +                }
 +                mods.push(do_nl(before, tok));
 +            }
 +            R_CURLY => {
 +                if indent > 0 {
 +                    mods.push(do_indent(after, tok, indent));
 +                }
 +                mods.push(do_nl(after, tok));
 +            }
 +            LIFETIME_IDENT if is_next(is_text, true) => {
 +                mods.push(do_ws(after, tok));
 +            }
 +            MUT_KW if is_next(|it| it == SELF_KW, false) => {
 +                mods.push(do_ws(after, tok));
 +            }
 +            AS_KW | DYN_KW | IMPL_KW | CONST_KW => {
 +                mods.push(do_ws(after, tok));
 +            }
++            T![;] if is_next(|it| it != R_CURLY, true) => {
 +                if indent > 0 {
 +                    mods.push(do_indent(after, tok, indent));
 +                }
 +                mods.push(do_nl(after, tok));
 +            }
 +            T![=] if is_next(|it| it == T![>], false) => {
 +                // FIXME: this branch is for `=>` in macro_rules!, which is currently parsed as
 +                // two separate symbols.
 +                mods.push(do_ws(before, tok));
 +                mods.push(do_ws(after, &tok.next_token().unwrap()));
 +            }
 +            T![->] | T![=] | T![=>] => {
 +                mods.push(do_ws(before, tok));
 +                mods.push(do_ws(after, tok));
 +            }
 +            T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => {
 +                mods.push(do_ws(after, tok));
 +            }
 +            _ => (),
 +        }
 +
 +        last = Some(tok.kind());
 +    }
 +
 +    for (pos, insert) in mods {
 +        ted::insert(pos, insert);
 +    }
 +
 +    if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) {
 +        ted::remove(it);
 +    }
 +
 +    syn
 +}
 +
 +fn is_text(k: SyntaxKind) -> bool {
 +    k.is_keyword() || k.is_literal() || k == IDENT || k == UNDERSCORE
 +}
index d52adaee535f99371b3ca9547ef380fb9cfa65d6,0000000000000000000000000000000000000000..c5c50d88dd28a5f7db15bd1931daf5d0d1185b83
mode 100644,000000..100644
--- /dev/null
@@@ -1,563 -1,0 +1,578 @@@
- use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HirDisplay, Semantics, TypeInfo};
 +//! Logic for rendering the different hover messages
 +use std::fmt::Display;
 +
 +use either::Either;
-                 Err(_) => it.value(db).map(|x| format!("{}", x)),
++use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
 +use ide_db::{
 +    base_db::SourceDatabase,
 +    defs::Definition,
 +    famous_defs::FamousDefs,
 +    generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
++    syntax_helpers::insert_whitespace_into_node,
 +    RootDatabase,
 +};
 +use itertools::Itertools;
 +use stdx::format_to;
 +use syntax::{
 +    algo, ast, match_ast, AstNode, Direction,
 +    SyntaxKind::{LET_EXPR, LET_STMT},
 +    SyntaxToken, T,
 +};
 +
 +use crate::{
 +    doc_links::{remove_links, rewrite_links},
 +    hover::walk_and_push_ty,
 +    markdown_remove::remove_markdown,
 +    HoverAction, HoverConfig, HoverResult, Markup,
 +};
 +
 +pub(super) fn type_info(
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &HoverConfig,
 +    expr_or_pat: &Either<ast::Expr, ast::Pat>,
 +) -> Option<HoverResult> {
 +    let TypeInfo { original, adjusted } = match expr_or_pat {
 +        Either::Left(expr) => sema.type_of_expr(expr)?,
 +        Either::Right(pat) => sema.type_of_pat(pat)?,
 +    };
 +
 +    let mut res = HoverResult::default();
 +    let mut targets: Vec<hir::ModuleDef> = Vec::new();
 +    let mut push_new_def = |item: hir::ModuleDef| {
 +        if !targets.contains(&item) {
 +            targets.push(item);
 +        }
 +    };
 +    walk_and_push_ty(sema.db, &original, &mut push_new_def);
 +
 +    res.markup = if let Some(adjusted_ty) = adjusted {
 +        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
 +        let original = original.display(sema.db).to_string();
 +        let adjusted = adjusted_ty.display(sema.db).to_string();
 +        let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
 +        format!(
 +            "{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
 +            original,
 +            adjusted,
 +            apad = static_text_diff_len + adjusted.len().max(original.len()),
 +            opad = original.len(),
 +            bt_start = if config.markdown() { "```text\n" } else { "" },
 +            bt_end = if config.markdown() { "```\n" } else { "" }
 +        )
 +        .into()
 +    } else {
 +        if config.markdown() {
 +            Markup::fenced_block(&original.display(sema.db))
 +        } else {
 +            original.display(sema.db).to_string().into()
 +        }
 +    };
 +    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
 +    Some(res)
 +}
 +
 +pub(super) fn try_expr(
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &HoverConfig,
 +    try_expr: &ast::TryExpr,
 +) -> Option<HoverResult> {
 +    let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
 +    let mut ancestors = try_expr.syntax().ancestors();
 +    let mut body_ty = loop {
 +        let next = ancestors.next()?;
 +        break match_ast! {
 +            match next {
 +                ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
 +                ast::Item(__) => return None,
 +                ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
 +                ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
 +                    sema.type_of_expr(&block_expr.into())?.original
 +                } else {
 +                    continue;
 +                },
 +                _ => continue,
 +            }
 +        };
 +    };
 +
 +    if inner_ty == body_ty {
 +        return None;
 +    }
 +
 +    let mut inner_ty = inner_ty;
 +    let mut s = "Try Target".to_owned();
 +
 +    let adts = inner_ty.as_adt().zip(body_ty.as_adt());
 +    if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts {
 +        let famous_defs = FamousDefs(sema, sema.scope(try_expr.syntax())?.krate());
 +        // special case for two options, there is no value in showing them
 +        if let Some(option_enum) = famous_defs.core_option_Option() {
 +            if inner == option_enum && body == option_enum {
 +                cov_mark::hit!(hover_try_expr_opt_opt);
 +                return None;
 +            }
 +        }
 +
 +        // special case two results to show the error variants only
 +        if let Some(result_enum) = famous_defs.core_result_Result() {
 +            if inner == result_enum && body == result_enum {
 +                let error_type_args =
 +                    inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1));
 +                if let Some((inner, body)) = error_type_args {
 +                    inner_ty = inner;
 +                    body_ty = body;
 +                    s = "Try Error".to_owned();
 +                }
 +            }
 +        }
 +    }
 +
 +    let mut res = HoverResult::default();
 +
 +    let mut targets: Vec<hir::ModuleDef> = Vec::new();
 +    let mut push_new_def = |item: hir::ModuleDef| {
 +        if !targets.contains(&item) {
 +            targets.push(item);
 +        }
 +    };
 +    walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
 +    walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
 +    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
 +
 +    let inner_ty = inner_ty.display(sema.db).to_string();
 +    let body_ty = body_ty.display(sema.db).to_string();
 +    let ty_len_max = inner_ty.len().max(body_ty.len());
 +
 +    let l = "Propagated as: ".len() - " Type: ".len();
 +    let static_text_len_diff = l as isize - s.len() as isize;
 +    let tpad = static_text_len_diff.max(0) as usize;
 +    let ppad = static_text_len_diff.min(0).abs() as usize;
 +
 +    res.markup = format!(
 +        "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}",
 +        s,
 +        inner_ty,
 +        body_ty,
 +        pad0 = ty_len_max + tpad,
 +        pad1 = ty_len_max + ppad,
 +        bt_start = if config.markdown() { "```text\n" } else { "" },
 +        bt_end = if config.markdown() { "```\n" } else { "" }
 +    )
 +    .into();
 +    Some(res)
 +}
 +
 +pub(super) fn deref_expr(
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &HoverConfig,
 +    deref_expr: &ast::PrefixExpr,
 +) -> Option<HoverResult> {
 +    let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
 +    let TypeInfo { original, adjusted } =
 +        sema.type_of_expr(&ast::Expr::from(deref_expr.clone()))?;
 +
 +    let mut res = HoverResult::default();
 +    let mut targets: Vec<hir::ModuleDef> = Vec::new();
 +    let mut push_new_def = |item: hir::ModuleDef| {
 +        if !targets.contains(&item) {
 +            targets.push(item);
 +        }
 +    };
 +    walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
 +    walk_and_push_ty(sema.db, &original, &mut push_new_def);
 +
 +    res.markup = if let Some(adjusted_ty) = adjusted {
 +        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
 +        let original = original.display(sema.db).to_string();
 +        let adjusted = adjusted_ty.display(sema.db).to_string();
 +        let inner = inner_ty.display(sema.db).to_string();
 +        let type_len = "To type: ".len();
 +        let coerced_len = "Coerced to: ".len();
 +        let deref_len = "Dereferenced from: ".len();
 +        let max_len = (original.len() + type_len)
 +            .max(adjusted.len() + coerced_len)
 +            .max(inner.len() + deref_len);
 +        format!(
 +            "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
 +            inner,
 +            original,
 +            adjusted,
 +            ipad = max_len - deref_len,
 +            apad = max_len - type_len,
 +            opad = max_len - coerced_len,
 +            bt_start = if config.markdown() { "```text\n" } else { "" },
 +            bt_end = if config.markdown() { "```\n" } else { "" }
 +        )
 +        .into()
 +    } else {
 +        let original = original.display(sema.db).to_string();
 +        let inner = inner_ty.display(sema.db).to_string();
 +        let type_len = "To type: ".len();
 +        let deref_len = "Dereferenced from: ".len();
 +        let max_len = (original.len() + type_len).max(inner.len() + deref_len);
 +        format!(
 +            "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\n{bt_end}",
 +            inner,
 +            original,
 +            ipad = max_len - deref_len,
 +            apad = max_len - type_len,
 +            bt_start = if config.markdown() { "```text\n" } else { "" },
 +            bt_end = if config.markdown() { "```\n" } else { "" }
 +        )
 +        .into()
 +    };
 +    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
 +
 +    Some(res)
 +}
 +
 +pub(super) fn keyword(
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &HoverConfig,
 +    token: &SyntaxToken,
 +) -> Option<HoverResult> {
 +    if !token.kind().is_keyword() || !config.documentation.is_some() || !config.keywords {
 +        return None;
 +    }
 +    let parent = token.parent()?;
 +    let famous_defs = FamousDefs(sema, sema.scope(&parent)?.krate());
 +
 +    let KeywordHint { description, keyword_mod, actions } = keyword_hints(sema, token, parent);
 +
 +    let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
 +    let docs = doc_owner.attrs(sema.db).docs()?;
 +    let markup = process_markup(
 +        sema.db,
 +        Definition::Module(doc_owner),
 +        &markup(Some(docs.into()), description, None)?,
 +        config,
 +    );
 +    Some(HoverResult { markup, actions })
 +}
 +
 +pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
 +    let (path, tt) = attr.as_simple_call()?;
 +    if !tt.syntax().text_range().contains(token.text_range().start()) {
 +        return None;
 +    }
 +    let (is_clippy, lints) = match &*path {
 +        "feature" => (false, FEATURES),
 +        "allow" | "deny" | "forbid" | "warn" => {
 +            let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
 +                .filter(|t| t.kind() == T![:])
 +                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
 +                .filter(|t| t.kind() == T![:])
 +                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
 +                .map_or(false, |t| {
 +                    t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
 +                });
 +            if is_clippy {
 +                (true, CLIPPY_LINTS)
 +            } else {
 +                (false, DEFAULT_LINTS)
 +            }
 +        }
 +        _ => return None,
 +    };
 +
 +    let tmp;
 +    let needle = if is_clippy {
 +        tmp = format!("clippy::{}", token.text());
 +        &tmp
 +    } else {
 +        &*token.text()
 +    };
 +
 +    let lint =
 +        lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
 +    Some(HoverResult {
 +        markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
 +        ..Default::default()
 +    })
 +}
 +
 +pub(super) fn process_markup(
 +    db: &RootDatabase,
 +    def: Definition,
 +    markup: &Markup,
 +    config: &HoverConfig,
 +) -> Markup {
 +    let markup = markup.as_str();
 +    let markup = if !config.markdown() {
 +        remove_markdown(markup)
 +    } else if config.links_in_hover {
 +        rewrite_links(db, markup, def)
 +    } else {
 +        remove_links(markup)
 +    };
 +    Markup::from(markup)
 +}
 +
 +fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
 +    match def {
 +        Definition::Field(f) => Some(f.parent_def(db).name(db)),
 +        Definition::Local(l) => l.parent(db).name(db),
 +        Definition::Function(f) => match f.as_assoc_item(db)?.container(db) {
 +            hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
 +            hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
 +        },
 +        Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
 +        _ => None,
 +    }
 +    .map(|name| name.to_string())
 +}
 +
 +pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
 +    let crate_name =
 +        db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
 +    let module_path = module
 +        .path_to_root(db)
 +        .into_iter()
 +        .rev()
 +        .flat_map(|it| it.name(db).map(|name| name.to_string()));
 +    crate_name.into_iter().chain(module_path).chain(item_name).join("::")
 +}
 +
 +pub(super) fn definition(
 +    db: &RootDatabase,
 +    def: Definition,
 +    famous_defs: Option<&FamousDefs<'_, '_>>,
 +    config: &HoverConfig,
 +) -> Option<Markup> {
 +    let mod_path = definition_mod_path(db, &def);
 +    let (label, docs) = match def {
 +        Definition::Macro(it) => label_and_docs(db, it),
 +        Definition::Field(it) => label_and_docs(db, it),
 +        Definition::Module(it) => label_and_docs(db, it),
 +        Definition::Function(it) => label_and_docs(db, it),
 +        Definition::Adt(it) => label_and_docs(db, it),
 +        Definition::Variant(it) => label_and_docs(db, it),
 +        Definition::Const(it) => label_value_and_docs(db, it, |it| {
 +            let body = it.eval(db);
 +            match body {
 +                Ok(x) => Some(format!("{}", x)),
-         Definition::Static(it) => label_value_and_docs(db, it, |it| it.value(db)),
++                Err(_) => {
++                    let source = it.source(db)?;
++                    let mut body = source.value.body()?.syntax().clone();
++                    if source.file_id.is_macro() {
++                        body = insert_whitespace_into_node::insert_ws_into(body);
++                    }
++                    Some(body.to_string())
++                }
++            }
++        }),
++        Definition::Static(it) => label_value_and_docs(db, it, |it| {
++            let source = it.source(db)?;
++            let mut body = source.value.body()?.syntax().clone();
++            if source.file_id.is_macro() {
++                body = insert_whitespace_into_node::insert_ws_into(body);
 +            }
++            Some(body.to_string())
 +        }),
 +        Definition::Trait(it) => label_and_docs(db, it),
 +        Definition::TypeAlias(it) => label_and_docs(db, it),
 +        Definition::BuiltinType(it) => {
 +            return famous_defs
 +                .and_then(|fd| builtin(fd, it))
 +                .or_else(|| Some(Markup::fenced_block(&it.name())))
 +        }
 +        Definition::Local(it) => return local(db, it),
 +        Definition::SelfType(impl_def) => {
 +            impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
 +        }
 +        Definition::GenericParam(it) => label_and_docs(db, it),
 +        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
 +        // FIXME: We should be able to show more info about these
 +        Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
 +        Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
 +        Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
 +    };
 +
 +    let docs = match config.documentation {
 +        Some(_) => docs.or_else(|| {
 +            // docs are missing, for assoc items of trait impls try to fall back to the docs of the
 +            // original item of the trait
 +            let assoc = def.as_assoc_item(db)?;
 +            let trait_ = assoc.containing_trait_impl(db)?;
 +            let name = Some(assoc.name(db)?);
 +            let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
 +            item.docs(db)
 +        }),
 +        None => None,
 +    };
 +    let docs = docs.filter(|_| config.documentation.is_some()).map(Into::into);
 +    markup(docs, label, mod_path)
 +}
 +
 +fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
 +    let name = attr.name(db);
 +    let desc = format!("#[{}]", name);
 +
 +    let AttributeTemplate { word, list, name_value_str } = match attr.template(db) {
 +        Some(template) => template,
 +        None => return Some(Markup::fenced_block(&attr.name(db))),
 +    };
 +    let mut docs = "Valid forms are:".to_owned();
 +    if word {
 +        format_to!(docs, "\n - #\\[{}]", name);
 +    }
 +    if let Some(list) = list {
 +        format_to!(docs, "\n - #\\[{}({})]", name, list);
 +    }
 +    if let Some(name_value_str) = name_value_str {
 +        format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str);
 +    }
 +    markup(Some(docs.replace('*', "\\*")), desc, None)
 +}
 +
 +fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
 +where
 +    D: HasAttrs + HirDisplay,
 +{
 +    let label = def.display(db).to_string();
 +    let docs = def.attrs(db).docs();
 +    (label, docs)
 +}
 +
 +fn label_value_and_docs<D, E, V>(
 +    db: &RootDatabase,
 +    def: D,
 +    value_extractor: E,
 +) -> (String, Option<hir::Documentation>)
 +where
 +    D: HasAttrs + HirDisplay,
 +    E: Fn(&D) -> Option<V>,
 +    V: Display,
 +{
 +    let label = if let Some(value) = value_extractor(&def) {
 +        format!("{} = {}", def.display(db), value)
 +    } else {
 +        def.display(db).to_string()
 +    };
 +    let docs = def.attrs(db).docs();
 +    (label, docs)
 +}
 +
 +fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
 +    if let Definition::GenericParam(_) = def {
 +        return None;
 +    }
 +    def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
 +}
 +
 +fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
 +    let mut buf = String::new();
 +
 +    if let Some(mod_path) = mod_path {
 +        if !mod_path.is_empty() {
 +            format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
 +        }
 +    }
 +    format_to!(buf, "```rust\n{}\n```", desc);
 +
 +    if let Some(doc) = docs {
 +        format_to!(buf, "\n___\n\n{}", doc);
 +    }
 +    Some(buf.into())
 +}
 +
 +fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option<Markup> {
 +    // std exposes prim_{} modules with docstrings on the root to document the builtins
 +    let primitive_mod = format!("prim_{}", builtin.name());
 +    let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
 +    let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
 +    markup(Some(docs.into()), builtin.name().to_string(), None)
 +}
 +
 +fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
 +    let db = famous_defs.0.db;
 +    let std_crate = famous_defs.std()?;
 +    let std_root_module = std_crate.root_module(db);
 +    std_root_module
 +        .children(db)
 +        .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
 +}
 +
 +fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
 +    let ty = it.ty(db);
 +    let ty = ty.display_truncated(db, None);
 +    let is_mut = if it.is_mut(db) { "mut " } else { "" };
 +    let desc = match it.source(db).value {
 +        Either::Left(ident) => {
 +            let name = it.name(db);
 +            let let_kw = if ident
 +                .syntax()
 +                .parent()
 +                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
 +            {
 +                "let "
 +            } else {
 +                ""
 +            };
 +            format!("{}{}{}: {}", let_kw, is_mut, name, ty)
 +        }
 +        Either::Right(_) => format!("{}self: {}", is_mut, ty),
 +    };
 +    markup(None, desc, None)
 +}
 +
 +struct KeywordHint {
 +    description: String,
 +    keyword_mod: String,
 +    actions: Vec<HoverAction>,
 +}
 +
 +impl KeywordHint {
 +    fn new(description: String, keyword_mod: String) -> Self {
 +        Self { description, keyword_mod, actions: Vec::default() }
 +    }
 +}
 +
 +fn keyword_hints(
 +    sema: &Semantics<'_, RootDatabase>,
 +    token: &SyntaxToken,
 +    parent: syntax::SyntaxNode,
 +) -> KeywordHint {
 +    match token.kind() {
 +        T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
 +            let keyword_mod = format!("{}_keyword", token.text());
 +
 +            match ast::Expr::cast(parent).and_then(|site| sema.type_of_expr(&site)) {
 +                // ignore the unit type ()
 +                Some(ty) if !ty.adjusted.as_ref().unwrap_or(&ty.original).is_unit() => {
 +                    let mut targets: Vec<hir::ModuleDef> = Vec::new();
 +                    let mut push_new_def = |item: hir::ModuleDef| {
 +                        if !targets.contains(&item) {
 +                            targets.push(item);
 +                        }
 +                    };
 +                    walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
 +
 +                    let ty = ty.adjusted();
 +                    let description = format!("{}: {}", token.text(), ty.display(sema.db));
 +
 +                    KeywordHint {
 +                        description,
 +                        keyword_mod,
 +                        actions: vec![HoverAction::goto_type_from_targets(sema.db, targets)],
 +                    }
 +                }
 +                _ => KeywordHint {
 +                    description: token.text().to_string(),
 +                    keyword_mod,
 +                    actions: Vec::new(),
 +                },
 +            }
 +        }
 +        T![fn] => {
 +            let module = match ast::FnPtrType::cast(parent) {
 +                // treat fn keyword inside function pointer type as primitive
 +                Some(_) => format!("prim_{}", token.text()),
 +                None => format!("{}_keyword", token.text()),
 +            };
 +            KeywordHint::new(token.text().to_string(), module)
 +        }
 +        T![Self] => KeywordHint::new(token.text().to_string(), "self_upper_keyword".into()),
 +        _ => KeywordHint::new(token.text().to_string(), format!("{}_keyword", token.text())),
 +    }
 +}
index 685eb4521ebdaf7f10ee93be5f92d346d76f2c1d,0000000000000000000000000000000000000000..4b8b47783d1269438341714a6a1866b94fa50d8a
mode 100644,000000..100644
--- /dev/null
@@@ -1,5115 -1,0 +1,5173 @@@
 +use expect_test::{expect, Expect};
 +use ide_db::base_db::{FileLoader, FileRange};
 +use syntax::TextRange;
 +
 +use crate::{fixture, hover::HoverDocFormat, HoverConfig};
 +
 +fn check_hover_no_result(ra_fixture: &str) {
 +    let (analysis, position) = fixture::position(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: true,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
 +        )
 +        .unwrap();
 +    assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap());
 +}
 +
 +#[track_caller]
 +fn check(ra_fixture: &str, expect: Expect) {
 +    let (analysis, position) = fixture::position(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: true,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
 +        )
 +        .unwrap()
 +        .unwrap();
 +
 +    let content = analysis.db.file_text(position.file_id);
 +    let hovered_element = &content[hover.range];
 +
 +    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
 +    expect.assert_eq(&actual)
 +}
 +
 +fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
 +    let (analysis, position) = fixture::position(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: false,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
 +        )
 +        .unwrap()
 +        .unwrap();
 +
 +    let content = analysis.db.file_text(position.file_id);
 +    let hovered_element = &content[hover.range];
 +
 +    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
 +    expect.assert_eq(&actual)
 +}
 +
 +fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
 +    let (analysis, position) = fixture::position(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: true,
 +                documentation: Some(HoverDocFormat::PlainText),
 +                keywords: true,
 +            },
 +            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
 +        )
 +        .unwrap()
 +        .unwrap();
 +
 +    let content = analysis.db.file_text(position.file_id);
 +    let hovered_element = &content[hover.range];
 +
 +    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
 +    expect.assert_eq(&actual)
 +}
 +
 +fn check_actions(ra_fixture: &str, expect: Expect) {
 +    let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: true,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            FileRange { file_id, range: position.range_or_empty() },
 +        )
 +        .unwrap()
 +        .unwrap();
 +    expect.assert_debug_eq(&hover.info.actions)
 +}
 +
 +fn check_hover_range(ra_fixture: &str, expect: Expect) {
 +    let (analysis, range) = fixture::range(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: false,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            range,
 +        )
 +        .unwrap()
 +        .unwrap();
 +    expect.assert_eq(hover.info.markup.as_str())
 +}
 +
 +fn check_hover_range_no_results(ra_fixture: &str) {
 +    let (analysis, range) = fixture::range(ra_fixture);
 +    let hover = analysis
 +        .hover(
 +            &HoverConfig {
 +                links_in_hover: false,
 +                documentation: Some(HoverDocFormat::Markdown),
 +                keywords: true,
 +            },
 +            range,
 +        )
 +        .unwrap();
 +    assert!(hover.is_none());
 +}
 +
 +#[test]
 +fn hover_descend_macros_avoids_duplicates() {
 +    check(
 +        r#"
 +macro_rules! dupe_use {
 +    ($local:ident) => {
 +        {
 +            $local;
 +            $local;
 +        }
 +    }
 +}
 +fn foo() {
 +    let local = 0;
 +    dupe_use!(local$0);
 +}
 +"#,
 +        expect![[r#"
 +            *local*
 +
 +            ```rust
 +            let local: i32
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_all_macro_descends() {
 +    check(
 +        r#"
 +macro_rules! m {
 +    ($name:ident) => {
 +        /// Outer
 +        fn $name() {}
 +
 +        mod module {
 +            /// Inner
 +            fn $name() {}
 +        }
 +    };
 +}
 +
 +m!(ab$0c);
 +            "#,
 +        expect![[r#"
 +            *abc*
 +
 +            ```rust
 +            test::module
 +            ```
 +
 +            ```rust
 +            fn abc()
 +            ```
 +
 +            ---
 +
 +            Inner
 +            ---
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            fn abc()
 +            ```
 +
 +            ---
 +
 +            Outer
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_type_of_an_expression() {
 +    check(
 +        r#"
 +pub fn foo() -> u32 { 1 }
 +
 +fn main() {
 +    let foo_test = foo()$0;
 +}
 +"#,
 +        expect![[r#"
 +            *foo()*
 +            ```rust
 +            u32
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_remove_markdown_if_configured() {
 +    check_hover_no_markdown(
 +        r#"
 +pub fn foo() -> u32 { 1 }
 +
 +fn main() {
 +    let foo_test = foo()$0;
 +}
 +"#,
 +        expect![[r#"
 +            *foo()*
 +            u32
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_long_type_of_an_expression() {
 +    check(
 +        r#"
 +struct Scan<A, B, C> { a: A, b: B, c: C }
 +struct Iter<I> { inner: I }
 +enum Option<T> { Some(T), None }
 +
 +struct OtherStruct<T> { i: T }
 +
 +fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
 +    Iter { inner: Scan { a, b, c } }
 +}
 +
 +fn main() {
 +    let num: i32 = 55;
 +    let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
 +        Option::Some(*memo + value)
 +    };
 +    let number = 5u32;
 +    let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
 +}
 +"#,
 +        expect![[r#"
 +                *iter*
 +
 +                ```rust
 +                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_fn_signature() {
 +    // Single file with result
 +    check(
 +        r#"
 +pub fn foo() -> u32 { 1 }
 +
 +fn main() { let foo_test = fo$0o(); }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo() -> u32
 +                ```
 +            "#]],
 +    );
 +
 +    // Multiple candidates but results are ambiguous.
 +    check(
 +        r#"
 +//- /a.rs
 +pub fn foo() -> u32 { 1 }
 +
 +//- /b.rs
 +pub fn foo() -> &str { "" }
 +
 +//- /c.rs
 +pub fn foo(a: u32, b: u32) {}
 +
 +//- /main.rs
 +mod a;
 +mod b;
 +mod c;
 +
 +fn main() { let foo_test = fo$0o(); }
 +        "#,
 +        expect![[r#"
 +                *foo*
 +                ```rust
 +                {unknown}
 +                ```
 +            "#]],
 +    );
 +
 +    // Use literal `crate` in path
 +    check(
 +        r#"
 +pub struct X;
 +
 +fn foo() -> crate::X { X }
 +
 +fn main() { f$0oo(); }
 +        "#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            fn foo() -> crate::X
 +            ```
 +        "#]],
 +    );
 +
 +    // Check `super` in path
 +    check(
 +        r#"
 +pub struct X;
 +
 +mod m { pub fn foo() -> super::X { super::X } }
 +
 +fn main() { m::f$0oo(); }
 +        "#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test::m
 +                ```
 +
 +                ```rust
 +                pub fn foo() -> super::X
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_omits_unnamed_where_preds() {
 +    check(
 +        r#"
 +pub fn foo(bar: impl T) { }
 +
 +fn main() { fo$0o(); }
 +        "#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            pub fn foo(bar: impl T)
 +            ```
 +        "#]],
 +    );
 +    check(
 +        r#"
 +pub fn foo<V: AsRef<str>>(bar: impl T, baz: V) { }
 +
 +fn main() { fo$0o(); }
 +        "#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            pub fn foo<V>(bar: impl T, baz: V)
 +            where
 +                V: AsRef<str>,
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_fn_signature_with_type_params() {
 +    check(
 +        r#"
 +pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
 +
 +fn main() { let foo_test = fo$0o(); }
 +        "#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo<'a, T>(b: &'a T) -> &'a str
 +                where
 +                    T: AsRef<str>,
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_fn_signature_on_fn_name() {
 +    check(
 +        r#"
 +pub fn foo$0(a: u32, b: u32) -> u32 {}
 +
 +fn main() { }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo(a: u32, b: u32) -> u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_fn_doc() {
 +    check(
 +        r#"
 +/// # Example
 +/// ```
 +/// # use std::path::Path;
 +/// #
 +/// foo(Path::new("hello, world!"))
 +/// ```
 +pub fn foo$0(_: &Path) {}
 +
 +fn main() { }
 +"#,
 +        expect![[r##"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo(_: &Path)
 +                ```
 +
 +                ---
 +
 +                # Example
 +
 +                ```
 +                # use std::path::Path;
 +                #
 +                foo(Path::new("hello, world!"))
 +                ```
 +            "##]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_fn_doc_attr_raw_string() {
 +    check(
 +        r##"
 +#[doc = r#"Raw string doc attr"#]
 +pub fn foo$0(_: &Path) {}
 +
 +fn main() { }
 +"##,
 +        expect![[r##"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo(_: &Path)
 +                ```
 +
 +                ---
 +
 +                Raw string doc attr
 +            "##]],
 +    );
 +}
 +
 +#[test]
 +fn hover_shows_struct_field_info() {
 +    // Hovering over the field when instantiating
 +    check(
 +        r#"
 +struct Foo { field_a: u32 }
 +
 +fn main() {
 +    let foo = Foo { field_a$0: 0, };
 +}
 +"#,
 +        expect![[r#"
 +                *field_a*
 +
 +                ```rust
 +                test::Foo
 +                ```
 +
 +                ```rust
 +                field_a: u32
 +                ```
 +            "#]],
 +    );
 +
 +    // Hovering over the field in the definition
 +    check(
 +        r#"
 +struct Foo { field_a$0: u32 }
 +
 +fn main() {
 +    let foo = Foo { field_a: 0 };
 +}
 +"#,
 +        expect![[r#"
 +                *field_a*
 +
 +                ```rust
 +                test::Foo
 +                ```
 +
 +                ```rust
 +                field_a: u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_const_static() {
 +    check(
 +        r#"const foo$0: u32 = 123;"#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const foo: u32 = 123 (0x7B)
 +            ```
 +        "#]],
 +    );
 +    check(
 +        r#"
 +const foo$0: u32 = {
 +    let x = foo();
 +    x + 100
 +};"#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const foo: u32 = {
 +                let x = foo();
 +                x + 100
 +            }
 +            ```
 +        "#]],
 +    );
 +
 +    check(
 +        r#"static foo$0: u32 = 456;"#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            static foo: u32 = 456
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_default_generic_types() {
 +    check(
 +        r#"
 +struct Test<K, T = u8> { k: K, t: T }
 +
 +fn main() {
 +    let zz$0 = Test { t: 23u8, k: 33 };
 +}"#,
 +        expect![[r#"
 +                *zz*
 +
 +                ```rust
 +                let zz: Test<i32>
 +                ```
 +            "#]],
 +    );
 +    check_hover_range(
 +        r#"
 +struct Test<K, T = u8> { k: K, t: T }
 +
 +fn main() {
 +    let $0zz$0 = Test { t: 23u8, k: 33 };
 +}"#,
 +        expect![[r#"
 +            ```rust
 +            Test<i32, u8>
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_some() {
 +    check(
 +        r#"
 +enum Option<T> { Some(T) }
 +use Option::Some;
 +
 +fn main() { So$0me(12); }
 +"#,
 +        expect![[r#"
 +                *Some*
 +
 +                ```rust
 +                test::Option
 +                ```
 +
 +                ```rust
 +                Some(T)
 +                ```
 +            "#]],
 +    );
 +
 +    check(
 +        r#"
 +enum Option<T> { Some(T) }
 +use Option::Some;
 +
 +fn main() { let b$0ar = Some(12); }
 +"#,
 +        expect![[r#"
 +                *bar*
 +
 +                ```rust
 +                let bar: Option<i32>
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_enum_variant() {
 +    check(
 +        r#"
 +enum Option<T> {
 +    /// The None variant
 +    Non$0e
 +}
 +"#,
 +        expect![[r#"
 +                *None*
 +
 +                ```rust
 +                test::Option
 +                ```
 +
 +                ```rust
 +                None
 +                ```
 +
 +                ---
 +
 +                The None variant
 +            "#]],
 +    );
 +
 +    check(
 +        r#"
 +enum Option<T> {
 +    /// The Some variant
 +    Some(T)
 +}
 +fn main() {
 +    let s = Option::Som$0e(12);
 +}
 +"#,
 +        expect![[r#"
 +                *Some*
 +
 +                ```rust
 +                test::Option
 +                ```
 +
 +                ```rust
 +                Some(T)
 +                ```
 +
 +                ---
 +
 +                The Some variant
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_for_local_variable() {
 +    check(
 +        r#"fn func(foo: i32) { fo$0o; }"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                foo: i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_for_local_variable_pat() {
 +    check(
 +        r#"fn func(fo$0o: i32) {}"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                foo: i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_local_var_edge() {
 +    check(
 +        r#"fn func(foo: i32) { if true { $0foo; }; }"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                foo: i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_for_param_edge() {
 +    check(
 +        r#"fn func($0foo: i32) {}"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                foo: i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_for_param_with_multiple_traits() {
 +    check(
 +        r#"
 +            //- minicore: sized
 +            trait Deref {
 +                type Target: ?Sized;
 +            }
 +            trait DerefMut {
 +                type Target: ?Sized;
 +            }
 +            fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
 +        expect![[r#"
 +                *_x*
 +
 +                ```rust
 +                _x: impl Deref<Target = u8> + DerefMut<Target = u8>
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_infer_associated_method_result() {
 +    check(
 +        r#"
 +struct Thing { x: u32 }
 +
 +impl Thing {
 +    fn new() -> Thing { Thing { x: 0 } }
 +}
 +
 +fn main() { let foo_$0test = Thing::new(); }
 +"#,
 +        expect![[r#"
 +                *foo_test*
 +
 +                ```rust
 +                let foo_test: Thing
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_infer_associated_method_exact() {
 +    check(
 +        r#"
 +mod wrapper {
 +    pub struct Thing { x: u32 }
 +
 +    impl Thing {
 +        pub fn new() -> Thing { Thing { x: 0 } }
 +    }
 +}
 +
 +fn main() { let foo_test = wrapper::Thing::new$0(); }
 +"#,
 +        expect![[r#"
 +                *new*
 +
 +                ```rust
 +                test::wrapper::Thing
 +                ```
 +
 +                ```rust
 +                pub fn new() -> Thing
 +                ```
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_infer_associated_const_in_pattern() {
 +    check(
 +        r#"
 +struct X;
 +impl X {
 +    const C: u32 = 1;
 +}
 +
 +fn main() {
 +    match 1 {
 +        X::C$0 => {},
 +        2 => {},
 +        _ => {}
 +    };
 +}
 +"#,
 +        expect![[r#"
 +            *C*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const C: u32 = 1
 +            ```
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_self() {
 +    check(
 +        r#"
 +struct Thing { x: u32 }
 +impl Thing {
 +    fn new() -> Self { Self$0 { x: 0 } }
 +}
 +"#,
 +        expect![[r#"
 +                *Self*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                struct Thing
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +struct Thing { x: u32 }
 +impl Thing {
 +    fn new() -> Self$0 { Self { x: 0 } }
 +}
 +"#,
 +        expect![[r#"
 +                *Self*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                struct Thing
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +enum Thing { A }
 +impl Thing {
 +    pub fn new() -> Self$0 { Thing::A }
 +}
 +"#,
 +        expect![[r#"
 +                *Self*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                enum Thing
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +        enum Thing { A }
 +        impl Thing {
 +            pub fn thing(a: Self$0) {}
 +        }
 +        "#,
 +        expect![[r#"
 +                *Self*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                enum Thing
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_shadowing_pat() {
 +    check(
 +        r#"
 +fn x() {}
 +
 +fn y() {
 +    let x = 0i32;
 +    x$0;
 +}
 +"#,
 +        expect![[r#"
 +                *x*
 +
 +                ```rust
 +                let x: i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_macro_invocation() {
 +    check(
 +        r#"
 +macro_rules! foo { () => {} }
 +
 +fn f() { fo$0o!(); }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                macro_rules! foo
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_macro2_invocation() {
 +    check(
 +        r#"
 +/// foo bar
 +///
 +/// foo bar baz
 +macro foo() {}
 +
 +fn f() { fo$0o!(); }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                macro foo
 +                ```
 +
 +                ---
 +
 +                foo bar
 +
 +                foo bar baz
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_tuple_field() {
 +    check(
 +        r#"struct TS(String, i32$0);"#,
 +        expect![[r#"
 +                *i32*
 +
 +                ```rust
 +                i32
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_through_macro() {
 +    check(
 +        r#"
 +macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
 +fn foo() {}
 +id! {
 +    fn bar() { fo$0o(); }
 +}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn foo()
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_attr() {
 +    check(
 +        r#"
 +//- proc_macros: identity
 +#[proc_macros::identity]
 +fn foo$0() {}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn foo()
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_expr_in_macro() {
 +    check(
 +        r#"
 +macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
 +fn foo(bar:u32) { let a = id!(ba$0r); }
 +"#,
 +        expect![[r#"
 +                *bar*
 +
 +                ```rust
 +                bar: u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_expr_in_macro_recursive() {
 +    check(
 +        r#"
 +macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
 +macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
 +fn foo(bar:u32) { let a = id!(ba$0r); }
 +"#,
 +        expect![[r#"
 +                *bar*
 +
 +                ```rust
 +                bar: u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_func_in_macro_recursive() {
 +    check(
 +        r#"
 +macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
 +macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
 +fn bar() -> u32 { 0 }
 +fn foo() { let a = id!([0u32, bar($0)] ); }
 +"#,
 +        expect![[r#"
 +                *bar()*
 +                ```rust
 +                u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_literal_string_in_macro() {
 +    check(
 +        r#"
 +macro_rules! arr { ($($tt:tt)*) => { [$($tt)*] } }
 +fn foo() {
 +    let mastered_for_itunes = "";
 +    let _ = arr!("Tr$0acks", &mastered_for_itunes);
 +}
 +"#,
 +        expect![[r#"
 +                *"Tracks"*
 +                ```rust
 +                &str
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_through_assert_macro() {
 +    check(
 +        r#"
 +#[rustc_builtin_macro]
 +macro_rules! assert {}
 +
 +fn bar() -> bool { true }
 +fn foo() {
 +    assert!(ba$0r());
 +}
 +"#,
 +        expect![[r#"
 +                *bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn bar() -> bool
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_multiple_actions() {
 +    check_actions(
 +        r#"
 +struct Bar;
 +struct Foo { bar: Bar }
 +
 +fn foo(Foo { b$0ar }: &Foo) {}
 +        "#,
 +        expect![[r#"
 +            [
 +                GoToType(
 +                    [
 +                        HoverGotoTypeData {
 +                            mod_path: "test::Bar",
 +                            nav: NavigationTarget {
 +                                file_id: FileId(
 +                                    0,
 +                                ),
 +                                full_range: 0..11,
 +                                focus_range: 7..10,
 +                                name: "Bar",
 +                                kind: Struct,
 +                                description: "struct Bar",
 +                            },
 +                        },
 +                    ],
 +                ),
 +            ]
 +        "#]],
 +    )
 +}
 +
 +#[test]
 +fn test_hover_through_literal_string_in_builtin_macro() {
 +    check_hover_no_result(
 +        r#"
 +            #[rustc_builtin_macro]
 +            macro_rules! format {}
 +
 +            fn foo() {
 +                format!("hel$0lo {}", 0);
 +            }
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn test_hover_non_ascii_space_doc() {
 +    check(
 +        "
 +/// <- `\u{3000}` here
 +fn foo() { }
 +
 +fn bar() { fo$0o(); }
 +",
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn foo()
 +                ```
 +
 +                ---
 +
 +                \<- ` ` here
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_function_show_qualifiers() {
 +    check(
 +        r#"async fn foo$0() {}"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                async fn foo()
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"pub const unsafe fn foo$0() {}"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub const unsafe fn foo()
 +                ```
 +            "#]],
 +    );
 +    // Top level `pub(crate)` will be displayed as no visibility.
 +    check(
 +        r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test::m
 +                ```
 +
 +                ```rust
 +                pub(crate) async unsafe extern "C" fn foo()
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_function_show_types() {
 +    check(
 +        r#"fn foo$0(a: i32, b:i32) -> i32 { 0 }"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn foo(a: i32, b: i32) -> i32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_function_pointer_show_identifiers() {
 +    check(
 +        r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type foo = fn(a: i32, b: i32) -> i32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_function_pointer_no_identifier() {
 +    check(
 +        r#"type foo$0 = fn(i32, _: i32) -> i32;"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type foo = fn(i32, i32) -> i32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_trait_show_qualifiers() {
 +    check_actions(
 +        r"unsafe trait foo$0() {}",
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 13,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_extern_crate() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +extern crate st$0d;
 +//- /std/lib.rs crate:std
 +//! Standard library for this test
 +//!
 +//! Printed?
 +//! abc123
 +"#,
 +        expect![[r#"
 +                *std*
 +
 +                ```rust
 +                extern crate std
 +                ```
 +
 +                ---
 +
 +                Standard library for this test
 +
 +                Printed?
 +                abc123
 +            "#]],
 +    );
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +extern crate std as ab$0c;
 +//- /std/lib.rs crate:std
 +//! Standard library for this test
 +//!
 +//! Printed?
 +//! abc123
 +"#,
 +        expect![[r#"
 +                *abc*
 +
 +                ```rust
 +                extern crate std
 +                ```
 +
 +                ---
 +
 +                Standard library for this test
 +
 +                Printed?
 +                abc123
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_mod_with_same_name_as_function() {
 +    check(
 +        r#"
 +use self::m$0y::Bar;
 +mod my { pub struct Bar; }
 +
 +fn my() {}
 +"#,
 +        expect![[r#"
 +                *my*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                mod my
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_struct_doc_comment() {
 +    check(
 +        r#"
 +/// This is an example
 +/// multiline doc
 +///
 +/// # Example
 +///
 +/// ```
 +/// let five = 5;
 +///
 +/// assert_eq!(6, my_crate::add_one(5));
 +/// ```
 +struct Bar;
 +
 +fn foo() { let bar = Ba$0r; }
 +"#,
 +        expect![[r##"
 +                *Bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                struct Bar
 +                ```
 +
 +                ---
 +
 +                This is an example
 +                multiline doc
 +
 +                # Example
 +
 +                ```
 +                let five = 5;
 +
 +                assert_eq!(6, my_crate::add_one(5));
 +                ```
 +            "##]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_struct_doc_attr() {
 +    check(
 +        r#"
 +#[doc = "bar docs"]
 +struct Bar;
 +
 +fn foo() { let bar = Ba$0r; }
 +"#,
 +        expect![[r#"
 +                *Bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                struct Bar
 +                ```
 +
 +                ---
 +
 +                bar docs
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_struct_doc_attr_multiple_and_mixed() {
 +    check(
 +        r#"
 +/// bar docs 0
 +#[doc = "bar docs 1"]
 +#[doc = "bar docs 2"]
 +struct Bar;
 +
 +fn foo() { let bar = Ba$0r; }
 +"#,
 +        expect![[r#"
 +                *Bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                struct Bar
 +                ```
 +
 +                ---
 +
 +                bar docs 0
 +                bar docs 1
 +                bar docs 2
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_external_url() {
 +    check(
 +        r#"
 +pub struct Foo;
 +/// [external](https://www.google.com)
 +pub struct B$0ar
 +"#,
 +        expect![[r#"
 +                *Bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub struct Bar
 +                ```
 +
 +                ---
 +
 +                [external](https://www.google.com)
 +            "#]],
 +    );
 +}
 +
 +// Check that we don't rewrite links which we can't identify
 +#[test]
 +fn test_hover_unknown_target() {
 +    check(
 +        r#"
 +pub struct Foo;
 +/// [baz](Baz)
 +pub struct B$0ar
 +"#,
 +        expect![[r#"
 +                *Bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub struct Bar
 +                ```
 +
 +                ---
 +
 +                [baz](Baz)
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_no_links() {
 +    check_hover_no_links(
 +        r#"
 +/// Test cases:
 +/// case 1.  bare URL: https://www.example.com/
 +/// case 2.  inline URL with title: [example](https://www.example.com/)
 +/// case 3.  code reference: [`Result`]
 +/// case 4.  code reference but miss footnote: [`String`]
 +/// case 5.  autolink: <http://www.example.com/>
 +/// case 6.  email address: <test@example.com>
 +/// case 7.  reference: [example][example]
 +/// case 8.  collapsed link: [example][]
 +/// case 9.  shortcut link: [example]
 +/// case 10. inline without URL: [example]()
 +/// case 11. reference: [foo][foo]
 +/// case 12. reference: [foo][bar]
 +/// case 13. collapsed link: [foo][]
 +/// case 14. shortcut link: [foo]
 +/// case 15. inline without URL: [foo]()
 +/// case 16. just escaped text: \[foo]
 +/// case 17. inline link: [Foo](foo::Foo)
 +///
 +/// [`Result`]: ../../std/result/enum.Result.html
 +/// [^example]: https://www.example.com/
 +pub fn fo$0o() {}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                pub fn foo()
 +                ```
 +
 +                ---
 +
 +                Test cases:
 +                case 1.  bare URL: https://www.example.com/
 +                case 2.  inline URL with title: [example](https://www.example.com/)
 +                case 3.  code reference: `Result`
 +                case 4.  code reference but miss footnote: `String`
 +                case 5.  autolink: http://www.example.com/
 +                case 6.  email address: test@example.com
 +                case 7.  reference: example
 +                case 8.  collapsed link: example
 +                case 9.  shortcut link: example
 +                case 10. inline without URL: example
 +                case 11. reference: foo
 +                case 12. reference: foo
 +                case 13. collapsed link: foo
 +                case 14. shortcut link: foo
 +                case 15. inline without URL: foo
 +                case 16. just escaped text: \[foo\]
 +                case 17. inline link: Foo
 +
 +                [^example]: https://www.example.com/
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_macro_generated_struct_fn_doc_comment() {
 +    cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
 +
 +    check(
 +        r#"
 +macro_rules! bar {
 +    () => {
 +        struct Bar;
 +        impl Bar {
 +            /// Do the foo
 +            fn foo(&self) {}
 +        }
 +    }
 +}
 +
 +bar!();
 +
 +fn foo() { let bar = Bar; bar.fo$0o(); }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test::Bar
 +                ```
 +
 +                ```rust
 +                fn foo(&self)
 +                ```
 +
 +                ---
 +
 +                Do the foo
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_macro_generated_struct_fn_doc_attr() {
 +    cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
 +
 +    check(
 +        r#"
 +macro_rules! bar {
 +    () => {
 +        struct Bar;
 +        impl Bar {
 +            #[doc = "Do the foo"]
 +            fn foo(&self) {}
 +        }
 +    }
 +}
 +
 +bar!();
 +
 +fn foo() { let bar = Bar; bar.fo$0o(); }
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test::Bar
 +                ```
 +
 +                ```rust
 +                fn foo(&self)
 +                ```
 +
 +                ---
 +
 +                Do the foo
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_variadic_function() {
 +    check(
 +        r#"
 +extern "C" {
 +    pub fn foo(bar: i32, ...) -> i32;
 +}
 +
 +fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } }
 +"#,
 +        expect![[r#"
 +            *foo*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            pub unsafe fn foo(bar: i32, ...) -> i32
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_trait_has_impl_action() {
 +    check_actions(
 +        r#"trait foo$0() {}"#,
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 6,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_struct_has_impl_action() {
 +    check_actions(
 +        r"struct foo$0() {}",
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 7,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_union_has_impl_action() {
 +    check_actions(
 +        r#"union foo$0() {}"#,
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 6,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_enum_has_impl_action() {
 +    check_actions(
 +        r"enum foo$0() { A, B }",
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 5,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_self_has_impl_action() {
 +    check_actions(
 +        r#"struct foo where Self$0:;"#,
 +        expect![[r#"
 +                [
 +                    Implementation(
 +                        FilePosition {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            offset: 7,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_test_has_action() {
 +    check_actions(
 +        r#"
 +#[test]
 +fn foo_$0test() {}
 +"#,
 +        expect![[r#"
 +            [
 +                Reference(
 +                    FilePosition {
 +                        file_id: FileId(
 +                            0,
 +                        ),
 +                        offset: 11,
 +                    },
 +                ),
 +                Runnable(
 +                    Runnable {
 +                        use_name_in_title: false,
 +                        nav: NavigationTarget {
 +                            file_id: FileId(
 +                                0,
 +                            ),
 +                            full_range: 0..24,
 +                            focus_range: 11..19,
 +                            name: "foo_test",
 +                            kind: Function,
 +                        },
 +                        kind: Test {
 +                            test_id: Path(
 +                                "foo_test",
 +                            ),
 +                            attr: TestAttr {
 +                                ignore: false,
 +                            },
 +                        },
 +                        cfg: None,
 +                    },
 +                ),
 +            ]
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_test_mod_has_action() {
 +    check_actions(
 +        r#"
 +mod tests$0 {
 +    #[test]
 +    fn foo_test() {}
 +}
 +"#,
 +        expect![[r#"
 +                [
 +                    Runnable(
 +                        Runnable {
 +                            use_name_in_title: false,
 +                            nav: NavigationTarget {
 +                                file_id: FileId(
 +                                    0,
 +                                ),
 +                                full_range: 0..46,
 +                                focus_range: 4..9,
 +                                name: "tests",
 +                                kind: Module,
 +                                description: "mod tests",
 +                            },
 +                            kind: TestMod {
 +                                path: "tests",
 +                            },
 +                            cfg: None,
 +                        },
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_struct_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +struct S{ f1: u32 }
 +
 +fn main() { let s$0t = S{ f1:0 }; }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..19,
 +                                    focus_range: 7..8,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_generic_struct_has_goto_type_actions() {
 +    check_actions(
 +        r#"
 +struct Arg(u32);
 +struct S<T>{ f1: T }
 +
 +fn main() { let s$0t = S{ f1:Arg(0) }; }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 17..37,
 +                                    focus_range: 24..25,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Arg",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..16,
 +                                    focus_range: 7..10,
 +                                    name: "Arg",
 +                                    kind: Struct,
 +                                    description: "struct Arg",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_generic_struct_has_flattened_goto_type_actions() {
 +    check_actions(
 +        r#"
 +struct Arg(u32);
 +struct S<T>{ f1: T }
 +
 +fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 17..37,
 +                                    focus_range: 24..25,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Arg",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..16,
 +                                    focus_range: 7..10,
 +                                    name: "Arg",
 +                                    kind: Struct,
 +                                    description: "struct Arg",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_tuple_has_goto_type_actions() {
 +    check_actions(
 +        r#"
 +struct A(u32);
 +struct B(u32);
 +mod M {
 +    pub struct C(u32);
 +}
 +
 +fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::A",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..14,
 +                                    focus_range: 7..8,
 +                                    name: "A",
 +                                    kind: Struct,
 +                                    description: "struct A",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::B",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 15..29,
 +                                    focus_range: 22..23,
 +                                    name: "B",
 +                                    kind: Struct,
 +                                    description: "struct B",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::M::C",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 42..60,
 +                                    focus_range: 53..54,
 +                                    name: "C",
 +                                    kind: Struct,
 +                                    description: "pub struct C",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_return_impl_trait_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +fn foo() -> impl Foo {}
 +
 +fn main() { let s$0t = foo(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_generic_return_impl_trait_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo<T> {}
 +struct S;
 +fn foo() -> impl Foo<S> {}
 +
 +fn main() { let s$0t = foo(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..15,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 16..25,
 +                                    focus_range: 23..24,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_return_impl_traits_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +trait Bar {}
 +fn foo() -> impl Foo + Bar {}
 +
 +fn main() { let s$0t = foo(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Bar",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 13..25,
 +                                    focus_range: 19..22,
 +                                    name: "Bar",
 +                                    kind: Trait,
 +                                    description: "trait Bar",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_generic_return_impl_traits_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo<T> {}
 +trait Bar<T> {}
 +struct S1 {}
 +struct S2 {}
 +
 +fn foo() -> impl Foo<S1> + Bar<S2> {}
 +
 +fn main() { let s$0t = foo(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..15,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Bar",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 16..31,
 +                                    focus_range: 22..25,
 +                                    name: "Bar",
 +                                    kind: Trait,
 +                                    description: "trait Bar<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S1",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 32..44,
 +                                    focus_range: 39..41,
 +                                    name: "S1",
 +                                    kind: Struct,
 +                                    description: "struct S1",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S2",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 45..57,
 +                                    focus_range: 52..54,
 +                                    name: "S2",
 +                                    kind: Struct,
 +                                    description: "struct S2",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_arg_impl_trait_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +fn foo(ar$0g: &impl Foo) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_arg_impl_traits_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +trait Bar<T> {}
 +struct S{}
 +
 +fn foo(ar$0g: &impl Foo + Bar<S>) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Bar",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 13..28,
 +                                    focus_range: 19..22,
 +                                    name: "Bar",
 +                                    kind: Trait,
 +                                    description: "trait Bar<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 29..39,
 +                                    focus_range: 36..37,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_async_block_impl_trait_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +//- /main.rs crate:main deps:core
 +// we don't use minicore here so that this test doesn't randomly fail
 +// when someone edits minicore
 +struct S;
 +fn foo() {
 +    let fo$0o = async { S };
 +}
 +//- /core.rs crate:core
 +pub mod future {
 +    #[lang = "future_trait"]
 +    pub trait Future {}
 +}
 +"#,
 +        expect![[r#"
 +            [
 +                GoToType(
 +                    [
 +                        HoverGotoTypeData {
 +                            mod_path: "core::future::Future",
 +                            nav: NavigationTarget {
 +                                file_id: FileId(
 +                                    1,
 +                                ),
 +                                full_range: 21..69,
 +                                focus_range: 60..66,
 +                                name: "Future",
 +                                kind: Trait,
 +                                description: "pub trait Future",
 +                            },
 +                        },
 +                        HoverGotoTypeData {
 +                            mod_path: "main::S",
 +                            nav: NavigationTarget {
 +                                file_id: FileId(
 +                                    0,
 +                                ),
 +                                full_range: 0..110,
 +                                focus_range: 108..109,
 +                                name: "S",
 +                                kind: Struct,
 +                                description: "struct S",
 +                            },
 +                        },
 +                    ],
 +                ),
 +            ]
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo<T> {}
 +struct S {}
 +fn foo(ar$0g: &impl Foo<S>) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..15,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 16..27,
 +                                    focus_range: 23..24,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_dyn_return_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +struct S;
 +impl Foo for S {}
 +
 +struct B<T>{}
 +fn foo() -> B<dyn Foo> {}
 +
 +fn main() { let s$0t = foo(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::B",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 42..55,
 +                                    focus_range: 49..50,
 +                                    name: "B",
 +                                    kind: Struct,
 +                                    description: "struct B<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_dyn_arg_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +fn foo(ar$0g: &dyn Foo) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_generic_dyn_arg_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo<T> {}
 +struct S {}
 +fn foo(ar$0g: &dyn Foo<S>) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..15,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 16..27,
 +                                    focus_range: 23..24,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_goto_type_action_links_order() {
 +    check_actions(
 +        r#"
 +trait ImplTrait<T> {}
 +trait DynTrait<T> {}
 +struct B<T> {}
 +struct S {}
 +
 +fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::ImplTrait",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..21,
 +                                    focus_range: 6..15,
 +                                    name: "ImplTrait",
 +                                    kind: Trait,
 +                                    description: "trait ImplTrait<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::B",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 43..57,
 +                                    focus_range: 50..51,
 +                                    name: "B",
 +                                    kind: Struct,
 +                                    description: "struct B<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::DynTrait",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 22..42,
 +                                    focus_range: 28..36,
 +                                    name: "DynTrait",
 +                                    kind: Trait,
 +                                    description: "trait DynTrait<T>",
 +                                },
 +                            },
 +                            HoverGotoTypeData {
 +                                mod_path: "test::S",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 58..69,
 +                                    focus_range: 65..66,
 +                                    name: "S",
 +                                    kind: Struct,
 +                                    description: "struct S",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_associated_type_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {
 +    type Item;
 +    fn get(self) -> Self::Item {}
 +}
 +
 +struct Bar{}
 +struct S{}
 +
 +impl Foo for S { type Item = Bar; }
 +
 +fn test() -> impl Foo { S {} }
 +
 +fn main() { let s$0t = test().get(); }
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..62,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_const_param_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +struct Bar;
 +struct Foo<const BAR: Bar>;
 +
 +impl<const BAR: Bar> Foo<BAR$0> {}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Bar",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..11,
 +                                    focus_range: 7..10,
 +                                    name: "Bar",
 +                                    kind: Struct,
 +                                    description: "struct Bar",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_type_param_has_goto_type_action() {
 +    check_actions(
 +        r#"
 +trait Foo {}
 +
 +fn foo<T: Foo>(t: T$0){}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..12,
 +                                    focus_range: 6..9,
 +                                    name: "Foo",
 +                                    kind: Trait,
 +                                    description: "trait Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_self_has_go_to_type() {
 +    check_actions(
 +        r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self$0) {}
 +}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..11,
 +                                    focus_range: 7..10,
 +                                    name: "Foo",
 +                                    kind: Struct,
 +                                    description: "struct Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_displays_normalized_crate_names() {
 +    check(
 +        r#"
 +//- /lib.rs crate:name-with-dashes
 +pub mod wrapper {
 +    pub struct Thing { x: u32 }
 +
 +    impl Thing {
 +        pub fn new() -> Thing { Thing { x: 0 } }
 +    }
 +}
 +
 +//- /main.rs crate:main deps:name-with-dashes
 +fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
 +"#,
 +        expect![[r#"
 +            *new*
 +
 +            ```rust
 +            name_with_dashes::wrapper::Thing
 +            ```
 +
 +            ```rust
 +            pub fn new() -> Thing
 +            ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_field_pat_shorthand_ref_match_ergonomics() {
 +    check(
 +        r#"
 +struct S {
 +    f: i32,
 +}
 +
 +fn main() {
 +    let s = S { f: 0 };
 +    let S { f$0 } = &s;
 +}
 +"#,
 +        expect![[r#"
 +            *f*
 +
 +            ```rust
 +            f: &i32
 +            ```
 +            ---
 +
 +            ```rust
 +            test::S
 +            ```
 +
 +            ```rust
 +            f: i32
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_order() {
 +    check(
 +        r#"
 +struct Foo;
 +struct S$0T<const C: usize = 1, T = Foo>(T);
 +"#,
 +        expect![[r#"
 +            *ST*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            struct ST<const C: usize, T = Foo>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_positive_i8_literal() {
 +    check(
 +        r#"
 +struct Const<const N: i8>;
 +
 +fn main() {
 +    let v$0alue = Const::<1>;
 +}
 +"#,
 +        expect![[r#"
 +            *value*
 +
 +            ```rust
 +            let value: Const<1>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_zero_i8_literal() {
 +    check(
 +        r#"
 +struct Const<const N: i8>;
 +
 +fn main() {
 +    let v$0alue = Const::<0>;
 +}
 +"#,
 +        expect![[r#"
 +            *value*
 +
 +            ```rust
 +            let value: Const<0>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_negative_i8_literal() {
 +    check(
 +        r#"
 +struct Const<const N: i8>;
 +
 +fn main() {
 +    let v$0alue = Const::<-1>;
 +}
 +"#,
 +        expect![[r#"
 +            *value*
 +
 +            ```rust
 +            let value: Const<-1>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_bool_literal() {
 +    check(
 +        r#"
 +struct Const<const F: bool>;
 +
 +fn main() {
 +    let v$0alue = Const::<true>;
 +}
 +"#,
 +        expect![[r#"
 +            *value*
 +
 +            ```rust
 +            let value: Const<true>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn const_generic_char_literal() {
 +    check(
 +        r#"
 +struct Const<const C: char>;
 +
 +fn main() {
 +    let v$0alue = Const::<'🦀'>;
 +}
 +"#,
 +        expect![[r#"
 +            *value*
 +
 +            ```rust
 +            let value: Const<'🦀'>
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_self_param_shows_type() {
 +    check(
 +        r#"
 +struct Foo {}
 +impl Foo {
 +    fn bar(&sel$0f) {}
 +}
 +"#,
 +        expect![[r#"
 +                *self*
 +
 +                ```rust
 +                self: &Foo
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_self_param_shows_type_for_arbitrary_self_type() {
 +    check(
 +        r#"
 +struct Arc<T>(T);
 +struct Foo {}
 +impl Foo {
 +    fn bar(sel$0f: Arc<Foo>) {}
 +}
 +"#,
 +        expect![[r#"
 +                *self*
 +
 +                ```rust
 +                self: Arc<Foo>
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_doc_outer_inner() {
 +    check(
 +        r#"
 +/// Be quick;
 +mod Foo$0 {
 +    //! time is mana
 +
 +    /// This comment belongs to the function
 +    fn foo() {}
 +}
 +"#,
 +        expect![[r#"
 +                *Foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                mod Foo
 +                ```
 +
 +                ---
 +
 +                Be quick;
 +                time is mana
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_doc_outer_inner_attribue() {
 +    check(
 +        r#"
 +#[doc = "Be quick;"]
 +mod Foo$0 {
 +    #![doc = "time is mana"]
 +
 +    #[doc = "This comment belongs to the function"]
 +    fn foo() {}
 +}
 +"#,
 +        expect![[r#"
 +                *Foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                mod Foo
 +                ```
 +
 +                ---
 +
 +                Be quick;
 +                time is mana
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_doc_block_style_indentend() {
 +    check(
 +        r#"
 +/**
 +    foo
 +    ```rust
 +    let x = 3;
 +    ```
 +*/
 +fn foo$0() {}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn foo()
 +                ```
 +
 +                ---
 +
 +                foo
 +
 +                ```rust
 +                let x = 3;
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_comments_dont_highlight_parent() {
 +    cov_mark::check!(no_highlight_on_comment_hover);
 +    check_hover_no_result(
 +        r#"
 +fn no_hover() {
 +    // no$0hover
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn hover_label() {
 +    check(
 +        r#"
 +fn foo() {
 +    'label$0: loop {}
 +}
 +"#,
 +        expect![[r#"
 +            *'label*
 +
 +            ```rust
 +            'label
 +            ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_lifetime() {
 +    check(
 +        r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
 +        expect![[r#"
 +            *'lifetime*
 +
 +            ```rust
 +            'lifetime
 +            ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_type_param() {
 +    check(
 +        r#"
 +//- minicore: sized
 +struct Foo<T>(T);
 +trait TraitA {}
 +trait TraitB {}
 +impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {}
 +"#,
 +        expect![[r#"
 +                *T*
 +
 +                ```rust
 +                T: TraitA + TraitB
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +//- minicore: sized
 +struct Foo<T>(T);
 +impl<T> Foo<T$0> {}
 +"#,
 +        expect![[r#"
 +                *T*
 +
 +                ```rust
 +                T
 +                ```
 +                "#]],
 +    );
 +    // lifetimes bounds arent being tracked yet
 +    check(
 +        r#"
 +//- minicore: sized
 +struct Foo<T>(T);
 +impl<T: 'static> Foo<T$0> {}
 +"#,
 +        expect![[r#"
 +                *T*
 +
 +                ```rust
 +                T
 +                ```
 +                "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_type_param_sized_bounds() {
 +    // implicit `: Sized` bound
 +    check(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +struct Foo<T>(T);
 +impl<T: Trait> Foo<T$0> {}
 +"#,
 +        expect![[r#"
 +                *T*
 +
 +                ```rust
 +                T: Trait
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +//- minicore: sized
 +trait Trait {}
 +struct Foo<T>(T);
 +impl<T: Trait + ?Sized> Foo<T$0> {}
 +"#,
 +        expect![[r#"
 +                *T*
 +
 +                ```rust
 +                T: Trait + ?Sized
 +                ```
 +            "#]],
 +    );
 +}
 +
 +mod type_param_sized_bounds {
 +    use super::*;
 +
 +    #[test]
 +    fn single_implicit() {
 +        check(
 +            r#"
 +//- minicore: sized
 +fn foo<T$0>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn single_explicit() {
 +        check(
 +            r#"
 +//- minicore: sized
 +fn foo<T$0: Sized>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn single_relaxed() {
 +        check(
 +            r#"
 +//- minicore: sized
 +fn foo<T$0: ?Sized>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T: ?Sized
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn multiple_implicit() {
 +        check(
 +            r#"
 +//- minicore: sized
 +trait Trait {}
 +fn foo<T$0: Trait>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T: Trait
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn multiple_explicit() {
 +        check(
 +            r#"
 +//- minicore: sized
 +trait Trait {}
 +fn foo<T$0: Trait + Sized>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T: Trait
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn multiple_relaxed() {
 +        check(
 +            r#"
 +//- minicore: sized
 +trait Trait {}
 +fn foo<T$0: Trait + ?Sized>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T: Trait + ?Sized
 +                    ```
 +                "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn mixed() {
 +        check(
 +            r#"
 +//- minicore: sized
 +fn foo<T$0: ?Sized + Sized + Sized>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T
 +                    ```
 +                "#]],
 +        );
 +        check(
 +            r#"
 +//- minicore: sized
 +trait Trait {}
 +fn foo<T$0: Sized + ?Sized + Sized + Trait>() {}
 +"#,
 +            expect![[r#"
 +                    *T*
 +
 +                    ```rust
 +                    T: Trait
 +                    ```
 +                "#]],
 +        );
 +    }
 +}
 +
 +#[test]
 +fn hover_const_generic_type_alias() {
 +    check(
 +        r#"
 +struct Foo<const LEN: usize>;
 +type Fo$0o2 = Foo<2>;
 +"#,
 +        expect![[r#"
 +                *Foo2*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type Foo2 = Foo<2>
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_const_param() {
 +    check(
 +        r#"
 +struct Foo<const LEN: usize>;
 +impl<const LEN: usize> Foo<LEN$0> {}
 +"#,
 +        expect![[r#"
 +                *LEN*
 +
 +                ```rust
 +                const LEN: usize
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_const_eval() {
 +    // show hex for <10
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: usize = 1 << 3;
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: usize = 8
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show hex for >10
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: usize = (1 << 3) + (1 << 2);
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: usize = 12 (0xC)
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show original body when const eval fails
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: usize = 2 - 3;
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: usize = 2 - 3
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // don't show hex for negatives
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: i32 = 2 - 3;
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: i32 = -1
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: &str = "bar";
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: &str = "bar"
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show char literal
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: char = 'a';
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: char = 'a'
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show escaped char literal
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: char = '\x61';
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: char = 'a'
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show byte literal
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: u8 = b'a';
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: u8 = 97 (0x61)
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show escaped byte literal
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: u8 = b'\x61';
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: u8 = 97 (0x61)
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    // show float literal
 +    check(
 +        r#"
 +    /// This is a doc
 +    const FOO$0: f64 = 1.0234;
 +    "#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: f64 = 1.0234
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    //show float typecasted from int
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: f32 = 1f32;
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: f32 = 1.0
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +    //show f64 typecasted from float
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO$0: f64 = 1.0f64;
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: f64 = 1.0
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_const_pat() {
 +    check(
 +        r#"
 +/// This is a doc
 +const FOO: usize = 3;
 +fn foo() {
 +    match 5 {
 +        FOO$0 => (),
 +        _ => ()
 +    }
 +}
 +"#,
 +        expect![[r#"
 +            *FOO*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            const FOO: usize = 3
 +            ```
 +
 +            ---
 +
 +            This is a doc
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn array_repeat_exp() {
 +    check(
 +        r#"
 +fn main() {
 +    let til$0e4 = [0_u32; (4 * 8 * 8) / 32];
 +}
 +        "#,
 +        expect![[r#"
 +            *tile4*
 +
 +            ```rust
 +            let tile4: [u32; 8]
 +            ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_mod_def() {
 +    check(
 +        r#"
 +//- /main.rs
 +mod foo$0;
 +//- /foo.rs
 +//! For the horde!
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                mod foo
 +                ```
 +
 +                ---
 +
 +                For the horde!
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_self_in_use() {
 +    check(
 +        r#"
 +//! This should not appear
 +mod foo {
 +    /// But this should appear
 +    pub mod bar {}
 +}
 +use foo::bar::{self$0};
 +"#,
 +        expect![[r#"
 +                *self*
 +
 +                ```rust
 +                test::foo
 +                ```
 +
 +                ```rust
 +                mod bar
 +                ```
 +
 +                ---
 +
 +                But this should appear
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_keyword() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +fn f() { retur$0n; }
 +//- /libstd.rs crate:std
 +/// Docs for return_keyword
 +mod return_keyword {}
 +"#,
 +        expect![[r#"
 +                *return*
 +
 +                ```rust
 +                return
 +                ```
 +
 +                ---
 +
 +                Docs for return_keyword
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_keyword_doc() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +fn foo() {
 +    let bar = mov$0e || {};
 +}
 +//- /libstd.rs crate:std
 +#[doc(keyword = "move")]
 +/// [closure]
 +/// [closures][closure]
 +/// [threads]
 +/// <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
 +///
 +/// [closure]: ../book/ch13-01-closures.html
 +/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
 +mod move_keyword {}
 +"#,
 +        expect![[r##"
 +            *move*
 +
 +            ```rust
 +            move
 +            ```
 +
 +            ---
 +
 +            [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
 +            [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
 +            [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads)
 +            <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
 +        "##]],
 +    );
 +}
 +
 +#[test]
 +fn hover_keyword_as_primitive() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +type F = f$0n(i32) -> i32;
 +//- /libstd.rs crate:std
 +/// Docs for prim_fn
 +mod prim_fn {}
 +"#,
 +        expect![[r#"
 +                *fn*
 +
 +                ```rust
 +                fn
 +                ```
 +
 +                ---
 +
 +                Docs for prim_fn
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_builtin() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:std
 +cosnt _: &str$0 = ""; }
 +
 +//- /libstd.rs crate:std
 +/// Docs for prim_str
 +/// [`foo`](../std/keyword.foo.html)
 +mod prim_str {}
 +"#,
 +        expect![[r#"
 +                *str*
 +
 +                ```rust
 +                str
 +                ```
 +
 +                ---
 +
 +                Docs for prim_str
 +                [`foo`](https://doc.rust-lang.org/nightly/std/keyword.foo.html)
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_macro_expanded_function() {
 +    check(
 +        r#"
 +struct S<'a, T>(&'a T);
 +trait Clone {}
 +macro_rules! foo {
 +    () => {
 +        fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
 +            't: 't + 't,
 +            for<'a> T: Clone + 'a
 +        { 0 as _ }
 +    };
 +}
 +
 +foo!();
 +
 +fn main() {
 +    bar$0;
 +}
 +"#,
 +        expect![[r#"
 +                *bar*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
 +                where
 +                    T: Clone + 't,
 +                    't: 't + 't,
 +                    for<'a> T: Clone + 'a,
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_intra_doc_links() {
 +    check(
 +        r#"
 +
 +pub mod theitem {
 +    /// This is the item. Cool!
 +    pub struct TheItem;
 +}
 +
 +/// Gives you a [`TheItem$0`].
 +///
 +/// [`TheItem`]: theitem::TheItem
 +pub fn gimme() -> theitem::TheItem {
 +    theitem::TheItem
 +}
 +"#,
 +        expect![[r#"
 +                *[`TheItem`]*
 +
 +                ```rust
 +                test::theitem
 +                ```
 +
 +                ```rust
 +                pub struct TheItem
 +                ```
 +
 +                ---
 +
 +                This is the item. Cool!
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn test_hover_trait_assoc_typealias() {
 +    check(
 +        r#"
 +        fn main() {}
 +
 +trait T1 {
 +    type Bar;
 +    type Baz;
 +}
 +
 +struct Foo;
 +
 +mod t2 {
 +    pub trait T2 {
 +        type Bar;
 +    }
 +}
 +
 +use t2::T2;
 +
 +impl T2 for Foo {
 +    type Bar = String;
 +}
 +
 +impl T1 for Foo {
 +    type Bar = <Foo as t2::T2>::Ba$0r;
 +    //                          ^^^ unresolvedReference
 +}
 +        "#,
 +        expect![[r#"
 +*Bar*
 +
 +```rust
 +test::t2
 +```
 +
 +```rust
 +pub type Bar
 +```
 +"#]],
 +    );
 +}
 +#[test]
 +fn hover_generic_assoc() {
 +    check(
 +        r#"
 +fn foo<T: A>() where T::Assoc$0: {}
 +
 +trait A {
 +    type Assoc;
 +}"#,
 +        expect![[r#"
 +                *Assoc*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type Assoc
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +fn foo<T: A>() {
 +    let _: <T>::Assoc$0;
 +}
 +
 +trait A {
 +    type Assoc;
 +}"#,
 +        expect![[r#"
 +                *Assoc*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type Assoc
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +trait A where
 +    Self::Assoc$0: ,
 +{
 +    type Assoc;
 +}"#,
 +        expect![[r#"
 +                *Assoc*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                type Assoc
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn string_shadowed_with_inner_items() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:alloc
 +
 +/// Custom `String` type.
 +struct String;
 +
 +fn f() {
 +    let _: String$0;
 +
 +    fn inner() {}
 +}
 +
 +//- /alloc.rs crate:alloc
 +#[prelude_import]
 +pub use string::*;
 +
 +mod string {
 +    /// This is `alloc::String`.
 +    pub struct String;
 +}
 +"#,
 +        expect![[r#"
 +                *String*
 +
 +                ```rust
 +                main
 +                ```
 +
 +                ```rust
 +                struct String
 +                ```
 +
 +                ---
 +
 +                Custom `String` type.
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn function_doesnt_shadow_crate_in_use_tree() {
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:foo
 +use foo$0::{foo};
 +
 +//- /foo.rs crate:foo
 +pub fn foo() {}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                extern crate foo
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_feature() {
 +    check(
 +        r#"#![feature(box_syntax$0)]"#,
 +        expect![[r##"
 +                *box_syntax*
 +                ```
 +                box_syntax
 +                ```
 +                ___
 +
 +                # `box_syntax`
 +
 +                The tracking issue for this feature is: [#49733]
 +
 +                [#49733]: https://github.com/rust-lang/rust/issues/49733
 +
 +                See also [`box_patterns`](box-patterns.md)
 +
 +                ------------------------
 +
 +                Currently the only stable way to create a `Box` is via the `Box::new` method.
 +                Also it is not possible in stable Rust to destructure a `Box` in a match
 +                pattern. The unstable `box` keyword can be used to create a `Box`. An example
 +                usage would be:
 +
 +                ```rust
 +                #![feature(box_syntax)]
 +
 +                fn main() {
 +                    let b = box 5;
 +                }
 +                ```
 +
 +            "##]],
 +    )
 +}
 +
 +#[test]
 +fn hover_lint() {
 +    check(
 +        r#"#![allow(arithmetic_overflow$0)]"#,
 +        expect![[r#"
 +                *arithmetic_overflow*
 +                ```
 +                arithmetic_overflow
 +                ```
 +                ___
 +
 +                arithmetic operation overflows
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_clippy_lint() {
 +    check(
 +        r#"#![allow(clippy::almost_swapped$0)]"#,
 +        expect![[r#"
 +                *almost_swapped*
 +                ```
 +                clippy::almost_swapped
 +                ```
 +                ___
 +
 +                Checks for `foo = bar; bar = foo` sequences.
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_attr_path_qualifier() {
 +    check(
 +        r#"
 +//- /foo.rs crate:foo
 +
 +//- /lib.rs crate:main.rs deps:foo
 +#[fo$0o::bar()]
 +struct Foo;
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                extern crate foo
 +                ```
 +            "#]],
 +    )
 +}
 +
 +#[test]
 +fn hover_rename() {
 +    check(
 +        r#"
 +use self as foo$0;
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                extern crate test
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +mod bar {}
 +use bar::{self as foo$0};
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                test
 +                ```
 +
 +                ```rust
 +                mod bar
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +mod bar {
 +    use super as foo$0;
 +}
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                extern crate test
 +                ```
 +            "#]],
 +    );
 +    check(
 +        r#"
 +use crate as foo$0;
 +"#,
 +        expect![[r#"
 +                *foo*
 +
 +                ```rust
 +                extern crate test
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_attribute_in_macro() {
 +    check(
 +        r#"
 +//- minicore:derive
 +macro_rules! identity {
 +    ($struct:item) => {
 +        $struct
 +    };
 +}
 +#[rustc_builtin_macro]
 +pub macro Copy {}
 +identity!{
 +    #[derive(Copy$0)]
 +    struct Foo;
 +}
 +"#,
 +        expect![[r#"
 +            *Copy*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            macro Copy
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_derive_input() {
 +    check(
 +        r#"
 +//- minicore:derive
 +#[rustc_builtin_macro]
 +pub macro Copy {}
 +#[derive(Copy$0)]
 +struct Foo;
 +"#,
 +        expect![[r#"
 +            *Copy*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            macro Copy
 +            ```
 +        "#]],
 +    );
 +    check(
 +        r#"
 +//- minicore:derive
 +mod foo {
 +    #[rustc_builtin_macro]
 +    pub macro Copy {}
 +}
 +#[derive(foo::Copy$0)]
 +struct Foo;
 +"#,
 +        expect![[r#"
 +            *Copy*
 +
 +            ```rust
 +            test::foo
 +            ```
 +
 +            ```rust
 +            macro Copy
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_math() {
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = $01 + 2 * 3$0 }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            i32
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = 1 $0+ 2 * $03 }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            i32
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = 1 + $02 * 3$0 }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            i32
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_arrays() {
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = $0[1, 2, 3, 4]$0 }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            [i32; 4]
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = [1, 2, $03, 4]$0 }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            [i32; 4]
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f() { let expr = [1, 2, $03$0, 4] }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            i32
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_functions() {
 +    check_hover_range(
 +        r#"
 +fn f<T>(a: &[T]) { }
 +fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            fn f<i32>(&[i32])
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f<T>(a: &[T]) { }
 +fn b() { f($0&[1, 2, 3, 4, 5]$0); }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            &[i32; 5]
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_shows_nothing_when_invalid() {
 +    check_hover_range_no_results(
 +        r#"
 +fn f<T>(a: &[T]) { }
 +fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
 +"#,
 +    );
 +
 +    check_hover_range_no_results(
 +        r#"
 +fn f<T>$0(a: &[T]) { }
 +fn b() { f(&[1, 2, 3,$0 4, 5]); }
 +"#,
 +    );
 +
 +    check_hover_range_no_results(
 +        r#"
 +fn $0f() { let expr = [1, 2, 3, 4]$0 }
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn hover_range_shows_unit_for_statements() {
 +    check_hover_range(
 +        r#"
 +fn f<T>(a: &[T]) { }
 +fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
 +"#,
 +        expect![[r#"
 +            ```rust
 +            ()
 +            ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn f() { let expr$0 = $0[1, 2, 3, 4] }
 +"#,
 +        expect![[r#"
 +            ```rust
 +            ()
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_for_pat() {
 +    check_hover_range(
 +        r#"
 +fn foo() {
 +    let $0x$0 = 0;
 +}
 +"#,
 +        expect![[r#"
 +                ```rust
 +                i32
 +                ```"#]],
 +    );
 +
 +    check_hover_range(
 +        r#"
 +fn foo() {
 +    let $0x$0 = "";
 +}
 +"#,
 +        expect![[r#"
 +                ```rust
 +                &str
 +                ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_shows_coercions_if_applicable_expr() {
 +    check_hover_range(
 +        r#"
 +fn foo() {
 +    let x: &u32 = $0&&&&&0$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Type:       &&&&&u32
 +                Coerced to:     &u32
 +                ```
 +            "#]],
 +    );
 +    check_hover_range(
 +        r#"
 +fn foo() {
 +    let x: *const u32 = $0&0$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Type:             &u32
 +                Coerced to: *const u32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_range_shows_type_actions() {
 +    check_actions(
 +        r#"
 +struct Foo;
 +fn foo() {
 +    let x: &Foo = $0&&&&&Foo$0;
 +}
 +"#,
 +        expect![[r#"
 +                [
 +                    GoToType(
 +                        [
 +                            HoverGotoTypeData {
 +                                mod_path: "test::Foo",
 +                                nav: NavigationTarget {
 +                                    file_id: FileId(
 +                                        0,
 +                                    ),
 +                                    full_range: 0..11,
 +                                    focus_range: 7..10,
 +                                    name: "Foo",
 +                                    kind: Struct,
 +                                    description: "struct Foo",
 +                                },
 +                            },
 +                        ],
 +                    ),
 +                ]
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_try_expr_res() {
 +    check_hover_range(
 +        r#"
 +//- minicore:result
 +struct FooError;
 +
 +fn foo() -> Result<(), FooError> {
 +    Ok($0Result::<(), FooError>::Ok(())?$0)
 +}
 +"#,
 +        expect![[r#"
 +                ```rust
 +                ()
 +                ```"#]],
 +    );
 +    check_hover_range(
 +        r#"
 +//- minicore:result
 +struct FooError;
 +struct BarError;
 +
 +fn foo() -> Result<(), FooError> {
 +    Ok($0Result::<(), BarError>::Ok(())?$0)
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Try Error Type: BarError
 +                Propagated as:  FooError
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_try_expr() {
 +    check_hover_range(
 +        r#"
 +struct NotResult<T, U>(T, U);
 +struct Short;
 +struct Looooong;
 +
 +fn foo() -> NotResult<(), Looooong> {
 +    $0NotResult((), Short)?$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Try Target Type:    NotResult<(), Short>
 +                Propagated as:   NotResult<(), Looooong>
 +                ```
 +            "#]],
 +    );
 +    check_hover_range(
 +        r#"
 +struct NotResult<T, U>(T, U);
 +struct Short;
 +struct Looooong;
 +
 +fn foo() -> NotResult<(), Short> {
 +    $0NotResult((), Looooong)?$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Try Target Type: NotResult<(), Looooong>
 +                Propagated as:      NotResult<(), Short>
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_try_expr_option() {
 +    cov_mark::check!(hover_try_expr_opt_opt);
 +    check_hover_range(
 +        r#"
 +//- minicore: option, try
 +
 +fn foo() -> Option<()> {
 +    $0Some(0)?$0;
 +    None
 +}
 +"#,
 +        expect![[r#"
 +                ```rust
 +                <Option<i32> as Try>::Output
 +                ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_deref_expr() {
 +    check_hover_range(
 +        r#"
 +//- minicore: deref
 +use core::ops::Deref;
 +
 +struct DerefExample<T> {
 +    value: T
 +}
 +
 +impl<T> Deref for DerefExample<T> {
 +    type Target = T;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.value
 +    }
 +}
 +
 +fn foo() {
 +    let x = DerefExample { value: 0 };
 +    let y: i32 = $0*x$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Dereferenced from: DerefExample<i32>
 +                To type:                         i32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_deref_expr_with_coercion() {
 +    check_hover_range(
 +        r#"
 +//- minicore: deref
 +use core::ops::Deref;
 +
 +struct DerefExample<T> {
 +    value: T
 +}
 +
 +impl<T> Deref for DerefExample<T> {
 +    type Target = T;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.value
 +    }
 +}
 +
 +fn foo() {
 +    let x = DerefExample { value: &&&&&0 };
 +    let y: &i32 = $0*x$0;
 +}
 +"#,
 +        expect![[r#"
 +                ```text
 +                Dereferenced from: DerefExample<&&&&&i32>
 +                To type:                         &&&&&i32
 +                Coerced to:                          &i32
 +                ```
 +            "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_intra_in_macro() {
 +    check(
 +        r#"
 +macro_rules! foo_macro {
 +    ($(#[$attr:meta])* $name:ident) => {
 +        $(#[$attr])*
 +        pub struct $name;
 +    }
 +}
 +
 +foo_macro!(
 +    /// Doc comment for [`Foo$0`]
 +    Foo
 +);
 +"#,
 +        expect![[r#"
 +            *[`Foo`]*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            pub struct Foo
 +            ```
 +
 +            ---
 +
 +            Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_intra_in_attr() {
 +    check(
 +        r#"
 +#[doc = "Doc comment for [`Foo$0`]"]
 +pub struct Foo;
 +"#,
 +        expect![[r#"
 +            *[`Foo`]*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            pub struct Foo
 +            ```
 +
 +            ---
 +
 +            Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_inert_attr() {
 +    check(
 +        r#"
 +#[doc$0 = ""]
 +pub struct Foo;
 +"#,
 +        expect![[r##"
 +            *doc*
 +
 +            ```rust
 +            #[doc]
 +            ```
 +
 +            ---
 +
 +            Valid forms are:
 +
 +            * \#\[doc(hidden|inline|...)\]
 +            * \#\[doc = string\]
 +        "##]],
 +    );
 +    check(
 +        r#"
 +#[allow$0()]
 +pub struct Foo;
 +"#,
 +        expect![[r##"
 +            *allow*
 +
 +            ```rust
 +            #[allow]
 +            ```
 +
 +            ---
 +
 +            Valid forms are:
 +
 +            * \#\[allow(lint1, lint2, ..., /\*opt\*/ reason = "...")\]
 +        "##]],
 +    );
 +}
 +
 +#[test]
 +fn hover_dollar_crate() {
 +    // $crate should be resolved to the right crate name.
 +
 +    check(
 +        r#"
 +//- /main.rs crate:main deps:dep
 +dep::m!(KONST$0);
 +//- /dep.rs crate:dep
 +#[macro_export]
 +macro_rules! m {
 +    ( $name:ident ) => { const $name: $crate::Type = $crate::Type; };
 +}
 +
 +pub struct Type;
 +"#,
 +        expect![[r#"
 +            *KONST*
 +
 +            ```rust
 +            main
 +            ```
 +
 +            ```rust
 +            const KONST: dep::Type = $crate::Type
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_record_variant() {
 +    check(
 +        r#"
 +enum Enum {
 +    RecordV$0 { field: u32 }
 +}
 +"#,
 +        expect![[r#"
 +            *RecordV*
 +
 +            ```rust
 +            test::Enum
 +            ```
 +
 +            ```rust
 +            RecordV { field: u32 }
 +            ```
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_trait_impl_assoc_item_def_doc_forwarding() {
 +    check(
 +        r#"
 +trait T {
 +    /// Trait docs
 +    fn func() {}
 +}
 +impl T for () {
 +    fn func$0() {}
 +}
 +"#,
 +        expect![[r#"
 +            *func*
 +
 +            ```rust
 +            test
 +            ```
 +
 +            ```rust
 +            fn func()
 +            ```
 +
 +            ---
 +
 +            Trait docs
 +        "#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_ranged_macro_call() {
 +    check_hover_range(
 +        r#"
 +macro_rules! __rust_force_expr {
 +    ($e:expr) => {
 +        $e
 +    };
 +}
 +macro_rules! vec {
 +    ($elem:expr) => {
 +        __rust_force_expr!($elem)
 +    };
 +}
 +
 +struct Struct;
 +impl Struct {
 +    fn foo(self) {}
 +}
 +
 +fn f() {
 +    $0vec![Struct]$0;
 +}
 +"#,
 +        expect![[r#"
 +            ```rust
 +            Struct
 +            ```"#]],
 +    );
 +}
 +
 +#[test]
 +fn hover_deref() {
 +    check(
 +        r#"
 +//- minicore: deref
 +
 +struct Struct(usize);
 +
 +impl core::ops::Deref for Struct {
 +    type Target = usize;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.0
 +    }
 +}
 +
 +fn f() {
 +    $0*Struct(0);
 +}
 +"#,
 +        expect![[r#"
 +            ***
 +
 +            ```rust
 +            test::Struct
 +            ```
 +
 +            ```rust
 +            fn deref(&self) -> &Self::Target
 +            ```
 +        "#]],
 +    );
 +}
++
++#[test]
++fn static_const_macro_expanded_body() {
++    check(
++        r#"
++macro_rules! m {
++    () => {
++        pub const V: i8 = {
++            let e = 123;
++            f(e) // Prevent const eval from evaluating this constant, we want to print the body's code.
++        };
++    };
++}
++m!();
++fn main() { $0V; }
++"#,
++        expect![[r#"
++            *V*
++
++            ```rust
++            test
++            ```
++
++            ```rust
++            pub const V: i8 = {
++              let e = 123;
++              f(e)
++            }
++            ```
++        "#]],
++    );
++    check(
++        r#"
++macro_rules! m {
++    () => {
++        pub static V: i8 = {
++            let e = 123;
++        };
++    };
++}
++m!();
++fn main() { $0V; }
++"#,
++        expect![[r#"
++            *V*
++
++            ```rust
++            test
++            ```
++
++            ```rust
++            pub static V: i8 = {
++              let e = 123;
++            }
++            ```
++        "#]],
++    );
++}
index e9034daefa8d46c1dea2afce1e07683f52f82f66,0000000000000000000000000000000000000000..d1b1d2c331a513d3ca6a17d4de2609f672f6ed92
mode 100644,000000..100644
--- /dev/null
@@@ -1,2818 -1,0 +1,2928 @@@
-     pub label: String,
++use std::fmt;
++
 +use either::Either;
 +use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
 +use ide_db::{
 +    base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
 +    RootDatabase,
 +};
 +use itertools::Itertools;
 +use stdx::to_lower_snake_case;
 +use syntax::{
 +    ast::{self, AstNode, HasArgList, HasGenericParams, HasName, UnaryOp},
 +    match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
 +    TextSize, T,
 +};
 +
 +use crate::FileId;
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub struct InlayHintsConfig {
 +    pub render_colons: bool,
 +    pub type_hints: bool,
 +    pub parameter_hints: bool,
 +    pub chaining_hints: bool,
 +    pub reborrow_hints: ReborrowHints,
 +    pub closure_return_type_hints: ClosureReturnTypeHints,
 +    pub binding_mode_hints: bool,
 +    pub lifetime_elision_hints: LifetimeElisionHints,
 +    pub param_names_for_lifetime_elision_hints: bool,
 +    pub hide_named_constructor_hints: bool,
 +    pub hide_closure_initialization_hints: bool,
 +    pub max_length: Option<usize>,
 +    pub closing_brace_hints_min_lines: Option<usize>,
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub enum ClosureReturnTypeHints {
 +    Always,
 +    WithBlock,
 +    Never,
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub enum LifetimeElisionHints {
 +    Always,
 +    SkipTrivial,
 +    Never,
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub enum ReborrowHints {
 +    Always,
 +    MutableOnly,
 +    Never,
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub enum InlayKind {
 +    BindingModeHint,
 +    ChainingHint,
 +    ClosingBraceHint,
 +    ClosureReturnTypeHint,
 +    GenericParamListHint,
 +    ImplicitReborrowHint,
 +    LifetimeHint,
 +    ParameterHint,
 +    TypeHint,
 +}
 +
 +#[derive(Debug)]
 +pub struct InlayHint {
 +    pub range: TextRange,
 +    pub kind: InlayKind,
-     let name = |it: ast::Name| it.syntax().text_range().start();
++    pub label: InlayHintLabel,
 +    pub tooltip: Option<InlayTooltip>,
 +}
 +
 +#[derive(Debug)]
 +pub enum InlayTooltip {
 +    String(String),
 +    HoverRanged(FileId, TextRange),
 +    HoverOffset(FileId, TextSize),
 +}
 +
++pub struct InlayHintLabel {
++    pub parts: Vec<InlayHintLabelPart>,
++}
++
++impl InlayHintLabel {
++    pub fn as_simple_str(&self) -> Option<&str> {
++        match &*self.parts {
++            [part] => part.as_simple_str(),
++            _ => None,
++        }
++    }
++
++    pub fn prepend_str(&mut self, s: &str) {
++        match &mut *self.parts {
++            [part, ..] if part.as_simple_str().is_some() => part.text = format!("{s}{}", part.text),
++            _ => self.parts.insert(0, InlayHintLabelPart { text: s.into(), linked_location: None }),
++        }
++    }
++
++    pub fn append_str(&mut self, s: &str) {
++        match &mut *self.parts {
++            [.., part] if part.as_simple_str().is_some() => part.text.push_str(s),
++            _ => self.parts.push(InlayHintLabelPart { text: s.into(), linked_location: None }),
++        }
++    }
++}
++
++impl From<String> for InlayHintLabel {
++    fn from(s: String) -> Self {
++        Self { parts: vec![InlayHintLabelPart { text: s, linked_location: None }] }
++    }
++}
++
++impl fmt::Display for InlayHintLabel {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
++    }
++}
++
++impl fmt::Debug for InlayHintLabel {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        f.debug_list().entries(&self.parts).finish()
++    }
++}
++
++pub struct InlayHintLabelPart {
++    pub text: String,
++    /// Source location represented by this label part. The client will use this to fetch the part's
++    /// hover tooltip, and Ctrl+Clicking the label part will navigate to the definition the location
++    /// refers to (not necessarily the location itself).
++    /// When setting this, no tooltip must be set on the containing hint, or VS Code will display
++    /// them both.
++    pub linked_location: Option<FileRange>,
++}
++
++impl InlayHintLabelPart {
++    pub fn as_simple_str(&self) -> Option<&str> {
++        match self {
++            Self { text, linked_location: None } => Some(text),
++            _ => None,
++        }
++    }
++}
++
++impl fmt::Debug for InlayHintLabelPart {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        match self.as_simple_str() {
++            Some(string) => string.fmt(f),
++            None => f
++                .debug_struct("InlayHintLabelPart")
++                .field("text", &self.text)
++                .field("linked_location", &self.linked_location)
++                .finish(),
++        }
++    }
++}
++
 +// Feature: Inlay Hints
 +//
 +// rust-analyzer shows additional information inline with the source code.
 +// Editors usually render this using read-only virtual text snippets interspersed with code.
 +//
 +// rust-analyzer by default shows hints for
 +//
 +// * types of local variables
 +// * names of function arguments
 +// * types of chained expressions
 +//
 +// Optionally, one can enable additional hints for
 +//
 +// * return types of closure expressions
 +// * elided lifetimes
 +// * compiler inserted reborrows
 +//
 +// |===
 +// | Editor  | Action Name
 +//
 +// | VS Code | **rust-analyzer: Toggle inlay hints*
 +// |===
 +//
 +// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
 +pub(crate) fn inlay_hints(
 +    db: &RootDatabase,
 +    file_id: FileId,
 +    range_limit: Option<FileRange>,
 +    config: &InlayHintsConfig,
 +) -> Vec<InlayHint> {
 +    let _p = profile::span("inlay_hints");
 +    let sema = Semantics::new(db);
 +    let file = sema.parse(file_id);
 +    let file = file.syntax();
 +
 +    let mut acc = Vec::new();
 +
 +    if let Some(scope) = sema.scope(&file) {
 +        let famous_defs = FamousDefs(&sema, scope.krate());
 +
 +        let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
 +        match range_limit {
 +            Some(FileRange { range, .. }) => match file.covering_element(range) {
 +                NodeOrToken::Token(_) => return acc,
 +                NodeOrToken::Node(n) => n
 +                    .descendants()
 +                    .filter(|descendant| range.intersect(descendant.text_range()).is_some())
 +                    .for_each(hints),
 +            },
 +            None => file.descendants().for_each(hints),
 +        };
 +    }
 +
 +    acc
 +}
 +
 +fn hints(
 +    hints: &mut Vec<InlayHint>,
 +    famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
 +    config: &InlayHintsConfig,
 +    file_id: FileId,
 +    node: SyntaxNode,
 +) {
 +    closing_brace_hints(hints, sema, config, file_id, node.clone());
 +    match_ast! {
 +        match node {
 +            ast::Expr(expr) => {
 +                chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
 +                match expr {
 +                    ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
 +                    ast::Expr::MethodCallExpr(it) => {
 +                        param_name_hints(hints, sema, config, ast::Expr::from(it))
 +                    }
 +                    ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
 +                    // We could show reborrows for all expressions, but usually that is just noise to the user
 +                    // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
 +                    ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
 +                    _ => None,
 +                }
 +            },
 +            ast::Pat(it) => {
 +                binding_mode_hints(hints, sema, config, &it);
 +                if let ast::Pat::IdentPat(it) = it {
 +                    bind_pat_hints(hints, sema, config, file_id, &it);
 +                }
 +                Some(())
 +            },
 +            ast::Item(it) => match it {
 +                // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
 +                ast::Item::Impl(_) => None,
 +                ast::Item::Fn(it) => fn_lifetime_fn_hints(hints, config, it),
 +                // static type elisions
 +                ast::Item::Static(it) => implicit_static_hints(hints, config, Either::Left(it)),
 +                ast::Item::Const(it) => implicit_static_hints(hints, config, Either::Right(it)),
 +                _ => None,
 +            },
 +            // FIXME: fn-ptr type, dyn fn type, and trait object type elisions
 +            ast::Type(_) => None,
 +            _ => None,
 +        }
 +    };
 +}
 +
 +fn closing_brace_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    file_id: FileId,
 +    node: SyntaxNode,
 +) -> Option<()> {
 +    let min_lines = config.closing_brace_hints_min_lines?;
 +
-     let (label, name_offset) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
++    let name = |it: ast::Name| it.syntax().text_range();
 +
 +    let mut closing_token;
-                     (match trait_ {
++    let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
 +        closing_token = item_list.r_curly_token()?;
 +
 +        let parent = item_list.syntax().parent()?;
 +        match_ast! {
 +            match parent {
 +                ast::Impl(imp) => {
 +                    let imp = sema.to_def(&imp)?;
 +                    let ty = imp.self_ty(sema.db);
 +                    let trait_ = imp.trait_(sema.db);
-                     }, None)
++                    let hint_text = match trait_ {
 +                        Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
 +                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
-             mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range().start()),
++                    };
++                    (hint_text, None)
 +                },
 +                ast::Trait(tr) => {
 +                    (format!("trait {}", tr.name()?), tr.name().map(name))
 +                },
 +                _ => return None,
 +            }
 +        }
 +    } else if let Some(list) = ast::ItemList::cast(node.clone()) {
 +        closing_token = list.r_curly_token()?;
 +
 +        let module = ast::Module::cast(list.syntax().parent()?)?;
 +        (format!("mod {}", module.name()?), module.name().map(name))
 +    } else if let Some(block) = ast::BlockExpr::cast(node.clone()) {
 +        closing_token = block.stmt_list()?.r_curly_token()?;
 +
 +        let parent = block.syntax().parent()?;
 +        match_ast! {
 +            match parent {
 +                ast::Fn(it) => {
 +                    // FIXME: this could include parameters, but `HirDisplay` prints too much info
 +                    // and doesn't respect the max length either, so the hints end up way too long
 +                    (format!("fn {}", it.name()?), it.name().map(name))
 +                },
 +                ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)),
 +                ast::Const(it) => {
 +                    if it.underscore_token().is_some() {
 +                        ("const _".into(), None)
 +                    } else {
 +                        (format!("const {}", it.name()?), it.name().map(name))
 +                    }
 +                },
 +                _ => return None,
 +            }
 +        }
 +    } else if let Some(mac) = ast::MacroCall::cast(node.clone()) {
 +        let last_token = mac.syntax().last_token()?;
 +        if last_token.kind() != T![;] && last_token.kind() != SyntaxKind::R_CURLY {
 +            return None;
 +        }
 +        closing_token = last_token;
 +
 +        (
 +            format!("{}!", mac.path()?),
-         label,
-         tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)),
++            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
 +        )
 +    } else {
 +        return None;
 +    };
 +
 +    if let Some(mut next) = closing_token.next_token() {
 +        if next.kind() == T![;] {
 +            if let Some(tok) = next.next_token() {
 +                closing_token = next;
 +                next = tok;
 +            }
 +        }
 +        if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) {
 +            // Only display the hint if the `}` is the last token on the line
 +            return None;
 +        }
 +    }
 +
 +    let mut lines = 1;
 +    node.text().for_each_chunk(|s| lines += s.matches('\n').count());
 +    if lines < min_lines {
 +        return None;
 +    }
 +
++    let linked_location = name_range.map(|range| FileRange { file_id, range });
 +    acc.push(InlayHint {
 +        range: closing_token.text_range(),
 +        kind: InlayKind::ClosingBraceHint,
-                 label: "'static".to_owned(),
++        label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
++        tooltip: None, // provided by label part location
 +    });
 +
 +    None
 +}
 +
 +fn implicit_static_hints(
 +    acc: &mut Vec<InlayHint>,
 +    config: &InlayHintsConfig,
 +    statik_or_const: Either<ast::Static, ast::Const>,
 +) -> Option<()> {
 +    if config.lifetime_elision_hints != LifetimeElisionHints::Always {
 +        return None;
 +    }
 +
 +    if let Either::Right(it) = &statik_or_const {
 +        if ast::AssocItemList::can_cast(
 +            it.syntax().parent().map_or(SyntaxKind::EOF, |it| it.kind()),
 +        ) {
 +            return None;
 +        }
 +    }
 +
 +    if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) {
 +        if ty.lifetime().is_none() {
 +            let t = ty.amp_token()?;
 +            acc.push(InlayHint {
 +                range: t.text_range(),
 +                kind: InlayKind::LifetimeHint,
-     let mk_lt_hint = |t: SyntaxToken, label| InlayHint {
++                label: "'static".to_owned().into(),
 +                tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
 +            });
 +        }
 +    }
 +
 +    Some(())
 +}
 +
 +fn fn_lifetime_fn_hints(
 +    acc: &mut Vec<InlayHint>,
 +    config: &InlayHintsConfig,
 +    func: ast::Fn,
 +) -> Option<()> {
 +    if config.lifetime_elision_hints == LifetimeElisionHints::Never {
 +        return None;
 +    }
 +
-         label,
++    let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
 +        range: t.text_range(),
 +        kind: InlayKind::LifetimeHint,
-                 ),
++        label: label.into(),
 +        tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
 +    };
 +
 +    let param_list = func.param_list()?;
 +    let generic_param_list = func.generic_param_list();
 +    let ret_type = func.ret_type();
 +    let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
 +
 +    let is_elided = |lt: &Option<ast::Lifetime>| match lt {
 +        Some(lt) => matches!(lt.text().as_str(), "'_"),
 +        None => true,
 +    };
 +
 +    let potential_lt_refs = {
 +        let mut acc: Vec<_> = vec![];
 +        if let Some(self_param) = &self_param {
 +            let lifetime = self_param.lifetime();
 +            let is_elided = is_elided(&lifetime);
 +            acc.push((None, self_param.amp_token(), lifetime, is_elided));
 +        }
 +        param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| {
 +            // FIXME: check path types
 +            walk_ty(&ty, &mut |ty| match ty {
 +                ast::Type::RefType(r) => {
 +                    let lifetime = r.lifetime();
 +                    let is_elided = is_elided(&lifetime);
 +                    acc.push((
 +                        pat.as_ref().and_then(|it| match it {
 +                            ast::Pat::IdentPat(p) => p.name(),
 +                            _ => None,
 +                        }),
 +                        r.amp_token(),
 +                        lifetime,
 +                        is_elided,
 +                    ))
 +                }
 +                _ => (),
 +            })
 +        });
 +        acc
 +    };
 +
 +    // allocate names
 +    let mut gen_idx_name = {
 +        let mut gen = (0u8..).map(|idx| match idx {
 +            idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
 +            idx => format!("'{idx}").into(),
 +        });
 +        move || gen.next().unwrap_or_default()
 +    };
 +    let mut allocated_lifetimes = vec![];
 +
 +    let mut used_names: FxHashMap<SmolStr, usize> =
 +        match config.param_names_for_lifetime_elision_hints {
 +            true => generic_param_list
 +                .iter()
 +                .flat_map(|gpl| gpl.lifetime_params())
 +                .filter_map(|param| param.lifetime())
 +                .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
 +                .collect(),
 +            false => Default::default(),
 +        };
 +    {
 +        let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided);
 +        if let Some(_) = &self_param {
 +            if let Some(_) = potential_lt_refs.next() {
 +                allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
 +                    // self can't be used as a lifetime, so no need to check for collisions
 +                    "'self".into()
 +                } else {
 +                    gen_idx_name()
 +                });
 +            }
 +        }
 +        potential_lt_refs.for_each(|(name, ..)| {
 +            let name = match name {
 +                Some(it) if config.param_names_for_lifetime_elision_hints => {
 +                    if let Some(c) = used_names.get_mut(it.text().as_str()) {
 +                        *c += 1;
 +                        SmolStr::from(format!("'{text}{c}", text = it.text().as_str()))
 +                    } else {
 +                        used_names.insert(it.text().as_str().into(), 0);
 +                        SmolStr::from_iter(["\'", it.text().as_str()])
 +                    }
 +                }
 +                _ => gen_idx_name(),
 +            };
 +            allocated_lifetimes.push(name);
 +        });
 +    }
 +
 +    // fetch output lifetime if elision rule applies
 +    let output = match potential_lt_refs.as_slice() {
 +        [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => {
 +            match lifetime {
 +                Some(lt) => match lt.text().as_str() {
 +                    "'_" => allocated_lifetimes.get(0).cloned(),
 +                    "'static" => None,
 +                    name => Some(name.into()),
 +                },
 +                None => allocated_lifetimes.get(0).cloned(),
 +            }
 +        }
 +        [..] => None,
 +    };
 +
 +    if allocated_lifetimes.is_empty() && output.is_none() {
 +        return None;
 +    }
 +
 +    // apply hints
 +    // apply output if required
 +    let mut is_trivial = true;
 +    if let (Some(output_lt), Some(r)) = (&output, ret_type) {
 +        if let Some(ty) = r.ty() {
 +            walk_ty(&ty, &mut |ty| match ty {
 +                ast::Type::RefType(ty) if ty.lifetime().is_none() => {
 +                    if let Some(amp) = ty.amp_token() {
 +                        is_trivial = false;
 +                        acc.push(mk_lt_hint(amp, output_lt.to_string()));
 +                    }
 +                }
 +                _ => (),
 +            })
 +        }
 +    }
 +
 +    if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial {
 +        return None;
 +    }
 +
 +    let mut a = allocated_lifetimes.iter();
 +    for (_, amp_token, _, is_elided) in potential_lt_refs {
 +        if is_elided {
 +            let t = amp_token?;
 +            let lt = a.next()?;
 +            acc.push(mk_lt_hint(t, lt.to_string()));
 +        }
 +    }
 +
 +    // generate generic param list things
 +    match (generic_param_list, allocated_lifetimes.as_slice()) {
 +        (_, []) => (),
 +        (Some(gpl), allocated_lifetimes) => {
 +            let angle_tok = gpl.l_angle_token()?;
 +            let is_empty = gpl.generic_params().next().is_none();
 +            acc.push(InlayHint {
 +                range: angle_tok.text_range(),
 +                kind: InlayKind::LifetimeHint,
 +                label: format!(
 +                    "{}{}",
 +                    allocated_lifetimes.iter().format(", "),
 +                    if is_empty { "" } else { ", " }
-             .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()),
++                )
++                .into(),
 +                tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
 +            });
 +        }
 +        (None, allocated_lifetimes) => acc.push(InlayHint {
 +            range: func.name()?.syntax().text_range(),
 +            kind: InlayKind::GenericParamListHint,
 +            label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
 +            tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
 +        }),
 +    }
 +    Some(())
 +}
 +
 +fn closure_ret_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    famous_defs: &FamousDefs<'_, '_>,
 +    config: &InlayHintsConfig,
 +    file_id: FileId,
 +    closure: ast::ClosureExpr,
 +) -> Option<()> {
 +    if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
 +        return None;
 +    }
 +
 +    if closure.ret_type().is_some() {
 +        return None;
 +    }
 +
 +    if !closure_has_block_body(&closure)
 +        && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
 +    {
 +        return None;
 +    }
 +
 +    let param_list = closure.param_list()?;
 +
 +    let closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
 +    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
 +    let callable = ty.as_callable(sema.db)?;
 +    let ty = callable.return_type();
 +    if ty.is_unit() {
 +        return None;
 +    }
 +    acc.push(InlayHint {
 +        range: param_list.syntax().text_range(),
 +        kind: InlayKind::ClosureReturnTypeHint,
 +        label: hint_iterator(sema, &famous_defs, config, &ty)
-         label: label.to_string(),
++            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
++            .into(),
 +        tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
 +    });
 +    Some(())
 +}
 +
 +fn reborrow_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    expr: &ast::Expr,
 +) -> Option<()> {
 +    if config.reborrow_hints == ReborrowHints::Never {
 +        return None;
 +    }
 +
 +    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
 +    let desc_expr = descended.as_ref().unwrap_or(expr);
 +    let mutability = sema.is_implicit_reborrow(desc_expr)?;
 +    let label = match mutability {
 +        hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
 +        hir::Mutability::Mut => "&mut *",
 +        _ => return None,
 +    };
 +    acc.push(InlayHint {
 +        range: expr.syntax().text_range(),
 +        kind: InlayKind::ImplicitReborrowHint,
-                 label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| {
-                     ty.display_truncated(sema.db, config.max_length).to_string()
-                 }),
++        label: label.to_string().into(),
 +        tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
 +    });
 +    Some(())
 +}
 +
 +fn chaining_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    famous_defs: &FamousDefs<'_, '_>,
 +    config: &InlayHintsConfig,
 +    file_id: FileId,
 +    expr: &ast::Expr,
 +) -> Option<()> {
 +    if !config.chaining_hints {
 +        return None;
 +    }
 +
 +    if matches!(expr, ast::Expr::RecordExpr(_)) {
 +        return None;
 +    }
 +
 +    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
 +    let desc_expr = descended.as_ref().unwrap_or(expr);
 +
 +    let mut tokens = expr
 +        .syntax()
 +        .siblings_with_tokens(Direction::Next)
 +        .filter_map(NodeOrToken::into_token)
 +        .filter(|t| match t.kind() {
 +            SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
 +            SyntaxKind::COMMENT => false,
 +            _ => true,
 +        });
 +
 +    // Chaining can be defined as an expression whose next sibling tokens are newline and dot
 +    // Ignoring extra whitespace and comments
 +    let next = tokens.next()?.kind();
 +    if next == SyntaxKind::WHITESPACE {
 +        let mut next_next = tokens.next()?.kind();
 +        while next_next == SyntaxKind::WHITESPACE {
 +            next_next = tokens.next()?.kind();
 +        }
 +        if next_next == T![.] {
 +            let ty = sema.type_of_expr(desc_expr)?.original;
 +            if ty.is_unknown() {
 +                return None;
 +            }
 +            if matches!(expr, ast::Expr::PathExpr(_)) {
 +                if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
 +                    if st.fields(sema.db).is_empty() {
 +                        return None;
 +                    }
 +                }
 +            }
 +            acc.push(InlayHint {
 +                range: expr.syntax().text_range(),
 +                kind: InlayKind::ChainingHint,
-                 label: param_name,
++                label: hint_iterator(sema, &famous_defs, config, &ty)
++                    .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
++                    .into(),
 +                tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
 +            });
 +        }
 +    }
 +    Some(())
 +}
 +
 +fn param_name_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    expr: ast::Expr,
 +) -> Option<()> {
 +    if !config.parameter_hints {
 +        return None;
 +    }
 +
 +    let (callable, arg_list) = get_callable(sema, &expr)?;
 +    let hints = callable
 +        .params(sema.db)
 +        .into_iter()
 +        .zip(arg_list.args())
 +        .filter_map(|((param, _ty), arg)| {
 +            // Only annotate hints for expressions that exist in the original file
 +            let range = sema.original_range_opt(arg.syntax())?;
 +            let (param_name, name_syntax) = match param.as_ref()? {
 +                Either::Left(pat) => ("self".to_string(), pat.name()),
 +                Either::Right(pat) => match pat {
 +                    ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
 +                    _ => return None,
 +                },
 +            };
 +            Some((name_syntax, param_name, arg, range))
 +        })
 +        .filter(|(_, param_name, arg, _)| {
 +            !should_hide_param_name_hint(sema, &callable, param_name, arg)
 +        })
 +        .map(|(param, param_name, _, FileRange { range, .. })| {
 +            let mut tooltip = None;
 +            if let Some(name) = param {
 +                if let hir::CallableKind::Function(f) = callable.kind() {
 +                    // assert the file is cached so we can map out of macros
 +                    if let Some(_) = sema.source(f) {
 +                        tooltip = sema.original_range_opt(name.syntax());
 +                    }
 +                }
 +            }
 +
 +            InlayHint {
 +                range,
 +                kind: InlayKind::ParameterHint,
-             label: r.to_string(),
++                label: param_name.into(),
 +                tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
 +            }
 +        });
 +
 +    acc.extend(hints);
 +    Some(())
 +}
 +
 +fn binding_mode_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    pat: &ast::Pat,
 +) -> Option<()> {
 +    if !config.binding_mode_hints {
 +        return None;
 +    }
 +
 +    let range = pat.syntax().text_range();
 +    sema.pattern_adjustments(&pat).iter().for_each(|ty| {
 +        let reference = ty.is_reference();
 +        let mut_reference = ty.is_mutable_reference();
 +        let r = match (reference, mut_reference) {
 +            (true, true) => "&mut",
 +            (true, false) => "&",
 +            _ => return,
 +        };
 +        acc.push(InlayHint {
 +            range,
 +            kind: InlayKind::BindingModeHint,
-                 label: bm.to_string(),
++            label: r.to_string().into(),
 +            tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
 +        });
 +    });
 +    match pat {
 +        ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
 +            let bm = sema.binding_mode_of_pat(pat)?;
 +            let bm = match bm {
 +                hir::BindingMode::Move => return None,
 +                hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
 +                hir::BindingMode::Ref(Mutability::Shared) => "ref",
 +            };
 +            acc.push(InlayHint {
 +                range,
 +                kind: InlayKind::BindingModeHint,
-         label,
++                label: bm.to_string().into(),
 +                tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
 +            });
 +        }
 +        _ => (),
 +    }
 +
 +    Some(())
 +}
 +
 +fn bind_pat_hints(
 +    acc: &mut Vec<InlayHint>,
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    file_id: FileId,
 +    pat: &ast::IdentPat,
 +) -> Option<()> {
 +    if !config.type_hints {
 +        return None;
 +    }
 +
 +    let descended = sema.descend_node_into_attributes(pat.clone()).pop();
 +    let desc_pat = descended.as_ref().unwrap_or(pat);
 +    let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
 +
 +    if should_not_display_type_hint(sema, config, pat, &ty) {
 +        return None;
 +    }
 +
 +    let krate = sema.scope(desc_pat.syntax())?.krate();
 +    let famous_defs = FamousDefs(sema, krate);
 +    let label = hint_iterator(sema, &famous_defs, config, &ty);
 +
 +    let label = match label {
 +        Some(label) => label,
 +        None => {
 +            let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
 +            if config.hide_named_constructor_hints
 +                && is_named_constructor(sema, pat, &ty_name).is_some()
 +            {
 +                return None;
 +            }
 +            ty_name
 +        }
 +    };
 +
 +    acc.push(InlayHint {
 +        range: match pat.name() {
 +            Some(name) => name.syntax().text_range(),
 +            None => pat.syntax().text_range(),
 +        },
 +        kind: InlayKind::TypeHint,
-                         label: "B",
++        label: label.into(),
 +        tooltip: pat
 +            .name()
 +            .map(|it| it.syntax().text_range())
 +            .map(|it| InlayTooltip::HoverRanged(file_id, it)),
 +    });
 +
 +    Some(())
 +}
 +
 +fn is_named_constructor(
 +    sema: &Semantics<'_, RootDatabase>,
 +    pat: &ast::IdentPat,
 +    ty_name: &str,
 +) -> Option<()> {
 +    let let_node = pat.syntax().parent()?;
 +    let expr = match_ast! {
 +        match let_node {
 +            ast::LetStmt(it) => it.initializer(),
 +            ast::LetExpr(it) => it.expr(),
 +            _ => None,
 +        }
 +    }?;
 +
 +    let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
 +    // unwrap postfix expressions
 +    let expr = match expr {
 +        ast::Expr::TryExpr(it) => it.expr(),
 +        ast::Expr::AwaitExpr(it) => it.expr(),
 +        expr => Some(expr),
 +    }?;
 +    let expr = match expr {
 +        ast::Expr::CallExpr(call) => match call.expr()? {
 +            ast::Expr::PathExpr(path) => path,
 +            _ => return None,
 +        },
 +        ast::Expr::PathExpr(path) => path,
 +        _ => return None,
 +    };
 +    let path = expr.path()?;
 +
 +    let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
 +    let callable_kind = callable.map(|it| it.kind());
 +    let qual_seg = match callable_kind {
 +        Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
 +            path.qualifier()?.segment()
 +        }
 +        _ => path.segment(),
 +    }?;
 +
 +    let ctor_name = match qual_seg.kind()? {
 +        ast::PathSegmentKind::Name(name_ref) => {
 +            match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
 +                Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
 +                None => name_ref.to_string(),
 +            }
 +        }
 +        ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
 +        _ => return None,
 +    };
 +    (ctor_name == ty_name).then(|| ())
 +}
 +
 +/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
 +fn hint_iterator(
 +    sema: &Semantics<'_, RootDatabase>,
 +    famous_defs: &FamousDefs<'_, '_>,
 +    config: &InlayHintsConfig,
 +    ty: &hir::Type,
 +) -> Option<String> {
 +    let db = sema.db;
 +    let strukt = ty.strip_references().as_adt()?;
 +    let krate = strukt.module(db).krate();
 +    if krate != famous_defs.core()? {
 +        return None;
 +    }
 +    let iter_trait = famous_defs.core_iter_Iterator()?;
 +    let iter_mod = famous_defs.core_iter()?;
 +
 +    // Assert that this struct comes from `core::iter`.
 +    if !(strukt.visibility(db) == hir::Visibility::Public
 +        && strukt.module(db).path_to_root(db).contains(&iter_mod))
 +    {
 +        return None;
 +    }
 +
 +    if ty.impls_trait(db, iter_trait, &[]) {
 +        let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
 +            hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
 +            _ => None,
 +        })?;
 +        if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) {
 +            const LABEL_START: &str = "impl Iterator<Item = ";
 +            const LABEL_END: &str = ">";
 +
 +            let ty_display = hint_iterator(sema, famous_defs, config, &ty)
 +                .map(|assoc_type_impl| assoc_type_impl.to_string())
 +                .unwrap_or_else(|| {
 +                    ty.display_truncated(
 +                        db,
 +                        config
 +                            .max_length
 +                            .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
 +                    )
 +                    .to_string()
 +                });
 +            return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END));
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
 +    if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
 +        let pat_text = bind_pat.to_string();
 +        enum_data
 +            .variants(db)
 +            .into_iter()
 +            .map(|variant| variant.name(db).to_smol_str())
 +            .any(|enum_name| enum_name == pat_text)
 +    } else {
 +        false
 +    }
 +}
 +
 +fn should_not_display_type_hint(
 +    sema: &Semantics<'_, RootDatabase>,
 +    config: &InlayHintsConfig,
 +    bind_pat: &ast::IdentPat,
 +    pat_ty: &hir::Type,
 +) -> bool {
 +    let db = sema.db;
 +
 +    if pat_ty.is_unknown() {
 +        return true;
 +    }
 +
 +    if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
 +        if s.fields(db).is_empty() && s.name(db).to_smol_str() == bind_pat.to_string() {
 +            return true;
 +        }
 +    }
 +
 +    if config.hide_closure_initialization_hints {
 +        if let Some(parent) = bind_pat.syntax().parent() {
 +            if let Some(it) = ast::LetStmt::cast(parent.clone()) {
 +                if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
 +                    if closure_has_block_body(&closure) {
 +                        return true;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    for node in bind_pat.syntax().ancestors() {
 +        match_ast! {
 +            match node {
 +                ast::LetStmt(it) => return it.ty().is_some(),
 +                // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
 +                ast::Param(it) => return it.ty().is_some(),
 +                ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
 +                ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
 +                ast::IfExpr(_) => return false,
 +                ast::WhileExpr(_) => return false,
 +                ast::ForExpr(it) => {
 +                    // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
 +                    // Type of expr should be iterable.
 +                    return it.in_token().is_none() ||
 +                        it.iterable()
 +                            .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
 +                            .map(TypeInfo::original)
 +                            .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
 +                },
 +                _ => (),
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
 +    matches!(closure.body(), Some(ast::Expr::BlockExpr(_)))
 +}
 +
 +fn should_hide_param_name_hint(
 +    sema: &Semantics<'_, RootDatabase>,
 +    callable: &hir::Callable,
 +    param_name: &str,
 +    argument: &ast::Expr,
 +) -> bool {
 +    // These are to be tested in the `parameter_hint_heuristics` test
 +    // hide when:
 +    // - the parameter name is a suffix of the function's name
 +    // - the argument is a qualified constructing or call expression where the qualifier is an ADT
 +    // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
 +    //   of argument with _ splitting it off
 +    // - param starts with `ra_fixture`
 +    // - param is a well known name in a unary function
 +
 +    let param_name = param_name.trim_start_matches('_');
 +    if param_name.is_empty() {
 +        return true;
 +    }
 +
 +    if matches!(argument, ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(UnaryOp::Not)) {
 +        return false;
 +    }
 +
 +    let fn_name = match callable.kind() {
 +        hir::CallableKind::Function(it) => Some(it.name(sema.db).to_smol_str()),
 +        _ => None,
 +    };
 +    let fn_name = fn_name.as_deref();
 +    is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
 +        || is_argument_similar_to_param_name(argument, param_name)
 +        || param_name.starts_with("ra_fixture")
 +        || (callable.n_params() == 1 && is_obvious_param(param_name))
 +        || is_adt_constructor_similar_to_param_name(sema, argument, param_name)
 +}
 +
 +fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
 +    // check whether param_name and argument are the same or
 +    // whether param_name is a prefix/suffix of argument(split at `_`)
 +    let argument = match get_string_representation(argument) {
 +        Some(argument) => argument,
 +        None => return false,
 +    };
 +
 +    // std is honestly too panic happy...
 +    let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
 +
 +    let param_name = param_name.trim_start_matches('_');
 +    let argument = argument.trim_start_matches('_');
 +
 +    match str_split_at(argument, param_name.len()) {
 +        Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
 +            return rest.is_empty() || rest.starts_with('_');
 +        }
 +        _ => (),
 +    }
 +    match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
 +        Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
 +            return rest.is_empty() || rest.ends_with('_');
 +        }
 +        _ => (),
 +    }
 +    false
 +}
 +
 +/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
 +///
 +/// `fn strip_suffix(suffix)` will be hidden.
 +/// `fn stripsuffix(suffix)` will not be hidden.
 +fn is_param_name_suffix_of_fn_name(
 +    param_name: &str,
 +    callable: &Callable,
 +    fn_name: Option<&str>,
 +) -> bool {
 +    match (callable.n_params(), fn_name) {
 +        (1, Some(function)) => {
 +            function == param_name
 +                || function
 +                    .len()
 +                    .checked_sub(param_name.len())
 +                    .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
 +                    .map_or(false, |(prefix, suffix)| {
 +                        suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
 +                    })
 +        }
 +        _ => false,
 +    }
 +}
 +
 +fn is_adt_constructor_similar_to_param_name(
 +    sema: &Semantics<'_, RootDatabase>,
 +    argument: &ast::Expr,
 +    param_name: &str,
 +) -> bool {
 +    let path = match argument {
 +        ast::Expr::CallExpr(c) => c.expr().and_then(|e| match e {
 +            ast::Expr::PathExpr(p) => p.path(),
 +            _ => None,
 +        }),
 +        ast::Expr::PathExpr(p) => p.path(),
 +        ast::Expr::RecordExpr(r) => r.path(),
 +        _ => return false,
 +    };
 +    let path = match path {
 +        Some(it) => it,
 +        None => return false,
 +    };
 +    (|| match sema.resolve_path(&path)? {
 +        hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
 +            Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name)
 +        }
 +        hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::Variant(_)) => {
 +            if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name {
 +                return Some(true);
 +            }
 +            let qual = path.qualifier()?;
 +            match sema.resolve_path(&qual)? {
 +                hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
 +                    Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name)
 +                }
 +                _ => None,
 +            }
 +        }
 +        _ => None,
 +    })()
 +    .unwrap_or(false)
 +}
 +
 +fn get_string_representation(expr: &ast::Expr) -> Option<String> {
 +    match expr {
 +        ast::Expr::MethodCallExpr(method_call_expr) => {
 +            let name_ref = method_call_expr.name_ref()?;
 +            match name_ref.text().as_str() {
 +                "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
 +                name_ref => Some(name_ref.to_owned()),
 +            }
 +        }
 +        ast::Expr::MacroExpr(macro_expr) => {
 +            Some(macro_expr.macro_call()?.path()?.segment()?.to_string())
 +        }
 +        ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
 +        ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
 +        ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
 +        ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
 +        ast::Expr::CastExpr(cast_expr) => get_string_representation(&cast_expr.expr()?),
 +        _ => None,
 +    }
 +}
 +
 +fn is_obvious_param(param_name: &str) -> bool {
 +    // avoid displaying hints for common functions like map, filter, etc.
 +    // or other obvious words used in std
 +    let is_obvious_param_name =
 +        matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
 +    param_name.len() == 1 || is_obvious_param_name
 +}
 +
 +fn get_callable(
 +    sema: &Semantics<'_, RootDatabase>,
 +    expr: &ast::Expr,
 +) -> Option<(hir::Callable, ast::ArgList)> {
 +    match expr {
 +        ast::Expr::CallExpr(expr) => {
 +            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
 +            let expr = descended.as_ref().unwrap_or(expr);
 +            sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
 +        }
 +        ast::Expr::MethodCallExpr(expr) => {
 +            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
 +            let expr = descended.as_ref().unwrap_or(expr);
 +            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
 +        }
 +        _ => None,
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use expect_test::{expect, Expect};
 +    use ide_db::base_db::FileRange;
 +    use itertools::Itertools;
 +    use syntax::{TextRange, TextSize};
 +    use test_utils::extract_annotations;
 +
 +    use crate::inlay_hints::ReborrowHints;
 +    use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
 +
 +    use super::ClosureReturnTypeHints;
 +
 +    const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
 +        render_colons: false,
 +        type_hints: false,
 +        parameter_hints: false,
 +        chaining_hints: false,
 +        lifetime_elision_hints: LifetimeElisionHints::Never,
 +        closure_return_type_hints: ClosureReturnTypeHints::Never,
 +        reborrow_hints: ReborrowHints::Always,
 +        binding_mode_hints: false,
 +        hide_named_constructor_hints: false,
 +        hide_closure_initialization_hints: false,
 +        param_names_for_lifetime_elision_hints: false,
 +        max_length: None,
 +        closing_brace_hints_min_lines: None,
 +    };
 +    const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
 +        type_hints: true,
 +        parameter_hints: true,
 +        chaining_hints: true,
 +        reborrow_hints: ReborrowHints::Always,
 +        closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
 +        binding_mode_hints: true,
 +        lifetime_elision_hints: LifetimeElisionHints::Always,
 +        ..DISABLED_CONFIG
 +    };
 +
 +    #[track_caller]
 +    fn check(ra_fixture: &str) {
 +        check_with_config(TEST_CONFIG, ra_fixture);
 +    }
 +
 +    #[track_caller]
 +    fn check_params(ra_fixture: &str) {
 +        check_with_config(
 +            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
 +            ra_fixture,
 +        );
 +    }
 +
 +    #[track_caller]
 +    fn check_types(ra_fixture: &str) {
 +        check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
 +    }
 +
 +    #[track_caller]
 +    fn check_chains(ra_fixture: &str) {
 +        check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
 +    }
 +
 +    #[track_caller]
 +    fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
 +        let (analysis, file_id) = fixture::file(ra_fixture);
 +        let mut expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
 +        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
 +        let actual = inlay_hints
 +            .into_iter()
 +            .map(|it| (it.range, it.label.to_string()))
 +            .sorted_by_key(|(range, _)| range.start())
 +            .collect::<Vec<_>>();
 +        expected.sort_by_key(|(range, _)| range.start());
 +
 +        assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
 +    }
 +
 +    #[track_caller]
 +    fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
 +        let (analysis, file_id) = fixture::file(ra_fixture);
 +        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
 +        expect.assert_debug_eq(&inlay_hints)
 +    }
 +
 +    #[test]
 +    fn hints_disabled() {
 +        check_with_config(
 +            InlayHintsConfig { render_colons: true, ..DISABLED_CONFIG },
 +            r#"
 +fn foo(a: i32, b: i32) -> i32 { a + b }
 +fn main() {
 +    let _x = foo(4, 4);
 +}"#,
 +        );
 +    }
 +
 +    // Parameter hint tests
 +
 +    #[test]
 +    fn param_hints_only() {
 +        check_params(
 +            r#"
 +fn foo(a: i32, b: i32) -> i32 { a + b }
 +fn main() {
 +    let _x = foo(
 +        4,
 +      //^ a
 +        4,
 +      //^ b
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_hints_on_closure() {
 +        check_params(
 +            r#"
 +fn main() {
 +    let clo = |a: u8, b: u8| a + b;
 +    clo(
 +        1,
 +      //^ a
 +        2,
 +      //^ b
 +    );
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_name_similar_to_fn_name_still_hints() {
 +        check_params(
 +            r#"
 +fn max(x: i32, y: i32) -> i32 { x + y }
 +fn main() {
 +    let _x = max(
 +        4,
 +      //^ x
 +        4,
 +      //^ y
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_name_similar_to_fn_name() {
 +        check_params(
 +            r#"
 +fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
 +fn main() {
 +    let _x = param_with_underscore(
 +        4,
 +    );
 +}"#,
 +        );
 +        check_params(
 +            r#"
 +fn param_with_underscore(underscore: i32) -> i32 { underscore }
 +fn main() {
 +    let _x = param_with_underscore(
 +        4,
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_name_same_as_fn_name() {
 +        check_params(
 +            r#"
 +fn foo(foo: i32) -> i32 { foo }
 +fn main() {
 +    let _x = foo(
 +        4,
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn never_hide_param_when_multiple_params() {
 +        check_params(
 +            r#"
 +fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
 +fn main() {
 +    let _x = foo(
 +        4,
 +      //^ foo
 +        8,
 +      //^ bar
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_hints_look_through_as_ref_and_clone() {
 +        check_params(
 +            r#"
 +fn foo(bar: i32, baz: f32) {}
 +
 +fn main() {
 +    let bar = 3;
 +    let baz = &"baz";
 +    let fez = 1.0;
 +    foo(bar.clone(), bar.clone());
 +                   //^^^^^^^^^^^ baz
 +    foo(bar.as_ref(), bar.as_ref());
 +                    //^^^^^^^^^^^^ baz
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn self_param_hints() {
 +        check_params(
 +            r#"
 +struct Foo;
 +
 +impl Foo {
 +    fn foo(self: Self) {}
 +    fn bar(self: &Self) {}
 +}
 +
 +fn main() {
 +    Foo::foo(Foo);
 +           //^^^ self
 +    Foo::bar(&Foo);
 +           //^^^^ self
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn param_name_hints_show_for_literals() {
 +        check_params(
 +            r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
 +fn main() {
 +    test(
 +        0xa_b,
 +      //^^^^^ a
 +        0xa_b,
 +      //^^^^^ b
 +    );
 +}"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn function_call_parameter_hint() {
 +        check_params(
 +            r#"
 +//- minicore: option
 +struct FileId {}
 +struct SmolStr {}
 +
 +struct TextRange {}
 +struct SyntaxKind {}
 +struct NavigationTarget {}
 +
 +struct Test {}
 +
 +impl Test {
 +    fn method(&self, mut param: i32) -> i32 { param * 2 }
 +
 +    fn from_syntax(
 +        file_id: FileId,
 +        name: SmolStr,
 +        focus_range: Option<TextRange>,
 +        full_range: TextRange,
 +        kind: SyntaxKind,
 +        docs: Option<String>,
 +    ) -> NavigationTarget {
 +        NavigationTarget {}
 +    }
 +}
 +
 +fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
 +    foo + bar
 +}
 +
 +fn main() {
 +    let not_literal = 1;
 +    let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);
 +                         //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last
 +    let t: Test = Test {};
 +    t.method(123);
 +           //^^^ param
 +    Test::method(&t,      3456);
 +               //^^ self  ^^^^ param
 +    Test::from_syntax(
 +        FileId {},
 +        "impl".into(),
 +      //^^^^^^^^^^^^^ name
 +        None,
 +      //^^^^ focus_range
 +        TextRange {},
 +      //^^^^^^^^^^^^ full_range
 +        SyntaxKind {},
 +      //^^^^^^^^^^^^^ kind
 +        None,
 +      //^^^^ docs
 +    );
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn parameter_hint_heuristics() {
 +        check_params(
 +            r#"
 +fn check(ra_fixture_thing: &str) {}
 +
 +fn map(f: i32) {}
 +fn filter(predicate: i32) {}
 +
 +fn strip_suffix(suffix: &str) {}
 +fn stripsuffix(suffix: &str) {}
 +fn same(same: u32) {}
 +fn same2(_same2: u32) {}
 +
 +fn enum_matches_param_name(completion_kind: CompletionKind) {}
 +
 +fn foo(param: u32) {}
 +fn bar(param_eter: u32) {}
 +
 +enum CompletionKind {
 +    Keyword,
 +}
 +
 +fn non_ident_pat((a, b): (u32, u32)) {}
 +
 +fn main() {
 +    const PARAM: u32 = 0;
 +    foo(PARAM);
 +    foo(!PARAM);
 +     // ^^^^^^ param
 +    check("");
 +
 +    map(0);
 +    filter(0);
 +
 +    strip_suffix("");
 +    stripsuffix("");
 +              //^^ suffix
 +    same(0);
 +    same2(0);
 +
 +    enum_matches_param_name(CompletionKind::Keyword);
 +
 +    let param = 0;
 +    foo(param);
 +    foo(param as _);
 +    let param_end = 0;
 +    foo(param_end);
 +    let start_param = 0;
 +    foo(start_param);
 +    let param2 = 0;
 +    foo(param2);
 +      //^^^^^^ param
 +
 +    macro_rules! param {
 +        () => {};
 +    };
 +    foo(param!());
 +
 +    let param_eter = 0;
 +    bar(param_eter);
 +    let param_eter_end = 0;
 +    bar(param_eter_end);
 +    let start_param_eter = 0;
 +    bar(start_param_eter);
 +    let param_eter2 = 0;
 +    bar(param_eter2);
 +      //^^^^^^^^^^^ param_eter
 +
 +    non_ident_pat((0, 0));
 +}"#,
 +        );
 +    }
 +
 +    // Type-Hint tests
 +
 +    #[test]
 +    fn type_hints_only() {
 +        check_types(
 +            r#"
 +fn foo(a: i32, b: i32) -> i32 { a + b }
 +fn main() {
 +    let _x = foo(4, 4);
 +      //^^ i32
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn type_hints_bindings_after_at() {
 +        check_types(
 +            r#"
 +//- minicore: option
 +fn main() {
 +    let ref foo @ bar @ ref mut baz = 0;
 +          //^^^ &i32
 +                //^^^ i32
 +                              //^^^ &mut i32
 +    let [x @ ..] = [0];
 +       //^ [i32; 1]
 +    if let x @ Some(_) = Some(0) {}
 +         //^ Option<i32>
 +    let foo @ (bar, baz) = (3, 3);
 +      //^^^ (i32, i32)
 +             //^^^ i32
 +                  //^^^ i32
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn default_generic_types_should_not_be_displayed() {
 +        check(
 +            r#"
 +struct Test<K, T = u8> { k: K, t: T }
 +
 +fn main() {
 +    let zz = Test { t: 23u8, k: 33 };
 +      //^^ Test<i32>
 +    let zz_ref = &zz;
 +      //^^^^^^ &Test<i32>
 +    let test = || zz;
 +      //^^^^ || -> Test<i32>
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn shorten_iterators_in_associated_params() {
 +        check_types(
 +            r#"
 +//- minicore: iterators
 +use core::iter;
 +
 +pub struct SomeIter<T> {}
 +
 +impl<T> SomeIter<T> {
 +    pub fn new() -> Self { SomeIter {} }
 +    pub fn push(&mut self, t: T) {}
 +}
 +
 +impl<T> Iterator for SomeIter<T> {
 +    type Item = T;
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +
 +fn main() {
 +    let mut some_iter = SomeIter::new();
 +          //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
 +      some_iter.push(iter::repeat(2).take(2));
 +    let iter_of_iters = some_iter.take(2);
 +      //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_call_method_return_associated_types_with_generic() {
 +        check_types(
 +            r#"
 +            pub trait Default {
 +                fn default() -> Self;
 +            }
 +            pub trait Foo {
 +                type Bar: Default;
 +            }
 +
 +            pub fn quux<T: Foo>() -> T::Bar {
 +                let y = Default::default();
 +                  //^ <T as Foo>::Bar
 +
 +                y
 +            }
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn fn_hints() {
 +        check_types(
 +            r#"
 +//- minicore: fn, sized
 +fn foo() -> impl Fn() { loop {} }
 +fn foo1() -> impl Fn(f64) { loop {} }
 +fn foo2() -> impl Fn(f64, f64) { loop {} }
 +fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
 +fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
 +fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
 +fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
 +fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
 +
 +fn main() {
 +    let foo = foo();
 +     // ^^^ impl Fn()
 +    let foo = foo1();
 +     // ^^^ impl Fn(f64)
 +    let foo = foo2();
 +     // ^^^ impl Fn(f64, f64)
 +    let foo = foo3();
 +     // ^^^ impl Fn(f64, f64) -> u32
 +    let foo = foo4();
 +     // ^^^ &dyn Fn(f64, f64) -> u32
 +    let foo = foo5();
 +     // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
 +    let foo = foo6();
 +     // ^^^ impl Fn(f64, f64) -> u32
 +    let foo = foo7();
 +     // ^^^ *const impl Fn(f64, f64) -> u32
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn check_hint_range_limit() {
 +        let fixture = r#"
 +        //- minicore: fn, sized
 +        fn foo() -> impl Fn() { loop {} }
 +        fn foo1() -> impl Fn(f64) { loop {} }
 +        fn foo2() -> impl Fn(f64, f64) { loop {} }
 +        fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
 +        fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
 +        fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
 +        fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
 +        fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
 +
 +        fn main() {
 +            let foo = foo();
 +            let foo = foo1();
 +            let foo = foo2();
 +             // ^^^ impl Fn(f64, f64)
 +            let foo = foo3();
 +             // ^^^ impl Fn(f64, f64) -> u32
 +            let foo = foo4();
 +            let foo = foo5();
 +            let foo = foo6();
 +            let foo = foo7();
 +        }
 +        "#;
 +        let (analysis, file_id) = fixture::file(fixture);
 +        let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
 +        let inlay_hints = analysis
 +            .inlay_hints(
 +                &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
 +                file_id,
 +                Some(FileRange {
 +                    file_id,
 +                    range: TextRange::new(TextSize::from(500), TextSize::from(600)),
 +                }),
 +            )
 +            .unwrap();
 +        let actual =
 +            inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
 +        assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
 +    }
 +
 +    #[test]
 +    fn fn_hints_ptr_rpit_fn_parentheses() {
 +        check_types(
 +            r#"
 +//- minicore: fn, sized
 +trait Trait {}
 +
 +fn foo1() -> *const impl Fn() { loop {} }
 +fn foo2() -> *const (impl Fn() + Sized) { loop {} }
 +fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
 +fn foo4() -> *const (impl Sized + Fn()) { loop {} }
 +fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
 +fn foo6() -> *const (impl Fn() + Trait) { loop {} }
 +fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
 +fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
 +fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
 +fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
 +
 +fn main() {
 +    let foo = foo1();
 +    //  ^^^ *const impl Fn()
 +    let foo = foo2();
 +    //  ^^^ *const impl Fn()
 +    let foo = foo3();
 +    //  ^^^ *const (impl Fn() + ?Sized)
 +    let foo = foo4();
 +    //  ^^^ *const impl Fn()
 +    let foo = foo5();
 +    //  ^^^ *const (impl Fn() + ?Sized)
 +    let foo = foo6();
 +    //  ^^^ *const (impl Fn() + Trait)
 +    let foo = foo7();
 +    //  ^^^ *const (impl Fn() + Trait)
 +    let foo = foo8();
 +    //  ^^^ *const (impl Fn() + Trait + ?Sized)
 +    let foo = foo9();
 +    //  ^^^ *const (impl Fn() -> u8 + ?Sized)
 +    let foo = foo10();
 +    //  ^^^ *const impl Fn()
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn unit_structs_have_no_type_hints() {
 +        check_types(
 +            r#"
 +//- minicore: result
 +struct SyntheticSyntax;
 +
 +fn main() {
 +    match Ok(()) {
 +        Ok(_) => (),
 +        Err(SyntheticSyntax) => (),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn let_statement() {
 +        check_types(
 +            r#"
 +#[derive(PartialEq)]
 +enum Option<T> { None, Some(T) }
 +
 +#[derive(PartialEq)]
 +struct Test { a: Option<u32>, b: u8 }
 +
 +fn main() {
 +    struct InnerStruct {}
 +
 +    let test = 54;
 +      //^^^^ i32
 +    let test: i32 = 33;
 +    let mut test = 33;
 +          //^^^^ i32
 +    let _ = 22;
 +    let test = "test";
 +      //^^^^ &str
 +    let test = InnerStruct {};
 +      //^^^^ InnerStruct
 +
 +    let test = unresolved();
 +
 +    let test = (42, 'a');
 +      //^^^^ (i32, char)
 +    let (a,    (b,     (c,)) = (2, (3, (9.2,));
 +       //^ i32  ^ i32   ^ f64
 +    let &x = &92;
 +       //^ i32
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn if_expr() {
 +        check_types(
 +            r#"
 +//- minicore: option
 +struct Test { a: Option<u32>, b: u8 }
 +
 +fn main() {
 +    let test = Some(Test { a: Some(3), b: 1 });
 +      //^^^^ Option<Test>
 +    if let None = &test {};
 +    if let test = &test {};
 +         //^^^^ &Option<Test>
 +    if let Some(test) = &test {};
 +              //^^^^ &Test
 +    if let Some(Test { a,             b }) = &test {};
 +                     //^ &Option<u32> ^ &u8
 +    if let Some(Test { a: x,             b: y }) = &test {};
 +                        //^ &Option<u32>    ^ &u8
 +    if let Some(Test { a: Some(x),  b: y }) = &test {};
 +                             //^ &u32  ^ &u8
 +    if let Some(Test { a: None,  b: y }) = &test {};
 +                                  //^ &u8
 +    if let Some(Test { b: y, .. }) = &test {};
 +                        //^ &u8
 +    if test == None {}
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn while_expr() {
 +        check_types(
 +            r#"
 +//- minicore: option
 +struct Test { a: Option<u32>, b: u8 }
 +
 +fn main() {
 +    let test = Some(Test { a: Some(3), b: 1 });
 +      //^^^^ Option<Test>
 +    while let Some(Test { a: Some(x),  b: y }) = &test {};
 +                                //^ &u32  ^ &u8
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn match_arm_list() {
 +        check_types(
 +            r#"
 +//- minicore: option
 +struct Test { a: Option<u32>, b: u8 }
 +
 +fn main() {
 +    match Some(Test { a: Some(3), b: 1 }) {
 +        None => (),
 +        test => (),
 +      //^^^^ Option<Test>
 +        Some(Test { a: Some(x), b: y }) => (),
 +                          //^ u32  ^ u8
 +        _ => {}
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn complete_for_hint() {
 +        check_types(
 +            r#"
 +//- minicore: iterator
 +pub struct Vec<T> {}
 +
 +impl<T> Vec<T> {
 +    pub fn new() -> Self { Vec {} }
 +    pub fn push(&mut self, t: T) {}
 +}
 +
 +impl<T> IntoIterator for Vec<T> {
 +    type Item=T;
 +}
 +
 +fn main() {
 +    let mut data = Vec::new();
 +          //^^^^ Vec<&str>
 +    data.push("foo");
 +    for i in data {
 +      //^ &str
 +      let z = i;
 +        //^ &str
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn multi_dyn_trait_bounds() {
 +        check_types(
 +            r#"
 +pub struct Vec<T> {}
 +
 +impl<T> Vec<T> {
 +    pub fn new() -> Self { Vec {} }
 +}
 +
 +pub struct Box<T> {}
 +
 +trait Display {}
 +auto trait Sync {}
 +
 +fn main() {
 +    // The block expression wrapping disables the constructor hint hiding logic
 +    let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
 +      //^^ Vec<Box<&(dyn Display + Sync)>>
 +    let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
 +      //^^ Vec<Box<*const (dyn Display + Sync)>>
 +    let _v = { Vec::<Box<dyn Display + Sync>>::new() };
 +      //^^ Vec<Box<dyn Display + Sync>>
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn shorten_iterator_hints() {
 +        check_types(
 +            r#"
 +//- minicore: iterators
 +use core::iter;
 +
 +struct MyIter;
 +
 +impl Iterator for MyIter {
 +    type Item = ();
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +
 +fn main() {
 +    let _x = MyIter;
 +      //^^ MyIter
 +    let _x = iter::repeat(0);
 +      //^^ impl Iterator<Item = i32>
 +    fn generic<T: Clone>(t: T) {
 +        let _x = iter::repeat(t);
 +          //^^ impl Iterator<Item = T>
 +        let _chained = iter::repeat(t).take(10);
 +          //^^^^^^^^ impl Iterator<Item = T>
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_constructor_and_enum_type_hints() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                type_hints: true,
 +                hide_named_constructor_hints: true,
 +                ..DISABLED_CONFIG
 +            },
 +            r#"
 +//- minicore: try, option
 +use core::ops::ControlFlow;
 +
 +mod x {
 +    pub mod y { pub struct Foo; }
 +    pub struct Foo;
 +    pub enum AnotherEnum {
 +        Variant()
 +    };
 +}
 +struct Struct;
 +struct TupleStruct();
 +
 +impl Struct {
 +    fn new() -> Self {
 +        Struct
 +    }
 +    fn try_new() -> ControlFlow<(), Self> {
 +        ControlFlow::Continue(Struct)
 +    }
 +}
 +
 +struct Generic<T>(T);
 +impl Generic<i32> {
 +    fn new() -> Self {
 +        Generic(0)
 +    }
 +}
 +
 +enum Enum {
 +    Variant(u32)
 +}
 +
 +fn times2(value: i32) -> i32 {
 +    2 * value
 +}
 +
 +fn main() {
 +    let enumb = Enum::Variant(0);
 +
 +    let strukt = x::Foo;
 +    let strukt = x::y::Foo;
 +    let strukt = Struct;
 +    let strukt = Struct::new();
 +
 +    let tuple_struct = TupleStruct();
 +
 +    let generic0 = Generic::new();
 +    //  ^^^^^^^^ Generic<i32>
 +    let generic1 = Generic(0);
 +    //  ^^^^^^^^ Generic<i32>
 +    let generic2 = Generic::<i32>::new();
 +    let generic3 = <Generic<i32>>::new();
 +    let generic4 = Generic::<i32>(0);
 +
 +
 +    let option = Some(0);
 +    //  ^^^^^^ Option<i32>
 +    let func = times2;
 +    //  ^^^^ fn times2(i32) -> i32
 +    let closure = |x: i32| x * 2;
 +    //  ^^^^^^^ |i32| -> i32
 +}
 +
 +fn fallible() -> ControlFlow<()> {
 +    let strukt = Struct::try_new()?;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn shows_constructor_type_hints_when_enabled() {
 +        check_types(
 +            r#"
 +//- minicore: try
 +use core::ops::ControlFlow;
 +
 +struct Struct;
 +struct TupleStruct();
 +
 +impl Struct {
 +    fn new() -> Self {
 +        Struct
 +    }
 +    fn try_new() -> ControlFlow<(), Self> {
 +        ControlFlow::Continue(Struct)
 +    }
 +}
 +
 +struct Generic<T>(T);
 +impl Generic<i32> {
 +    fn new() -> Self {
 +        Generic(0)
 +    }
 +}
 +
 +fn main() {
 +    let strukt = Struct::new();
 +     // ^^^^^^ Struct
 +    let tuple_struct = TupleStruct();
 +     // ^^^^^^^^^^^^ TupleStruct
 +    let generic0 = Generic::new();
 +     // ^^^^^^^^ Generic<i32>
 +    let generic1 = Generic::<i32>::new();
 +     // ^^^^^^^^ Generic<i32>
 +    let generic2 = <Generic<i32>>::new();
 +     // ^^^^^^^^ Generic<i32>
 +}
 +
 +fn fallible() -> ControlFlow<()> {
 +    let strukt = Struct::try_new()?;
 +     // ^^^^^^ Struct
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn closures() {
 +        check(
 +            r#"
 +fn main() {
 +    let mut start = 0;
 +          //^^^^^ i32
 +    (0..2).for_each(|increment      | { start += increment; });
 +                   //^^^^^^^^^ i32
 +
 +    let multiply =
 +      //^^^^^^^^ |i32, i32| -> i32
 +      | a,     b| a * b
 +      //^ i32  ^ i32
 +
 +    ;
 +
 +    let _: i32 = multiply(1,  2);
 +                        //^ a ^ b
 +    let multiply_ref = &multiply;
 +      //^^^^^^^^^^^^ &|i32, i32| -> i32
 +
 +    let return_42 = || 42;
 +      //^^^^^^^^^ || -> i32
 +      || { 42 };
 +    //^^ i32
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_type_hints_for_closure_without_block() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                closure_return_type_hints: ClosureReturnTypeHints::Always,
 +                ..DISABLED_CONFIG
 +            },
 +            r#"
 +fn main() {
 +    let a = || { 0 };
 +          //^^ i32
 +    let b = || 0;
 +          //^^ i32
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn skip_closure_type_hints() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                type_hints: true,
 +                hide_closure_initialization_hints: true,
 +                ..DISABLED_CONFIG
 +            },
 +            r#"
 +//- minicore: fn
 +fn main() {
 +    let multiple_2 = |x: i32| { x * 2 };
 +
 +    let multiple_2 = |x: i32| x * 2;
 +    //  ^^^^^^^^^^ |i32| -> i32
 +
 +    let (not) = (|x: bool| { !x });
 +    //   ^^^ |bool| -> bool
 +
 +    let (is_zero, _b) = (|x: usize| { x == 0 }, false);
 +    //   ^^^^^^^ |usize| -> bool
 +    //            ^^ bool
 +
 +    let plus_one = |x| { x + 1 };
 +    //              ^ u8
 +    foo(plus_one);
 +
 +    let add_mul = bar(|x: u8| { x + 1 });
 +    //  ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
 +
 +    let closure = if let Some(6) = add_mul(2).checked_sub(1) {
 +    //  ^^^^^^^ fn(i32) -> i32
 +        |x: i32| { x * 2 }
 +    } else {
 +        |x: i32| { x * 3 }
 +    };
 +}
 +
 +fn foo(f: impl FnOnce(u8) -> u8) {}
 +
 +fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
 +    move |x: u8| f(x) * 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hint_truncation() {
 +        check_with_config(
 +            InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
 +            r#"
 +struct Smol<T>(T);
 +
 +struct VeryLongOuterName<T>(T);
 +
 +fn main() {
 +    let a = Smol(0u32);
 +      //^ Smol<u32>
 +    let b = VeryLongOuterName(0usize);
 +      //^ VeryLongOuterName<…>
 +    let c = Smol(Smol(0u32))
 +      //^ Smol<Smol<…>>
 +}"#,
 +        );
 +    }
 +
 +    // Chaining hint tests
 +
 +    #[test]
 +    fn chaining_hints_ignore_comments() {
 +        check_expect(
 +            InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
 +            r#"
 +struct A(B);
 +impl A { fn into_b(self) -> B { self.0 } }
 +struct B(C);
 +impl B { fn into_c(self) -> C { self.0 } }
 +struct C;
 +
 +fn main() {
 +    let c = A(B(C))
 +        .into_b() // This is a comment
 +        // This is another comment
 +        .into_c();
 +}
 +"#,
 +            expect![[r#"
 +                [
 +                    InlayHint {
 +                        range: 147..172,
 +                        kind: ChainingHint,
-                         label: "A",
++                        label: [
++                            "B",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                147..172,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 147..154,
 +                        kind: ChainingHint,
-                         label: "C",
++                        label: [
++                            "A",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                147..154,
 +                            ),
 +                        ),
 +                    },
 +                ]
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn chaining_hints_without_newlines() {
 +        check_chains(
 +            r#"
 +struct A(B);
 +impl A { fn into_b(self) -> B { self.0 } }
 +struct B(C);
 +impl B { fn into_c(self) -> C { self.0 } }
 +struct C;
 +
 +fn main() {
 +    let c = A(B(C)).into_b().into_c();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_access_chaining_hints() {
 +        check_expect(
 +            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
 +            r#"
 +struct A { pub b: B }
 +struct B { pub c: C }
 +struct C(pub bool);
 +struct D;
 +
 +impl D {
 +    fn foo(&self) -> i32 { 42 }
 +}
 +
 +fn main() {
 +    let x = A { b: B { c: C(true) } }
 +        .b
 +        .c
 +        .0;
 +    let x = D
 +        .foo();
 +}"#,
 +            expect![[r#"
 +                [
 +                    InlayHint {
 +                        range: 143..190,
 +                        kind: ChainingHint,
-                         label: "B",
++                        label: [
++                            "C",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                143..190,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 143..179,
 +                        kind: ChainingHint,
-                         label: "B<X<i32, bool>>",
++                        label: [
++                            "B",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                143..179,
 +                            ),
 +                        ),
 +                    },
 +                ]
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn generic_chaining_hints() {
 +        check_expect(
 +            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
 +            r#"
 +struct A<T>(T);
 +struct B<T>(T);
 +struct C<T>(T);
 +struct X<T,R>(T, R);
 +
 +impl<T> A<T> {
 +    fn new(t: T) -> Self { A(t) }
 +    fn into_b(self) -> B<T> { B(self.0) }
 +}
 +impl<T> B<T> {
 +    fn into_c(self) -> C<T> { C(self.0) }
 +}
 +fn main() {
 +    let c = A::new(X(42, true))
 +        .into_b()
 +        .into_c();
 +}
 +"#,
 +            expect![[r#"
 +                [
 +                    InlayHint {
 +                        range: 246..283,
 +                        kind: ChainingHint,
-                         label: "A<X<i32, bool>>",
++                        label: [
++                            "B<X<i32, bool>>",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                246..283,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 246..265,
 +                        kind: ChainingHint,
-                         label: "impl Iterator<Item = ()>",
++                        label: [
++                            "A<X<i32, bool>>",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                246..265,
 +                            ),
 +                        ),
 +                    },
 +                ]
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn shorten_iterator_chaining_hints() {
 +        check_expect(
 +            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
 +            r#"
 +//- minicore: iterators
 +use core::iter;
 +
 +struct MyIter;
 +
 +impl Iterator for MyIter {
 +    type Item = ();
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +
 +fn main() {
 +    let _x = MyIter.by_ref()
 +        .take(5)
 +        .by_ref()
 +        .take(5)
 +        .by_ref();
 +}
 +"#,
 +            expect![[r#"
 +                [
 +                    InlayHint {
 +                        range: 174..241,
 +                        kind: ChainingHint,
-                         label: "impl Iterator<Item = ()>",
++                        label: [
++                            "impl Iterator<Item = ()>",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                174..241,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 174..224,
 +                        kind: ChainingHint,
-                         label: "impl Iterator<Item = ()>",
++                        label: [
++                            "impl Iterator<Item = ()>",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                174..224,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 174..206,
 +                        kind: ChainingHint,
-                         label: "&mut MyIter",
++                        label: [
++                            "impl Iterator<Item = ()>",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                174..206,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 174..189,
 +                        kind: ChainingHint,
-                         label: "Struct",
++                        label: [
++                            "&mut MyIter",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                174..189,
 +                            ),
 +                        ),
 +                    },
 +                ]
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_in_attr_call() {
 +        check_expect(
 +            TEST_CONFIG,
 +            r#"
 +//- proc_macros: identity, input_replace
 +struct Struct;
 +impl Struct {
 +    fn chain(self) -> Self {
 +        self
 +    }
 +}
 +#[proc_macros::identity]
 +fn main() {
 +    let strukt = Struct;
 +    strukt
 +        .chain()
 +        .chain()
 +        .chain();
 +    Struct::chain(strukt);
 +}
 +"#,
 +            expect![[r#"
 +                [
 +                    InlayHint {
 +                        range: 124..130,
 +                        kind: TypeHint,
-                         label: "Struct",
++                        label: [
++                            "Struct",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                124..130,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 145..185,
 +                        kind: ChainingHint,
-                         label: "Struct",
++                        label: [
++                            "Struct",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                145..185,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 145..168,
 +                        kind: ChainingHint,
-                         label: "self",
++                        label: [
++                            "Struct",
++                        ],
 +                        tooltip: Some(
 +                            HoverRanged(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                145..168,
 +                            ),
 +                        ),
 +                    },
 +                    InlayHint {
 +                        range: 222..228,
 +                        kind: ParameterHint,
++                        label: [
++                            "self",
++                        ],
 +                        tooltip: Some(
 +                            HoverOffset(
 +                                FileId(
 +                                    0,
 +                                ),
 +                                42,
 +                            ),
 +                        ),
 +                    },
 +                ]
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_lifetimes() {
 +        check(
 +            r#"
 +fn empty() {}
 +
 +fn no_gpl(a: &()) {}
 + //^^^^^^<'0>
 +          // ^'0
 +fn empty_gpl<>(a: &()) {}
 +      //    ^'0   ^'0
 +fn partial<'b>(a: &(), b: &'b ()) {}
 +//        ^'0, $  ^'0
 +fn partial<'a>(a: &'a (), b: &()) {}
 +//        ^'0, $             ^'0
 +
 +fn single_ret(a: &()) -> &() {}
 +// ^^^^^^^^^^<'0>
 +              // ^'0     ^'0
 +fn full_mul(a: &(), b: &()) {}
 +// ^^^^^^^^<'0, '1>
 +            // ^'0     ^'1
 +
 +fn foo<'c>(a: &'c ()) -> &() {}
 +                      // ^'c
 +
 +fn nested_in(a: &   &X< &()>) {}
 +// ^^^^^^^^^<'0, '1, '2>
 +              //^'0 ^'1 ^'2
 +fn nested_out(a: &()) -> &   &X< &()>{}
 +// ^^^^^^^^^^<'0>
 +               //^'0     ^'0 ^'0 ^'0
 +
 +impl () {
 +    fn foo(&self) {}
 +    // ^^^<'0>
 +        // ^'0
 +    fn foo(&self) -> &() {}
 +    // ^^^<'0>
 +        // ^'0       ^'0
 +    fn foo(&self, a: &()) -> &() {}
 +    // ^^^<'0, '1>
 +        // ^'0       ^'1     ^'0
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_lifetimes_named() {
 +        check_with_config(
 +            InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
 +            r#"
 +fn nested_in<'named>(named: &        &X<      &()>) {}
 +//          ^'named1, 'named2, 'named3, $
 +                          //^'named1 ^'named2 ^'named3
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_lifetimes_trivial_skip() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                lifetime_elision_hints: LifetimeElisionHints::SkipTrivial,
 +                ..TEST_CONFIG
 +            },
 +            r#"
 +fn no_gpl(a: &()) {}
 +fn empty_gpl<>(a: &()) {}
 +fn partial<'b>(a: &(), b: &'b ()) {}
 +fn partial<'a>(a: &'a (), b: &()) {}
 +
 +fn single_ret(a: &()) -> &() {}
 +// ^^^^^^^^^^<'0>
 +              // ^'0     ^'0
 +fn full_mul(a: &(), b: &()) {}
 +
 +fn foo<'c>(a: &'c ()) -> &() {}
 +                      // ^'c
 +
 +fn nested_in(a: &   &X< &()>) {}
 +fn nested_out(a: &()) -> &   &X< &()>{}
 +// ^^^^^^^^^^<'0>
 +               //^'0     ^'0 ^'0 ^'0
 +
 +impl () {
 +    fn foo(&self) {}
 +    fn foo(&self) -> &() {}
 +    // ^^^<'0>
 +        // ^'0       ^'0
 +    fn foo(&self, a: &()) -> &() {}
 +    // ^^^<'0, '1>
 +        // ^'0       ^'1     ^'0
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_lifetimes_static() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                lifetime_elision_hints: LifetimeElisionHints::Always,
 +                ..TEST_CONFIG
 +            },
 +            r#"
 +trait Trait {}
 +static S: &str = "";
 +//        ^'static
 +const C: &str = "";
 +//       ^'static
 +const C: &dyn Trait = panic!();
 +//       ^'static
 +
 +impl () {
 +    const C: &str = "";
 +    const C: &dyn Trait = panic!();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_implicit_reborrow() {
 +        check_with_config(
 +            InlayHintsConfig {
 +                reborrow_hints: ReborrowHints::Always,
 +                parameter_hints: true,
 +                ..DISABLED_CONFIG
 +            },
 +            r#"
 +fn __() {
 +    let unique = &mut ();
 +    let r_mov = unique;
 +    let foo: &mut _ = unique;
 +                    //^^^^^^ &mut *
 +    ref_mut_id(unique);
 +             //^^^^^^ mut_ref
 +             //^^^^^^ &mut *
 +    let shared = ref_id(unique);
 +                      //^^^^^^ shared_ref
 +                      //^^^^^^ &*
 +    let mov = shared;
 +    let r_mov: &_ = shared;
 +    ref_id(shared);
 +         //^^^^^^ shared_ref
 +
 +    identity(unique);
 +    identity(shared);
 +}
 +fn identity<T>(t: T) -> T {
 +    t
 +}
 +fn ref_mut_id(mut_ref: &mut ()) -> &mut () {
 +    mut_ref
 +  //^^^^^^^ &mut *
 +}
 +fn ref_id(shared_ref: &()) -> &() {
 +    shared_ref
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_binding_modes() {
 +        check_with_config(
 +            InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
 +            r#"
 +fn __(
 +    (x,): (u32,),
 +    (x,): &(u32,),
 +  //^^^^&
 +   //^ ref
 +    (x,): &mut (u32,)
 +  //^^^^&mut
 +   //^ ref mut
 +) {
 +    let (x,) = (0,);
 +    let (x,) = &(0,);
 +      //^^^^ &
 +       //^ ref
 +    let (x,) = &mut (0,);
 +      //^^^^ &mut
 +       //^ ref mut
 +    let &mut (x,) = &mut (0,);
 +    let (ref mut x,) = &mut (0,);
 +      //^^^^^^^^^^^^ &mut
 +    let &mut (ref mut x,) = &mut (0,);
 +    let (mut x,) = &mut (0,);
 +      //^^^^^^^^ &mut
 +    match (0,) {
 +        (x,) => ()
 +    }
 +    match &(0,) {
 +        (x,) => ()
 +      //^^^^ &
 +       //^ ref
 +    }
 +    match &mut (0,) {
 +        (x,) => ()
 +      //^^^^ &mut
 +       //^ ref mut
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn hints_closing_brace() {
 +        check_with_config(
 +            InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
 +            r#"
 +fn a() {}
 +
 +fn f() {
 +} // no hint unless `}` is the last token on the line
 +
 +fn g() {
 +  }
 +//^ fn g
 +
 +fn h<T>(with: T, arguments: u8, ...) {
 +  }
 +//^ fn h
 +
 +trait Tr {
 +    fn f();
 +    fn g() {
 +    }
 +  //^ fn g
 +  }
 +//^ trait Tr
 +impl Tr for () {
 +  }
 +//^ impl Tr for ()
 +impl dyn Tr {
 +  }
 +//^ impl dyn Tr
 +
 +static S0: () = 0;
 +static S1: () = {};
 +static S2: () = {
 + };
 +//^ static S2
 +const _: () = {
 + };
 +//^ const _
 +
 +mod m {
 +  }
 +//^ mod m
 +
 +m! {}
 +m!();
 +m!(
 + );
 +//^ m!
 +
 +m! {
 +  }
 +//^ m!
 +
 +fn f() {
 +    let v = vec![
 +    ];
 +  }
 +//^ fn f
 +"#,
 +        );
 +    }
 +}
index d61d69a090b33850049acfa36b8327ba7b887965,0000000000000000000000000000000000000000..0552330814aa4b1bd85762119136531489c20086
mode 100644,000000..100644
--- /dev/null
@@@ -1,710 -1,0 +1,710 @@@
-         ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-         LifetimeElisionHints, ReborrowHints,
 +//! ide crate provides "ide-centric" APIs for the rust-analyzer. That is,
 +//! it generally operates with files and text ranges, and returns results as
 +//! Strings, suitable for displaying to the human.
 +//!
 +//! What powers this API are the `RootDatabase` struct, which defines a `salsa`
 +//! database, and the `hir` crate, where majority of the analysis happens.
 +//! However, IDE specific bits of the analysis (most notably completion) happen
 +//! in this crate.
 +
 +// For proving that RootDatabase is RefUnwindSafe.
 +#![recursion_limit = "128"]
 +#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
 +
 +#[allow(unused)]
 +macro_rules! eprintln {
 +    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
 +}
 +
 +#[cfg(test)]
 +mod fixture;
 +
 +mod markup;
 +mod prime_caches;
 +mod navigation_target;
 +
 +mod annotations;
 +mod call_hierarchy;
 +mod signature_help;
 +mod doc_links;
 +mod highlight_related;
 +mod expand_macro;
 +mod extend_selection;
 +mod file_structure;
 +mod fn_references;
 +mod folding_ranges;
 +mod goto_declaration;
 +mod goto_definition;
 +mod goto_implementation;
 +mod goto_type_definition;
 +mod hover;
 +mod inlay_hints;
 +mod join_lines;
 +mod markdown_remove;
 +mod matching_brace;
 +mod moniker;
 +mod move_item;
 +mod parent_module;
 +mod references;
 +mod rename;
 +mod runnables;
 +mod ssr;
 +mod static_index;
 +mod status;
 +mod syntax_highlighting;
 +mod syntax_tree;
 +mod typing;
 +mod view_crate_graph;
 +mod view_hir;
 +mod view_item_tree;
 +mod shuffle_crate_graph;
 +
 +use std::sync::Arc;
 +
 +use cfg::CfgOptions;
 +use ide_db::{
 +    base_db::{
 +        salsa::{self, ParallelDatabase},
 +        CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
 +    },
 +    symbol_index, LineIndexDatabase,
 +};
 +use syntax::SourceFile;
 +
 +use crate::navigation_target::{ToNav, TryToNav};
 +
 +pub use crate::{
 +    annotations::{Annotation, AnnotationConfig, AnnotationKind},
 +    call_hierarchy::CallItem,
 +    expand_macro::ExpandedMacro,
 +    file_structure::{StructureNode, StructureNodeKind},
 +    folding_ranges::{Fold, FoldKind},
 +    highlight_related::{HighlightRelatedConfig, HighlightedRange},
 +    hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
 +    inlay_hints::{
++        ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
++        InlayTooltip, LifetimeElisionHints, ReborrowHints,
 +    },
 +    join_lines::JoinLinesConfig,
 +    markup::Markup,
 +    moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
 +    move_item::Direction,
 +    navigation_target::NavigationTarget,
 +    prime_caches::ParallelPrimeCachesProgress,
 +    references::ReferenceSearchResult,
 +    rename::RenameError,
 +    runnables::{Runnable, RunnableKind, TestId},
 +    signature_help::SignatureHelp,
 +    static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
 +    syntax_highlighting::{
 +        tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
 +        HighlightConfig, HlRange,
 +    },
 +};
 +pub use hir::{Documentation, Semantics};
 +pub use ide_assists::{
 +    Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
 +};
 +pub use ide_completion::{
 +    CallableSnippets, CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance,
 +    Snippet, SnippetScope,
 +};
 +pub use ide_db::{
 +    base_db::{
 +        Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
 +        SourceRoot, SourceRootId,
 +    },
 +    label::Label,
 +    line_index::{LineCol, LineColUtf16, LineIndex},
 +    search::{ReferenceCategory, SearchScope},
 +    source_change::{FileSystemEdit, SourceChange},
 +    symbol_index::Query,
 +    RootDatabase, SymbolKind,
 +};
 +pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, ExprFillDefaultMode, Severity};
 +pub use ide_ssr::SsrError;
 +pub use syntax::{TextRange, TextSize};
 +pub use text_edit::{Indel, TextEdit};
 +
 +pub type Cancellable<T> = Result<T, Cancelled>;
 +
 +/// Info associated with a text range.
 +#[derive(Debug)]
 +pub struct RangeInfo<T> {
 +    pub range: TextRange,
 +    pub info: T,
 +}
 +
 +impl<T> RangeInfo<T> {
 +    pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
 +        RangeInfo { range, info }
 +    }
 +}
 +
 +/// `AnalysisHost` stores the current state of the world.
 +#[derive(Debug)]
 +pub struct AnalysisHost {
 +    db: RootDatabase,
 +}
 +
 +impl AnalysisHost {
 +    pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
 +        AnalysisHost { db: RootDatabase::new(lru_capacity) }
 +    }
 +
 +    pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
 +        self.db.update_lru_capacity(lru_capacity);
 +    }
 +
 +    /// Returns a snapshot of the current state, which you can query for
 +    /// semantic information.
 +    pub fn analysis(&self) -> Analysis {
 +        Analysis { db: self.db.snapshot() }
 +    }
 +
 +    /// Applies changes to the current state of the world. If there are
 +    /// outstanding snapshots, they will be canceled.
 +    pub fn apply_change(&mut self, change: Change) {
 +        self.db.apply_change(change)
 +    }
 +
 +    /// NB: this clears the database
 +    pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes)> {
 +        self.db.per_query_memory_usage()
 +    }
 +    pub fn request_cancellation(&mut self) {
 +        self.db.request_cancellation();
 +    }
 +    pub fn raw_database(&self) -> &RootDatabase {
 +        &self.db
 +    }
 +    pub fn raw_database_mut(&mut self) -> &mut RootDatabase {
 +        &mut self.db
 +    }
 +
 +    pub fn shuffle_crate_graph(&mut self) {
 +        shuffle_crate_graph::shuffle_crate_graph(&mut self.db);
 +    }
 +}
 +
 +impl Default for AnalysisHost {
 +    fn default() -> AnalysisHost {
 +        AnalysisHost::new(None)
 +    }
 +}
 +
 +/// Analysis is a snapshot of a world state at a moment in time. It is the main
 +/// entry point for asking semantic information about the world. When the world
 +/// state is advanced using `AnalysisHost::apply_change` method, all existing
 +/// `Analysis` are canceled (most method return `Err(Canceled)`).
 +#[derive(Debug)]
 +pub struct Analysis {
 +    db: salsa::Snapshot<RootDatabase>,
 +}
 +
 +// As a general design guideline, `Analysis` API are intended to be independent
 +// from the language server protocol. That is, when exposing some functionality
 +// we should think in terms of "what API makes most sense" and not in terms of
 +// "what types LSP uses". Although currently LSP is the only consumer of the
 +// API, the API should in theory be usable as a library, or via a different
 +// protocol.
 +impl Analysis {
 +    // Creates an analysis instance for a single file, without any external
 +    // dependencies, stdlib support or ability to apply changes. See
 +    // `AnalysisHost` for creating a fully-featured analysis.
 +    pub fn from_single_file(text: String) -> (Analysis, FileId) {
 +        let mut host = AnalysisHost::default();
 +        let file_id = FileId(0);
 +        let mut file_set = FileSet::default();
 +        file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string()));
 +        let source_root = SourceRoot::new_local(file_set);
 +
 +        let mut change = Change::new();
 +        change.set_roots(vec![source_root]);
 +        let mut crate_graph = CrateGraph::default();
 +        // FIXME: cfg options
 +        // Default to enable test for single file.
 +        let mut cfg_options = CfgOptions::default();
 +        cfg_options.insert_atom("test".into());
 +        crate_graph.add_crate_root(
 +            file_id,
 +            Edition::CURRENT,
 +            None,
 +            None,
 +            cfg_options.clone(),
 +            cfg_options,
 +            Env::default(),
 +            Ok(Vec::new()),
 +            false,
 +            CrateOrigin::CratesIo { repo: None },
 +        );
 +        change.change_file(file_id, Some(Arc::new(text)));
 +        change.set_crate_graph(crate_graph);
 +        host.apply_change(change);
 +        (host.analysis(), file_id)
 +    }
 +
 +    /// Debug info about the current state of the analysis.
 +    pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
 +        self.with_db(|db| status::status(&*db, file_id))
 +    }
 +
 +    pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancellable<()>
 +    where
 +        F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
 +    {
 +        self.with_db(move |db| prime_caches::parallel_prime_caches(db, num_worker_threads, &cb))
 +    }
 +
 +    /// Gets the text of the source file.
 +    pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
 +        self.with_db(|db| db.file_text(file_id))
 +    }
 +
 +    /// Gets the syntax tree of the file.
 +    pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
 +        self.with_db(|db| db.parse(file_id).tree())
 +    }
 +
 +    /// Returns true if this file belongs to an immutable library.
 +    pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
 +        use ide_db::base_db::SourceDatabaseExt;
 +        self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
 +    }
 +
 +    /// Gets the file's `LineIndex`: data structure to convert between absolute
 +    /// offsets and line/column representation.
 +    pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
 +        self.with_db(|db| db.line_index(file_id))
 +    }
 +
 +    /// Selects the next syntactic nodes encompassing the range.
 +    pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
 +        self.with_db(|db| extend_selection::extend_selection(db, frange))
 +    }
 +
 +    /// Returns position of the matching brace (all types of braces are
 +    /// supported).
 +    pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
 +        self.with_db(|db| {
 +            let parse = db.parse(position.file_id);
 +            let file = parse.tree();
 +            matching_brace::matching_brace(&file, position.offset)
 +        })
 +    }
 +
 +    /// Returns a syntax tree represented as `String`, for debug purposes.
 +    // FIXME: use a better name here.
 +    pub fn syntax_tree(
 +        &self,
 +        file_id: FileId,
 +        text_range: Option<TextRange>,
 +    ) -> Cancellable<String> {
 +        self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
 +    }
 +
 +    pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
 +        self.with_db(|db| view_hir::view_hir(db, position))
 +    }
 +
 +    pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
 +        self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
 +    }
 +
 +    /// Renders the crate graph to GraphViz "dot" syntax.
 +    pub fn view_crate_graph(&self, full: bool) -> Cancellable<Result<String, String>> {
 +        self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
 +    }
 +
 +    pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
 +        self.with_db(|db| expand_macro::expand_macro(db, position))
 +    }
 +
 +    /// Returns an edit to remove all newlines in the range, cleaning up minor
 +    /// stuff like trailing commas.
 +    pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable<TextEdit> {
 +        self.with_db(|db| {
 +            let parse = db.parse(frange.file_id);
 +            join_lines::join_lines(config, &parse.tree(), frange.range)
 +        })
 +    }
 +
 +    /// Returns an edit which should be applied when opening a new line, fixing
 +    /// up minor stuff like continuing the comment.
 +    /// The edit will be a snippet (with `$0`).
 +    pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
 +        self.with_db(|db| typing::on_enter(db, position))
 +    }
 +
 +    /// Returns an edit which should be applied after a character was typed.
 +    ///
 +    /// This is useful for some on-the-fly fixups, like adding `;` to `let =`
 +    /// automatically.
 +    pub fn on_char_typed(
 +        &self,
 +        position: FilePosition,
 +        char_typed: char,
 +        autoclose: bool,
 +    ) -> Cancellable<Option<SourceChange>> {
 +        // Fast path to not even parse the file.
 +        if !typing::TRIGGER_CHARS.contains(char_typed) {
 +            return Ok(None);
 +        }
 +        if char_typed == '<' && !autoclose {
 +            return Ok(None);
 +        }
 +
 +        self.with_db(|db| typing::on_char_typed(db, position, char_typed))
 +    }
 +
 +    /// Returns a tree representation of symbols in the file. Useful to draw a
 +    /// file outline.
 +    pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> {
 +        self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
 +    }
 +
 +    /// Returns a list of the places in the file where type hints can be displayed.
 +    pub fn inlay_hints(
 +        &self,
 +        config: &InlayHintsConfig,
 +        file_id: FileId,
 +        range: Option<FileRange>,
 +    ) -> Cancellable<Vec<InlayHint>> {
 +        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
 +    }
 +
 +    /// Returns the set of folding ranges.
 +    pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> {
 +        self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree()))
 +    }
 +
 +    /// Fuzzy searches for a symbol.
 +    pub fn symbol_search(&self, query: Query) -> Cancellable<Vec<NavigationTarget>> {
 +        self.with_db(|db| {
 +            symbol_index::world_symbols(db, query)
 +                .into_iter() // xx: should we make this a par iter?
 +                .filter_map(|s| s.try_to_nav(db))
 +                .collect::<Vec<_>>()
 +        })
 +    }
 +
 +    /// Returns the definitions from the symbol at `position`.
 +    pub fn goto_definition(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
 +        self.with_db(|db| goto_definition::goto_definition(db, position))
 +    }
 +
 +    /// Returns the declaration from the symbol at `position`.
 +    pub fn goto_declaration(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
 +        self.with_db(|db| goto_declaration::goto_declaration(db, position))
 +    }
 +
 +    /// Returns the impls from the symbol at `position`.
 +    pub fn goto_implementation(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
 +        self.with_db(|db| goto_implementation::goto_implementation(db, position))
 +    }
 +
 +    /// Returns the type definitions for the symbol at `position`.
 +    pub fn goto_type_definition(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
 +        self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
 +    }
 +
 +    /// Finds all usages of the reference at point.
 +    pub fn find_all_refs(
 +        &self,
 +        position: FilePosition,
 +        search_scope: Option<SearchScope>,
 +    ) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
 +        self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
 +    }
 +
 +    /// Finds all methods and free functions for the file. Does not return tests!
 +    pub fn find_all_methods(&self, file_id: FileId) -> Cancellable<Vec<FileRange>> {
 +        self.with_db(|db| fn_references::find_all_methods(db, file_id))
 +    }
 +
 +    /// Returns a short text describing element at position.
 +    pub fn hover(
 +        &self,
 +        config: &HoverConfig,
 +        range: FileRange,
 +    ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
 +        self.with_db(|db| hover::hover(db, range, config))
 +    }
 +
 +    /// Returns moniker of symbol at position.
 +    pub fn moniker(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<moniker::MonikerResult>>>> {
 +        self.with_db(|db| moniker::moniker(db, position))
 +    }
 +
 +    /// Return URL(s) for the documentation of the symbol under the cursor.
 +    pub fn external_docs(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<doc_links::DocumentationLink>> {
 +        self.with_db(|db| doc_links::external_docs(db, &position))
 +    }
 +
 +    /// Computes parameter information at the given position.
 +    pub fn signature_help(&self, position: FilePosition) -> Cancellable<Option<SignatureHelp>> {
 +        self.with_db(|db| signature_help::signature_help(db, position))
 +    }
 +
 +    /// Computes call hierarchy candidates for the given file position.
 +    pub fn call_hierarchy(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
 +        self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
 +    }
 +
 +    /// Computes incoming calls for the given file position.
 +    pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
 +        self.with_db(|db| call_hierarchy::incoming_calls(db, position))
 +    }
 +
 +    /// Computes outgoing calls for the given file position.
 +    pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
 +        self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
 +    }
 +
 +    /// Returns a `mod name;` declaration which created the current module.
 +    pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
 +        self.with_db(|db| parent_module::parent_module(db, position))
 +    }
 +
 +    /// Returns crates this file belongs too.
 +    pub fn crate_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
 +        self.with_db(|db| parent_module::crate_for(db, file_id))
 +    }
 +
 +    /// Returns the edition of the given crate.
 +    pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
 +        self.with_db(|db| db.crate_graph()[crate_id].edition)
 +    }
 +
 +    /// Returns the root file of the given crate.
 +    pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
 +        self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
 +    }
 +
 +    /// Returns the set of possible targets to run for the current file.
 +    pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
 +        self.with_db(|db| runnables::runnables(db, file_id))
 +    }
 +
 +    /// Returns the set of tests for the given file position.
 +    pub fn related_tests(
 +        &self,
 +        position: FilePosition,
 +        search_scope: Option<SearchScope>,
 +    ) -> Cancellable<Vec<Runnable>> {
 +        self.with_db(|db| runnables::related_tests(db, position, search_scope))
 +    }
 +
 +    /// Computes syntax highlighting for the given file
 +    pub fn highlight(
 +        &self,
 +        highlight_config: HighlightConfig,
 +        file_id: FileId,
 +    ) -> Cancellable<Vec<HlRange>> {
 +        self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
 +    }
 +
 +    /// Computes all ranges to highlight for a given item in a file.
 +    pub fn highlight_related(
 +        &self,
 +        config: HighlightRelatedConfig,
 +        position: FilePosition,
 +    ) -> Cancellable<Option<Vec<HighlightedRange>>> {
 +        self.with_db(|db| {
 +            highlight_related::highlight_related(&Semantics::new(db), config, position)
 +        })
 +    }
 +
 +    /// Computes syntax highlighting for the given file range.
 +    pub fn highlight_range(
 +        &self,
 +        highlight_config: HighlightConfig,
 +        frange: FileRange,
 +    ) -> Cancellable<Vec<HlRange>> {
 +        self.with_db(|db| {
 +            syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
 +        })
 +    }
 +
 +    /// Computes syntax highlighting for the given file.
 +    pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
 +        self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
 +    }
 +
 +    /// Computes completions at the given position.
 +    pub fn completions(
 +        &self,
 +        config: &CompletionConfig,
 +        position: FilePosition,
 +        trigger_character: Option<char>,
 +    ) -> Cancellable<Option<Vec<CompletionItem>>> {
 +        self.with_db(|db| {
 +            ide_completion::completions(db, config, position, trigger_character).map(Into::into)
 +        })
 +    }
 +
 +    /// Resolves additional completion data at the position given.
 +    pub fn resolve_completion_edits(
 +        &self,
 +        config: &CompletionConfig,
 +        position: FilePosition,
 +        imports: impl IntoIterator<Item = (String, String)> + std::panic::UnwindSafe,
 +    ) -> Cancellable<Vec<TextEdit>> {
 +        Ok(self
 +            .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?
 +            .unwrap_or_default())
 +    }
 +
 +    /// Computes the set of diagnostics for the given file.
 +    pub fn diagnostics(
 +        &self,
 +        config: &DiagnosticsConfig,
 +        resolve: AssistResolveStrategy,
 +        file_id: FileId,
 +    ) -> Cancellable<Vec<Diagnostic>> {
 +        self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id))
 +    }
 +
 +    /// Convenience function to return assists + quick fixes for diagnostics
 +    pub fn assists_with_fixes(
 +        &self,
 +        assist_config: &AssistConfig,
 +        diagnostics_config: &DiagnosticsConfig,
 +        resolve: AssistResolveStrategy,
 +        frange: FileRange,
 +    ) -> Cancellable<Vec<Assist>> {
 +        let include_fixes = match &assist_config.allowed {
 +            Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
 +            None => true,
 +        };
 +
 +        self.with_db(|db| {
 +            let diagnostic_assists = if include_fixes {
 +                ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
 +                    .into_iter()
 +                    .flat_map(|it| it.fixes.unwrap_or_default())
 +                    .filter(|it| it.target.intersect(frange.range).is_some())
 +                    .collect()
 +            } else {
 +                Vec::new()
 +            };
 +            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
 +            let assists = ide_assists::assists(db, assist_config, resolve, frange);
 +
 +            let mut res = diagnostic_assists;
 +            res.extend(ssr_assists.into_iter());
 +            res.extend(assists.into_iter());
 +
 +            res
 +        })
 +    }
 +
 +    /// Returns the edit required to rename reference at the position to the new
 +    /// name.
 +    pub fn rename(
 +        &self,
 +        position: FilePosition,
 +        new_name: &str,
 +    ) -> Cancellable<Result<SourceChange, RenameError>> {
 +        self.with_db(|db| rename::rename(db, position, new_name))
 +    }
 +
 +    pub fn prepare_rename(
 +        &self,
 +        position: FilePosition,
 +    ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
 +        self.with_db(|db| rename::prepare_rename(db, position))
 +    }
 +
 +    pub fn will_rename_file(
 +        &self,
 +        file_id: FileId,
 +        new_name_stem: &str,
 +    ) -> Cancellable<Option<SourceChange>> {
 +        self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem))
 +    }
 +
 +    pub fn structural_search_replace(
 +        &self,
 +        query: &str,
 +        parse_only: bool,
 +        resolve_context: FilePosition,
 +        selections: Vec<FileRange>,
 +    ) -> Cancellable<Result<SourceChange, SsrError>> {
 +        self.with_db(|db| {
 +            let rule: ide_ssr::SsrRule = query.parse()?;
 +            let mut match_finder =
 +                ide_ssr::MatchFinder::in_context(db, resolve_context, selections)?;
 +            match_finder.add_rule(rule)?;
 +            let edits = if parse_only { Default::default() } else { match_finder.edits() };
 +            Ok(SourceChange::from(edits))
 +        })
 +    }
 +
 +    pub fn annotations(
 +        &self,
 +        config: &AnnotationConfig,
 +        file_id: FileId,
 +    ) -> Cancellable<Vec<Annotation>> {
 +        self.with_db(|db| annotations::annotations(db, config, file_id))
 +    }
 +
 +    pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
 +        self.with_db(|db| annotations::resolve_annotation(db, annotation))
 +    }
 +
 +    pub fn move_item(
 +        &self,
 +        range: FileRange,
 +        direction: Direction,
 +    ) -> Cancellable<Option<TextEdit>> {
 +        self.with_db(|db| move_item::move_item(db, range, direction))
 +    }
 +
 +    /// Performs an operation on the database that may be canceled.
 +    ///
 +    /// rust-analyzer needs to be able to answer semantic questions about the
 +    /// code while the code is being modified. A common problem is that a
 +    /// long-running query is being calculated when a new change arrives.
 +    ///
 +    /// We can't just apply the change immediately: this will cause the pending
 +    /// query to see inconsistent state (it will observe an absence of
 +    /// repeatable read). So what we do is we **cancel** all pending queries
 +    /// before applying the change.
 +    ///
 +    /// Salsa implements cancellation by unwinding with a special value and
 +    /// catching it on the API boundary.
 +    fn with_db<F, T>(&self, f: F) -> Cancellable<T>
 +    where
 +        F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
 +    {
 +        Cancelled::catch(|| f(&self.db))
 +    }
 +}
 +
 +#[test]
 +fn analysis_is_send() {
 +    fn is_send<T: Send>() {}
 +    is_send::<Analysis>();
 +}
index d9b669afbe81cd3be1baea15b57d53788b842a9c,0000000000000000000000000000000000000000..e79cf3d3fd6e9ffe535e5ff9aee9f007c52ec157
mode 100644,000000..100644
--- /dev/null
@@@ -1,1911 -1,0 +1,1911 @@@
-             .collect(),
 +//! This module is responsible for implementing handlers for Language Server
 +//! Protocol. The majority of requests are fulfilled by calling into the
 +//! `ide` crate.
 +
 +use std::{
 +    io::Write as _,
 +    process::{self, Stdio},
 +};
 +
 +use anyhow::Context;
 +use ide::{
 +    AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
 +    HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SingleResolve,
 +    SourceChange, TextEdit,
 +};
 +use ide_db::SymbolKind;
 +use lsp_server::ErrorCode;
 +use lsp_types::{
 +    CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
 +    CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
 +    CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
 +    FoldingRangeParams, HoverContents, InlayHint, InlayHintParams, Location, LocationLink,
 +    NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
 +    SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
 +    SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
 +    SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
 +};
 +use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
 +use serde_json::json;
 +use stdx::{format_to, never};
 +use syntax::{algo, ast, AstNode, TextRange, TextSize, T};
 +use vfs::AbsPathBuf;
 +
 +use crate::{
 +    cargo_target_spec::CargoTargetSpec,
 +    config::{RustfmtConfig, WorkspaceSymbolConfig},
 +    diff::diff,
 +    from_proto,
 +    global_state::{GlobalState, GlobalStateSnapshot},
 +    line_index::LineEndings,
 +    lsp_ext::{self, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams},
 +    lsp_utils::{all_edits_are_disjoint, invalid_params_error},
 +    to_proto, LspError, Result,
 +};
 +
 +pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
 +    state.proc_macro_clients.clear();
 +    state.proc_macro_changed = false;
 +    state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
 +    state.fetch_build_data_queue.request_op("reload workspace request".to_string());
 +    Ok(())
 +}
 +
 +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
 +    let _p = profile::span("handle_stop_flycheck");
 +    state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
 +    Ok(())
 +}
 +
 +pub(crate) fn handle_analyzer_status(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::AnalyzerStatusParams,
 +) -> Result<String> {
 +    let _p = profile::span("handle_analyzer_status");
 +
 +    let mut buf = String::new();
 +
 +    let mut file_id = None;
 +    if let Some(tdi) = params.text_document {
 +        match from_proto::file_id(&snap, &tdi.uri) {
 +            Ok(it) => file_id = Some(it),
 +            Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
 +        }
 +    }
 +
 +    if snap.workspaces.is_empty() {
 +        buf.push_str("No workspaces\n")
 +    } else {
 +        buf.push_str("Workspaces:\n");
 +        format_to!(
 +            buf,
 +            "Loaded {:?} packages across {} workspace{}.\n",
 +            snap.workspaces.iter().map(|w| w.n_packages()).sum::<usize>(),
 +            snap.workspaces.len(),
 +            if snap.workspaces.len() == 1 { "" } else { "s" }
 +        );
 +    }
 +    buf.push_str("\nAnalysis:\n");
 +    buf.push_str(
 +        &snap
 +            .analysis
 +            .status(file_id)
 +            .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
 +    );
 +    Ok(buf)
 +}
 +
 +pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
 +    let _p = profile::span("handle_memory_usage");
 +    let mut mem = state.analysis_host.per_query_memory_usage();
 +    mem.push(("Remaining".into(), profile::memory_usage().allocated));
 +
 +    let mut out = String::new();
 +    for (name, bytes) in mem {
 +        format_to!(out, "{:>8} {}\n", bytes, name);
 +    }
 +    Ok(out)
 +}
 +
 +pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> Result<()> {
 +    state.analysis_host.shuffle_crate_graph();
 +    Ok(())
 +}
 +
 +pub(crate) fn handle_syntax_tree(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::SyntaxTreeParams,
 +) -> Result<String> {
 +    let _p = profile::span("handle_syntax_tree");
 +    let id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(id)?;
 +    let text_range = params.range.and_then(|r| from_proto::text_range(&line_index, r).ok());
 +    let res = snap.analysis.syntax_tree(id, text_range)?;
 +    Ok(res)
 +}
 +
 +pub(crate) fn handle_view_hir(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<String> {
 +    let _p = profile::span("handle_view_hir");
 +    let position = from_proto::file_position(&snap, params)?;
 +    let res = snap.analysis.view_hir(position)?;
 +    Ok(res)
 +}
 +
 +pub(crate) fn handle_view_file_text(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentIdentifier,
 +) -> Result<String> {
 +    let file_id = from_proto::file_id(&snap, &params.uri)?;
 +    Ok(snap.analysis.file_text(file_id)?.to_string())
 +}
 +
 +pub(crate) fn handle_view_item_tree(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::ViewItemTreeParams,
 +) -> Result<String> {
 +    let _p = profile::span("handle_view_item_tree");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let res = snap.analysis.view_item_tree(file_id)?;
 +    Ok(res)
 +}
 +
 +pub(crate) fn handle_view_crate_graph(
 +    snap: GlobalStateSnapshot,
 +    params: ViewCrateGraphParams,
 +) -> Result<String> {
 +    let _p = profile::span("handle_view_crate_graph");
 +    let dot = snap.analysis.view_crate_graph(params.full)??;
 +    Ok(dot)
 +}
 +
 +pub(crate) fn handle_expand_macro(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::ExpandMacroParams,
 +) -> Result<Option<lsp_ext::ExpandedMacro>> {
 +    let _p = profile::span("handle_expand_macro");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let offset = from_proto::offset(&line_index, params.position)?;
 +
 +    let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?;
 +    Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
 +}
 +
 +pub(crate) fn handle_selection_range(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::SelectionRangeParams,
 +) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
 +    let _p = profile::span("handle_selection_range");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let res: Result<Vec<lsp_types::SelectionRange>> = params
 +        .positions
 +        .into_iter()
 +        .map(|position| {
 +            let offset = from_proto::offset(&line_index, position)?;
 +            let mut ranges = Vec::new();
 +            {
 +                let mut range = TextRange::new(offset, offset);
 +                loop {
 +                    ranges.push(range);
 +                    let frange = FileRange { file_id, range };
 +                    let next = snap.analysis.extend_selection(frange)?;
 +                    if next == range {
 +                        break;
 +                    } else {
 +                        range = next
 +                    }
 +                }
 +            }
 +            let mut range = lsp_types::SelectionRange {
 +                range: to_proto::range(&line_index, *ranges.last().unwrap()),
 +                parent: None,
 +            };
 +            for &r in ranges.iter().rev().skip(1) {
 +                range = lsp_types::SelectionRange {
 +                    range: to_proto::range(&line_index, r),
 +                    parent: Some(Box::new(range)),
 +                }
 +            }
 +            Ok(range)
 +        })
 +        .collect();
 +
 +    Ok(Some(res?))
 +}
 +
 +pub(crate) fn handle_matching_brace(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::MatchingBraceParams,
 +) -> Result<Vec<Position>> {
 +    let _p = profile::span("handle_matching_brace");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    params
 +        .positions
 +        .into_iter()
 +        .map(|position| {
 +            let offset = from_proto::offset(&line_index, position);
 +            offset.map(|offset| {
 +                let offset = match snap.analysis.matching_brace(FilePosition { file_id, offset }) {
 +                    Ok(Some(matching_brace_offset)) => matching_brace_offset,
 +                    Err(_) | Ok(None) => offset,
 +                };
 +                to_proto::position(&line_index, offset)
 +            })
 +        })
 +        .collect()
 +}
 +
 +pub(crate) fn handle_join_lines(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::JoinLinesParams,
 +) -> Result<Vec<lsp_types::TextEdit>> {
 +    let _p = profile::span("handle_join_lines");
 +
 +    let config = snap.config.join_lines();
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let mut res = TextEdit::default();
 +    for range in params.ranges {
 +        let range = from_proto::text_range(&line_index, range)?;
 +        let edit = snap.analysis.join_lines(&config, FileRange { file_id, range })?;
 +        match res.union(edit) {
 +            Ok(()) => (),
 +            Err(_edit) => {
 +                // just ignore overlapping edits
 +            }
 +        }
 +    }
 +
 +    Ok(to_proto::text_edit_vec(&line_index, res))
 +}
 +
 +pub(crate) fn handle_on_enter(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
 +    let _p = profile::span("handle_on_enter");
 +    let position = from_proto::file_position(&snap, params)?;
 +    let edit = match snap.analysis.on_enter(position)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +    let line_index = snap.file_line_index(position.file_id)?;
 +    let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit);
 +    Ok(Some(edit))
 +}
 +
 +pub(crate) fn handle_on_type_formatting(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::DocumentOnTypeFormattingParams,
 +) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
 +    let _p = profile::span("handle_on_type_formatting");
 +    let mut position = from_proto::file_position(&snap, params.text_document_position)?;
 +    let line_index = snap.file_line_index(position.file_id)?;
 +
 +    // in `ide`, the `on_type` invariant is that
 +    // `text.char_at(position) == typed_char`.
 +    position.offset -= TextSize::of('.');
 +    let char_typed = params.ch.chars().next().unwrap_or('\0');
 +
 +    let text = snap.analysis.file_text(position.file_id)?;
 +    if stdx::never!(!text[usize::from(position.offset)..].starts_with(char_typed)) {
 +        return Ok(None);
 +    }
 +
 +    // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
 +    // but it requires precise cursor positioning to work, and one can't
 +    // position the cursor with on_type formatting. So, let's just toggle this
 +    // feature off here, hoping that we'll enable it one day, 😿.
 +    if char_typed == '>' {
 +        return Ok(None);
 +    }
 +
 +    let edit =
 +        snap.analysis.on_char_typed(position, char_typed, snap.config.typing_autoclose_angle())?;
 +    let edit = match edit {
 +        Some(it) => it,
 +        None => return Ok(None),
 +    };
 +
 +    // This should be a single-file edit
 +    let (_, text_edit) = edit.source_file_edits.into_iter().next().unwrap();
 +
 +    let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
 +    Ok(Some(change))
 +}
 +
 +pub(crate) fn handle_document_symbol(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::DocumentSymbolParams,
 +) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
 +    let _p = profile::span("handle_document_symbol");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
 +
 +    for symbol in snap.analysis.file_structure(file_id)? {
 +        let mut tags = Vec::new();
 +        if symbol.deprecated {
 +            tags.push(SymbolTag::DEPRECATED)
 +        };
 +
 +        #[allow(deprecated)]
 +        let doc_symbol = lsp_types::DocumentSymbol {
 +            name: symbol.label,
 +            detail: symbol.detail,
 +            kind: to_proto::structure_node_kind(symbol.kind),
 +            tags: Some(tags),
 +            deprecated: Some(symbol.deprecated),
 +            range: to_proto::range(&line_index, symbol.node_range),
 +            selection_range: to_proto::range(&line_index, symbol.navigation_range),
 +            children: None,
 +        };
 +        parents.push((doc_symbol, symbol.parent));
 +    }
 +
 +    // Builds hierarchy from a flat list, in reverse order (so that indices
 +    // makes sense)
 +    let document_symbols = {
 +        let mut acc = Vec::new();
 +        while let Some((mut node, parent_idx)) = parents.pop() {
 +            if let Some(children) = &mut node.children {
 +                children.reverse();
 +            }
 +            let parent = match parent_idx {
 +                None => &mut acc,
 +                Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
 +            };
 +            parent.push(node);
 +        }
 +        acc.reverse();
 +        acc
 +    };
 +
 +    let res = if snap.config.hierarchical_symbols() {
 +        document_symbols.into()
 +    } else {
 +        let url = to_proto::url(&snap, file_id);
 +        let mut symbol_information = Vec::<SymbolInformation>::new();
 +        for symbol in document_symbols {
 +            flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
 +        }
 +        symbol_information.into()
 +    };
 +    return Ok(Some(res));
 +
 +    fn flatten_document_symbol(
 +        symbol: &lsp_types::DocumentSymbol,
 +        container_name: Option<String>,
 +        url: &Url,
 +        res: &mut Vec<SymbolInformation>,
 +    ) {
 +        let mut tags = Vec::new();
 +
 +        #[allow(deprecated)]
 +        if let Some(true) = symbol.deprecated {
 +            tags.push(SymbolTag::DEPRECATED)
 +        }
 +
 +        #[allow(deprecated)]
 +        res.push(SymbolInformation {
 +            name: symbol.name.clone(),
 +            kind: symbol.kind,
 +            tags: Some(tags),
 +            deprecated: symbol.deprecated,
 +            location: Location::new(url.clone(), symbol.range),
 +            container_name,
 +        });
 +
 +        for child in symbol.children.iter().flatten() {
 +            flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
 +        }
 +    }
 +}
 +
 +pub(crate) fn handle_workspace_symbol(
 +    snap: GlobalStateSnapshot,
 +    params: WorkspaceSymbolParams,
 +) -> Result<Option<Vec<SymbolInformation>>> {
 +    let _p = profile::span("handle_workspace_symbol");
 +
 +    let config = snap.config.workspace_symbol();
 +    let (all_symbols, libs) = decide_search_scope_and_kind(&params, &config);
 +    let limit = config.search_limit;
 +
 +    let query = {
 +        let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
 +        let mut q = Query::new(query);
 +        if !all_symbols {
 +            q.only_types();
 +        }
 +        if libs {
 +            q.libs();
 +        }
 +        q.limit(limit);
 +        q
 +    };
 +    let mut res = exec_query(&snap, query)?;
 +    if res.is_empty() && !all_symbols {
 +        let mut query = Query::new(params.query);
 +        query.limit(limit);
 +        res = exec_query(&snap, query)?;
 +    }
 +
 +    return Ok(Some(res));
 +
 +    fn decide_search_scope_and_kind(
 +        params: &WorkspaceSymbolParams,
 +        config: &WorkspaceSymbolConfig,
 +    ) -> (bool, bool) {
 +        // Support old-style parsing of markers in the query.
 +        let mut all_symbols = params.query.contains('#');
 +        let mut libs = params.query.contains('*');
 +
 +        // If no explicit marker was set, check request params. If that's also empty
 +        // use global config.
 +        if !all_symbols {
 +            let search_kind = match params.search_kind {
 +                Some(ref search_kind) => search_kind,
 +                None => &config.search_kind,
 +            };
 +            all_symbols = match search_kind {
 +                lsp_ext::WorkspaceSymbolSearchKind::OnlyTypes => false,
 +                lsp_ext::WorkspaceSymbolSearchKind::AllSymbols => true,
 +            }
 +        }
 +
 +        if !libs {
 +            let search_scope = match params.search_scope {
 +                Some(ref search_scope) => search_scope,
 +                None => &config.search_scope,
 +            };
 +            libs = match search_scope {
 +                lsp_ext::WorkspaceSymbolSearchScope::Workspace => false,
 +                lsp_ext::WorkspaceSymbolSearchScope::WorkspaceAndDependencies => true,
 +            }
 +        }
 +
 +        (all_symbols, libs)
 +    }
 +
 +    fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
 +        let mut res = Vec::new();
 +        for nav in snap.analysis.symbol_search(query)? {
 +            let container_name = nav.container_name.as_ref().map(|v| v.to_string());
 +
 +            #[allow(deprecated)]
 +            let info = SymbolInformation {
 +                name: nav.name.to_string(),
 +                kind: nav
 +                    .kind
 +                    .map(to_proto::symbol_kind)
 +                    .unwrap_or(lsp_types::SymbolKind::VARIABLE),
 +                tags: None,
 +                location: to_proto::location_from_nav(snap, nav)?,
 +                container_name,
 +                deprecated: None,
 +            };
 +            res.push(info);
 +        }
 +        Ok(res)
 +    }
 +}
 +
 +pub(crate) fn handle_will_rename_files(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::RenameFilesParams,
 +) -> Result<Option<lsp_types::WorkspaceEdit>> {
 +    let _p = profile::span("handle_will_rename_files");
 +
 +    let source_changes: Vec<SourceChange> = params
 +        .files
 +        .into_iter()
 +        .filter_map(|file_rename| {
 +            let from = Url::parse(&file_rename.old_uri).ok()?;
 +            let to = Url::parse(&file_rename.new_uri).ok()?;
 +
 +            let from_path = from.to_file_path().ok()?;
 +            let to_path = to.to_file_path().ok()?;
 +
 +            // Limit to single-level moves for now.
 +            match (from_path.parent(), to_path.parent()) {
 +                (Some(p1), Some(p2)) if p1 == p2 => {
 +                    if from_path.is_dir() {
 +                        // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
 +                        let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
 +                        old_folder_name.push('/');
 +                        let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
 +
 +                        let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
 +                        let new_file_name = to_path.file_name()?.to_str()?;
 +                        Some((
 +                            snap.url_to_file_id(&imitate_from_url).ok()?,
 +                            new_file_name.to_string(),
 +                        ))
 +                    } else {
 +                        let old_name = from_path.file_stem()?.to_str()?;
 +                        let new_name = to_path.file_stem()?.to_str()?;
 +                        match (old_name, new_name) {
 +                            ("mod", _) => None,
 +                            (_, "mod") => None,
 +                            _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
 +                        }
 +                    }
 +                }
 +                _ => None,
 +            }
 +        })
 +        .filter_map(|(file_id, new_name)| {
 +            snap.analysis.will_rename_file(file_id, &new_name).ok()?
 +        })
 +        .collect();
 +
 +    // Drop file system edits since we're just renaming things on the same level
 +    let mut source_changes = source_changes.into_iter();
 +    let mut source_change = source_changes.next().unwrap_or_default();
 +    source_change.file_system_edits.clear();
 +    // no collect here because we want to merge text edits on same file ids
 +    source_change.extend(source_changes.flat_map(|it| it.source_file_edits));
 +    if source_change.source_file_edits.is_empty() {
 +        Ok(None)
 +    } else {
 +        to_proto::workspace_edit(&snap, source_change).map(Some)
 +    }
 +}
 +
 +pub(crate) fn handle_goto_definition(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::GotoDefinitionParams,
 +) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
 +    let _p = profile::span("handle_goto_definition");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +    let nav_info = match snap.analysis.goto_definition(position)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +    let src = FileRange { file_id: position.file_id, range: nav_info.range };
 +    let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_goto_declaration(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::request::GotoDeclarationParams,
 +) -> Result<Option<lsp_types::request::GotoDeclarationResponse>> {
 +    let _p = profile::span("handle_goto_declaration");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?;
 +    let nav_info = match snap.analysis.goto_declaration(position)? {
 +        None => return handle_goto_definition(snap, params),
 +        Some(it) => it,
 +    };
 +    let src = FileRange { file_id: position.file_id, range: nav_info.range };
 +    let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_goto_implementation(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::request::GotoImplementationParams,
 +) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
 +    let _p = profile::span("handle_goto_implementation");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +    let nav_info = match snap.analysis.goto_implementation(position)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +    let src = FileRange { file_id: position.file_id, range: nav_info.range };
 +    let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_goto_type_definition(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::request::GotoTypeDefinitionParams,
 +) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
 +    let _p = profile::span("handle_goto_type_definition");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +    let nav_info = match snap.analysis.goto_type_definition(position)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +    let src = FileRange { file_id: position.file_id, range: nav_info.range };
 +    let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_parent_module(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
 +    let _p = profile::span("handle_parent_module");
 +    if let Ok(file_path) = &params.text_document.uri.to_file_path() {
 +        if file_path.file_name().unwrap_or_default() == "Cargo.toml" {
 +            // search workspaces for parent packages or fallback to workspace root
 +            let abs_path_buf = match AbsPathBuf::try_from(file_path.to_path_buf()).ok() {
 +                Some(abs_path_buf) => abs_path_buf,
 +                None => return Ok(None),
 +            };
 +
 +            let manifest_path = match ManifestPath::try_from(abs_path_buf).ok() {
 +                Some(manifest_path) => manifest_path,
 +                None => return Ok(None),
 +            };
 +
 +            let links: Vec<LocationLink> = snap
 +                .workspaces
 +                .iter()
 +                .filter_map(|ws| match ws {
 +                    ProjectWorkspace::Cargo { cargo, .. } => cargo.parent_manifests(&manifest_path),
 +                    _ => None,
 +                })
 +                .flatten()
 +                .map(|parent_manifest_path| LocationLink {
 +                    origin_selection_range: None,
 +                    target_uri: to_proto::url_from_abs_path(&parent_manifest_path),
 +                    target_range: Range::default(),
 +                    target_selection_range: Range::default(),
 +                })
 +                .collect::<_>();
 +            return Ok(Some(links.into()));
 +        }
 +
 +        // check if invoked at the crate root
 +        let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +        let crate_id = match snap.analysis.crate_for(file_id)?.first() {
 +            Some(&crate_id) => crate_id,
 +            None => return Ok(None),
 +        };
 +        let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
 +            Some(it) => it,
 +            None => return Ok(None),
 +        };
 +
 +        if snap.analysis.crate_root(crate_id)? == file_id {
 +            let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
 +            let res = vec![LocationLink {
 +                origin_selection_range: None,
 +                target_uri: cargo_toml_url,
 +                target_range: Range::default(),
 +                target_selection_range: Range::default(),
 +            }]
 +            .into();
 +            return Ok(Some(res));
 +        }
 +    }
 +
 +    // locate parent module by semantics
 +    let position = from_proto::file_position(&snap, params)?;
 +    let navs = snap.analysis.parent_module(position)?;
 +    let res = to_proto::goto_definition_response(&snap, None, navs)?;
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_runnables(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::RunnablesParams,
 +) -> Result<Vec<lsp_ext::Runnable>> {
 +    let _p = profile::span("handle_runnables");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
 +    let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
 +
 +    let expect_test = match offset {
 +        Some(offset) => {
 +            let source_file = snap.analysis.parse(file_id)?;
 +            algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
 +                .and_then(|it| it.path()?.segment()?.name_ref())
 +                .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
 +        }
 +        None => false,
 +    };
 +
 +    let mut res = Vec::new();
 +    for runnable in snap.analysis.runnables(file_id)? {
 +        if should_skip_for_offset(&runnable, offset) {
 +            continue;
 +        }
 +        if should_skip_target(&runnable, cargo_spec.as_ref()) {
 +            continue;
 +        }
 +        let mut runnable = to_proto::runnable(&snap, runnable)?;
 +        if expect_test {
 +            runnable.label = format!("{} + expect", runnable.label);
 +            runnable.args.expect_test = Some(true);
 +        }
 +        res.push(runnable);
 +    }
 +
 +    // Add `cargo check` and `cargo test` for all targets of the whole package
 +    let config = snap.config.runnables();
 +    match cargo_spec {
 +        Some(spec) => {
 +            for cmd in ["check", "test"] {
 +                res.push(lsp_ext::Runnable {
 +                    label: format!("cargo {} -p {} --all-targets", cmd, spec.package),
 +                    location: None,
 +                    kind: lsp_ext::RunnableKind::Cargo,
 +                    args: lsp_ext::CargoRunnable {
 +                        workspace_root: Some(spec.workspace_root.clone().into()),
 +                        override_cargo: config.override_cargo.clone(),
 +                        cargo_args: vec![
 +                            cmd.to_string(),
 +                            "--package".to_string(),
 +                            spec.package.clone(),
 +                            "--all-targets".to_string(),
 +                        ],
 +                        cargo_extra_args: config.cargo_extra_args.clone(),
 +                        executable_args: Vec::new(),
 +                        expect_test: None,
 +                    },
 +                })
 +            }
 +        }
 +        None => {
 +            if !snap.config.linked_projects().is_empty()
 +                || !snap
 +                    .config
 +                    .discovered_projects
 +                    .as_ref()
 +                    .map(|projects| projects.is_empty())
 +                    .unwrap_or(true)
 +            {
 +                res.push(lsp_ext::Runnable {
 +                    label: "cargo check --workspace".to_string(),
 +                    location: None,
 +                    kind: lsp_ext::RunnableKind::Cargo,
 +                    args: lsp_ext::CargoRunnable {
 +                        workspace_root: None,
 +                        override_cargo: config.override_cargo,
 +                        cargo_args: vec!["check".to_string(), "--workspace".to_string()],
 +                        cargo_extra_args: config.cargo_extra_args,
 +                        executable_args: Vec::new(),
 +                        expect_test: None,
 +                    },
 +                });
 +            }
 +        }
 +    }
 +    Ok(res)
 +}
 +
 +fn should_skip_for_offset(runnable: &Runnable, offset: Option<TextSize>) -> bool {
 +    match offset {
 +        None => false,
 +        _ if matches!(&runnable.kind, RunnableKind::TestMod { .. }) => false,
 +        Some(offset) => !runnable.nav.full_range.contains_inclusive(offset),
 +    }
 +}
 +
 +pub(crate) fn handle_related_tests(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<Vec<lsp_ext::TestInfo>> {
 +    let _p = profile::span("handle_related_tests");
 +    let position = from_proto::file_position(&snap, params)?;
 +
 +    let tests = snap.analysis.related_tests(position, None)?;
 +    let mut res = Vec::new();
 +    for it in tests {
 +        if let Ok(runnable) = to_proto::runnable(&snap, it) {
 +            res.push(lsp_ext::TestInfo { runnable })
 +        }
 +    }
 +
 +    Ok(res)
 +}
 +
 +pub(crate) fn handle_completion(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::CompletionParams,
 +) -> Result<Option<lsp_types::CompletionResponse>> {
 +    let _p = profile::span("handle_completion");
 +    let text_document_position = params.text_document_position.clone();
 +    let position = from_proto::file_position(&snap, params.text_document_position)?;
 +    let completion_trigger_character =
 +        params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
 +
 +    if Some(':') == completion_trigger_character {
 +        let source_file = snap.analysis.parse(position.file_id)?;
 +        let left_token = source_file.syntax().token_at_offset(position.offset).left_biased();
 +        let completion_triggered_after_single_colon = match left_token {
 +            Some(left_token) => left_token.kind() == T![:],
 +            None => true,
 +        };
 +        if completion_triggered_after_single_colon {
 +            return Ok(None);
 +        }
 +    }
 +
 +    let completion_config = &snap.config.completion();
 +    let items = match snap.analysis.completions(
 +        completion_config,
 +        position,
 +        completion_trigger_character,
 +    )? {
 +        None => return Ok(None),
 +        Some(items) => items,
 +    };
 +    let line_index = snap.file_line_index(position.file_id)?;
 +
 +    let items =
 +        to_proto::completion_items(&snap.config, &line_index, text_document_position, items);
 +
 +    let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
 +    Ok(Some(completion_list.into()))
 +}
 +
 +pub(crate) fn handle_completion_resolve(
 +    snap: GlobalStateSnapshot,
 +    mut original_completion: CompletionItem,
 +) -> Result<CompletionItem> {
 +    let _p = profile::span("handle_completion_resolve");
 +
 +    if !all_edits_are_disjoint(&original_completion, &[]) {
 +        return Err(invalid_params_error(
 +            "Received a completion with overlapping edits, this is not LSP-compliant".to_string(),
 +        )
 +        .into());
 +    }
 +
 +    let data = match original_completion.data.take() {
 +        Some(it) => it,
 +        None => return Ok(original_completion),
 +    };
 +
 +    let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?;
 +
 +    let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let offset = from_proto::offset(&line_index, resolve_data.position.position)?;
 +
 +    let additional_edits = snap
 +        .analysis
 +        .resolve_completion_edits(
 +            &snap.config.completion(),
 +            FilePosition { file_id, offset },
 +            resolve_data
 +                .imports
 +                .into_iter()
 +                .map(|import| (import.full_import_path, import.imported_name)),
 +        )?
 +        .into_iter()
 +        .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
 +        .collect::<Vec<_>>();
 +
 +    if !all_edits_are_disjoint(&original_completion, &additional_edits) {
 +        return Err(LspError::new(
 +            ErrorCode::InternalError as i32,
 +            "Import edit overlaps with the original completion edits, this is not LSP-compliant"
 +                .into(),
 +        )
 +        .into());
 +    }
 +
 +    if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
 +        original_additional_edits.extend(additional_edits.into_iter())
 +    } else {
 +        original_completion.additional_text_edits = Some(additional_edits);
 +    }
 +
 +    Ok(original_completion)
 +}
 +
 +pub(crate) fn handle_folding_range(
 +    snap: GlobalStateSnapshot,
 +    params: FoldingRangeParams,
 +) -> Result<Option<Vec<FoldingRange>>> {
 +    let _p = profile::span("handle_folding_range");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let folds = snap.analysis.folding_ranges(file_id)?;
 +    let text = snap.analysis.file_text(file_id)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let line_folding_only = snap.config.line_folding_only();
 +    let res = folds
 +        .into_iter()
 +        .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
 +        .collect();
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_signature_help(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::SignatureHelpParams,
 +) -> Result<Option<lsp_types::SignatureHelp>> {
 +    let _p = profile::span("handle_signature_help");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +    let help = match snap.analysis.signature_help(position)? {
 +        Some(it) => it,
 +        None => return Ok(None),
 +    };
 +    let config = snap.config.call_info();
 +    let res = to_proto::signature_help(help, config, snap.config.signature_help_label_offsets());
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_hover(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::HoverParams,
 +) -> Result<Option<lsp_ext::Hover>> {
 +    let _p = profile::span("handle_hover");
 +    let range = match params.position {
 +        PositionOrRange::Position(position) => Range::new(position, position),
 +        PositionOrRange::Range(range) => range,
 +    };
 +
 +    let file_range = from_proto::file_range(&snap, params.text_document, range)?;
 +    let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
 +        None => return Ok(None),
 +        Some(info) => info,
 +    };
 +
 +    let line_index = snap.file_line_index(file_range.file_id)?;
 +    let range = to_proto::range(&line_index, info.range);
 +    let markup_kind =
 +        snap.config.hover().documentation.map_or(ide::HoverDocFormat::Markdown, |kind| kind);
 +    let hover = lsp_ext::Hover {
 +        hover: lsp_types::Hover {
 +            contents: HoverContents::Markup(to_proto::markup_content(
 +                info.info.markup,
 +                markup_kind,
 +            )),
 +            range: Some(range),
 +        },
 +        actions: if snap.config.hover_actions().none() {
 +            Vec::new()
 +        } else {
 +            prepare_hover_actions(&snap, &info.info.actions)
 +        },
 +    };
 +
 +    Ok(Some(hover))
 +}
 +
 +pub(crate) fn handle_prepare_rename(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<Option<PrepareRenameResponse>> {
 +    let _p = profile::span("handle_prepare_rename");
 +    let position = from_proto::file_position(&snap, params)?;
 +
 +    let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
 +
 +    let line_index = snap.file_line_index(position.file_id)?;
 +    let range = to_proto::range(&line_index, change.range);
 +    Ok(Some(PrepareRenameResponse::Range(range)))
 +}
 +
 +pub(crate) fn handle_rename(
 +    snap: GlobalStateSnapshot,
 +    params: RenameParams,
 +) -> Result<Option<WorkspaceEdit>> {
 +    let _p = profile::span("handle_rename");
 +    let position = from_proto::file_position(&snap, params.text_document_position)?;
 +
 +    let mut change =
 +        snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
 +
 +    // this is kind of a hack to prevent double edits from happening when moving files
 +    // When a module gets renamed by renaming the mod declaration this causes the file to move
 +    // which in turn will trigger a WillRenameFiles request to the server for which we reply with a
 +    // a second identical set of renames, the client will then apply both edits causing incorrect edits
 +    // with this we only emit source_file_edits in the WillRenameFiles response which will do the rename instead
 +    // See https://github.com/microsoft/vscode-languageserver-node/issues/752 for more info
 +    if !change.file_system_edits.is_empty() && snap.config.will_rename() {
 +        change.source_file_edits.clear();
 +    }
 +    let workspace_edit = to_proto::workspace_edit(&snap, change)?;
 +    Ok(Some(workspace_edit))
 +}
 +
 +pub(crate) fn handle_references(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::ReferenceParams,
 +) -> Result<Option<Vec<Location>>> {
 +    let _p = profile::span("handle_references");
 +    let position = from_proto::file_position(&snap, params.text_document_position)?;
 +
 +    let refs = match snap.analysis.find_all_refs(position, None)? {
 +        None => return Ok(None),
 +        Some(refs) => refs,
 +    };
 +
 +    let include_declaration = params.context.include_declaration;
 +    let locations = refs
 +        .into_iter()
 +        .flat_map(|refs| {
 +            let decl = if include_declaration {
 +                refs.declaration.map(|decl| FileRange {
 +                    file_id: decl.nav.file_id,
 +                    range: decl.nav.focus_or_full_range(),
 +                })
 +            } else {
 +                None
 +            };
 +            refs.references
 +                .into_iter()
 +                .flat_map(|(file_id, refs)| {
 +                    refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
 +                })
 +                .chain(decl)
 +        })
 +        .filter_map(|frange| to_proto::location(&snap, frange).ok())
 +        .collect();
 +
 +    Ok(Some(locations))
 +}
 +
 +pub(crate) fn handle_formatting(
 +    snap: GlobalStateSnapshot,
 +    params: DocumentFormattingParams,
 +) -> Result<Option<Vec<lsp_types::TextEdit>>> {
 +    let _p = profile::span("handle_formatting");
 +
 +    run_rustfmt(&snap, params.text_document, None)
 +}
 +
 +pub(crate) fn handle_range_formatting(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::DocumentRangeFormattingParams,
 +) -> Result<Option<Vec<lsp_types::TextEdit>>> {
 +    let _p = profile::span("handle_range_formatting");
 +
 +    run_rustfmt(&snap, params.text_document, Some(params.range))
 +}
 +
 +pub(crate) fn handle_code_action(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::CodeActionParams,
 +) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
 +    let _p = profile::span("handle_code_action");
 +
 +    if !snap.config.code_action_literals() {
 +        // We intentionally don't support command-based actions, as those either
 +        // require either custom client-code or server-initiated edits. Server
 +        // initiated edits break causality, so we avoid those.
 +        return Ok(None);
 +    }
 +
 +    let line_index =
 +        snap.file_line_index(from_proto::file_id(&snap, &params.text_document.uri)?)?;
 +    let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?;
 +
 +    let mut assists_config = snap.config.assist();
 +    assists_config.allowed = params
 +        .context
 +        .only
 +        .clone()
 +        .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
 +
 +    let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
 +
 +    let code_action_resolve_cap = snap.config.code_action_resolve();
 +    let resolve = if code_action_resolve_cap {
 +        AssistResolveStrategy::None
 +    } else {
 +        AssistResolveStrategy::All
 +    };
 +    let assists = snap.analysis.assists_with_fixes(
 +        &assists_config,
 +        &snap.config.diagnostics(),
 +        resolve,
 +        frange,
 +    )?;
 +    for (index, assist) in assists.into_iter().enumerate() {
 +        let resolve_data =
 +            if code_action_resolve_cap { Some((index, params.clone())) } else { None };
 +        let code_action = to_proto::code_action(&snap, assist, resolve_data)?;
 +        res.push(code_action)
 +    }
 +
 +    // Fixes from `cargo check`.
 +    for fix in
 +        snap.check_fixes.values().filter_map(|it| it.get(&frange.file_id)).into_iter().flatten()
 +    {
 +        // FIXME: this mapping is awkward and shouldn't exist. Refactor
 +        // `snap.check_fixes` to not convert to LSP prematurely.
 +        let intersect_fix_range = fix
 +            .ranges
 +            .iter()
 +            .copied()
 +            .filter_map(|range| from_proto::text_range(&line_index, range).ok())
 +            .any(|fix_range| fix_range.intersect(frange.range).is_some());
 +        if intersect_fix_range {
 +            res.push(fix.action.clone());
 +        }
 +    }
 +
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_code_action_resolve(
 +    snap: GlobalStateSnapshot,
 +    mut code_action: lsp_ext::CodeAction,
 +) -> Result<lsp_ext::CodeAction> {
 +    let _p = profile::span("handle_code_action_resolve");
 +    let params = match code_action.data.take() {
 +        Some(it) => it,
 +        None => return Err(invalid_params_error("code action without data".to_string()).into()),
 +    };
 +
 +    let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let range = from_proto::text_range(&line_index, params.code_action_params.range)?;
 +    let frange = FileRange { file_id, range };
 +
 +    let mut assists_config = snap.config.assist();
 +    assists_config.allowed = params
 +        .code_action_params
 +        .context
 +        .only
 +        .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
 +
 +    let (assist_index, assist_resolve) = match parse_action_id(&params.id) {
 +        Ok(parsed_data) => parsed_data,
 +        Err(e) => {
 +            return Err(invalid_params_error(format!(
 +                "Failed to parse action id string '{}': {}",
 +                params.id, e
 +            ))
 +            .into())
 +        }
 +    };
 +
 +    let expected_assist_id = assist_resolve.assist_id.clone();
 +    let expected_kind = assist_resolve.assist_kind;
 +
 +    let assists = snap.analysis.assists_with_fixes(
 +        &assists_config,
 +        &snap.config.diagnostics(),
 +        AssistResolveStrategy::Single(assist_resolve),
 +        frange,
 +    )?;
 +
 +    let assist = match assists.get(assist_index) {
 +        Some(assist) => assist,
 +        None => return Err(invalid_params_error(format!(
 +            "Failed to find the assist for index {} provided by the resolve request. Resolve request assist id: {}",
 +            assist_index, params.id,
 +        ))
 +        .into())
 +    };
 +    if assist.id.0 != expected_assist_id || assist.id.1 != expected_kind {
 +        return Err(invalid_params_error(format!(
 +            "Mismatching assist at index {} for the resolve parameters given. Resolve request assist id: {}, actual id: {:?}.",
 +            assist_index, params.id, assist.id
 +        ))
 +        .into());
 +    }
 +    let ca = to_proto::code_action(&snap, assist.clone(), None)?;
 +    code_action.edit = ca.edit;
 +    code_action.command = ca.command;
 +    Ok(code_action)
 +}
 +
 +fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> {
 +    let id_parts = action_id.split(':').collect::<Vec<_>>();
 +    match id_parts.as_slice() {
 +        [assist_id_string, assist_kind_string, index_string] => {
 +            let assist_kind: AssistKind = assist_kind_string.parse()?;
 +            let index: usize = match index_string.parse() {
 +                Ok(index) => index,
 +                Err(e) => return Err(format!("Incorrect index string: {}", e)),
 +            };
 +            Ok((index, SingleResolve { assist_id: assist_id_string.to_string(), assist_kind }))
 +        }
 +        _ => Err("Action id contains incorrect number of segments".to_string()),
 +    }
 +}
 +
 +pub(crate) fn handle_code_lens(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::CodeLensParams,
 +) -> Result<Option<Vec<CodeLens>>> {
 +    let _p = profile::span("handle_code_lens");
 +
 +    let lens_config = snap.config.lens();
 +    if lens_config.none() {
 +        // early return before any db query!
 +        return Ok(Some(Vec::default()));
 +    }
 +
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
 +
 +    let annotations = snap.analysis.annotations(
 +        &AnnotationConfig {
 +            binary_target: cargo_target_spec
 +                .map(|spec| {
 +                    matches!(
 +                        spec.target_kind,
 +                        TargetKind::Bin | TargetKind::Example | TargetKind::Test
 +                    )
 +                })
 +                .unwrap_or(false),
 +            annotate_runnables: lens_config.runnable(),
 +            annotate_impls: lens_config.implementations,
 +            annotate_references: lens_config.refs_adt,
 +            annotate_method_references: lens_config.method_refs,
 +            annotate_enum_variant_references: lens_config.enum_variant_refs,
 +        },
 +        file_id,
 +    )?;
 +
 +    let mut res = Vec::new();
 +    for a in annotations {
 +        to_proto::code_lens(&mut res, &snap, a)?;
 +    }
 +
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_code_lens_resolve(
 +    snap: GlobalStateSnapshot,
 +    code_lens: CodeLens,
 +) -> Result<CodeLens> {
 +    let annotation = from_proto::annotation(&snap, code_lens.clone())?;
 +    let annotation = snap.analysis.resolve_annotation(annotation)?;
 +
 +    let mut acc = Vec::new();
 +    to_proto::code_lens(&mut acc, &snap, annotation)?;
 +
 +    let res = match acc.pop() {
 +        Some(it) if acc.is_empty() => it,
 +        _ => {
 +            never!();
 +            code_lens
 +        }
 +    };
 +
 +    Ok(res)
 +}
 +
 +pub(crate) fn handle_document_highlight(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::DocumentHighlightParams,
 +) -> Result<Option<Vec<lsp_types::DocumentHighlight>>> {
 +    let _p = profile::span("handle_document_highlight");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +    let line_index = snap.file_line_index(position.file_id)?;
 +
 +    let refs = match snap.analysis.highlight_related(snap.config.highlight_related(), position)? {
 +        None => return Ok(None),
 +        Some(refs) => refs,
 +    };
 +    let res = refs
 +        .into_iter()
 +        .map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight {
 +            range: to_proto::range(&line_index, range),
 +            kind: category.map(to_proto::document_highlight_kind),
 +        })
 +        .collect();
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_ssr(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::SsrParams,
 +) -> Result<lsp_types::WorkspaceEdit> {
 +    let _p = profile::span("handle_ssr");
 +    let selections = params
 +        .selections
 +        .iter()
 +        .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
 +        .collect::<Result<Vec<_>, _>>()?;
 +    let position = from_proto::file_position(&snap, params.position)?;
 +    let source_change = snap.analysis.structural_search_replace(
 +        &params.query,
 +        params.parse_only,
 +        position,
 +        selections,
 +    )??;
 +    to_proto::workspace_edit(&snap, source_change)
 +}
 +
 +pub(crate) fn publish_diagnostics(
 +    snap: &GlobalStateSnapshot,
 +    file_id: FileId,
 +) -> Result<Vec<Diagnostic>> {
 +    let _p = profile::span("publish_diagnostics");
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let diagnostics: Vec<Diagnostic> = snap
 +        .analysis
 +        .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)?
 +        .into_iter()
 +        .map(|d| Diagnostic {
 +            range: to_proto::range(&line_index, d.range),
 +            severity: Some(to_proto::diagnostic_severity(d.severity)),
 +            code: Some(NumberOrString::String(d.code.as_str().to_string())),
 +            code_description: Some(lsp_types::CodeDescription {
 +                href: lsp_types::Url::parse(&format!(
 +                    "https://rust-analyzer.github.io/manual.html#{}",
 +                    d.code.as_str()
 +                ))
 +                .unwrap(),
 +            }),
 +            source: Some("rust-analyzer".to_string()),
 +            message: d.message,
 +            related_information: None,
 +            tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None },
 +            data: None,
 +        })
 +        .collect();
 +    Ok(diagnostics)
 +}
 +
 +pub(crate) fn handle_inlay_hints(
 +    snap: GlobalStateSnapshot,
 +    params: InlayHintParams,
 +) -> Result<Option<Vec<InlayHint>>> {
 +    let _p = profile::span("handle_inlay_hints");
 +    let document_uri = &params.text_document.uri;
 +    let file_id = from_proto::file_id(&snap, document_uri)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +    let range = from_proto::file_range(
 +        &snap,
 +        TextDocumentIdentifier::new(document_uri.to_owned()),
 +        params.range,
 +    )?;
 +    let inlay_hints_config = snap.config.inlay_hints();
 +    Ok(Some(
 +        snap.analysis
 +            .inlay_hints(&inlay_hints_config, file_id, Some(range))?
 +            .into_iter()
 +            .map(|it| {
 +                to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
 +            })
++            .collect::<Result<Vec<_>>>()?,
 +    ))
 +}
 +
 +pub(crate) fn handle_inlay_hints_resolve(
 +    snap: GlobalStateSnapshot,
 +    mut hint: InlayHint,
 +) -> Result<InlayHint> {
 +    let _p = profile::span("handle_inlay_hints_resolve");
 +    let data = match hint.data.take() {
 +        Some(it) => it,
 +        None => return Ok(hint),
 +    };
 +
 +    let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
 +
 +    let file_range = from_proto::file_range(
 +        &snap,
 +        resolve_data.text_document,
 +        match resolve_data.position {
 +            PositionOrRange::Position(pos) => Range::new(pos, pos),
 +            PositionOrRange::Range(range) => range,
 +        },
 +    )?;
 +    let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
 +        None => return Ok(hint),
 +        Some(info) => info,
 +    };
 +
 +    let markup_kind =
 +        snap.config.hover().documentation.map_or(ide::HoverDocFormat::Markdown, |kind| kind);
 +
 +    // FIXME: hover actions?
 +    hint.tooltip = Some(lsp_types::InlayHintTooltip::MarkupContent(to_proto::markup_content(
 +        info.info.markup,
 +        markup_kind,
 +    )));
 +    Ok(hint)
 +}
 +
 +pub(crate) fn handle_call_hierarchy_prepare(
 +    snap: GlobalStateSnapshot,
 +    params: CallHierarchyPrepareParams,
 +) -> Result<Option<Vec<CallHierarchyItem>>> {
 +    let _p = profile::span("handle_call_hierarchy_prepare");
 +    let position = from_proto::file_position(&snap, params.text_document_position_params)?;
 +
 +    let nav_info = match snap.analysis.call_hierarchy(position)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +
 +    let RangeInfo { range: _, info: navs } = nav_info;
 +    let res = navs
 +        .into_iter()
 +        .filter(|it| it.kind == Some(SymbolKind::Function))
 +        .map(|it| to_proto::call_hierarchy_item(&snap, it))
 +        .collect::<Result<Vec<_>>>()?;
 +
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_call_hierarchy_incoming(
 +    snap: GlobalStateSnapshot,
 +    params: CallHierarchyIncomingCallsParams,
 +) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
 +    let _p = profile::span("handle_call_hierarchy_incoming");
 +    let item = params.item;
 +
 +    let doc = TextDocumentIdentifier::new(item.uri);
 +    let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
 +    let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
 +
 +    let call_items = match snap.analysis.incoming_calls(fpos)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +
 +    let mut res = vec![];
 +
 +    for call_item in call_items.into_iter() {
 +        let file_id = call_item.target.file_id;
 +        let line_index = snap.file_line_index(file_id)?;
 +        let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
 +        res.push(CallHierarchyIncomingCall {
 +            from: item,
 +            from_ranges: call_item
 +                .ranges
 +                .into_iter()
 +                .map(|it| to_proto::range(&line_index, it))
 +                .collect(),
 +        });
 +    }
 +
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_call_hierarchy_outgoing(
 +    snap: GlobalStateSnapshot,
 +    params: CallHierarchyOutgoingCallsParams,
 +) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
 +    let _p = profile::span("handle_call_hierarchy_outgoing");
 +    let item = params.item;
 +
 +    let doc = TextDocumentIdentifier::new(item.uri);
 +    let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
 +    let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
 +
 +    let call_items = match snap.analysis.outgoing_calls(fpos)? {
 +        None => return Ok(None),
 +        Some(it) => it,
 +    };
 +
 +    let mut res = vec![];
 +
 +    for call_item in call_items.into_iter() {
 +        let file_id = call_item.target.file_id;
 +        let line_index = snap.file_line_index(file_id)?;
 +        let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
 +        res.push(CallHierarchyOutgoingCall {
 +            to: item,
 +            from_ranges: call_item
 +                .ranges
 +                .into_iter()
 +                .map(|it| to_proto::range(&line_index, it))
 +                .collect(),
 +        });
 +    }
 +
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_semantic_tokens_full(
 +    snap: GlobalStateSnapshot,
 +    params: SemanticTokensParams,
 +) -> Result<Option<SemanticTokensResult>> {
 +    let _p = profile::span("handle_semantic_tokens_full");
 +
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let text = snap.analysis.file_text(file_id)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let mut highlight_config = snap.config.highlighting_config();
 +    // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
 +    highlight_config.syntactic_name_ref_highlighting = !snap.proc_macros_loaded;
 +
 +    let highlights = snap.analysis.highlight(highlight_config, file_id)?;
 +    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 +
 +    // Unconditionally cache the tokens
 +    snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
 +
 +    Ok(Some(semantic_tokens.into()))
 +}
 +
 +pub(crate) fn handle_semantic_tokens_full_delta(
 +    snap: GlobalStateSnapshot,
 +    params: SemanticTokensDeltaParams,
 +) -> Result<Option<SemanticTokensFullDeltaResult>> {
 +    let _p = profile::span("handle_semantic_tokens_full_delta");
 +
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let text = snap.analysis.file_text(file_id)?;
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let mut highlight_config = snap.config.highlighting_config();
 +    // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
 +    highlight_config.syntactic_name_ref_highlighting = !snap.proc_macros_loaded;
 +
 +    let highlights = snap.analysis.highlight(highlight_config, file_id)?;
 +    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 +
 +    let mut cache = snap.semantic_tokens_cache.lock();
 +    let cached_tokens = cache.entry(params.text_document.uri).or_default();
 +
 +    if let Some(prev_id) = &cached_tokens.result_id {
 +        if *prev_id == params.previous_result_id {
 +            let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
 +            *cached_tokens = semantic_tokens;
 +            return Ok(Some(delta.into()));
 +        }
 +    }
 +
 +    *cached_tokens = semantic_tokens.clone();
 +
 +    Ok(Some(semantic_tokens.into()))
 +}
 +
 +pub(crate) fn handle_semantic_tokens_range(
 +    snap: GlobalStateSnapshot,
 +    params: SemanticTokensRangeParams,
 +) -> Result<Option<SemanticTokensRangeResult>> {
 +    let _p = profile::span("handle_semantic_tokens_range");
 +
 +    let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
 +    let text = snap.analysis.file_text(frange.file_id)?;
 +    let line_index = snap.file_line_index(frange.file_id)?;
 +
 +    let highlights = snap.analysis.highlight_range(snap.config.highlighting_config(), frange)?;
 +    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 +    Ok(Some(semantic_tokens.into()))
 +}
 +
 +pub(crate) fn handle_open_docs(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_types::TextDocumentPositionParams,
 +) -> Result<Option<lsp_types::Url>> {
 +    let _p = profile::span("handle_open_docs");
 +    let position = from_proto::file_position(&snap, params)?;
 +
 +    let remote = snap.analysis.external_docs(position)?;
 +
 +    Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
 +}
 +
 +pub(crate) fn handle_open_cargo_toml(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::OpenCargoTomlParams,
 +) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
 +    let _p = profile::span("handle_open_cargo_toml");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +
 +    let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
 +        Some(it) => it,
 +        None => return Ok(None),
 +    };
 +
 +    let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
 +    let res: lsp_types::GotoDefinitionResponse =
 +        Location::new(cargo_toml_url, Range::default()).into();
 +    Ok(Some(res))
 +}
 +
 +pub(crate) fn handle_move_item(
 +    snap: GlobalStateSnapshot,
 +    params: lsp_ext::MoveItemParams,
 +) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
 +    let _p = profile::span("handle_move_item");
 +    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
 +    let range = from_proto::file_range(&snap, params.text_document, params.range)?;
 +
 +    let direction = match params.direction {
 +        lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
 +        lsp_ext::MoveItemDirection::Down => ide::Direction::Down,
 +    };
 +
 +    match snap.analysis.move_item(range, direction)? {
 +        Some(text_edit) => {
 +            let line_index = snap.file_line_index(file_id)?;
 +            Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
 +        }
 +        None => Ok(vec![]),
 +    }
 +}
 +
 +fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
 +    lsp_ext::CommandLink { tooltip: Some(tooltip), command }
 +}
 +
 +fn show_impl_command_link(
 +    snap: &GlobalStateSnapshot,
 +    position: &FilePosition,
 +) -> Option<lsp_ext::CommandLinkGroup> {
 +    if snap.config.hover_actions().implementations && snap.config.client_commands().show_reference {
 +        if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
 +            let uri = to_proto::url(snap, position.file_id);
 +            let line_index = snap.file_line_index(position.file_id).ok()?;
 +            let position = to_proto::position(&line_index, position.offset);
 +            let locations: Vec<_> = nav_data
 +                .info
 +                .into_iter()
 +                .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
 +                .collect();
 +            let title = to_proto::implementation_title(locations.len());
 +            let command = to_proto::command::show_references(title, &uri, position, locations);
 +
 +            return Some(lsp_ext::CommandLinkGroup {
 +                commands: vec![to_command_link(command, "Go to implementations".into())],
 +                ..Default::default()
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +fn show_ref_command_link(
 +    snap: &GlobalStateSnapshot,
 +    position: &FilePosition,
 +) -> Option<lsp_ext::CommandLinkGroup> {
 +    if snap.config.hover_actions().references && snap.config.client_commands().show_reference {
 +        if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
 +            let uri = to_proto::url(snap, position.file_id);
 +            let line_index = snap.file_line_index(position.file_id).ok()?;
 +            let position = to_proto::position(&line_index, position.offset);
 +            let locations: Vec<_> = ref_search_res
 +                .into_iter()
 +                .flat_map(|res| res.references)
 +                .flat_map(|(file_id, ranges)| {
 +                    ranges.into_iter().filter_map(move |(range, _)| {
 +                        to_proto::location(snap, FileRange { file_id, range }).ok()
 +                    })
 +                })
 +                .collect();
 +            let title = to_proto::reference_title(locations.len());
 +            let command = to_proto::command::show_references(title, &uri, position, locations);
 +
 +            return Some(lsp_ext::CommandLinkGroup {
 +                commands: vec![to_command_link(command, "Go to references".into())],
 +                ..Default::default()
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +fn runnable_action_links(
 +    snap: &GlobalStateSnapshot,
 +    runnable: Runnable,
 +) -> Option<lsp_ext::CommandLinkGroup> {
 +    let hover_actions_config = snap.config.hover_actions();
 +    if !hover_actions_config.runnable() {
 +        return None;
 +    }
 +
 +    let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
 +    if should_skip_target(&runnable, cargo_spec.as_ref()) {
 +        return None;
 +    }
 +
 +    let client_commands_config = snap.config.client_commands();
 +    if !(client_commands_config.run_single || client_commands_config.debug_single) {
 +        return None;
 +    }
 +
 +    let title = runnable.title();
 +    let r = to_proto::runnable(snap, runnable).ok()?;
 +
 +    let mut group = lsp_ext::CommandLinkGroup::default();
 +
 +    if hover_actions_config.run && client_commands_config.run_single {
 +        let run_command = to_proto::command::run_single(&r, &title);
 +        group.commands.push(to_command_link(run_command, r.label.clone()));
 +    }
 +
 +    if hover_actions_config.debug && client_commands_config.debug_single {
 +        let dbg_command = to_proto::command::debug_single(&r);
 +        group.commands.push(to_command_link(dbg_command, r.label));
 +    }
 +
 +    Some(group)
 +}
 +
 +fn goto_type_action_links(
 +    snap: &GlobalStateSnapshot,
 +    nav_targets: &[HoverGotoTypeData],
 +) -> Option<lsp_ext::CommandLinkGroup> {
 +    if !snap.config.hover_actions().goto_type_def
 +        || nav_targets.is_empty()
 +        || !snap.config.client_commands().goto_location
 +    {
 +        return None;
 +    }
 +
 +    Some(lsp_ext::CommandLinkGroup {
 +        title: Some("Go to ".into()),
 +        commands: nav_targets
 +            .iter()
 +            .filter_map(|it| {
 +                to_proto::command::goto_location(snap, &it.nav)
 +                    .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
 +            })
 +            .collect(),
 +    })
 +}
 +
 +fn prepare_hover_actions(
 +    snap: &GlobalStateSnapshot,
 +    actions: &[HoverAction],
 +) -> Vec<lsp_ext::CommandLinkGroup> {
 +    actions
 +        .iter()
 +        .filter_map(|it| match it {
 +            HoverAction::Implementation(position) => show_impl_command_link(snap, position),
 +            HoverAction::Reference(position) => show_ref_command_link(snap, position),
 +            HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()),
 +            HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
 +        })
 +        .collect()
 +}
 +
 +fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
 +    match runnable.kind {
 +        RunnableKind::Bin => {
 +            // Do not suggest binary run on other target than binary
 +            match &cargo_spec {
 +                Some(spec) => !matches!(
 +                    spec.target_kind,
 +                    TargetKind::Bin | TargetKind::Example | TargetKind::Test
 +                ),
 +                None => true,
 +            }
 +        }
 +        _ => false,
 +    }
 +}
 +
 +fn run_rustfmt(
 +    snap: &GlobalStateSnapshot,
 +    text_document: TextDocumentIdentifier,
 +    range: Option<lsp_types::Range>,
 +) -> Result<Option<Vec<lsp_types::TextEdit>>> {
 +    let file_id = from_proto::file_id(snap, &text_document.uri)?;
 +    let file = snap.analysis.file_text(file_id)?;
 +    let crate_ids = snap.analysis.crate_for(file_id)?;
 +
 +    let line_index = snap.file_line_index(file_id)?;
 +
 +    let mut command = match snap.config.rustfmt() {
 +        RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
 +            let mut cmd = process::Command::new(toolchain::rustfmt());
 +            cmd.args(extra_args);
 +            // try to chdir to the file so we can respect `rustfmt.toml`
 +            // FIXME: use `rustfmt --config-path` once
 +            // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
 +            match text_document.uri.to_file_path() {
 +                Ok(mut path) => {
 +                    // pop off file name
 +                    if path.pop() && path.is_dir() {
 +                        cmd.current_dir(path);
 +                    }
 +                }
 +                Err(_) => {
 +                    tracing::error!(
 +                        "Unable to get file path for {}, rustfmt.toml might be ignored",
 +                        text_document.uri
 +                    );
 +                }
 +            }
 +            if let Some(&crate_id) = crate_ids.first() {
 +                // Assume all crates are in the same edition
 +                let edition = snap.analysis.crate_edition(crate_id)?;
 +                cmd.arg("--edition");
 +                cmd.arg(edition.to_string());
 +            }
 +
 +            if let Some(range) = range {
 +                if !enable_range_formatting {
 +                    return Err(LspError::new(
 +                        ErrorCode::InvalidRequest as i32,
 +                        String::from(
 +                            "rustfmt range formatting is unstable. \
 +                            Opt-in by using a nightly build of rustfmt and setting \
 +                            `rustfmt.rangeFormatting.enable` to true in your LSP configuration",
 +                        ),
 +                    )
 +                    .into());
 +                }
 +
 +                let frange = from_proto::file_range(snap, text_document, range)?;
 +                let start_line = line_index.index.line_col(frange.range.start()).line;
 +                let end_line = line_index.index.line_col(frange.range.end()).line;
 +
 +                cmd.arg("--unstable-features");
 +                cmd.arg("--file-lines");
 +                cmd.arg(
 +                    json!([{
 +                        "file": "stdin",
 +                        "range": [start_line, end_line]
 +                    }])
 +                    .to_string(),
 +                );
 +            }
 +
 +            cmd
 +        }
 +        RustfmtConfig::CustomCommand { command, args } => {
 +            let mut cmd = process::Command::new(command);
 +            cmd.args(args);
 +            cmd
 +        }
 +    };
 +
 +    let mut rustfmt = command
 +        .stdin(Stdio::piped())
 +        .stdout(Stdio::piped())
 +        .stderr(Stdio::piped())
 +        .spawn()
 +        .context(format!("Failed to spawn {:?}", command))?;
 +
 +    rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
 +
 +    let output = rustfmt.wait_with_output()?;
 +    let captured_stdout = String::from_utf8(output.stdout)?;
 +    let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
 +
 +    if !output.status.success() {
 +        let rustfmt_not_installed =
 +            captured_stderr.contains("not installed") || captured_stderr.contains("not available");
 +
 +        return match output.status.code() {
 +            Some(1) if !rustfmt_not_installed => {
 +                // While `rustfmt` doesn't have a specific exit code for parse errors this is the
 +                // likely cause exiting with 1. Most Language Servers swallow parse errors on
 +                // formatting because otherwise an error is surfaced to the user on top of the
 +                // syntax error diagnostics they're already receiving. This is especially jarring
 +                // if they have format on save enabled.
 +                tracing::warn!(
 +                    ?command,
 +                    %captured_stderr,
 +                    "rustfmt exited with status 1"
 +                );
 +                Ok(None)
 +            }
 +            _ => {
 +                // Something else happened - e.g. `rustfmt` is missing or caught a signal
 +                Err(LspError::new(
 +                    -32900,
 +                    format!(
 +                        r#"rustfmt exited with:
 +                           Status: {}
 +                           stdout: {}
 +                           stderr: {}"#,
 +                        output.status, captured_stdout, captured_stderr,
 +                    ),
 +                )
 +                .into())
 +            }
 +        };
 +    }
 +
 +    let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
 +
 +    if line_index.endings != new_line_endings {
 +        // If line endings are different, send the entire file.
 +        // Diffing would not work here, as the line endings might be the only
 +        // difference.
 +        Ok(Some(to_proto::text_edit_vec(
 +            &line_index,
 +            TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
 +        )))
 +    } else if *file == new_text {
 +        // The document is already formatted correctly -- no edits needed.
 +        Ok(None)
 +    } else {
 +        Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
 +    }
 +}
index f23bbca6387659aa24d32e97036154883bea9a33,0000000000000000000000000000000000000000..e47f70fff39e0a3a48a07947d1b925a4c8ad2282
mode 100644,000000..100644
--- /dev/null
@@@ -1,722 -1,0 +1,724 @@@
-                         if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
 +//! Project loading & configuration updates.
 +//!
 +//! This is quite tricky. The main problem is time and changes -- there's no
 +//! fixed "project" rust-analyzer is working with, "current project" is itself
 +//! mutable state. For example, when the user edits `Cargo.toml` by adding a new
 +//! dependency, project model changes. What's more, switching project model is
 +//! not instantaneous -- it takes time to run `cargo metadata` and (for proc
 +//! macros) `cargo check`.
 +//!
 +//! The main guiding principle here is, as elsewhere in rust-analyzer,
 +//! robustness. We try not to assume that the project model exists or is
 +//! correct. Instead, we try to provide a best-effort service. Even if the
 +//! project is currently loading and we don't have a full project model, we
 +//! still want to respond to various  requests.
 +use std::{mem, sync::Arc};
 +
 +use flycheck::{FlycheckConfig, FlycheckHandle};
 +use hir::db::DefDatabase;
 +use ide::Change;
 +use ide_db::base_db::{
 +    CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
 +    ProcMacroLoadResult, SourceRoot, VfsPath,
 +};
 +use proc_macro_api::{MacroDylib, ProcMacroServer};
 +use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
 +use syntax::SmolStr;
 +use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
 +
 +use crate::{
 +    config::{Config, FilesWatcher, LinkedProject},
 +    global_state::GlobalState,
 +    lsp_ext,
 +    main_loop::Task,
 +    op_queue::Cause,
 +};
 +
 +#[derive(Debug)]
 +pub(crate) enum ProjectWorkspaceProgress {
 +    Begin,
 +    Report(String),
 +    End(Vec<anyhow::Result<ProjectWorkspace>>),
 +}
 +
 +#[derive(Debug)]
 +pub(crate) enum BuildDataProgress {
 +    Begin,
 +    Report(String),
 +    End((Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)),
 +}
 +
 +impl GlobalState {
 +    pub(crate) fn is_quiescent(&self) -> bool {
 +        !(self.fetch_workspaces_queue.op_in_progress()
 +            || self.fetch_build_data_queue.op_in_progress()
 +            || self.vfs_progress_config_version < self.vfs_config_version
 +            || self.vfs_progress_n_done < self.vfs_progress_n_total)
 +    }
 +
 +    pub(crate) fn update_configuration(&mut self, config: Config) {
 +        let _p = profile::span("GlobalState::update_configuration");
 +        let old_config = mem::replace(&mut self.config, Arc::new(config));
 +        if self.config.lru_capacity() != old_config.lru_capacity() {
 +            self.analysis_host.update_lru_capacity(self.config.lru_capacity());
 +        }
 +        if self.config.linked_projects() != old_config.linked_projects() {
 +            self.fetch_workspaces_queue.request_op("linked projects changed".to_string())
 +        } else if self.config.flycheck() != old_config.flycheck() {
 +            self.reload_flycheck();
 +        }
 +
 +        if self.analysis_host.raw_database().enable_proc_attr_macros()
 +            != self.config.expand_proc_attr_macros()
 +        {
 +            self.analysis_host
 +                .raw_database_mut()
 +                .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
 +        }
 +    }
 +
 +    pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
 +        let mut status = lsp_ext::ServerStatusParams {
 +            health: lsp_ext::Health::Ok,
 +            quiescent: self.is_quiescent(),
 +            message: None,
 +        };
 +
 +        if self.proc_macro_changed {
 +            status.health = lsp_ext::Health::Warning;
 +            status.message =
 +                Some("Reload required due to source changes of a procedural macro.".into())
 +        }
 +        if let Err(_) = self.fetch_build_data_error() {
 +            status.health = lsp_ext::Health::Warning;
 +            status.message =
 +                Some("Failed to run build scripts of some packages, check the logs.".to_string());
 +        }
 +        if !self.config.cargo_autoreload()
 +            && self.is_quiescent()
 +            && self.fetch_workspaces_queue.op_requested()
 +        {
 +            status.health = lsp_ext::Health::Warning;
 +            status.message = Some("Workspace reload required".to_string())
 +        }
 +
 +        if let Err(error) = self.fetch_workspace_error() {
 +            status.health = lsp_ext::Health::Error;
 +            status.message = Some(error)
 +        }
 +        status
 +    }
 +
 +    pub(crate) fn fetch_workspaces(&mut self, cause: Cause) {
 +        tracing::info!(%cause, "will fetch workspaces");
 +
 +        self.task_pool.handle.spawn_with_sender({
 +            let linked_projects = self.config.linked_projects();
 +            let detached_files = self.config.detached_files().to_vec();
 +            let cargo_config = self.config.cargo();
 +
 +            move |sender| {
 +                let progress = {
 +                    let sender = sender.clone();
 +                    move |msg| {
 +                        sender
 +                            .send(Task::FetchWorkspace(ProjectWorkspaceProgress::Report(msg)))
 +                            .unwrap()
 +                    }
 +                };
 +
 +                sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();
 +
 +                let mut workspaces = linked_projects
 +                    .iter()
 +                    .map(|project| match project {
 +                        LinkedProject::ProjectManifest(manifest) => {
 +                            project_model::ProjectWorkspace::load(
 +                                manifest.clone(),
 +                                &cargo_config,
 +                                &progress,
 +                            )
 +                        }
 +                        LinkedProject::InlineJsonProject(it) => {
 +                            project_model::ProjectWorkspace::load_inline(
 +                                it.clone(),
 +                                cargo_config.target.as_deref(),
 +                            )
 +                        }
 +                    })
 +                    .collect::<Vec<_>>();
 +
 +                if !detached_files.is_empty() {
 +                    workspaces
 +                        .push(project_model::ProjectWorkspace::load_detached_files(detached_files));
 +                }
 +
 +                tracing::info!("did fetch workspaces {:?}", workspaces);
 +                sender
 +                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
 +                    .unwrap();
 +            }
 +        });
 +    }
 +
 +    pub(crate) fn fetch_build_data(&mut self, cause: Cause) {
 +        tracing::info!(%cause, "will fetch build data");
 +        let workspaces = Arc::clone(&self.workspaces);
 +        let config = self.config.cargo();
 +        self.task_pool.handle.spawn_with_sender(move |sender| {
 +            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
 +
 +            let progress = {
 +                let sender = sender.clone();
 +                move |msg| {
 +                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
 +                }
 +            };
 +            let mut res = Vec::new();
 +            for ws in workspaces.iter() {
 +                res.push(ws.run_build_scripts(&config, &progress));
 +            }
 +            sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap();
 +        });
 +    }
 +
 +    pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
 +        let _p = profile::span("GlobalState::switch_workspaces");
 +        tracing::info!(%cause, "will switch workspaces");
 +
 +        if let Err(error_message) = self.fetch_workspace_error() {
 +            self.show_and_log_error(error_message, None);
 +            if !self.workspaces.is_empty() {
 +                // It only makes sense to switch to a partially broken workspace
 +                // if we don't have any workspace at all yet.
 +                return;
 +            }
 +        }
 +
 +        if let Err(error) = self.fetch_build_data_error() {
 +            self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
 +        }
 +
 +        let workspaces = self
 +            .fetch_workspaces_queue
 +            .last_op_result()
 +            .iter()
 +            .filter_map(|res| res.as_ref().ok().cloned())
 +            .collect::<Vec<_>>();
 +
 +        fn eq_ignore_build_data<'a>(
 +            left: &'a ProjectWorkspace,
 +            right: &'a ProjectWorkspace,
 +        ) -> bool {
 +            let key = |p: &'a ProjectWorkspace| match p {
 +                ProjectWorkspace::Cargo {
 +                    cargo,
 +                    sysroot,
 +                    rustc,
 +                    rustc_cfg,
 +                    cfg_overrides,
 +
 +                    build_scripts: _,
 +                    toolchain: _,
 +                } => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)),
 +                _ => None,
 +            };
 +            match (key(left), key(right)) {
 +                (Some(lk), Some(rk)) => lk == rk,
 +                _ => left == right,
 +            }
 +        }
 +
 +        let same_workspaces = workspaces.len() == self.workspaces.len()
 +            && workspaces
 +                .iter()
 +                .zip(self.workspaces.iter())
 +                .all(|(l, r)| eq_ignore_build_data(l, r));
 +
 +        if same_workspaces {
 +            let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
 +            if Arc::ptr_eq(workspaces, &self.workspaces) {
 +                tracing::debug!("set build scripts to workspaces");
 +
 +                let workspaces = workspaces
 +                    .iter()
 +                    .cloned()
 +                    .zip(build_scripts)
 +                    .map(|(mut ws, bs)| {
 +                        ws.set_build_scripts(bs.as_ref().ok().cloned().unwrap_or_default());
 +                        ws
 +                    })
 +                    .collect::<Vec<_>>();
 +
 +                // Workspaces are the same, but we've updated build data.
 +                self.workspaces = Arc::new(workspaces);
 +            } else {
 +                tracing::info!("build scripts do not match the version of the active workspace");
 +                // Current build scripts do not match the version of the active
 +                // workspace, so there's nothing for us to update.
 +                return;
 +            }
 +        } else {
 +            tracing::debug!("abandon build scripts for workspaces");
 +
 +            // Here, we completely changed the workspace (Cargo.toml edit), so
 +            // we don't care about build-script results, they are stale.
 +            self.workspaces = Arc::new(workspaces)
 +        }
 +
 +        if let FilesWatcher::Client = self.config.files().watcher {
 +            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
 +                watchers: self
 +                    .workspaces
 +                    .iter()
 +                    .flat_map(|ws| ws.to_roots())
 +                    .filter(|it| it.is_local)
 +                    .flat_map(|root| {
 +                        root.include.into_iter().flat_map(|it| {
 +                            [
 +                                format!("{}/**/*.rs", it.display()),
 +                                format!("{}/**/Cargo.toml", it.display()),
 +                                format!("{}/**/Cargo.lock", it.display()),
 +                            ]
 +                        })
 +                    })
 +                    .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
 +                    .collect(),
 +            };
 +            let registration = lsp_types::Registration {
 +                id: "workspace/didChangeWatchedFiles".to_string(),
 +                method: "workspace/didChangeWatchedFiles".to_string(),
 +                register_options: Some(serde_json::to_value(registration_options).unwrap()),
 +            };
 +            self.send_request::<lsp_types::request::RegisterCapability>(
 +                lsp_types::RegistrationParams { registrations: vec![registration] },
 +                |_, _| (),
 +            );
 +        }
 +
 +        let mut change = Change::new();
 +
 +        let files_config = self.config.files();
 +        let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
 +
 +        let standalone_server_name =
 +            format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
 +
 +        if self.proc_macro_clients.is_empty() {
 +            if let Some((path, args)) = self.config.proc_macro_srv() {
 +                tracing::info!("Spawning proc-macro servers");
 +                self.proc_macro_clients = self
 +                    .workspaces
 +                    .iter()
 +                    .map(|ws| {
 +                        let mut args = args.clone();
 +                        let mut path = path.clone();
 +
++                        if let ProjectWorkspace::Cargo { sysroot, .. }
++                        | ProjectWorkspace::Json { sysroot, .. } = ws
++                        {
 +                            tracing::debug!("Found a cargo workspace...");
 +                            if let Some(sysroot) = sysroot.as_ref() {
 +                                tracing::debug!("Found a cargo workspace with a sysroot...");
 +                                let server_path =
 +                                    sysroot.root().join("libexec").join(&standalone_server_name);
 +                                if std::fs::metadata(&server_path).is_ok() {
 +                                    tracing::debug!(
 +                                        "And the server exists at {}",
 +                                        server_path.display()
 +                                    );
 +                                    path = server_path;
 +                                    args = vec![];
 +                                } else {
 +                                    tracing::debug!(
 +                                        "And the server does not exist at {}",
 +                                        server_path.display()
 +                                    );
 +                                }
 +                            }
 +                        }
 +
 +                        tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
 +                        ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
 +                            let error = format!(
 +                                "Failed to run proc-macro server from path {}, error: {:?}",
 +                                path.display(),
 +                                err
 +                            );
 +                            tracing::error!(error);
 +                            error
 +                        })
 +                    })
 +                    .collect()
 +            };
 +        }
 +
 +        let watch = match files_config.watcher {
 +            FilesWatcher::Client => vec![],
 +            FilesWatcher::Server => project_folders.watch,
 +        };
 +        self.vfs_config_version += 1;
 +        self.loader.handle.set_config(vfs::loader::Config {
 +            load: project_folders.load,
 +            watch,
 +            version: self.vfs_config_version,
 +        });
 +
 +        // Create crate graph from all the workspaces
 +        let crate_graph = {
 +            let dummy_replacements = self.config.dummy_replacements();
 +
 +            let vfs = &mut self.vfs.write().0;
 +            let loader = &mut self.loader;
 +            let mem_docs = &self.mem_docs;
 +            let mut load = move |path: &AbsPath| {
 +                let _p = profile::span("GlobalState::load");
 +                let vfs_path = vfs::VfsPath::from(path.to_path_buf());
 +                if !mem_docs.contains(&vfs_path) {
 +                    let contents = loader.handle.load_sync(path);
 +                    vfs.set_file_contents(vfs_path.clone(), contents);
 +                }
 +                let res = vfs.file_id(&vfs_path);
 +                if res.is_none() {
 +                    tracing::warn!("failed to load {}", path.display())
 +                }
 +                res
 +            };
 +
 +            let mut crate_graph = CrateGraph::default();
 +            for (idx, ws) in self.workspaces.iter().enumerate() {
 +                let proc_macro_client = match self.proc_macro_clients.get(idx) {
 +                    Some(res) => res.as_ref().map_err(|e| &**e),
 +                    None => Err("Proc macros are disabled"),
 +                };
 +                let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| {
 +                    load_proc_macro(
 +                        proc_macro_client,
 +                        path,
 +                        dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
 +                    )
 +                };
 +                crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
 +            }
 +            crate_graph
 +        };
 +        change.set_crate_graph(crate_graph);
 +
 +        self.source_root_config = project_folders.source_root_config;
 +
 +        self.analysis_host.apply_change(change);
 +        self.process_changes();
 +        self.reload_flycheck();
 +        tracing::info!("did switch workspaces");
 +    }
 +
 +    fn fetch_workspace_error(&self) -> Result<(), String> {
 +        let mut buf = String::new();
 +
 +        for ws in self.fetch_workspaces_queue.last_op_result() {
 +            if let Err(err) = ws {
 +                stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
 +            }
 +        }
 +
 +        if buf.is_empty() {
 +            return Ok(());
 +        }
 +
 +        Err(buf)
 +    }
 +
 +    fn fetch_build_data_error(&self) -> Result<(), String> {
 +        let mut buf = String::new();
 +
 +        for ws in &self.fetch_build_data_queue.last_op_result().1 {
 +            match ws {
 +                Ok(data) => match data.error() {
 +                    Some(stderr) => stdx::format_to!(buf, "{:#}\n", stderr),
 +                    _ => (),
 +                },
 +                // io errors
 +                Err(err) => stdx::format_to!(buf, "{:#}\n", err),
 +            }
 +        }
 +
 +        if buf.is_empty() {
 +            Ok(())
 +        } else {
 +            Err(buf)
 +        }
 +    }
 +
 +    fn reload_flycheck(&mut self) {
 +        let _p = profile::span("GlobalState::reload_flycheck");
 +        let config = match self.config.flycheck() {
 +            Some(it) => it,
 +            None => {
 +                self.flycheck = Vec::new();
 +                self.diagnostics.clear_check_all();
 +                return;
 +            }
 +        };
 +
 +        let sender = self.flycheck_sender.clone();
 +        self.flycheck = self
 +            .workspaces
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(id, w)| match w {
 +                ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
 +                ProjectWorkspace::Json { project, .. } => {
 +                    // Enable flychecks for json projects if a custom flycheck command was supplied
 +                    // in the workspace configuration.
 +                    match config {
 +                        FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
 +                        _ => None,
 +                    }
 +                }
 +                ProjectWorkspace::DetachedFiles { .. } => None,
 +            })
 +            .map(|(id, root)| {
 +                let sender = sender.clone();
 +                FlycheckHandle::spawn(
 +                    id,
 +                    Box::new(move |msg| sender.send(msg).unwrap()),
 +                    config.clone(),
 +                    root.to_path_buf(),
 +                )
 +            })
 +            .collect();
 +    }
 +}
 +
 +#[derive(Default)]
 +pub(crate) struct ProjectFolders {
 +    pub(crate) load: Vec<vfs::loader::Entry>,
 +    pub(crate) watch: Vec<usize>,
 +    pub(crate) source_root_config: SourceRootConfig,
 +}
 +
 +impl ProjectFolders {
 +    pub(crate) fn new(
 +        workspaces: &[ProjectWorkspace],
 +        global_excludes: &[AbsPathBuf],
 +    ) -> ProjectFolders {
 +        let mut res = ProjectFolders::default();
 +        let mut fsc = FileSetConfig::builder();
 +        let mut local_filesets = vec![];
 +
 +        for root in workspaces.iter().flat_map(|ws| ws.to_roots()) {
 +            let file_set_roots: Vec<VfsPath> =
 +                root.include.iter().cloned().map(VfsPath::from).collect();
 +
 +            let entry = {
 +                let mut dirs = vfs::loader::Directories::default();
 +                dirs.extensions.push("rs".into());
 +                dirs.include.extend(root.include);
 +                dirs.exclude.extend(root.exclude);
 +                for excl in global_excludes {
 +                    if dirs
 +                        .include
 +                        .iter()
 +                        .any(|incl| incl.starts_with(excl) || excl.starts_with(incl))
 +                    {
 +                        dirs.exclude.push(excl.clone());
 +                    }
 +                }
 +
 +                vfs::loader::Entry::Directories(dirs)
 +            };
 +
 +            if root.is_local {
 +                res.watch.push(res.load.len());
 +            }
 +            res.load.push(entry);
 +
 +            if root.is_local {
 +                local_filesets.push(fsc.len());
 +            }
 +            fsc.add_file_set(file_set_roots)
 +        }
 +
 +        let fsc = fsc.build();
 +        res.source_root_config = SourceRootConfig { fsc, local_filesets };
 +
 +        res
 +    }
 +}
 +
 +#[derive(Default, Debug)]
 +pub(crate) struct SourceRootConfig {
 +    pub(crate) fsc: FileSetConfig,
 +    pub(crate) local_filesets: Vec<usize>,
 +}
 +
 +impl SourceRootConfig {
 +    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
 +        let _p = profile::span("SourceRootConfig::partition");
 +        self.fsc
 +            .partition(vfs)
 +            .into_iter()
 +            .enumerate()
 +            .map(|(idx, file_set)| {
 +                let is_local = self.local_filesets.contains(&idx);
 +                if is_local {
 +                    SourceRoot::new_local(file_set)
 +                } else {
 +                    SourceRoot::new_library(file_set)
 +                }
 +            })
 +            .collect()
 +    }
 +}
 +
 +/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
 +/// with an identity dummy expander.
 +pub(crate) fn load_proc_macro(
 +    server: Result<&ProcMacroServer, &str>,
 +    path: &AbsPath,
 +    dummy_replace: &[Box<str>],
 +) -> ProcMacroLoadResult {
 +    let res: Result<Vec<_>, String> = (|| {
 +        let dylib = MacroDylib::new(path.to_path_buf())
 +            .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
 +        let server = server.map_err(ToOwned::to_owned)?;
 +        let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
 +        if vec.is_empty() {
 +            return Err("proc macro library returned no proc macros".to_string());
 +        }
 +        Ok(vec
 +            .into_iter()
 +            .map(|expander| expander_to_proc_macro(expander, dummy_replace))
 +            .collect())
 +    })();
 +    return match res {
 +        Ok(proc_macros) => {
 +            tracing::info!(
 +                "Loaded proc-macros for {}: {:?}",
 +                path.display(),
 +                proc_macros.iter().map(|it| it.name.clone()).collect::<Vec<_>>()
 +            );
 +            Ok(proc_macros)
 +        }
 +        Err(e) => {
 +            tracing::warn!("proc-macro loading for {} failed: {e}", path.display());
 +            Err(e)
 +        }
 +    };
 +
 +    fn expander_to_proc_macro(
 +        expander: proc_macro_api::ProcMacro,
 +        dummy_replace: &[Box<str>],
 +    ) -> ProcMacro {
 +        let name = SmolStr::from(expander.name());
 +        let kind = match expander.kind() {
 +            proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive,
 +            proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
 +            proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
 +        };
 +        let expander: Arc<dyn ProcMacroExpander> =
 +            if dummy_replace.iter().any(|replace| &**replace == name) {
 +                match kind {
 +                    ProcMacroKind::Attr => Arc::new(IdentityExpander),
 +                    _ => Arc::new(EmptyExpander),
 +                }
 +            } else {
 +                Arc::new(Expander(expander))
 +            };
 +        ProcMacro { name, kind, expander }
 +    }
 +
 +    #[derive(Debug)]
 +    struct Expander(proc_macro_api::ProcMacro);
 +
 +    impl ProcMacroExpander for Expander {
 +        fn expand(
 +            &self,
 +            subtree: &tt::Subtree,
 +            attrs: Option<&tt::Subtree>,
 +            env: &Env,
 +        ) -> Result<tt::Subtree, ProcMacroExpansionError> {
 +            let env = env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect();
 +            match self.0.expand(subtree, attrs, env) {
 +                Ok(Ok(subtree)) => Ok(subtree),
 +                Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)),
 +                Err(err) => Err(ProcMacroExpansionError::System(err.to_string())),
 +            }
 +        }
 +    }
 +
 +    /// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user.
 +    #[derive(Debug)]
 +    struct IdentityExpander;
 +
 +    impl ProcMacroExpander for IdentityExpander {
 +        fn expand(
 +            &self,
 +            subtree: &tt::Subtree,
 +            _: Option<&tt::Subtree>,
 +            _: &Env,
 +        ) -> Result<tt::Subtree, ProcMacroExpansionError> {
 +            Ok(subtree.clone())
 +        }
 +    }
 +
 +    /// Empty expander, used for proc-macros that are deliberately ignored by the user.
 +    #[derive(Debug)]
 +    struct EmptyExpander;
 +
 +    impl ProcMacroExpander for EmptyExpander {
 +        fn expand(
 +            &self,
 +            _: &tt::Subtree,
 +            _: Option<&tt::Subtree>,
 +            _: &Env,
 +        ) -> Result<tt::Subtree, ProcMacroExpansionError> {
 +            Ok(tt::Subtree::default())
 +        }
 +    }
 +}
 +
 +pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
 +    const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
 +    const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
 +
 +    let file_name = match path.file_name().unwrap_or_default().to_str() {
 +        Some(it) => it,
 +        None => return false,
 +    };
 +
 +    if let "Cargo.toml" | "Cargo.lock" = file_name {
 +        return true;
 +    }
 +    if change_kind == ChangeKind::Modify {
 +        return false;
 +    }
 +
 +    // .cargo/config{.toml}
 +    if path.extension().unwrap_or_default() != "rs" {
 +        let is_cargo_config = matches!(file_name, "config.toml" | "config")
 +            && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false);
 +        return is_cargo_config;
 +    }
 +
 +    if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
 +        return true;
 +    }
 +    let parent = match path.parent() {
 +        Some(it) => it,
 +        None => return false,
 +    };
 +    if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) {
 +        return true;
 +    }
 +    if file_name == "main.rs" {
 +        let grand_parent = match parent.parent() {
 +            Some(it) => it,
 +            None => return false,
 +        };
 +        if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) {
 +            return true;
 +        }
 +    }
 +    false
 +}
index 102cd602950439a4188b5006116353dd164299f8,0000000000000000000000000000000000000000..e083b9d0e33789ae448a42bd698dcee79a46f6c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1394 -1,0 +1,1423 @@@
-     InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity,
-     SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
 +//! Conversion of rust-analyzer specific types to lsp_types equivalents.
 +use std::{
 +    iter::once,
 +    path,
 +    sync::atomic::{AtomicU32, Ordering},
 +};
 +
 +use ide::{
 +    Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
 +    CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
 +    Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
-     inlay_hint: InlayHint,
- ) -> lsp_types::InlayHint {
-     lsp_types::InlayHint {
++    InlayHintLabel, InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable,
++    Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange,
++    TextSize,
 +};
 +use itertools::Itertools;
 +use serde_json::to_value;
 +use vfs::AbsPath;
 +
 +use crate::{
 +    cargo_target_spec::CargoTargetSpec,
 +    config::{CallInfoConfig, Config},
 +    global_state::GlobalStateSnapshot,
 +    line_index::{LineEndings, LineIndex, OffsetEncoding},
 +    lsp_ext,
 +    lsp_utils::invalid_params_error,
 +    semantic_tokens, Result,
 +};
 +
 +pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
 +    let line_col = line_index.index.line_col(offset);
 +    match line_index.encoding {
 +        OffsetEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
 +        OffsetEncoding::Utf16 => {
 +            let line_col = line_index.index.to_utf16(line_col);
 +            lsp_types::Position::new(line_col.line, line_col.col)
 +        }
 +    }
 +}
 +
 +pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range {
 +    let start = position(line_index, range.start());
 +    let end = position(line_index, range.end());
 +    lsp_types::Range::new(start, end)
 +}
 +
 +pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
 +    match symbol_kind {
 +        SymbolKind::Function => lsp_types::SymbolKind::FUNCTION,
 +        SymbolKind::Struct => lsp_types::SymbolKind::STRUCT,
 +        SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
 +        SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
 +        SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
 +        SymbolKind::Macro
 +        | SymbolKind::BuiltinAttr
 +        | SymbolKind::Attribute
 +        | SymbolKind::Derive
 +        | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION,
 +        SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
 +        SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
 +            lsp_types::SymbolKind::TYPE_PARAMETER
 +        }
 +        SymbolKind::Field => lsp_types::SymbolKind::FIELD,
 +        SymbolKind::Static => lsp_types::SymbolKind::CONSTANT,
 +        SymbolKind::Const => lsp_types::SymbolKind::CONSTANT,
 +        SymbolKind::ConstParam => lsp_types::SymbolKind::CONSTANT,
 +        SymbolKind::Impl => lsp_types::SymbolKind::OBJECT,
 +        SymbolKind::Local
 +        | SymbolKind::SelfParam
 +        | SymbolKind::LifetimeParam
 +        | SymbolKind::ValueParam
 +        | SymbolKind::Label => lsp_types::SymbolKind::VARIABLE,
 +        SymbolKind::Union => lsp_types::SymbolKind::STRUCT,
 +    }
 +}
 +
 +pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolKind {
 +    match kind {
 +        StructureNodeKind::SymbolKind(symbol) => symbol_kind(symbol),
 +        StructureNodeKind::Region => lsp_types::SymbolKind::NAMESPACE,
 +    }
 +}
 +
 +pub(crate) fn document_highlight_kind(
 +    category: ReferenceCategory,
 +) -> lsp_types::DocumentHighlightKind {
 +    match category {
 +        ReferenceCategory::Read => lsp_types::DocumentHighlightKind::READ,
 +        ReferenceCategory::Write => lsp_types::DocumentHighlightKind::WRITE,
 +    }
 +}
 +
 +pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
 +    match severity {
 +        Severity::Error => lsp_types::DiagnosticSeverity::ERROR,
 +        Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT,
 +    }
 +}
 +
 +pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
 +    let value = crate::markdown::format_docs(documentation.as_str());
 +    let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
 +    lsp_types::Documentation::MarkupContent(markup_content)
 +}
 +
 +pub(crate) fn completion_item_kind(
 +    completion_item_kind: CompletionItemKind,
 +) -> lsp_types::CompletionItemKind {
 +    match completion_item_kind {
 +        CompletionItemKind::Binding => lsp_types::CompletionItemKind::VARIABLE,
 +        CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT,
 +        CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET,
 +        CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD,
 +        CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD,
 +        CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET,
 +        CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE,
 +        CompletionItemKind::SymbolKind(symbol) => match symbol {
 +            SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
 +            SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
 +            SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
 +            SymbolKind::Field => lsp_types::CompletionItemKind::FIELD,
 +            SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::Impl => lsp_types::CompletionItemKind::TEXT,
 +            SymbolKind::Label => lsp_types::CompletionItemKind::VARIABLE,
 +            SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
 +            SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
 +            SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
 +            SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
 +            SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
 +            SymbolKind::Static => lsp_types::CompletionItemKind::VALUE,
 +            SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT,
 +            SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE,
 +            SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT,
 +            SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
 +            SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
 +            SymbolKind::ValueParam => lsp_types::CompletionItemKind::VALUE,
 +            SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
 +            SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
 +            SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
 +        },
 +    }
 +}
 +
 +pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::TextEdit {
 +    let range = range(line_index, indel.delete);
 +    let new_text = match line_index.endings {
 +        LineEndings::Unix => indel.insert,
 +        LineEndings::Dos => indel.insert.replace('\n', "\r\n"),
 +    };
 +    lsp_types::TextEdit { range, new_text }
 +}
 +
 +pub(crate) fn completion_text_edit(
 +    line_index: &LineIndex,
 +    insert_replace_support: Option<lsp_types::Position>,
 +    indel: Indel,
 +) -> lsp_types::CompletionTextEdit {
 +    let text_edit = text_edit(line_index, indel);
 +    match insert_replace_support {
 +        Some(cursor_pos) => lsp_types::InsertReplaceEdit {
 +            new_text: text_edit.new_text,
 +            insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
 +            replace: text_edit.range,
 +        }
 +        .into(),
 +        None => text_edit.into(),
 +    }
 +}
 +
 +pub(crate) fn snippet_text_edit(
 +    line_index: &LineIndex,
 +    is_snippet: bool,
 +    indel: Indel,
 +) -> lsp_ext::SnippetTextEdit {
 +    let text_edit = text_edit(line_index, indel);
 +    let insert_text_format =
 +        if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) } else { None };
 +    lsp_ext::SnippetTextEdit {
 +        range: text_edit.range,
 +        new_text: text_edit.new_text,
 +        insert_text_format,
 +        annotation_id: None,
 +    }
 +}
 +
 +pub(crate) fn text_edit_vec(
 +    line_index: &LineIndex,
 +    text_edit: TextEdit,
 +) -> Vec<lsp_types::TextEdit> {
 +    text_edit.into_iter().map(|indel| self::text_edit(line_index, indel)).collect()
 +}
 +
 +pub(crate) fn snippet_text_edit_vec(
 +    line_index: &LineIndex,
 +    is_snippet: bool,
 +    text_edit: TextEdit,
 +) -> Vec<lsp_ext::SnippetTextEdit> {
 +    text_edit
 +        .into_iter()
 +        .map(|indel| self::snippet_text_edit(line_index, is_snippet, indel))
 +        .collect()
 +}
 +
 +pub(crate) fn completion_items(
 +    config: &Config,
 +    line_index: &LineIndex,
 +    tdpp: lsp_types::TextDocumentPositionParams,
 +    items: Vec<CompletionItem>,
 +) -> Vec<lsp_types::CompletionItem> {
 +    let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
 +    let mut res = Vec::with_capacity(items.len());
 +    for item in items {
 +        completion_item(&mut res, config, line_index, &tdpp, max_relevance, item)
 +    }
 +    res
 +}
 +
 +fn completion_item(
 +    acc: &mut Vec<lsp_types::CompletionItem>,
 +    config: &Config,
 +    line_index: &LineIndex,
 +    tdpp: &lsp_types::TextDocumentPositionParams,
 +    max_relevance: u32,
 +    item: CompletionItem,
 +) {
 +    let insert_replace_support = config.insert_replace_support().then(|| tdpp.position);
 +    let mut additional_text_edits = Vec::new();
 +
 +    // LSP does not allow arbitrary edits in completion, so we have to do a
 +    // non-trivial mapping here.
 +    let text_edit = {
 +        let mut text_edit = None;
 +        let source_range = item.source_range();
 +        for indel in item.text_edit().iter() {
 +            if indel.delete.contains_range(source_range) {
 +                text_edit = Some(if indel.delete == source_range {
 +                    self::completion_text_edit(line_index, insert_replace_support, indel.clone())
 +                } else {
 +                    assert!(source_range.end() == indel.delete.end());
 +                    let range1 = TextRange::new(indel.delete.start(), source_range.start());
 +                    let range2 = source_range;
 +                    let indel1 = Indel::replace(range1, String::new());
 +                    let indel2 = Indel::replace(range2, indel.insert.clone());
 +                    additional_text_edits.push(self::text_edit(line_index, indel1));
 +                    self::completion_text_edit(line_index, insert_replace_support, indel2)
 +                })
 +            } else {
 +                assert!(source_range.intersect(indel.delete).is_none());
 +                let text_edit = self::text_edit(line_index, indel.clone());
 +                additional_text_edits.push(text_edit);
 +            }
 +        }
 +        text_edit.unwrap()
 +    };
 +
 +    let insert_text_format = item.is_snippet().then(|| lsp_types::InsertTextFormat::SNIPPET);
 +    let tags = item.deprecated().then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
 +    let command = if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
 +        Some(command::trigger_parameter_hints())
 +    } else {
 +        None
 +    };
 +
 +    let mut lsp_item = lsp_types::CompletionItem {
 +        label: item.label().to_string(),
 +        detail: item.detail().map(|it| it.to_string()),
 +        filter_text: Some(item.lookup().to_string()),
 +        kind: Some(completion_item_kind(item.kind())),
 +        text_edit: Some(text_edit),
 +        additional_text_edits: Some(additional_text_edits),
 +        documentation: item.documentation().map(documentation),
 +        deprecated: Some(item.deprecated()),
 +        tags,
 +        command,
 +        insert_text_format,
 +        ..Default::default()
 +    };
 +
 +    if config.completion_label_details_support() {
 +        lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
 +            detail: None,
 +            description: lsp_item.detail.clone(),
 +        });
 +    }
 +
 +    set_score(&mut lsp_item, max_relevance, item.relevance());
 +
 +    if config.completion().enable_imports_on_the_fly {
 +        if let imports @ [_, ..] = item.imports_to_add() {
 +            let imports: Vec<_> = imports
 +                .iter()
 +                .filter_map(|import_edit| {
 +                    let import_path = &import_edit.import_path;
 +                    let import_name = import_path.segments().last()?;
 +                    Some(lsp_ext::CompletionImport {
 +                        full_import_path: import_path.to_string(),
 +                        imported_name: import_name.to_string(),
 +                    })
 +                })
 +                .collect();
 +            if !imports.is_empty() {
 +                let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports };
 +                lsp_item.data = Some(to_value(data).unwrap());
 +            }
 +        }
 +    }
 +
 +    if let Some((mutability, offset, relevance)) = item.ref_match() {
 +        let mut lsp_item_with_ref = lsp_item.clone();
 +        set_score(&mut lsp_item_with_ref, max_relevance, relevance);
 +        lsp_item_with_ref.label =
 +            format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
 +        lsp_item_with_ref.additional_text_edits.get_or_insert_with(Default::default).push(
 +            self::text_edit(
 +                line_index,
 +                Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
 +            ),
 +        );
 +
 +        acc.push(lsp_item_with_ref);
 +    };
 +
 +    acc.push(lsp_item);
 +
 +    fn set_score(
 +        res: &mut lsp_types::CompletionItem,
 +        max_relevance: u32,
 +        relevance: CompletionRelevance,
 +    ) {
 +        if relevance.is_relevant() && relevance.score() == max_relevance {
 +            res.preselect = Some(true);
 +        }
 +        // The relevance needs to be inverted to come up with a sort score
 +        // because the client will sort ascending.
 +        let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;
 +        // Zero pad the string to ensure values can be properly sorted
 +        // by the client. Hex format is used because it is easier to
 +        // visually compare very large values, which the sort text
 +        // tends to be since it is the opposite of the score.
 +        res.sort_text = Some(format!("{:08x}", sort_score));
 +    }
 +}
 +
 +pub(crate) fn signature_help(
 +    call_info: SignatureHelp,
 +    config: CallInfoConfig,
 +    label_offsets: bool,
 +) -> lsp_types::SignatureHelp {
 +    let (label, parameters) = match (config.params_only, label_offsets) {
 +        (concise, false) => {
 +            let params = call_info
 +                .parameter_labels()
 +                .map(|label| lsp_types::ParameterInformation {
 +                    label: lsp_types::ParameterLabel::Simple(label.to_string()),
 +                    documentation: None,
 +                })
 +                .collect::<Vec<_>>();
 +            let label =
 +                if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
 +            (label, params)
 +        }
 +        (false, true) => {
 +            let params = call_info
 +                .parameter_ranges()
 +                .iter()
 +                .map(|it| {
 +                    let start = call_info.signature[..it.start().into()].chars().count() as u32;
 +                    let end = call_info.signature[..it.end().into()].chars().count() as u32;
 +                    [start, end]
 +                })
 +                .map(|label_offsets| lsp_types::ParameterInformation {
 +                    label: lsp_types::ParameterLabel::LabelOffsets(label_offsets),
 +                    documentation: None,
 +                })
 +                .collect::<Vec<_>>();
 +            (call_info.signature, params)
 +        }
 +        (true, true) => {
 +            let mut params = Vec::new();
 +            let mut label = String::new();
 +            let mut first = true;
 +            for param in call_info.parameter_labels() {
 +                if !first {
 +                    label.push_str(", ");
 +                }
 +                first = false;
 +                let start = label.chars().count() as u32;
 +                label.push_str(param);
 +                let end = label.chars().count() as u32;
 +                params.push(lsp_types::ParameterInformation {
 +                    label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
 +                    documentation: None,
 +                });
 +            }
 +
 +            (label, params)
 +        }
 +    };
 +
 +    let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
 +        lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
 +            kind: lsp_types::MarkupKind::Markdown,
 +            value: doc,
 +        })
 +    });
 +
 +    let active_parameter = call_info.active_parameter.map(|it| it as u32);
 +
 +    let signature = lsp_types::SignatureInformation {
 +        label,
 +        documentation,
 +        parameters: Some(parameters),
 +        active_parameter,
 +    };
 +    lsp_types::SignatureHelp {
 +        signatures: vec![signature],
 +        active_signature: Some(0),
 +        active_parameter,
 +    }
 +}
 +
 +pub(crate) fn inlay_hint(
 +    snap: &GlobalStateSnapshot,
 +    line_index: &LineIndex,
 +    render_colons: bool,
-             InlayKind::BindingModeHint => inlay_hint.label != "&",
++    mut inlay_hint: InlayHint,
++) -> Result<lsp_types::InlayHint> {
++    match inlay_hint.kind {
++        InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
++        InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
++        InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "),
++        _ => {}
++    }
++
++    Ok(lsp_types::InlayHint {
 +        position: match inlay_hint.kind {
 +            // before annotated thing
 +            InlayKind::ParameterHint
 +            | InlayKind::ImplicitReborrowHint
 +            | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()),
 +            // after annotated thing
 +            InlayKind::ClosureReturnTypeHint
 +            | InlayKind::TypeHint
 +            | InlayKind::ChainingHint
 +            | InlayKind::GenericParamListHint
 +            | InlayKind::LifetimeHint
 +            | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()),
 +        },
 +        padding_left: Some(match inlay_hint.kind {
 +            InlayKind::TypeHint => !render_colons,
 +            InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true,
 +            InlayKind::BindingModeHint
 +            | InlayKind::ClosureReturnTypeHint
 +            | InlayKind::GenericParamListHint
 +            | InlayKind::ImplicitReborrowHint
 +            | InlayKind::LifetimeHint
 +            | InlayKind::ParameterHint => false,
 +        }),
 +        padding_right: Some(match inlay_hint.kind {
 +            InlayKind::ChainingHint
 +            | InlayKind::ClosureReturnTypeHint
 +            | InlayKind::GenericParamListHint
 +            | InlayKind::ImplicitReborrowHint
 +            | InlayKind::TypeHint
 +            | InlayKind::ClosingBraceHint => false,
-         label: lsp_types::InlayHintLabel::String(match inlay_hint.kind {
-             InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
-             InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
-             InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
-             _ => inlay_hint.label.clone(),
-         }),
++            InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
 +            InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
 +        }),
-             _ => lsp_types::InlayHintTooltip::String(inlay_hint.label),
 +        kind: match inlay_hint.kind {
 +            InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER),
 +            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
 +                Some(lsp_types::InlayHintKind::TYPE)
 +            }
 +            InlayKind::BindingModeHint
 +            | InlayKind::GenericParamListHint
 +            | InlayKind::LifetimeHint
 +            | InlayKind::ImplicitReborrowHint
 +            | InlayKind::ClosingBraceHint => None,
 +        },
 +        text_edits: None,
 +        data: (|| match inlay_hint.tooltip {
 +            Some(ide::InlayTooltip::HoverOffset(file_id, offset)) => {
 +                let uri = url(snap, file_id);
 +                let line_index = snap.file_line_index(file_id).ok()?;
 +
 +                let text_document = lsp_types::TextDocumentIdentifier { uri };
 +                to_value(lsp_ext::InlayHintResolveData {
 +                    text_document,
 +                    position: lsp_ext::PositionOrRange::Position(position(&line_index, offset)),
 +                })
 +                .ok()
 +            }
 +            Some(ide::InlayTooltip::HoverRanged(file_id, text_range)) => {
 +                let uri = url(snap, file_id);
 +                let text_document = lsp_types::TextDocumentIdentifier { uri };
 +                let line_index = snap.file_line_index(file_id).ok()?;
 +                to_value(lsp_ext::InlayHintResolveData {
 +                    text_document,
 +                    position: lsp_ext::PositionOrRange::Range(range(&line_index, text_range)),
 +                })
 +                .ok()
 +            }
 +            _ => None,
 +        })(),
 +        tooltip: Some(match inlay_hint.tooltip {
 +            Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
-     }
++            _ => lsp_types::InlayHintTooltip::String(inlay_hint.label.to_string()),
 +        }),
++        label: inlay_hint_label(snap, inlay_hint.label)?,
++    })
++}
++
++fn inlay_hint_label(
++    snap: &GlobalStateSnapshot,
++    label: InlayHintLabel,
++) -> Result<lsp_types::InlayHintLabel> {
++    Ok(match label.as_simple_str() {
++        Some(s) => lsp_types::InlayHintLabel::String(s.into()),
++        None => lsp_types::InlayHintLabel::LabelParts(
++            label
++                .parts
++                .into_iter()
++                .map(|part| {
++                    Ok(lsp_types::InlayHintLabelPart {
++                        value: part.text,
++                        tooltip: None,
++                        location: part
++                            .linked_location
++                            .map(|range| location(snap, range))
++                            .transpose()?,
++                        command: None,
++                    })
++                })
++                .collect::<Result<Vec<_>>>()?,
++        ),
++    })
 +}
 +
 +static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
 +
 +pub(crate) fn semantic_tokens(
 +    text: &str,
 +    line_index: &LineIndex,
 +    highlights: Vec<HlRange>,
 +) -> lsp_types::SemanticTokens {
 +    let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
 +    let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
 +
 +    for highlight_range in highlights {
 +        if highlight_range.highlight.is_empty() {
 +            continue;
 +        }
 +
 +        let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
 +        let token_index = semantic_tokens::type_index(ty);
 +        let modifier_bitset = mods.0;
 +
 +        for mut text_range in line_index.index.lines(highlight_range.range) {
 +            if text[text_range].ends_with('\n') {
 +                text_range =
 +                    TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
 +            }
 +            let range = range(line_index, text_range);
 +            builder.push(range, token_index, modifier_bitset);
 +        }
 +    }
 +
 +    builder.build()
 +}
 +
 +pub(crate) fn semantic_token_delta(
 +    previous: &lsp_types::SemanticTokens,
 +    current: &lsp_types::SemanticTokens,
 +) -> lsp_types::SemanticTokensDelta {
 +    let result_id = current.result_id.clone();
 +    let edits = semantic_tokens::diff_tokens(&previous.data, &current.data);
 +    lsp_types::SemanticTokensDelta { result_id, edits }
 +}
 +
 +fn semantic_token_type_and_modifiers(
 +    highlight: Highlight,
 +) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
 +    let mut mods = semantic_tokens::ModifierSet::default();
 +    let type_ = match highlight.tag {
 +        HlTag::Symbol(symbol) => match symbol {
 +            SymbolKind::Attribute => semantic_tokens::DECORATOR,
 +            SymbolKind::Derive => semantic_tokens::DERIVE,
 +            SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
 +            SymbolKind::Module => semantic_tokens::NAMESPACE,
 +            SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
 +            SymbolKind::Field => semantic_tokens::PROPERTY,
 +            SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER,
 +            SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
 +            SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
 +            SymbolKind::Label => semantic_tokens::LABEL,
 +            SymbolKind::ValueParam => semantic_tokens::PARAMETER,
 +            SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
 +            SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
 +            SymbolKind::Local => semantic_tokens::VARIABLE,
 +            SymbolKind::Function => {
 +                if highlight.mods.contains(HlMod::Associated) {
 +                    semantic_tokens::METHOD
 +                } else {
 +                    semantic_tokens::FUNCTION
 +                }
 +            }
 +            SymbolKind::Const => {
 +                mods |= semantic_tokens::CONSTANT;
 +                mods |= semantic_tokens::STATIC;
 +                semantic_tokens::VARIABLE
 +            }
 +            SymbolKind::Static => {
 +                mods |= semantic_tokens::STATIC;
 +                semantic_tokens::VARIABLE
 +            }
 +            SymbolKind::Struct => semantic_tokens::STRUCT,
 +            SymbolKind::Enum => semantic_tokens::ENUM,
 +            SymbolKind::Variant => semantic_tokens::ENUM_MEMBER,
 +            SymbolKind::Union => semantic_tokens::UNION,
 +            SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
 +            SymbolKind::Trait => semantic_tokens::INTERFACE,
 +            SymbolKind::Macro => semantic_tokens::MACRO,
 +            SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
 +            SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
 +        },
 +        HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
 +        HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
 +        HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
 +        HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER,
 +        HlTag::CharLiteral => semantic_tokens::CHAR,
 +        HlTag::Comment => semantic_tokens::COMMENT,
 +        HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
 +        HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
 +        HlTag::Keyword => semantic_tokens::KEYWORD,
 +        HlTag::None => semantic_tokens::GENERIC,
 +        HlTag::Operator(op) => match op {
 +            HlOperator::Bitwise => semantic_tokens::BITWISE,
 +            HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
 +            HlOperator::Logical => semantic_tokens::LOGICAL,
 +            HlOperator::Comparison => semantic_tokens::COMPARISON,
 +            HlOperator::Other => semantic_tokens::OPERATOR,
 +        },
 +        HlTag::StringLiteral => semantic_tokens::STRING,
 +        HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
 +        HlTag::Punctuation(punct) => match punct {
 +            HlPunct::Bracket => semantic_tokens::BRACKET,
 +            HlPunct::Brace => semantic_tokens::BRACE,
 +            HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
 +            HlPunct::Angle => semantic_tokens::ANGLE,
 +            HlPunct::Comma => semantic_tokens::COMMA,
 +            HlPunct::Dot => semantic_tokens::DOT,
 +            HlPunct::Colon => semantic_tokens::COLON,
 +            HlPunct::Semi => semantic_tokens::SEMICOLON,
 +            HlPunct::Other => semantic_tokens::PUNCTUATION,
 +            HlPunct::MacroBang => semantic_tokens::MACRO_BANG,
 +        },
 +    };
 +
 +    for modifier in highlight.mods.iter() {
 +        let modifier = match modifier {
 +            HlMod::Associated => continue,
 +            HlMod::Async => semantic_tokens::ASYNC,
 +            HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
 +            HlMod::Callable => semantic_tokens::CALLABLE,
 +            HlMod::Consuming => semantic_tokens::CONSUMING,
 +            HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
 +            HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
 +            HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY,
 +            HlMod::Definition => semantic_tokens::DECLARATION,
 +            HlMod::Documentation => semantic_tokens::DOCUMENTATION,
 +            HlMod::Injected => semantic_tokens::INJECTED,
 +            HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
 +            HlMod::Library => semantic_tokens::LIBRARY,
 +            HlMod::Mutable => semantic_tokens::MUTABLE,
 +            HlMod::Public => semantic_tokens::PUBLIC,
 +            HlMod::Reference => semantic_tokens::REFERENCE,
 +            HlMod::Static => semantic_tokens::STATIC,
 +            HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
 +            HlMod::Unsafe => semantic_tokens::UNSAFE,
 +        };
 +        mods |= modifier;
 +    }
 +
 +    (type_, mods)
 +}
 +
 +pub(crate) fn folding_range(
 +    text: &str,
 +    line_index: &LineIndex,
 +    line_folding_only: bool,
 +    fold: Fold,
 +) -> lsp_types::FoldingRange {
 +    let kind = match fold.kind {
 +        FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
 +        FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
 +        FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
 +        FoldKind::Mods
 +        | FoldKind::Block
 +        | FoldKind::ArgList
 +        | FoldKind::Consts
 +        | FoldKind::Statics
 +        | FoldKind::WhereClause
 +        | FoldKind::ReturnType
 +        | FoldKind::Array
 +        | FoldKind::MatchArm => None,
 +    };
 +
 +    let range = range(line_index, fold.range);
 +
 +    if line_folding_only {
 +        // Clients with line_folding_only == true (such as VSCode) will fold the whole end line
 +        // even if it contains text not in the folding range. To prevent that we exclude
 +        // range.end.line from the folding region if there is more text after range.end
 +        // on the same line.
 +        let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))]
 +            .chars()
 +            .take_while(|it| *it != '\n')
 +            .any(|it| !it.is_whitespace());
 +
 +        let end_line = if has_more_text_on_end_line {
 +            range.end.line.saturating_sub(1)
 +        } else {
 +            range.end.line
 +        };
 +
 +        lsp_types::FoldingRange {
 +            start_line: range.start.line,
 +            start_character: None,
 +            end_line,
 +            end_character: None,
 +            kind,
 +        }
 +    } else {
 +        lsp_types::FoldingRange {
 +            start_line: range.start.line,
 +            start_character: Some(range.start.character),
 +            end_line: range.end.line,
 +            end_character: Some(range.end.character),
 +            kind,
 +        }
 +    }
 +}
 +
 +pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url {
 +    snap.file_id_to_url(file_id)
 +}
 +
 +/// Returns a `Url` object from a given path, will lowercase drive letters if present.
 +/// This will only happen when processing windows paths.
 +///
 +/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
 +pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url {
 +    let url = lsp_types::Url::from_file_path(path).unwrap();
 +    match path.as_ref().components().next() {
 +        Some(path::Component::Prefix(prefix))
 +            if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
 +        {
 +            // Need to lowercase driver letter
 +        }
 +        _ => return url,
 +    }
 +
 +    let driver_letter_range = {
 +        let (scheme, drive_letter, _rest) = match url.as_str().splitn(3, ':').collect_tuple() {
 +            Some(it) => it,
 +            None => return url,
 +        };
 +        let start = scheme.len() + ':'.len_utf8();
 +        start..(start + drive_letter.len())
 +    };
 +
 +    // Note: lowercasing the `path` itself doesn't help, the `Url::parse`
 +    // machinery *also* canonicalizes the drive letter. So, just massage the
 +    // string in place.
 +    let mut url: String = url.into();
 +    url[driver_letter_range].make_ascii_lowercase();
 +    lsp_types::Url::parse(&url).unwrap()
 +}
 +
 +pub(crate) fn optional_versioned_text_document_identifier(
 +    snap: &GlobalStateSnapshot,
 +    file_id: FileId,
 +) -> lsp_types::OptionalVersionedTextDocumentIdentifier {
 +    let url = url(snap, file_id);
 +    let version = snap.url_file_version(&url);
 +    lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version }
 +}
 +
 +pub(crate) fn location(
 +    snap: &GlobalStateSnapshot,
 +    frange: FileRange,
 +) -> Result<lsp_types::Location> {
 +    let url = url(snap, frange.file_id);
 +    let line_index = snap.file_line_index(frange.file_id)?;
 +    let range = range(&line_index, frange.range);
 +    let loc = lsp_types::Location::new(url, range);
 +    Ok(loc)
 +}
 +
 +/// Prefer using `location_link`, if the client has the cap.
 +pub(crate) fn location_from_nav(
 +    snap: &GlobalStateSnapshot,
 +    nav: NavigationTarget,
 +) -> Result<lsp_types::Location> {
 +    let url = url(snap, nav.file_id);
 +    let line_index = snap.file_line_index(nav.file_id)?;
 +    let range = range(&line_index, nav.full_range);
 +    let loc = lsp_types::Location::new(url, range);
 +    Ok(loc)
 +}
 +
 +pub(crate) fn location_link(
 +    snap: &GlobalStateSnapshot,
 +    src: Option<FileRange>,
 +    target: NavigationTarget,
 +) -> Result<lsp_types::LocationLink> {
 +    let origin_selection_range = match src {
 +        Some(src) => {
 +            let line_index = snap.file_line_index(src.file_id)?;
 +            let range = range(&line_index, src.range);
 +            Some(range)
 +        }
 +        None => None,
 +    };
 +    let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
 +    let res = lsp_types::LocationLink {
 +        origin_selection_range,
 +        target_uri,
 +        target_range,
 +        target_selection_range,
 +    };
 +    Ok(res)
 +}
 +
 +fn location_info(
 +    snap: &GlobalStateSnapshot,
 +    target: NavigationTarget,
 +) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
 +    let line_index = snap.file_line_index(target.file_id)?;
 +
 +    let target_uri = url(snap, target.file_id);
 +    let target_range = range(&line_index, target.full_range);
 +    let target_selection_range =
 +        target.focus_range.map(|it| range(&line_index, it)).unwrap_or(target_range);
 +    Ok((target_uri, target_range, target_selection_range))
 +}
 +
 +pub(crate) fn goto_definition_response(
 +    snap: &GlobalStateSnapshot,
 +    src: Option<FileRange>,
 +    targets: Vec<NavigationTarget>,
 +) -> Result<lsp_types::GotoDefinitionResponse> {
 +    if snap.config.location_link() {
 +        let links = targets
 +            .into_iter()
 +            .map(|nav| location_link(snap, src, nav))
 +            .collect::<Result<Vec<_>>>()?;
 +        Ok(links.into())
 +    } else {
 +        let locations = targets
 +            .into_iter()
 +            .map(|nav| {
 +                location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
 +            })
 +            .collect::<Result<Vec<_>>>()?;
 +        Ok(locations.into())
 +    }
 +}
 +
 +fn outside_workspace_annotation_id() -> String {
 +    String::from("OutsideWorkspace")
 +}
 +
 +pub(crate) fn snippet_text_document_edit(
 +    snap: &GlobalStateSnapshot,
 +    is_snippet: bool,
 +    file_id: FileId,
 +    edit: TextEdit,
 +) -> Result<lsp_ext::SnippetTextDocumentEdit> {
 +    let text_document = optional_versioned_text_document_identifier(snap, file_id);
 +    let line_index = snap.file_line_index(file_id)?;
 +    let mut edits: Vec<_> =
 +        edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
 +
 +    if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
 +        for edit in &mut edits {
 +            edit.annotation_id = Some(outside_workspace_annotation_id())
 +        }
 +    }
 +    Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
 +}
 +
 +pub(crate) fn snippet_text_document_ops(
 +    snap: &GlobalStateSnapshot,
 +    file_system_edit: FileSystemEdit,
 +) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
 +    let mut ops = Vec::new();
 +    match file_system_edit {
 +        FileSystemEdit::CreateFile { dst, initial_contents } => {
 +            let uri = snap.anchored_path(&dst);
 +            let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile {
 +                uri: uri.clone(),
 +                options: None,
 +                annotation_id: None,
 +            });
 +            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file));
 +            if !initial_contents.is_empty() {
 +                let text_document =
 +                    lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None };
 +                let text_edit = lsp_ext::SnippetTextEdit {
 +                    range: lsp_types::Range::default(),
 +                    new_text: initial_contents,
 +                    insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
 +                    annotation_id: None,
 +                };
 +                let edit_file =
 +                    lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
 +                ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file));
 +            }
 +        }
 +        FileSystemEdit::MoveFile { src, dst } => {
 +            let old_uri = snap.file_id_to_url(src);
 +            let new_uri = snap.anchored_path(&dst);
 +            let mut rename_file =
 +                lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
 +            if snap.analysis.is_library_file(src).ok() == Some(true)
 +                && snap.config.change_annotation_support()
 +            {
 +                rename_file.annotation_id = Some(outside_workspace_annotation_id())
 +            }
 +            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
 +                rename_file,
 +            )))
 +        }
 +        FileSystemEdit::MoveDir { src, src_id, dst } => {
 +            let old_uri = snap.anchored_path(&src);
 +            let new_uri = snap.anchored_path(&dst);
 +            let mut rename_file =
 +                lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
 +            if snap.analysis.is_library_file(src_id).ok() == Some(true)
 +                && snap.config.change_annotation_support()
 +            {
 +                rename_file.annotation_id = Some(outside_workspace_annotation_id())
 +            }
 +            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
 +                rename_file,
 +            )))
 +        }
 +    }
 +    Ok(ops)
 +}
 +
 +pub(crate) fn snippet_workspace_edit(
 +    snap: &GlobalStateSnapshot,
 +    source_change: SourceChange,
 +) -> Result<lsp_ext::SnippetWorkspaceEdit> {
 +    let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
 +
 +    for op in source_change.file_system_edits {
 +        let ops = snippet_text_document_ops(snap, op)?;
 +        document_changes.extend_from_slice(&ops);
 +    }
 +    for (file_id, edit) in source_change.source_file_edits {
 +        let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?;
 +        document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
 +    }
 +    let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
 +        changes: None,
 +        document_changes: Some(document_changes),
 +        change_annotations: None,
 +    };
 +    if snap.config.change_annotation_support() {
 +        workspace_edit.change_annotations = Some(
 +            once((
 +                outside_workspace_annotation_id(),
 +                lsp_types::ChangeAnnotation {
 +                    label: String::from("Edit outside of the workspace"),
 +                    needs_confirmation: Some(true),
 +                    description: Some(String::from(
 +                        "This edit lies outside of the workspace and may affect dependencies",
 +                    )),
 +                },
 +            ))
 +            .collect(),
 +        )
 +    }
 +    Ok(workspace_edit)
 +}
 +
 +pub(crate) fn workspace_edit(
 +    snap: &GlobalStateSnapshot,
 +    source_change: SourceChange,
 +) -> Result<lsp_types::WorkspaceEdit> {
 +    assert!(!source_change.is_snippet);
 +    snippet_workspace_edit(snap, source_change).map(|it| it.into())
 +}
 +
 +impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
 +    fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
 +        lsp_types::WorkspaceEdit {
 +            changes: None,
 +            document_changes: snippet_workspace_edit.document_changes.map(|changes| {
 +                lsp_types::DocumentChanges::Operations(
 +                    changes
 +                        .into_iter()
 +                        .map(|change| match change {
 +                            lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
 +                                lsp_types::DocumentChangeOperation::Op(op)
 +                            }
 +                            lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
 +                                lsp_types::DocumentChangeOperation::Edit(
 +                                    lsp_types::TextDocumentEdit {
 +                                        text_document: edit.text_document,
 +                                        edits: edit.edits.into_iter().map(From::from).collect(),
 +                                    },
 +                                )
 +                            }
 +                        })
 +                        .collect(),
 +                )
 +            }),
 +            change_annotations: snippet_workspace_edit.change_annotations,
 +        }
 +    }
 +}
 +
 +impl From<lsp_ext::SnippetTextEdit>
 +    for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
 +{
 +    fn from(
 +        lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
 +    ) -> Self {
 +        match annotation_id {
 +            Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
 +                text_edit: lsp_types::TextEdit { range, new_text },
 +                annotation_id,
 +            }),
 +            None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
 +        }
 +    }
 +}
 +
 +pub(crate) fn call_hierarchy_item(
 +    snap: &GlobalStateSnapshot,
 +    target: NavigationTarget,
 +) -> Result<lsp_types::CallHierarchyItem> {
 +    let name = target.name.to_string();
 +    let detail = target.description.clone();
 +    let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION);
 +    let (uri, range, selection_range) = location_info(snap, target)?;
 +    Ok(lsp_types::CallHierarchyItem {
 +        name,
 +        kind,
 +        tags: None,
 +        detail,
 +        uri,
 +        range,
 +        selection_range,
 +        data: None,
 +    })
 +}
 +
 +pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
 +    match kind {
 +        AssistKind::None | AssistKind::Generate => lsp_types::CodeActionKind::EMPTY,
 +        AssistKind::QuickFix => lsp_types::CodeActionKind::QUICKFIX,
 +        AssistKind::Refactor => lsp_types::CodeActionKind::REFACTOR,
 +        AssistKind::RefactorExtract => lsp_types::CodeActionKind::REFACTOR_EXTRACT,
 +        AssistKind::RefactorInline => lsp_types::CodeActionKind::REFACTOR_INLINE,
 +        AssistKind::RefactorRewrite => lsp_types::CodeActionKind::REFACTOR_REWRITE,
 +    }
 +}
 +
 +pub(crate) fn code_action(
 +    snap: &GlobalStateSnapshot,
 +    assist: Assist,
 +    resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
 +) -> Result<lsp_ext::CodeAction> {
 +    let mut res = lsp_ext::CodeAction {
 +        title: assist.label.to_string(),
 +        group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
 +        kind: Some(code_action_kind(assist.id.1)),
 +        edit: None,
 +        is_preferred: None,
 +        data: None,
 +        command: None,
 +    };
 +
 +    if assist.trigger_signature_help && snap.config.client_commands().trigger_parameter_hints {
 +        res.command = Some(command::trigger_parameter_hints());
 +    }
 +
 +    match (assist.source_change, resolve_data) {
 +        (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
 +        (None, Some((index, code_action_params))) => {
 +            res.data = Some(lsp_ext::CodeActionData {
 +                id: format!("{}:{}:{}", assist.id.0, assist.id.1.name(), index),
 +                code_action_params,
 +            });
 +        }
 +        (None, None) => {
 +            stdx::never!("assist should always be resolved if client can't do lazy resolving")
 +        }
 +    };
 +    Ok(res)
 +}
 +
 +pub(crate) fn runnable(
 +    snap: &GlobalStateSnapshot,
 +    runnable: Runnable,
 +) -> Result<lsp_ext::Runnable> {
 +    let config = snap.config.runnables();
 +    let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?;
 +    let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
 +    let target = spec.as_ref().map(|s| s.target.clone());
 +    let (cargo_args, executable_args) =
 +        CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?;
 +    let label = runnable.label(target);
 +    let location = location_link(snap, None, runnable.nav)?;
 +
 +    Ok(lsp_ext::Runnable {
 +        label,
 +        location: Some(location),
 +        kind: lsp_ext::RunnableKind::Cargo,
 +        args: lsp_ext::CargoRunnable {
 +            workspace_root: workspace_root.map(|it| it.into()),
 +            override_cargo: config.override_cargo,
 +            cargo_args,
 +            cargo_extra_args: config.cargo_extra_args,
 +            executable_args,
 +            expect_test: None,
 +        },
 +    })
 +}
 +
 +pub(crate) fn code_lens(
 +    acc: &mut Vec<lsp_types::CodeLens>,
 +    snap: &GlobalStateSnapshot,
 +    annotation: Annotation,
 +) -> Result<()> {
 +    let client_commands_config = snap.config.client_commands();
 +    match annotation.kind {
 +        AnnotationKind::Runnable(run) => {
 +            let line_index = snap.file_line_index(run.nav.file_id)?;
 +            let annotation_range = range(&line_index, annotation.range);
 +
 +            let title = run.title();
 +            let can_debug = match run.kind {
 +                ide::RunnableKind::DocTest { .. } => false,
 +                ide::RunnableKind::TestMod { .. }
 +                | ide::RunnableKind::Test { .. }
 +                | ide::RunnableKind::Bench { .. }
 +                | ide::RunnableKind::Bin => true,
 +            };
 +            let r = runnable(snap, run)?;
 +
 +            let lens_config = snap.config.lens();
 +            if lens_config.run && client_commands_config.run_single {
 +                let command = command::run_single(&r, &title);
 +                acc.push(lsp_types::CodeLens {
 +                    range: annotation_range,
 +                    command: Some(command),
 +                    data: None,
 +                })
 +            }
 +            if lens_config.debug && can_debug && client_commands_config.debug_single {
 +                let command = command::debug_single(&r);
 +                acc.push(lsp_types::CodeLens {
 +                    range: annotation_range,
 +                    command: Some(command),
 +                    data: None,
 +                })
 +            }
 +        }
 +        AnnotationKind::HasImpls { file_id, data } => {
 +            if !client_commands_config.show_reference {
 +                return Ok(());
 +            }
 +            let line_index = snap.file_line_index(file_id)?;
 +            let annotation_range = range(&line_index, annotation.range);
 +            let url = url(snap, file_id);
 +
 +            let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
 +
 +            let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
 +
 +            let goto_params = lsp_types::request::GotoImplementationParams {
 +                text_document_position_params: doc_pos,
 +                work_done_progress_params: Default::default(),
 +                partial_result_params: Default::default(),
 +            };
 +
 +            let command = data.map(|ranges| {
 +                let locations: Vec<lsp_types::Location> = ranges
 +                    .into_iter()
 +                    .filter_map(|target| {
 +                        location(
 +                            snap,
 +                            FileRange { file_id: target.file_id, range: target.full_range },
 +                        )
 +                        .ok()
 +                    })
 +                    .collect();
 +
 +                command::show_references(
 +                    implementation_title(locations.len()),
 +                    &url,
 +                    annotation_range.start,
 +                    locations,
 +                )
 +            });
 +
 +            acc.push(lsp_types::CodeLens {
 +                range: annotation_range,
 +                command,
 +                data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
 +            })
 +        }
 +        AnnotationKind::HasReferences { file_id, data } => {
 +            if !client_commands_config.show_reference {
 +                return Ok(());
 +            }
 +            let line_index = snap.file_line_index(file_id)?;
 +            let annotation_range = range(&line_index, annotation.range);
 +            let url = url(snap, file_id);
 +
 +            let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
 +
 +            let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
 +
 +            let command = data.map(|ranges| {
 +                let locations: Vec<lsp_types::Location> =
 +                    ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
 +
 +                command::show_references(
 +                    reference_title(locations.len()),
 +                    &url,
 +                    annotation_range.start,
 +                    locations,
 +                )
 +            });
 +
 +            acc.push(lsp_types::CodeLens {
 +                range: annotation_range,
 +                command,
 +                data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
 +            })
 +        }
 +    }
 +    Ok(())
 +}
 +
 +pub(crate) mod command {
 +    use ide::{FileRange, NavigationTarget};
 +    use serde_json::to_value;
 +
 +    use crate::{
 +        global_state::GlobalStateSnapshot,
 +        lsp_ext,
 +        to_proto::{location, location_link},
 +    };
 +
 +    pub(crate) fn show_references(
 +        title: String,
 +        uri: &lsp_types::Url,
 +        position: lsp_types::Position,
 +        locations: Vec<lsp_types::Location>,
 +    ) -> lsp_types::Command {
 +        // We cannot use the 'editor.action.showReferences' command directly
 +        // because that command requires vscode types which we convert in the handler
 +        // on the client side.
 +
 +        lsp_types::Command {
 +            title,
 +            command: "rust-analyzer.showReferences".into(),
 +            arguments: Some(vec![
 +                to_value(uri).unwrap(),
 +                to_value(position).unwrap(),
 +                to_value(locations).unwrap(),
 +            ]),
 +        }
 +    }
 +
 +    pub(crate) fn run_single(runnable: &lsp_ext::Runnable, title: &str) -> lsp_types::Command {
 +        lsp_types::Command {
 +            title: title.to_string(),
 +            command: "rust-analyzer.runSingle".into(),
 +            arguments: Some(vec![to_value(runnable).unwrap()]),
 +        }
 +    }
 +
 +    pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
 +        lsp_types::Command {
 +            title: "Debug".into(),
 +            command: "rust-analyzer.debugSingle".into(),
 +            arguments: Some(vec![to_value(runnable).unwrap()]),
 +        }
 +    }
 +
 +    pub(crate) fn goto_location(
 +        snap: &GlobalStateSnapshot,
 +        nav: &NavigationTarget,
 +    ) -> Option<lsp_types::Command> {
 +        let value = if snap.config.location_link() {
 +            let link = location_link(snap, None, nav.clone()).ok()?;
 +            to_value(link).ok()?
 +        } else {
 +            let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
 +            let location = location(snap, range).ok()?;
 +            to_value(location).ok()?
 +        };
 +
 +        Some(lsp_types::Command {
 +            title: nav.name.to_string(),
 +            command: "rust-analyzer.gotoLocation".into(),
 +            arguments: Some(vec![value]),
 +        })
 +    }
 +
 +    pub(crate) fn trigger_parameter_hints() -> lsp_types::Command {
 +        lsp_types::Command {
 +            title: "triggerParameterHints".into(),
 +            command: "editor.action.triggerParameterHints".into(),
 +            arguments: None,
 +        }
 +    }
 +}
 +
 +pub(crate) fn implementation_title(count: usize) -> String {
 +    if count == 1 {
 +        "1 implementation".into()
 +    } else {
 +        format!("{} implementations", count)
 +    }
 +}
 +
 +pub(crate) fn reference_title(count: usize) -> String {
 +    if count == 1 {
 +        "1 reference".into()
 +    } else {
 +        format!("{} references", count)
 +    }
 +}
 +
 +pub(crate) fn markup_content(
 +    markup: Markup,
 +    kind: ide::HoverDocFormat,
 +) -> lsp_types::MarkupContent {
 +    let kind = match kind {
 +        ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
 +        ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
 +    };
 +    let value = crate::markdown::format_docs(markup.as_str());
 +    lsp_types::MarkupContent { kind, value }
 +}
 +
 +pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
 +    // This is wrong, but we don't have a better alternative I suppose?
 +    // https://github.com/microsoft/language-server-protocol/issues/1341
 +    invalid_params_error(err.to_string())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use std::sync::Arc;
 +
 +    use ide::Analysis;
 +
 +    use super::*;
 +
 +    #[test]
 +    fn conv_fold_line_folding_only_fixup() {
 +        let text = r#"mod a;
 +mod b;
 +mod c;
 +
 +fn main() {
 +    if cond {
 +        a::do_a();
 +    } else {
 +        b::do_b();
 +    }
 +}"#;
 +
 +        let (analysis, file_id) = Analysis::from_single_file(text.to_string());
 +        let folds = analysis.folding_ranges(file_id).unwrap();
 +        assert_eq!(folds.len(), 4);
 +
 +        let line_index = LineIndex {
 +            index: Arc::new(ide::LineIndex::new(text)),
 +            endings: LineEndings::Unix,
 +            encoding: OffsetEncoding::Utf16,
 +        };
 +        let converted: Vec<lsp_types::FoldingRange> =
 +            folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
 +
 +        let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
 +        assert_eq!(converted.len(), expected_lines.len());
 +        for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
 +            assert_eq!(folding_range.start_line, *start_line);
 +            assert_eq!(folding_range.start_character, None);
 +            assert_eq!(folding_range.end_line, *end_line);
 +            assert_eq!(folding_range.end_character, None);
 +        }
 +    }
 +
 +    // `Url` is not able to parse windows paths on unix machines.
 +    #[test]
 +    #[cfg(target_os = "windows")]
 +    fn test_lowercase_drive_letter() {
 +        use std::path::Path;
 +
 +        let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap());
 +        assert_eq!(url.to_string(), "file:///c:/Test");
 +
 +        let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap());
 +        assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
 +    }
 +}
index 8efd58e2c39aa48c098844ad853a30993d314f42,0000000000000000000000000000000000000000..eadebbe8a212bab90ff4a0857012ec762611865b
mode 100644,000000..100644
--- /dev/null
@@@ -1,834 -1,0 +1,840 @@@
- impl ast::TypeBoundList {
-     pub fn remove(&self) {
 +//! Structural editing for ast.
 +
 +use std::iter::{empty, successors};
 +
 +use parser::{SyntaxKind, T};
 +use rowan::SyntaxElement;
 +
 +use crate::{
 +    algo::{self, neighbor},
 +    ast::{self, edit::IndentLevel, make, HasGenericParams},
 +    ted::{self, Position},
 +    AstNode, AstToken, Direction,
 +    SyntaxKind::{ATTR, COMMENT, WHITESPACE},
 +    SyntaxNode, SyntaxToken,
 +};
 +
 +use super::HasName;
 +
 +pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause;
 +}
 +
 +impl GenericParamsOwnerEdit for ast::Fn {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
 +        match self.generic_param_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = if let Some(name) = self.name() {
 +                    Position::after(name.syntax)
 +                } else if let Some(fn_token) = self.fn_token() {
 +                    Position::after(fn_token)
 +                } else if let Some(param_list) = self.param_list() {
 +                    Position::before(param_list.syntax)
 +                } else {
 +                    Position::last_child_of(self.syntax())
 +                };
 +                create_generic_param_list(position)
 +            }
 +        }
 +    }
 +
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause {
 +        if self.where_clause().is_none() {
 +            let position = if let Some(ty) = self.ret_type() {
 +                Position::after(ty.syntax())
 +            } else if let Some(param_list) = self.param_list() {
 +                Position::after(param_list.syntax())
 +            } else {
 +                Position::last_child_of(self.syntax())
 +            };
 +            create_where_clause(position);
 +        }
 +        self.where_clause().unwrap()
 +    }
 +}
 +
 +impl GenericParamsOwnerEdit for ast::Impl {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
 +        match self.generic_param_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = match self.impl_token() {
 +                    Some(imp_token) => Position::after(imp_token),
 +                    None => Position::last_child_of(self.syntax()),
 +                };
 +                create_generic_param_list(position)
 +            }
 +        }
 +    }
 +
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause {
 +        if self.where_clause().is_none() {
 +            let position = match self.assoc_item_list() {
 +                Some(items) => Position::before(items.syntax()),
 +                None => Position::last_child_of(self.syntax()),
 +            };
 +            create_where_clause(position);
 +        }
 +        self.where_clause().unwrap()
 +    }
 +}
 +
 +impl GenericParamsOwnerEdit for ast::Trait {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
 +        match self.generic_param_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = if let Some(name) = self.name() {
 +                    Position::after(name.syntax)
 +                } else if let Some(trait_token) = self.trait_token() {
 +                    Position::after(trait_token)
 +                } else {
 +                    Position::last_child_of(self.syntax())
 +                };
 +                create_generic_param_list(position)
 +            }
 +        }
 +    }
 +
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause {
 +        if self.where_clause().is_none() {
 +            let position = match self.assoc_item_list() {
 +                Some(items) => Position::before(items.syntax()),
 +                None => Position::last_child_of(self.syntax()),
 +            };
 +            create_where_clause(position);
 +        }
 +        self.where_clause().unwrap()
 +    }
 +}
 +
 +impl GenericParamsOwnerEdit for ast::Struct {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
 +        match self.generic_param_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = if let Some(name) = self.name() {
 +                    Position::after(name.syntax)
 +                } else if let Some(struct_token) = self.struct_token() {
 +                    Position::after(struct_token)
 +                } else {
 +                    Position::last_child_of(self.syntax())
 +                };
 +                create_generic_param_list(position)
 +            }
 +        }
 +    }
 +
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause {
 +        if self.where_clause().is_none() {
 +            let tfl = self.field_list().and_then(|fl| match fl {
 +                ast::FieldList::RecordFieldList(_) => None,
 +                ast::FieldList::TupleFieldList(it) => Some(it),
 +            });
 +            let position = if let Some(tfl) = tfl {
 +                Position::after(tfl.syntax())
 +            } else if let Some(gpl) = self.generic_param_list() {
 +                Position::after(gpl.syntax())
 +            } else if let Some(name) = self.name() {
 +                Position::after(name.syntax())
 +            } else {
 +                Position::last_child_of(self.syntax())
 +            };
 +            create_where_clause(position);
 +        }
 +        self.where_clause().unwrap()
 +    }
 +}
 +
 +impl GenericParamsOwnerEdit for ast::Enum {
 +    fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
 +        match self.generic_param_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = if let Some(name) = self.name() {
 +                    Position::after(name.syntax)
 +                } else if let Some(enum_token) = self.enum_token() {
 +                    Position::after(enum_token)
 +                } else {
 +                    Position::last_child_of(self.syntax())
 +                };
 +                create_generic_param_list(position)
 +            }
 +        }
 +    }
 +
 +    fn get_or_create_where_clause(&self) -> ast::WhereClause {
 +        if self.where_clause().is_none() {
 +            let position = if let Some(gpl) = self.generic_param_list() {
 +                Position::after(gpl.syntax())
 +            } else if let Some(name) = self.name() {
 +                Position::after(name.syntax())
 +            } else {
 +                Position::last_child_of(self.syntax())
 +            };
 +            create_where_clause(position);
 +        }
 +        self.where_clause().unwrap()
 +    }
 +}
 +
 +fn create_where_clause(position: Position) {
 +    let where_clause = make::where_clause(empty()).clone_for_update();
 +    ted::insert(position, where_clause.syntax());
 +}
 +
 +fn create_generic_param_list(position: Position) -> ast::GenericParamList {
 +    let gpl = make::generic_param_list(empty()).clone_for_update();
 +    ted::insert_raw(position, gpl.syntax());
 +    gpl
 +}
 +
 +pub trait AttrsOwnerEdit: ast::HasAttrs {
 +    fn remove_attrs_and_docs(&self) {
 +        remove_attrs_and_docs(self.syntax());
 +
 +        fn remove_attrs_and_docs(node: &SyntaxNode) {
 +            let mut remove_next_ws = false;
 +            for child in node.children_with_tokens() {
 +                match child.kind() {
 +                    ATTR | COMMENT => {
 +                        remove_next_ws = true;
 +                        child.detach();
 +                        continue;
 +                    }
 +                    WHITESPACE if remove_next_ws => {
 +                        child.detach();
 +                    }
 +                    _ => (),
 +                }
 +                remove_next_ws = false;
 +            }
 +        }
 +    }
 +}
 +
 +impl<T: ast::HasAttrs> AttrsOwnerEdit for T {}
 +
 +impl ast::GenericParamList {
 +    pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
 +        match self.generic_params().last() {
 +            Some(last_param) => {
 +                let position = Position::after(last_param.syntax());
 +                let elements = vec![
 +                    make::token(T![,]).into(),
 +                    make::tokens::single_space().into(),
 +                    generic_param.syntax().clone().into(),
 +                ];
 +                ted::insert_all(position, elements);
 +            }
 +            None => {
 +                let after_l_angle = Position::after(self.l_angle_token().unwrap());
 +                ted::insert(after_l_angle, generic_param.syntax());
 +            }
 +        }
 +    }
 +}
 +
 +impl ast::WhereClause {
 +    pub fn add_predicate(&self, predicate: ast::WherePred) {
 +        if let Some(pred) = self.predicates().last() {
 +            if !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,]) {
 +                ted::append_child_raw(self.syntax(), make::token(T![,]));
 +            }
 +        }
 +        ted::append_child(self.syntax(), predicate.syntax());
 +    }
 +}
 +
- impl ast::UseTree {
-     pub fn remove(&self) {
++pub trait Removable: AstNode {
++    fn remove(&self);
++}
++
++impl Removable for ast::TypeBoundList {
++    fn remove(&self) {
 +        match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
 +            Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()),
 +            None => ted::remove(self.syntax()),
 +        }
 +    }
 +}
 +
 +impl ast::PathSegment {
 +    pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
 +        if self.generic_arg_list().is_none() {
 +            let arg_list = make::generic_arg_list().clone_for_update();
 +            ted::append_child(self.syntax(), arg_list.syntax());
 +        }
 +        self.generic_arg_list().unwrap()
 +    }
 +}
 +
- impl ast::Use {
-     pub fn remove(&self) {
++impl Removable for ast::UseTree {
++    fn remove(&self) {
 +        for dir in [Direction::Next, Direction::Prev] {
 +            if let Some(next_use_tree) = neighbor(self, dir) {
 +                let separators = self
 +                    .syntax()
 +                    .siblings_with_tokens(dir)
 +                    .skip(1)
 +                    .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
 +                ted::remove_all_iter(separators);
 +                break;
 +            }
 +        }
 +        ted::remove(self.syntax());
 +    }
++}
 +
++impl ast::UseTree {
 +    pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList {
 +        match self.use_tree_list() {
 +            Some(it) => it,
 +            None => {
 +                let position = Position::last_child_of(self.syntax());
 +                let use_tree_list = make::use_tree_list(empty()).clone_for_update();
 +                let mut elements = Vec::with_capacity(2);
 +                if self.coloncolon_token().is_none() {
 +                    elements.push(make::token(T![::]).into());
 +                }
 +                elements.push(use_tree_list.syntax().clone().into());
 +                ted::insert_all_raw(position, elements);
 +                use_tree_list
 +            }
 +        }
 +    }
 +
 +    /// Splits off the given prefix, making it the path component of the use tree,
 +    /// appending the rest of the path to all UseTreeList items.
 +    ///
 +    /// # Examples
 +    ///
 +    /// `prefix$0::suffix` -> `prefix::{suffix}`
 +    ///
 +    /// `prefix$0` -> `prefix::{self}`
 +    ///
 +    /// `prefix$0::*` -> `prefix::{*}`
 +    pub fn split_prefix(&self, prefix: &ast::Path) {
 +        debug_assert_eq!(self.path(), Some(prefix.top_path()));
 +        let path = self.path().unwrap();
 +        if &path == prefix && self.use_tree_list().is_none() {
 +            if self.star_token().is_some() {
 +                // path$0::* -> *
 +                self.coloncolon_token().map(ted::remove);
 +                ted::remove(prefix.syntax());
 +            } else {
 +                // path$0 -> self
 +                let self_suffix =
 +                    make::path_unqualified(make::path_segment_self()).clone_for_update();
 +                ted::replace(path.syntax(), self_suffix.syntax());
 +            }
 +        } else if split_path_prefix(prefix).is_none() {
 +            return;
 +        }
 +        // At this point, prefix path is detached; _self_ use tree has suffix path.
 +        // Next, transform 'suffix' use tree into 'prefix::{suffix}'
 +        let subtree = self.clone_subtree().clone_for_update();
 +        ted::remove_all_iter(self.syntax().children_with_tokens());
 +        ted::insert(Position::first_child_of(self.syntax()), prefix.syntax());
 +        self.get_or_create_use_tree_list().add_use_tree(subtree);
 +
 +        fn split_path_prefix(prefix: &ast::Path) -> Option<()> {
 +            let parent = prefix.parent_path()?;
 +            let segment = parent.segment()?;
 +            if algo::has_errors(segment.syntax()) {
 +                return None;
 +            }
 +            for p in successors(parent.parent_path(), |it| it.parent_path()) {
 +                p.segment()?;
 +            }
 +            prefix.parent_path().and_then(|p| p.coloncolon_token()).map(ted::remove);
 +            ted::remove(prefix.syntax());
 +            Some(())
 +        }
 +    }
 +}
 +
 +impl ast::UseTreeList {
 +    pub fn add_use_tree(&self, use_tree: ast::UseTree) {
 +        let (position, elements) = match self.use_trees().last() {
 +            Some(last_tree) => (
 +                Position::after(last_tree.syntax()),
 +                vec![
 +                    make::token(T![,]).into(),
 +                    make::tokens::single_space().into(),
 +                    use_tree.syntax.into(),
 +                ],
 +            ),
 +            None => {
 +                let position = match self.l_curly_token() {
 +                    Some(l_curly) => Position::after(l_curly),
 +                    None => Position::last_child_of(self.syntax()),
 +                };
 +                (position, vec![use_tree.syntax.into()])
 +            }
 +        };
 +        ted::insert_all_raw(position, elements);
 +    }
 +}
 +
- impl ast::MatchArm {
-     pub fn remove(&self) {
++impl Removable for ast::Use {
++    fn remove(&self) {
 +        let next_ws = self
 +            .syntax()
 +            .next_sibling_or_token()
 +            .and_then(|it| it.into_token())
 +            .and_then(ast::Whitespace::cast);
 +        if let Some(next_ws) = next_ws {
 +            let ws_text = next_ws.syntax().text();
 +            if let Some(rest) = ws_text.strip_prefix('\n') {
 +                if rest.is_empty() {
 +                    ted::remove(next_ws.syntax());
 +                } else {
 +                    ted::replace(next_ws.syntax(), make::tokens::whitespace(rest));
 +                }
 +            }
 +        }
 +        ted::remove(self.syntax());
 +    }
 +}
 +
 +impl ast::Impl {
 +    pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
 +        if self.assoc_item_list().is_none() {
 +            let assoc_item_list = make::assoc_item_list().clone_for_update();
 +            ted::append_child(self.syntax(), assoc_item_list.syntax());
 +        }
 +        self.assoc_item_list().unwrap()
 +    }
 +}
 +
 +impl ast::AssocItemList {
 +    pub fn add_item(&self, item: ast::AssocItem) {
 +        let (indent, position, whitespace) = match self.assoc_items().last() {
 +            Some(last_item) => (
 +                IndentLevel::from_node(last_item.syntax()),
 +                Position::after(last_item.syntax()),
 +                "\n\n",
 +            ),
 +            None => match self.l_curly_token() {
 +                Some(l_curly) => {
 +                    normalize_ws_between_braces(self.syntax());
 +                    (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
 +                }
 +                None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
 +            },
 +        };
 +        let elements: Vec<SyntaxElement<_>> = vec![
 +            make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(),
 +            item.syntax().clone().into(),
 +        ];
 +        ted::insert_all(position, elements);
 +    }
 +}
 +
 +impl ast::Fn {
 +    pub fn get_or_create_body(&self) -> ast::BlockExpr {
 +        if self.body().is_none() {
 +            let body = make::ext::empty_block_expr().clone_for_update();
 +            match self.semicolon_token() {
 +                Some(semi) => {
 +                    ted::replace(semi, body.syntax());
 +                    ted::insert(Position::before(body.syntax), make::tokens::single_space());
 +                }
 +                None => ted::append_child(self.syntax(), body.syntax()),
 +            }
 +        }
 +        self.body().unwrap()
 +    }
 +}
 +
++impl Removable for ast::MatchArm {
++    fn remove(&self) {
 +        if let Some(sibling) = self.syntax().prev_sibling_or_token() {
 +            if sibling.kind() == SyntaxKind::WHITESPACE {
 +                ted::remove(sibling);
 +            }
 +        }
 +        if let Some(sibling) = self.syntax().next_sibling_or_token() {
 +            if sibling.kind() == T![,] {
 +                ted::remove(sibling);
 +            }
 +        }
 +        ted::remove(self.syntax());
 +    }
 +}
 +
 +impl ast::MatchArmList {
 +    pub fn add_arm(&self, arm: ast::MatchArm) {
 +        normalize_ws_between_braces(self.syntax());
 +        let mut elements = Vec::new();
 +        let position = match self.arms().last() {
 +            Some(last_arm) => {
 +                if needs_comma(&last_arm) {
 +                    ted::append_child(last_arm.syntax(), make::token(SyntaxKind::COMMA));
 +                }
 +                Position::after(last_arm.syntax().clone())
 +            }
 +            None => match self.l_curly_token() {
 +                Some(it) => Position::after(it),
 +                None => Position::last_child_of(self.syntax()),
 +            },
 +        };
 +        let indent = IndentLevel::from_node(self.syntax()) + 1;
 +        elements.push(make::tokens::whitespace(&format!("\n{}", indent)).into());
 +        elements.push(arm.syntax().clone().into());
 +        if needs_comma(&arm) {
 +            ted::append_child(arm.syntax(), make::token(SyntaxKind::COMMA));
 +        }
 +        ted::insert_all(position, elements);
 +
 +        fn needs_comma(arm: &ast::MatchArm) -> bool {
 +            arm.expr().map_or(false, |e| !e.is_block_like()) && arm.comma_token().is_none()
 +        }
 +    }
 +}
 +
 +impl ast::RecordExprFieldList {
 +    pub fn add_field(&self, field: ast::RecordExprField) {
 +        let is_multiline = self.syntax().text().contains_char('\n');
 +        let whitespace = if is_multiline {
 +            let indent = IndentLevel::from_node(self.syntax()) + 1;
 +            make::tokens::whitespace(&format!("\n{}", indent))
 +        } else {
 +            make::tokens::single_space()
 +        };
 +
 +        if is_multiline {
 +            normalize_ws_between_braces(self.syntax());
 +        }
 +
 +        let position = match self.fields().last() {
 +            Some(last_field) => {
 +                let comma = get_or_insert_comma_after(last_field.syntax());
 +                Position::after(comma)
 +            }
 +            None => match self.l_curly_token() {
 +                Some(it) => Position::after(it),
 +                None => Position::last_child_of(self.syntax()),
 +            },
 +        };
 +
 +        ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
 +        if is_multiline {
 +            ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
 +        }
 +    }
 +}
 +
 +impl ast::RecordExprField {
 +    /// This will either replace the initializer, or in the case that this is a shorthand convert
 +    /// the initializer into the name ref and insert the expr as the new initializer.
 +    pub fn replace_expr(&self, expr: ast::Expr) {
 +        if self.name_ref().is_some() {
 +            match self.expr() {
 +                Some(prev) => ted::replace(prev.syntax(), expr.syntax()),
 +                None => ted::append_child(self.syntax(), expr.syntax()),
 +            }
 +            return;
 +        }
 +        // this is a shorthand
 +        if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() {
 +            if let Some(path) = path_expr.path() {
 +                if let Some(name_ref) = path.as_single_name_ref() {
 +                    path_expr.syntax().detach();
 +                    let children = vec![
 +                        name_ref.syntax().clone().into(),
 +                        ast::make::token(T![:]).into(),
 +                        ast::make::tokens::single_space().into(),
 +                        expr.syntax().clone().into(),
 +                    ];
 +                    ted::insert_all_raw(Position::last_child_of(self.syntax()), children);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl ast::RecordPatFieldList {
 +    pub fn add_field(&self, field: ast::RecordPatField) {
 +        let is_multiline = self.syntax().text().contains_char('\n');
 +        let whitespace = if is_multiline {
 +            let indent = IndentLevel::from_node(self.syntax()) + 1;
 +            make::tokens::whitespace(&format!("\n{}", indent))
 +        } else {
 +            make::tokens::single_space()
 +        };
 +
 +        if is_multiline {
 +            normalize_ws_between_braces(self.syntax());
 +        }
 +
 +        let position = match self.fields().last() {
 +            Some(last_field) => {
 +                let syntax = last_field.syntax();
 +                let comma = get_or_insert_comma_after(syntax);
 +                Position::after(comma)
 +            }
 +            None => match self.l_curly_token() {
 +                Some(it) => Position::after(it),
 +                None => Position::last_child_of(self.syntax()),
 +            },
 +        };
 +
 +        ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
 +        if is_multiline {
 +            ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
 +        }
 +    }
 +}
 +
 +fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
 +    let comma = match syntax
 +        .siblings_with_tokens(Direction::Next)
 +        .filter_map(|it| it.into_token())
 +        .find(|it| it.kind() == T![,])
 +    {
 +        Some(it) => it,
 +        None => {
 +            let comma = ast::make::token(T![,]);
 +            ted::insert(Position::after(syntax), &comma);
 +            comma
 +        }
 +    };
 +    comma
 +}
 +
 +impl ast::StmtList {
 +    pub fn push_front(&self, statement: ast::Stmt) {
 +        ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax());
 +    }
 +}
 +
 +impl ast::VariantList {
 +    pub fn add_variant(&self, variant: ast::Variant) {
 +        let (indent, position) = match self.variants().last() {
 +            Some(last_item) => (
 +                IndentLevel::from_node(last_item.syntax()),
 +                Position::after(get_or_insert_comma_after(last_item.syntax())),
 +            ),
 +            None => match self.l_curly_token() {
 +                Some(l_curly) => {
 +                    normalize_ws_between_braces(self.syntax());
 +                    (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly))
 +                }
 +                None => (IndentLevel::single(), Position::last_child_of(self.syntax())),
 +            },
 +        };
 +        let elements: Vec<SyntaxElement<_>> = vec![
 +            make::tokens::whitespace(&format!("{}{}", "\n", indent)).into(),
 +            variant.syntax().clone().into(),
 +            ast::make::token(T![,]).into(),
 +        ];
 +        ted::insert_all(position, elements);
 +    }
 +}
 +
 +fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
 +    let l = node
 +        .children_with_tokens()
 +        .filter_map(|it| it.into_token())
 +        .find(|it| it.kind() == T!['{'])?;
 +    let r = node
 +        .children_with_tokens()
 +        .filter_map(|it| it.into_token())
 +        .find(|it| it.kind() == T!['}'])?;
 +
 +    let indent = IndentLevel::from_node(node);
 +
 +    match l.next_sibling_or_token() {
 +        Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
 +            if ws.next_sibling_or_token()?.into_token()? == r {
 +                ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
 +            }
 +        }
 +        Some(ws) if ws.kind() == T!['}'] => {
 +            ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
 +        }
 +        _ => (),
 +    }
 +    Some(())
 +}
 +
 +pub trait Indent: AstNode + Clone + Sized {
 +    fn indent_level(&self) -> IndentLevel {
 +        IndentLevel::from_node(self.syntax())
 +    }
 +    fn indent(&self, by: IndentLevel) {
 +        by.increase_indent(self.syntax());
 +    }
 +    fn dedent(&self, by: IndentLevel) {
 +        by.decrease_indent(self.syntax());
 +    }
 +    fn reindent_to(&self, target_level: IndentLevel) {
 +        let current_level = IndentLevel::from_node(self.syntax());
 +        self.dedent(current_level);
 +        self.indent(target_level);
 +    }
 +}
 +
 +impl<N: AstNode + Clone> Indent for N {}
 +
 +#[cfg(test)]
 +mod tests {
 +    use std::fmt;
 +
 +    use stdx::trim_indent;
 +    use test_utils::assert_eq_text;
 +
 +    use crate::SourceFile;
 +
 +    use super::*;
 +
 +    fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
 +        let parse = SourceFile::parse(text);
 +        parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
 +    }
 +
 +    #[test]
 +    fn test_create_generic_param_list() {
 +        fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) {
 +            let gpl_owner = ast_mut_from_text::<N>(before);
 +            gpl_owner.get_or_create_generic_param_list();
 +            assert_eq!(gpl_owner.to_string(), after);
 +        }
 +
 +        check_create_gpl::<ast::Fn>("fn foo", "fn foo<>");
 +        check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}");
 +
 +        check_create_gpl::<ast::Impl>("impl", "impl<>");
 +        check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}");
 +        check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
 +
 +        check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>");
 +        check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}");
 +
 +        check_create_gpl::<ast::Struct>("struct A", "struct A<>");
 +        check_create_gpl::<ast::Struct>("struct A;", "struct A<>;");
 +        check_create_gpl::<ast::Struct>("struct A();", "struct A<>();");
 +        check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}");
 +
 +        check_create_gpl::<ast::Enum>("enum E", "enum E<>");
 +        check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
 +    }
 +
 +    #[test]
 +    fn test_increase_indent() {
 +        let arm_list = ast_mut_from_text::<ast::Fn>(
 +            "fn foo() {
 +    ;
 +    ;
 +}",
 +        );
 +        arm_list.indent(IndentLevel(2));
 +        assert_eq!(
 +            arm_list.to_string(),
 +            "fn foo() {
 +            ;
 +            ;
 +        }",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_variant_to_empty_enum() {
 +        let variant = make::variant(make::name("Bar"), None).clone_for_update();
 +
 +        check_add_variant(
 +            r#"
 +enum Foo {}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +}
 +"#,
 +            variant,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_variant_to_non_empty_enum() {
 +        let variant = make::variant(make::name("Baz"), None).clone_for_update();
 +
 +        check_add_variant(
 +            r#"
 +enum Foo {
 +    Bar,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +"#,
 +            variant,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_variant_with_tuple_field_list() {
 +        let variant = make::variant(
 +            make::name("Baz"),
 +            Some(ast::FieldList::TupleFieldList(make::tuple_field_list(std::iter::once(
 +                make::tuple_field(None, make::ty("bool")),
 +            )))),
 +        )
 +        .clone_for_update();
 +
 +        check_add_variant(
 +            r#"
 +enum Foo {
 +    Bar,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz(bool),
 +}
 +"#,
 +            variant,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_variant_with_record_field_list() {
 +        let variant = make::variant(
 +            make::name("Baz"),
 +            Some(ast::FieldList::RecordFieldList(make::record_field_list(std::iter::once(
 +                make::record_field(None, make::name("x"), make::ty("bool")),
 +            )))),
 +        )
 +        .clone_for_update();
 +
 +        check_add_variant(
 +            r#"
 +enum Foo {
 +    Bar,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz { x: bool },
 +}
 +"#,
 +            variant,
 +        );
 +    }
 +
 +    fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
 +        let enum_ = ast_mut_from_text::<ast::Enum>(before);
 +        enum_.variant_list().map(|it| it.add_variant(variant));
 +        let after = enum_.to_string();
 +        assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(&after.trim()));
 +    }
 +}