Skip to main content

resolve_lang/
lib.rs

1//! # resolve_lang
2//!
3//! Name resolution over HIR: every path and bare-identifier pattern of a
4//! [`hir_lang::Hir`] is bound to the binder or definition it names, under a
5//! language's scoping [`Policy`], with diagnostics (did-you-mean included)
6//! for what does not resolve, and a persistent [`Index`] of definitions and
7//! references for go-to-definition, find-references, rename, and document
8//! symbols.
9//!
10//! ## The lazy path
11//!
12//! [`resolve`] takes a unit and the interner its names came from, and
13//! returns the resolved unit, its diagnostics, and the index:
14//!
15//! ```
16//! use hir_lang::{Builder, Expr, Name, Res};
17//! use intern_lang::Interner;
18//!
19//! // fn double(n) { n }   fn main() { double }
20//! let mut names = Interner::new();
21//! let (n, double) = (Name::new(names.intern("n")), Name::new(names.intern("double")));
22//! let mut b = Builder::new();
23//! let (param, n_binder) = b.local_param(n);
24//! let use_n = b.name_expr(n);
25//! let body = b.block(&[], Some(use_n));
26//! let f = b.func(double, &[param], body);
27//! let use_double = b.name_expr(double);
28//! let main_body = b.block(&[], Some(use_double));
29//! let main = b.func(Name::new(names.intern("main")), &[], main_body);
30//! let root = b.module(None, &[f, main]);
31//! let hir = b.finish(root)?;
32//! let unit = hir.unit();
33//!
34//! let res = resolve_lang::resolve(hir, &names)?;
35//! assert!(res.is_clean());
36//! let hir = res.hir(unit).unwrap();
37//! let Expr::Path(p) = *hir.expr(use_n) else { unreachable!() };
38//! assert_eq!(hir.path(p).res, Res::Local(n_binder));
39//! let Expr::Path(p) = *hir.expr(use_double) else { unreachable!() };
40//! assert_eq!(hir.path(p).res, Res::Def(hir.def(hir_lang::Def::Item(f))));
41//! # Ok::<(), Box<dyn std::error::Error>>(())
42//! ```
43//!
44//! [`Resolver`] adds a policy, an [`Env`] for names outside the unit, and a
45//! [`Budget`]; [`Program`] resolves several units against each other.
46//!
47//! ## What resolution guarantees
48//!
49//! - Every path of the unit ends in exactly one of: resolved (fully, or a
50//!   prefix with the rest left to type-directed resolution, as HIR spec
51//!   ยง3.3 allows); resolved with exactly one access diagnostic (private or
52//!   protected); `Res::Err` with exactly one diagnostic; or untouched because
53//!   it was already resolved (or already an error) on input, or because it is
54//!   a bare identifier pattern that binds.
55//! - Resolutions are written through `Hir::resolve_partial`, so the result is
56//!   a valid `Hir`; a resolution hir-lang refuses becomes `Res::Err` with a
57//!   diagnostic instead.
58//! - Binders are found with `Hir::lookup_local` and the walk's scope events:
59//!   the HIR's scope rules are never re-derived here.
60//! - Resolution is deterministic, iterative (no recursion over input), and
61//!   bounded: linear in the program apart from glob propagation, inheritance
62//!   walks, and suggestions, each capped by the [`Budget`].
63//! - The [`Index`] is consistent: every reference's definition lists that
64//!   reference, and nothing else.
65//!
66//! ## `no_std`
67//!
68//! The crate needs only `alloc`. The default `std` feature is additive.
69
70#![cfg_attr(not(feature = "std"), no_std)]
71#![cfg_attr(docsrs, feature(doc_cfg))]
72#![forbid(unsafe_code)]
73#![deny(missing_docs)]
74#![deny(unused_must_use)]
75#![deny(unused_results)]
76#![deny(clippy::unwrap_used)]
77#![deny(clippy::expect_used)]
78#![deny(clippy::panic)]
79#![deny(clippy::todo)]
80#![deny(clippy::unimplemented)]
81#![deny(clippy::unreachable)]
82#![deny(clippy::print_stdout)]
83#![deny(clippy::print_stderr)]
84#![deny(clippy::dbg_macro)]
85
86extern crate alloc;
87
88mod collect;
89mod diag;
90mod env;
91mod error;
92mod imports;
93mod index;
94mod members;
95mod model;
96mod pass;
97mod policy;
98mod program;
99mod suggest;
100
101pub use diag::{DiagKind, Diagnostic};
102pub use env::{DefKind, Env, Export, MapEnv, NoEnv};
103pub use error::{Budget, Limit, ResolveError};
104pub use index::{
105    DefRef, Definition, Index, Location, Occurrence, Reference, References, RenameSet, SymbolKind,
106    Target,
107};
108pub use policy::{
109    Case, ClassScope, Hoist, ItemClass, ModuleScope, Namespace, NsSet, Policy, Redefinition,
110    Reexport, RootBinding, Shadowing,
111};
112pub use program::{ClassMember, Program, Resolution, ResolvedUnit, Resolver};
113
114use hir_lang::Hir;
115use intern_lang::Lookup;
116
117/// Resolves one unit with the default (lexical, Kraken-style) policy and no
118/// environment: the lazy path.
119///
120/// `names` is the interner the unit's names were interned in; it is read
121/// only to compare spellings for did-you-mean suggestions.
122///
123/// # Errors
124///
125/// [`ResolveError::BudgetExceeded`] if the unit exceeds the default
126/// [`Budget`]. Name errors in the program are diagnostics in the result, not
127/// errors.
128///
129/// # Examples
130///
131/// ```
132/// use hir_lang::{Builder, Name};
133/// use intern_lang::Interner;
134///
135/// let mut names = Interner::new();
136/// let mut b = Builder::new();
137/// let lenght = b.name_expr(Name::new(names.intern("lenght")));
138/// let body = b.block(&[], Some(lenght));
139/// let length = b.func(Name::new(names.intern("length")), &[], body);
140/// let root = b.module(None, &[length]);
141///
142/// let res = resolve_lang::resolve(b.finish(root)?, &names)?;
143/// let d = &res.diagnostics()[0];
144/// assert_eq!(d.message(&names), "cannot find value `lenght` in this scope; did you mean `length`?");
145/// # Ok::<(), Box<dyn std::error::Error>>(())
146/// ```
147pub fn resolve<L: Lookup>(hir: Hir, names: &L) -> Result<Resolution, ResolveError> {
148    Resolver::new(Policy::new()).resolve(hir, names)
149}
150
151/// Compiles and runs the `rust` code blocks in `README.md` and `docs/API.md` as
152/// part of `cargo test`, so the published examples cannot drift from the API.
153#[cfg(doctest)]
154#[doc = include_str!("../README.md")]
155#[doc = include_str!("../docs/API.md")]
156pub struct MarkdownDocTests;