Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Installation

We provide pre-built binaries on the [GitHub Releases page](https://github.com/helix-editor/helix/releases).

[![Packaging status](https://repology.org/badge/vertical-allrepos/helix.svg)](https://repology.org/project/helix/versions)

## OSX

A Homebrew tap is available:

```
brew tap helix-editor/helix
brew install helix
```

## Linux

### NixOS

A [flake](https://nixos.wiki/wiki/Flakes) containing the package is available in
the project root. The flake can also be used to spin up a reproducible development
shell for working on Helix with `nix develop`.

Flake outputs are cached for each push to master using
[Cachix](https://www.cachix.org/). The flake is configured to
automatically make use of this cache assuming the user accepts
the new settings on first use.

If you are using a version of Nix without flakes enabled you can
[install Cachix cli](https://docs.cachix.org/installation); `cachix use helix` will
configure Nix to use cached outputs when possible.

### Arch Linux

Releases are available in the `community` repository.

A [helix-git](https://aur.archlinux.org/packages/helix-git/) package is also available on the AUR, which builds the master branch.

### Fedora Linux

You can install the COPR package for Helix via

```
sudo dnf copr enable varlad/helix
sudo dnf install helix
```

### Void Linux

```
sudo xbps-install helix
```

## Build from source

```
git clone https://github.com/helix-editor/helix
cd helix
cargo install --path helix-term
```

This will install the `hx` binary to `$HOME/.cargo/bin`.

Helix also needs it's runtime files so make sure to copy/symlink the `runtime/` directory into the
config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overridden
via the `HELIX_RUNTIME` environment variable.

| OS                | command   |
|-------------------|-----------|
|windows(cmd.exe)   |`xcopy /e runtime %AppData%/helix/runtime`     |
|windows(powershell)|`xcopy /e runtime $Env:AppData\helix\runtime`  |
|linux/macos        |`ln -s $PWD/runtime ~/.config/helix/runtime`|

## Finishing up the installation 

To make sure everything is set up as expected you should finally run the helix healthcheck via 
```
hx --health
```
For more information on the information displayed in the healthcheck results refer to [Healthcheck](https://github.com/helix-editor/helix/wiki/Healthcheck).


### Building tree-sitter grammars

Tree-sitter grammars must be fetched and compiled if not pre-packaged.
Fetch grammars with `hx --grammar fetch` (requires `git`) and compile them
with `hx --grammar build` (requires a C++ compiler).

### Installing language servers

Language servers can optionally be installed if you want their features (auto-complete, diagnostics etc.).
Follow the [instructions on the wiki page](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) to add your language servers of choice.
/a> 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
//! Maps *syntax* of various definitions to their semantic ids.
//!
//! This is a very interesting module, and, in some sense, can be considered the
//! heart of the IDE parts of rust-analyzer.
//!
//! This module solves the following problem:
//!
//! > Given a piece of syntax, find the corresponding semantic definition (def).
//!
//! This problem is a part of more-or-less every IDE feature implemented. Every
//! IDE functionality (like goto to definition), conceptually starts with a
//! specific cursor position in a file. Starting with this text offset, we first
//! figure out what syntactic construct are we at: is this a pattern, an
//! expression, an item definition.
//!
//! Knowing only the syntax gives us relatively little info. For example,
//! looking at the syntax of the function we can realize that it is a part of an
//! `impl` block, but we won't be able to tell what trait function the current
//! function overrides, and whether it does that correctly. For that, we need to
//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
//! module does.
//!
//! As syntax trees are values and don't know their place of origin/identity,
//! this module also requires [`InFile`] wrappers to understand which specific
//! real or macro-expanded file the tree comes from.
//!
//! The actual algorithm to resolve syntax to def is curious in two aspects:
//!
//! * It is recursive
//! * It uses the inverse algorithm (what is the syntax for this def?)
//!
//! Specifically, the algorithm goes like this:
//!
//! 1. Find the syntactic container for the syntax. For example, field's
//!    container is the struct, and structs container is a module.
//! 2. Recursively get the def corresponding to container.
//! 3. Ask the container def for all child defs. These child defs contain
//!    the answer and answer's siblings.
//! 4. For each child def, ask for it's source.
//! 5. The child def whose source is the syntax node we've started with
//!    is the answer.
//!
//! It's interesting that both Roslyn and Kotlin contain very similar code
//! shape.
//!
//! Let's take a look at Roslyn:
//!
//!   <https://github.com/dotnet/roslyn/blob/36a0c338d6621cc5fe34b79d414074a95a6a489c/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs#L1403-L1429>
//!   <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1403>
//!
//! The `GetDeclaredType` takes `Syntax` as input, and returns `Symbol` as
//! output. First, it retrieves a `Symbol` for parent `Syntax`:
//!
//! * <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1423>
//!
//! Then, it iterates parent symbol's children, looking for one which has the
//! same text span as the original node:
//!
//!   <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1786>
//!
//! Now, let's look at Kotlin:
//!
//!   <https://github.com/JetBrains/kotlin/blob/a288b8b00e4754a1872b164999c6d3f3b8c8994a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt#L93-L125>
//!
//! This function starts with a syntax node (`KtExpression` is syntax, like all
//! `Kt` nodes), and returns a def. It uses
//! `getNonLocalContainingOrThisDeclaration` to get syntactic container for a
//! current node. Then, `findSourceNonLocalFirDeclaration` gets `Fir` for this
//! parent. Finally, `findElementIn` function traverses `Fir` children to find
//! one with the same source we originally started with.
//!
//! One question is left though -- where does the recursion stops? This happens
//! when we get to the file syntax node, which doesn't have a syntactic parent.
//! In that case, we loop through all the crates that might contain this file
//! and look for a module whose source is the given file.
//!
//! Note that the logic in this module is somewhat fundamentally imprecise --
//! due to conditional compilation and `#[path]` attributes, there's no
//! injective mapping from syntax nodes to defs. This is not an edge case --
//! more or less every item in a `lib.rs` is a part of two distinct crates: a
//! library with `--cfg test` and a library without.
//!
//! At the moment, we don't really handle this well and return the first answer
//! that works. Ideally, we should first let the caller to pick a specific
//! active crate for a given position, and then provide an API to resolve all
//! syntax nodes against this specific crate.

use either::Either;
use hir_def::{
    AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId,
    EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId,
    ImplId, LifetimeParamId, Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
    TypeParamId, UnionId, UseId, VariantId,
    dyn_map::{
        DynMap,
        keys::{self, Key},
    },
    hir::{BindingId, Expr, LabelId},
    nameres::{block_def_map, crate_def_map},
};
use hir_expand::{
    EditionedFileId, ExpansionInfo, HirFileId, InMacroFile, MacroCallId, attrs::AttrId,
    name::AsName,
};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use span::FileId;
use stdx::impl_from;
use syntax::{
    AstNode, AstPtr, SyntaxNode,
    ast::{self, HasAttrs, HasName},
};
use tt::TextRange;

use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource};

#[derive(Default)]
pub(super) struct SourceToDefCache {
    pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
    expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo>,
    pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
    pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroCallId>>,
    /// Rootnode to HirFileId cache
    pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
}

impl SourceToDefCache {
    pub(super) fn cache(
        root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>,
        root_node: SyntaxNode,
        file_id: HirFileId,
    ) {
        assert!(root_node.parent().is_none());
        let prev = root_to_file_cache.insert(root_node, file_id);
        assert!(prev.is_none() || prev == Some(file_id));
    }

    pub(super) fn get_or_insert_include_for(
        &mut self,
        db: &dyn HirDatabase,
        file: EditionedFileId,
    ) -> Option<MacroCallId> {
        if let Some(&m) = self.included_file_cache.get(&file) {
            return m;
        }
        self.included_file_cache.insert(file, None);
        for &crate_id in db.relevant_crates(file.file_id(db)).iter() {
            db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| {
                self.included_file_cache.insert(file_id, Some(macro_call_id));
            });
        }
        self.included_file_cache.get(&file).copied().flatten()
    }

    pub(super) fn get_or_insert_expansion(
        &mut self,
        db: &dyn HirDatabase,
        macro_file: MacroCallId,
    ) -> &ExpansionInfo {
        self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
            let exp_info = macro_file.expansion_info(db);

            let InMacroFile { file_id, value } = exp_info.expanded();
            Self::cache(&mut self.root_to_file_cache, value, file_id.into());

            exp_info
        })
    }
}

pub(super) struct SourceToDefCtx<'db, 'cache> {
    pub(super) db: &'db dyn HirDatabase,
    pub(super) cache: &'cache mut SourceToDefCache,
}

impl SourceToDefCtx<'_, '_> {
    pub(super) fn file_to_def(&mut self, file: FileId) -> &SmallVec<[ModuleId; 1]> {
        let _p = tracing::info_span!("SourceToDefCtx::file_to_def").entered();
        self.cache.file_to_def_cache.entry(file).or_insert_with(|| {
            let mut mods = SmallVec::new();

            for &crate_id in self.db.relevant_crates(file).iter() {
                // Note: `mod` declarations in block modules cannot be supported here
                let crate_def_map = crate_def_map(self.db, crate_id);
                let n_mods = mods.len();
                let modules = |file| crate_def_map.modules_for_file(self.db, file);
                mods.extend(modules(file));
                if mods.len() == n_mods {
                    mods.extend(
                        self.db
                            .include_macro_invoc(crate_id)
                            .iter()
                            .filter(|&&(_, file_id)| file_id.file_id(self.db) == file)
                            .flat_map(|&(macro_call_id, file_id)| {
                                self.cache.included_file_cache.insert(file_id, Some(macro_call_id));
                                modules(
                                    macro_call_id
                                        .lookup(self.db)
                                        .kind
                                        .file_id()
                                        .original_file(self.db)
                                        .file_id(self.db),
                                )
                            }),
                    );
                }
            }
            if mods.is_empty() {
                // FIXME: detached file
            }
            mods
        })
    }

    pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option<ModuleId> {
        let _p = tracing::info_span!("module_to_def").entered();
        let parent_declaration = self
            .parent_ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| {
                ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
            })
            .map(|it| it.transpose());

        let parent_module = match parent_declaration {
            Some(Either::Right(parent_block)) => self
                .block_to_def(parent_block.as_ref())
                .map(|block| block_def_map(self.db, block).root_module_id()),
            Some(Either::Left(parent_declaration)) => {
                self.module_to_def(parent_declaration.as_ref())
            }
            None => {
                let file_id = src.file_id.original_file(self.db);
                self.file_to_def(file_id.file_id(self.db)).first().copied()
            }
        }?;

        let child_name = src.value.name()?.as_name();
        let def_map = parent_module.def_map(self.db);
        let &child_id = def_map[parent_module].children.get(&child_name)?;
        Some(child_id)
    }

    pub(super) fn source_file_to_def(&mut self, src: InFile<&ast::SourceFile>) -> Option<ModuleId> {
        let _p = tracing::info_span!("source_file_to_def").entered();
        let file_id = src.file_id.original_file(self.db);
        self.file_to_def(file_id.file_id(self.db)).first().copied()
    }

    pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> {
        self.to_def(src, keys::TRAIT)
    }
    pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option<ImplId> {
        self.to_def(src, keys::IMPL)
    }
    pub(super) fn fn_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<FunctionId> {
        self.to_def(src, keys::FUNCTION)
    }
    pub(super) fn struct_to_def(&mut self, src: InFile<&ast::Struct>) -> Option<StructId> {
        self.to_def(src, keys::STRUCT)
    }
    pub(super) fn enum_to_def(&mut self, src: InFile<&ast::Enum>) -> Option<EnumId> {
        self.to_def(src, keys::ENUM)
    }
    pub(super) fn union_to_def(&mut self, src: InFile<&ast::Union>) -> Option<UnionId> {
        self.to_def(src, keys::UNION)
    }
    pub(super) fn static_to_def(&mut self, src: InFile<&ast::Static>) -> Option<StaticId> {
        self.to_def(src, keys::STATIC)
    }
    pub(super) fn const_to_def(&mut self, src: InFile<&ast::Const>) -> Option<ConstId> {
        self.to_def(src, keys::CONST)
    }
    pub(super) fn type_alias_to_def(
        &mut self,
        src: InFile<&ast::TypeAlias>,
    ) -> Option<TypeAliasId> {
        self.to_def(src, keys::TYPE_ALIAS)
    }
    pub(super) fn record_field_to_def(
        &mut self,
        src: InFile<&ast::RecordField>,
    ) -> Option<FieldId> {
        self.to_def(src, keys::RECORD_FIELD)
    }
    pub(super) fn tuple_field_to_def(&mut self, src: InFile<&ast::TupleField>) -> Option<FieldId> {
        self.to_def(src, keys::TUPLE_FIELD)
    }
    pub(super) fn block_to_def(&mut self, src: InFile<&ast::BlockExpr>) -> Option<BlockId> {
        self.to_def(src, keys::BLOCK)
    }
    pub(super) fn enum_variant_to_def(
        &mut self,
        src: InFile<&ast::Variant>,
    ) -> Option<EnumVariantId> {
        self.to_def(src, keys::ENUM_VARIANT)
    }
    pub(super) fn extern_crate_to_def(
        &mut self,
        src: InFile<&ast::ExternCrate>,
    ) -> Option<ExternCrateId> {
        self.to_def(src, keys::EXTERN_CRATE)
    }
    pub(super) fn extern_block_to_def(
        &mut self,
        src: InFile<&ast::ExternBlock>,
    ) -> Option<ExternBlockId> {
        self.to_def(src, keys::EXTERN_BLOCK)
    }
    #[allow(dead_code)]
    pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
        self.to_def(src, keys::USE)
    }
    pub(super) fn adt_to_def(
        &mut self,
        InFile { file_id, value }: InFile<&ast::Adt>,
    ) -> Option<AdtId> {
        match value {
            ast::Adt::Enum(it) => self.enum_to_def(InFile::new(file_id, it)).map(AdtId::EnumId),
            ast::Adt::Struct(it) => {
                self.struct_to_def(InFile::new(file_id, it)).map(AdtId::StructId)
            }
            ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId),
        }
    }

    pub(super) fn asm_operand_to_def(
        &mut self,
        src: InFile<&ast::AsmOperandNamed>,
    ) -> Option<InlineAsmOperand> {
        let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?;
        let index = asm
            .asm_pieces()
            .filter_map(|it| match it {
                ast::AsmPiece::AsmOperandNamed(it) => Some(it),
                _ => None,
            })
            .position(|it| it == *src.value)?;
        let container = self.find_pat_or_label_container(src.syntax_ref())?;
        let source_map = self.db.body_with_source_map(container).1;
        let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?.as_expr()?;
        Some(InlineAsmOperand { owner: container, expr, index })
    }

    pub(super) fn bind_pat_to_def(
        &mut self,
        src: InFile<&ast::IdentPat>,
    ) -> Option<(DefWithBodyId, BindingId)> {
        let container = self.find_pat_or_label_container(src.syntax_ref())?;
        let (body, source_map) = self.db.body_with_source_map(container);
        let src = src.cloned().map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        // the pattern could resolve to a constant, verify that this is not the case
        if let crate::Pat::Bind { id, .. } = body[pat_id.as_pat()?] {
            Some((container, id))
        } else {
            None
        }
    }
    pub(super) fn self_param_to_def(
        &mut self,
        src: InFile<&ast::SelfParam>,
    ) -> Option<(DefWithBodyId, BindingId)> {
        let container = self.find_pat_or_label_container(src.syntax_ref())?;
        let body = self.db.body(container);
        Some((container, body.self_param?))
    }
    pub(super) fn label_to_def(
        &mut self,
        src: InFile<&ast::Label>,
    ) -> Option<(DefWithBodyId, LabelId)> {
        let container = self.find_pat_or_label_container(src.syntax_ref())?;
        let source_map = self.db.body_with_source_map(container).1;

        let label_id = source_map.node_label(src)?;
        Some((container, label_id))
    }

    pub(super) fn label_ref_to_def(
        &mut self,
        src: InFile<&ast::Lifetime>,
    ) -> Option<(DefWithBodyId, LabelId)> {
        let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
        let container = self.find_pat_or_label_container(src.syntax_ref())?;
        let (body, source_map) = self.db.body_with_source_map(container);
        let break_or_continue =
            source_map.node_expr(src.with_value(&break_or_continue))?.as_expr()?;
        let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
            return None;
        };
        Some((container, label?))
    }

    /// (AttrId, derive attribute call id, derive call ids)
    pub(super) fn attr_to_derive_macro_call(
        &mut self,
        item: InFile<&ast::Adt>,
        src: InFile<ast::Attr>,
    ) -> Option<(AttrId, MacroCallId, &[Option<Either<MacroCallId, BuiltinDeriveImplId>>])> {
        let map = self.dyn_map(item)?;
        map[keys::DERIVE_MACRO_CALL]
            .get(&AstPtr::new(&src.value))
            .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
    }

    // FIXME: Make this more fine grained! This should be a `adt_has_derives`!
    pub(super) fn file_of_adt_has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
        self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty())
    }

    pub(super) fn derive_macro_calls<'slf>(
        &'slf mut self,
        adt: InFile<&ast::Adt>,
    ) -> Option<
        impl Iterator<
            Item = (AttrId, MacroCallId, &'slf [Option<Either<MacroCallId, BuiltinDeriveImplId>>]),
        > + use<'slf>,
    > {
        self.dyn_map(adt).as_ref().map(|&map| {
            let dyn_map = &map[keys::DERIVE_MACRO_CALL];
            adt.value
                .attrs()
                .filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
                .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
        })
    }

    fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
        &mut self,
        src: InFile<&Ast>,
        key: Key<Ast, ID>,
    ) -> Option<ID> {
        self.dyn_map(src)?[key].get(&AstPtr::new(src.value)).copied()
    }

    fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
        let container = self.find_container(src.map(|it| it.syntax()))?;
        Some(self.cache_for(container, src.file_id))
    }

    fn cache_for(&mut self, container: ChildContainer, file_id: HirFileId) -> &DynMap {
        let db = self.db;
        self.cache
            .dynmap_cache
            .entry((container, file_id))
            .or_insert_with(|| container.child_by_source(db, file_id))
    }

    pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
        self.to_def(src, keys::ATTR_MACRO_CALL)
    }

    pub(super) fn macro_call_to_macro_call(
        &mut self,
        src: InFile<&ast::MacroCall>,
    ) -> Option<MacroCallId> {
        self.to_def(src, keys::MACRO_CALL)
    }

    pub(super) fn type_param_to_def(
        &mut self,
        src: InFile<&ast::TypeParam>,
    ) -> Option<TypeParamId> {
        let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
        let dyn_map = self.cache_for(container, src.file_id);
        dyn_map[keys::TYPE_PARAM]
            .get(&AstPtr::new(src.value))
            .copied()
            .map(TypeParamId::from_unchecked)
    }

    pub(super) fn lifetime_param_to_def(
        &mut self,
        src: InFile<&ast::LifetimeParam>,
    ) -> Option<LifetimeParamId> {
        let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
        let dyn_map = self.cache_for(container, src.file_id);
        dyn_map[keys::LIFETIME_PARAM].get(&AstPtr::new(src.value)).copied()
    }

    pub(super) fn const_param_to_def(
        &mut self,
        src: InFile<&ast::ConstParam>,
    ) -> Option<ConstParamId> {
        let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
        let dyn_map = self.cache_for(container, src.file_id);
        dyn_map[keys::CONST_PARAM]
            .get(&AstPtr::new(src.value))
            .copied()
            .map(ConstParamId::from_unchecked)
    }

    pub(super) fn generic_param_to_def(
        &mut self,
        InFile { file_id, value }: InFile<&ast::GenericParam>,
    ) -> Option<GenericParamId> {
        match value {
            ast::GenericParam::ConstParam(it) => {
                self.const_param_to_def(InFile::new(file_id, it)).map(GenericParamId::ConstParamId)
            }
            ast::GenericParam::LifetimeParam(it) => self
                .lifetime_param_to_def(InFile::new(file_id, it))
                .map(GenericParamId::LifetimeParamId),
            ast::GenericParam::TypeParam(it) => {
                self.type_param_to_def(InFile::new(file_id, it)).map(GenericParamId::TypeParamId)
            }
        }
    }

    pub(super) fn macro_to_def(&mut self, src: InFile<&ast::Macro>) -> Option<MacroId> {
        self.dyn_map(src).and_then(|it| match src.value {
            ast::Macro::MacroRules(value) => {
                it[keys::MACRO_RULES].get(&AstPtr::new(value)).copied().map(MacroId::from)
            }
            ast::Macro::MacroDef(value) => {
                it[keys::MACRO2].get(&AstPtr::new(value)).copied().map(MacroId::from)
            }
        })
    }

    pub(super) fn proc_macro_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<MacroId> {
        self.dyn_map(src).and_then(|it| {
            it[keys::PROC_MACRO].get(&AstPtr::new(src.value)).copied().map(MacroId::from)
        })
    }

    pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
        let _p = tracing::info_span!("find_container").entered();
        let def = self.parent_ancestors_with_macros(src, |this, container, child| {
            this.container_to_def(container, child)
        });
        if let Some(def) = def {
            return Some(def);
        }

        let def = self
            .file_to_def(src.file_id.original_file(self.db).file_id(self.db))
            .first()
            .copied()?;
        Some(def.into())
    }

    fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
        self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
            let item = ast::Item::cast(value)?;
            match &item {
                ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
                ast::Item::Struct(it) => {
                    this.struct_to_def(InFile::new(file_id, it)).map(Into::into)
                }
                ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into),
                ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into),
                ast::Item::TypeAlias(it) => {
                    this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into)
                }
                ast::Item::Impl(it) => this.impl_to_def(InFile::new(file_id, it)).map(Into::into),
                _ => None,
            }
        })
    }

    // FIXME: Remove this when we do inference in signatures
    fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
        self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
            let item = match ast::Item::cast(value.clone()) {
                Some(it) => it,
                None => {
                    let variant = ast::Variant::cast(value)?;
                    return this
                        .enum_variant_to_def(InFile::new(file_id, &variant))
                        .map(Into::into);
                }
            };
            match &item {
                ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
                ast::Item::Const(it) => this.const_to_def(InFile::new(file_id, it)).map(Into::into),
                ast::Item::Static(it) => {
                    this.static_to_def(InFile::new(file_id, it)).map(Into::into)
                }
                _ => None,
            }
        })
    }

    /// Skips the attributed item that caused the macro invocation we are climbing up
    fn parent_ancestors_with_macros<T>(
        &mut self,
        node: InFile<&SyntaxNode>,
        mut cb: impl FnMut(
            &mut Self,
            /*parent: */ InFile<SyntaxNode>,
            /*child: */ &SyntaxNode,
        ) -> Option<T>,
    ) -> Option<T> {
        let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
            Some(parent) => Some(node.with_value(parent)),
            None => {
                let macro_file = node.file_id.macro_file()?;
                let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
                expansion_info.arg().map(|node| node?.parent()).transpose()
            }
        };
        let mut deepest_child_in_same_file = node.cloned();
        let mut node = node.cloned();
        while let Some(parent) = parent(self, node.as_ref()) {
            if parent.file_id != node.file_id {
                deepest_child_in_same_file = parent.clone();
            }
            if let Some(res) = cb(self, parent.clone(), &deepest_child_in_same_file.value) {
                return Some(res);
            }
            node = parent;
        }
        None
    }

    fn container_to_def(
        &mut self,
        container: InFile<SyntaxNode>,
        child: &SyntaxNode,
    ) -> Option<ChildContainer> {
        let cont = if let Some(item) = ast::Item::cast(container.value.clone()) {
            match &item {
                ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
                ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
                ast::Item::Impl(it) => self.impl_to_def(container.with_value(it))?.into(),
                ast::Item::Enum(it) => self.enum_to_def(container.with_value(it))?.into(),
                ast::Item::TypeAlias(it) => ChildContainer::GenericDefId(
                    self.type_alias_to_def(container.with_value(it))?.into(),
                ),
                ast::Item::Struct(it) => {
                    let def = self.struct_to_def(container.with_value(it))?;
                    let is_in_body = it.field_list().is_some_and(|it| {
                        it.syntax().text_range().contains(child.text_range().start())
                    });
                    if is_in_body {
                        VariantId::from(def).into()
                    } else {
                        ChildContainer::GenericDefId(def.into())
                    }
                }
                ast::Item::Union(it) => {
                    let def = self.union_to_def(container.with_value(it))?;
                    let is_in_body = it.record_field_list().is_some_and(|it| {
                        it.syntax().text_range().contains(child.text_range().start())
                    });
                    if is_in_body {
                        VariantId::from(def).into()
                    } else {
                        ChildContainer::GenericDefId(def.into())
                    }
                }
                ast::Item::Fn(it) => {
                    let def = self.fn_to_def(container.with_value(it))?;
                    let child_offset = child.text_range().start();
                    let is_in_body =
                        it.body().is_some_and(|it| it.syntax().text_range().contains(child_offset));
                    let in_param_pat = || {
                        it.param_list().is_some_and(|it| {
                            it.self_param()
                                .and_then(|it| {
                                    Some(TextRange::new(
                                        it.syntax().text_range().start(),
                                        it.name()?.syntax().text_range().end(),
                                    ))
                                })
                                .is_some_and(|r| r.contains_inclusive(child_offset))
                                || it
                                    .params()
                                    .filter_map(|it| it.pat())
                                    .any(|it| it.syntax().text_range().contains(child_offset))
                        })
                    };
                    if is_in_body || in_param_pat() {
                        DefWithBodyId::from(def).into()
                    } else {
                        ChildContainer::GenericDefId(def.into())
                    }
                }
                ast::Item::Static(it) => {
                    let def = self.static_to_def(container.with_value(it))?;
                    let is_in_body = it.body().is_some_and(|it| {
                        it.syntax().text_range().contains(child.text_range().start())
                    });
                    if is_in_body {
                        DefWithBodyId::from(def).into()
                    } else {
                        ChildContainer::GenericDefId(def.into())
                    }
                }
                ast::Item::Const(it) => {
                    let def = self.const_to_def(container.with_value(it))?;
                    let is_in_body = it.body().is_some_and(|it| {
                        it.syntax().text_range().contains(child.text_range().start())
                    });
                    if is_in_body {
                        DefWithBodyId::from(def).into()
                    } else {
                        ChildContainer::GenericDefId(def.into())
                    }
                }
                _ => return None,
            }
        } else if let Some(it) = ast::Variant::cast(container.value.clone()) {
            let def = self.enum_variant_to_def(InFile::new(container.file_id, &it))?;
            let is_in_body =
                it.eq_token().is_some_and(|it| it.text_range().end() < child.text_range().start());
            if is_in_body { DefWithBodyId::from(def).into() } else { VariantId::from(def).into() }
        } else {
            let it = match Either::<ast::Pat, ast::Name>::cast(container.value)? {
                Either::Left(it) => ast::Param::cast(it.syntax().parent()?)?.syntax().parent(),
                Either::Right(it) => ast::SelfParam::cast(it.syntax().parent()?)?.syntax().parent(),
            }
            .and_then(ast::ParamList::cast)?
            .syntax()
            .parent()
            .and_then(ast::Fn::cast)?;
            let def = self.fn_to_def(InFile::new(container.file_id, &it))?;
            DefWithBodyId::from(def).into()
        };
        Some(cont)
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum ChildContainer {
    DefWithBodyId(DefWithBodyId),
    ModuleId(ModuleId),
    TraitId(TraitId),
    ImplId(ImplId),
    EnumId(EnumId),
    VariantId(VariantId),
    /// XXX: this might be the same def as, for example an `EnumId`. However,
    /// here the children are generic parameters, and not, eg enum variants.
    GenericDefId(GenericDefId),
}
impl_from! {
    DefWithBodyId,
    ModuleId,
    TraitId,
    ImplId,
    EnumId,
    VariantId,
    GenericDefId
    for ChildContainer
}

impl ChildContainer {
    fn child_by_source(self, db: &dyn HirDatabase, file_id: HirFileId) -> DynMap {
        let _p = tracing::info_span!("ChildContainer::child_by_source").entered();
        match self {
            ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
            ChildContainer::ModuleId(it) => it.child_by_source(db, file_id),
            ChildContainer::TraitId(it) => it.child_by_source(db, file_id),
            ChildContainer::ImplId(it) => it.child_by_source(db, file_id),
            ChildContainer::EnumId(it) => it.child_by_source(db, file_id),
            ChildContainer::VariantId(it) => it.child_by_source(db, file_id),
            ChildContainer::GenericDefId(it) => it.child_by_source(db, file_id),
        }
    }
}