Skip to main content

AnalysisSession

Struct AnalysisSession 

Source
pub struct AnalysisSession { /* private fields */ }
Expand description

Long-lived analysis context. Owns the salsa database and tracks which stubs have been loaded.

Cheap to clone the inner db for parallel reads; writes funnel through Self::ingest_file, Self::invalidate_file, and the crate-internal Self::with_db_mut.

Implementations§

Source§

impl AnalysisSession

Source

pub fn analyze_paths( &self, paths: &[PathBuf], opts: &BatchOptions, ) -> AnalysisResult

Run the full batch analysis pipeline on a set of file paths.

Source

pub fn re_analyze_file( &self, file_path: &str, new_content: &str, opts: &BatchOptions, ) -> AnalysisResult

Re-analyze a single file (definition collection + body analysis) within the batch context.

Mirrors the old ProjectAnalyzer::re_analyze_file cache-aware path. Use Self::reanalyze_dependents for LSP-style per-file flows that don’t need batch options.

Source

pub fn collect_definitions(&self, paths: &[PathBuf])

Collect type definitions only from paths into the codebase without analyzing method bodies or emitting issues. Used to load vendor types.

When a disk-backed cache is attached, per-file StubSlice results from previous runs are reused on a content-hash match, eliminating the parse + definition-collection step. Cache misses run the normal pipeline and write back so subsequent runs hit.

Source§

impl AnalysisSession

Source

pub fn source_of(&self, file: &str) -> Option<Arc<str>>

Retrieve the source text the session has registered for file, if any. Returns None when the file has never been ingested. Used by the parallel re-analysis path to re-feed dependents to body analysis without the caller having to track sources independently.

Source

pub fn reanalyze_dependents(&self, file: &str) -> Vec<(Arc<str>, FileAnalysis)>

Re-analyze every transitive dependent of file in parallel.

When the user saves a file that other files depend on (e.g. editing a base class, an interface, or a trait), those dependents may have new diagnostics. This method computes them in parallel using rayon and returns the per-file analysis results so the LSP server can publish updated diagnostics in one batch.

Source text for dependents is retrieved from the session’s salsa inputs (set by previous ingest_file calls) — the caller doesn’t need to track or re-read files. Files for which the session has no source are silently skipped (returns the analyzable subset).

Cross-file inferred return types are resolved on demand via salsa.

Source

pub fn reanalyze_dependents_cancellable( &self, file: &str, cancel: &IndexCancel, ) -> Vec<(Arc<str>, FileAnalysis)>

Cancellable variant of Self::reanalyze_dependents.

The consumer flips cancel (typically because a newer edit arrived) to abandon the re-analysis; the flag is checked at each file boundary. Salsa cannot unwind the plain-Rust body-analysis walk mid-flight, so a file already in progress finishes, but no further files are started. Files skipped due to cancellation are simply absent from the returned vec — the consumer should drop a stale flag and start fresh work on each edit.

Source

pub fn reanalyze_files_cancellable( &self, files: &[Arc<str>], cancel: &IndexCancel, ) -> Vec<(Arc<str>, FileAnalysis)>

Re-analyze an explicit file set — typically the editor’s currently open files — after an edit elsewhere in the workspace.

This is the rust-analyzer diagnostics model: instead of computing the edited file’s transitive dependents (an O(all-ingested-files) graph rebuild on every keystroke), the caller passes the handful of files it actually publishes diagnostics for, and salsa memoization makes the unaffected ones ~free — analyze_file re-validates each file’s memo against what actually changed and only re-executes bodies the edit reaches. Per-edit cost is O(open files), independent of workspace size.

Files the session has no source for are silently skipped. Cancellation semantics match Self::reanalyze_dependents_cancellable.

Source

pub fn class_imports(&self, file: &str) -> Vec<(Arc<str>, Arc<str>)>

FQCNs that file imports via use statements but that aren’t yet loaded in the session.

Designed as the input to background prefetching: after the LSP server Return the use-import alias map for a file: a list of (alias, fqcn) pairs where alias is the local name (e.g. "Str") and fqcn is the fully-qualified name (e.g. "Illuminate\\Support\\Str").

Completion handlers can use this to expand a short class name written before :: into its FQN before looking up static members, mirroring the same alias expansion that go-to-definition already performs via symbol_at + definition_of.

Returns an empty Vec if the file has not been ingested or has no use imports.

Source

pub fn pending_lazy_loads(&self, file: &str) -> Vec<Arc<str>>

ingests an open buffer, it can call this and lazy-load the returned FQCNs on a worker thread so the user’s first Cmd+Click into vendor code doesn’t pay the file-read+parse cost.

Returns an empty Vec if the file hasn’t been ingested or has no unresolved imports.

Source

pub fn prefetch_imports(&self, file: &str) -> usize

Convenience: synchronously lazy-load every import of file that isn’t already in the codebase. Returns the number successfully loaded.

For non-blocking prefetch, call this from a worker thread:

ⓘ
let s = session.clone();  // AnalysisSession is wrapped in Arc by callers
std::thread::spawn(move || {
    s.prefetch_imports(&file_path);
});

Uses a single shared-visited two-tier BFS across all pending imports (see [Self::load_classes_transitive_bounded]) with a shallow depth so member access on imported types type-checks without pulling in the entire vendor tree.

Source

pub fn all_classes(&self) -> Vec<(Arc<str>, Option<Location>)>

All class / interface / trait / enum FQCNs currently known to the session, each paired with the file that defines them when available.

Use this to build workspace-wide views (outline, fuzzy search, etc.). Consumers implement their own search/match logic on top — the analyzer only exposes the iterator.

Source

pub fn all_functions(&self) -> Vec<(Arc<str>, Option<Location>)>

All global function FQNs currently known to the session, each paired with their declaration location when available.

Source

pub fn classes_named( &self, short_name: &str, ) -> Vec<(Arc<str>, Option<Location>)>

Every class/interface/trait/enum in the workspace whose own short (unqualified) name is exactly short_name, each paired with its FQCN and declaration location.

Compatibility helper for callers that surface a bare class name without first resolving it to a canonical FQCN.

Source

pub fn ancestors_of(&self, fqcn: &str) -> Vec<Arc<str>>

Every ancestor of fqcn — extended class, implemented interfaces, used traits, transitively — most-derived first. Does not include fqcn itself. Memoized per FQCN (shares class_ancestors_by_fqcn’s tracked cache with member-resolution paths).

Source

pub fn function_signature(&self, fqn: &str) -> Option<Arc<FunctionDef>>

Full signature for a global function, resolved by FQN.

Source

pub fn dependency_graph(&self) -> DependencyGraph

File dependency graph: which files depend on which other files. Used for incremental invalidation in LSP servers and build systems.

File dependency graph: which files depend on which other files. Used for incremental invalidation in LSP servers and build systems.

O(edges) — iterates symbol edges from the reference index and from file_structural_symbols, then resolves each symbol to its defining file via O(1) lookup. Total cost is O(E) where E is the number of (file, symbol) edges.

Source§

impl AnalysisSession

Source

pub fn ingest_file(&self, file: Arc<str>, source: Arc<str>)

definition-collection ingestion. Updates the file’s source text in the salsa db, runs definition collection, and ingests the resulting stub slice. Triggers stub loading on first call. Also updates the cache’s reverse- dependency graph for file so cross-file invalidation stays correct across incremental edits — without rebuilding the graph from scratch.

If file was previously ingested, its old definitions and reference locations are removed first so renames / deletions don’t leave stale state in the codebase. (Without this, long-running sessions would accumulate dead reference-location entries indefinitely.)

Source

pub fn ingest_file_prepared(&self, file: Arc<str>, source: Arc<str>)

Self::ingest_file followed by the file’s Phase-1 warm-up (Self::prepare_file_for_analysis): its direct class references are resolved and lazy-loaded now, at write time, instead of serially at the front of the next references / re-analysis read.

This is the host edit-path entry point (rust-analyzer’s discipline: mutation happens only when text changes; requests are pure reads). Lazy loads triggered by the warm-up go through plain Self::ingest_file, so faulting in a dependency never cascades into preparing its dependencies — the load frontier stays one file wide.

Source

pub fn set_file_text(&self, file: Arc<str>, source: Arc<str>)

Register source as the text of file in the salsa input layer without parsing or running definition collection.

This is the LSP-friendly bulk-population entry point: after a workspace scan, callers can feed every discovered file’s text to the session cheaply (an Arc clone plus a HashMap insert per file). Name resolution then happens on demand via Self::load_class, which reads the file from disk through the configured crate::ClassResolver and runs definition collection lazily when a class FQCN actually needs to resolve.

Contrast with Self::ingest_file, which eagerly parses, runs definition collection, and populates the symbol index. Use ingest_file for files the user is actively editing (where in-memory text diverges from disk); use set_file_text for files known only through the workspace scan.

Clears the negative cache: a previously-unresolvable FQCN may now resolve if its defining file is among the newly-registered set.

Source

pub fn set_vendor_files<I>(&self, files: I)
where I: IntoIterator<Item = (Arc<str>, Arc<str>)>,

Bulk-register vendor / library files with HIGH salsa durability.

HIGH-durability files are not expected to change during the session. When a LOW-durability project file is edited, salsa can skip O(N) dependency verification for every HIGH-durability file, reducing workspace_symbol_index re-verification cost to O(project files only).

Definition collection runs lazily on first symbol access; no parsing at call time.

Source

pub fn rebuild_workspace_symbol_index(&self)

Build or refresh the WorkspaceSymbolIndexSingleton from all currently registered files.

After this call, find_class_like, find_function, and find_global_constant read singleton.index(db) — a single Durability::HIGH tracked dep — instead of recomputing the full O(N_files) dep list via workspace_symbol_index. On subsequent LOW-durability (project-file) body edits the dep short-circuits in O(1).

Call this once after all vendor + stub + project files have been ingested (end of workspace warm-up). Also called automatically by Self::ingest_file when a file’s declared names change.

Source

pub fn set_workspace_files<I>(&self, files: I)
where I: IntoIterator<Item = (Arc<str>, Arc<str>)>,

Bulk variant of Self::set_file_text. Acquires the salsa write lock once for the entire batch instead of once per file.

The intended LSP scan loop is:

let files: Vec<_> = walk_workspace()
    .map(|path| (path, fs::read(&path).unwrap()))
    .collect();
session.set_workspace_files(files);

After this call, every file’s source text is known to salsa. No parsing has happened yet — Definition collection runs per file on the first load_class that needs to consult it.

Source

pub fn index_generation(&self) -> u64

The workspace generation epoch — the rust-analyzer-style “are we up to date” counter. Bumped whenever a file is added or removed. A consumer records this alongside the diagnostics it publishes for a file; when the value later advances (background indexing registered more files), those files become candidates for re-analysis + re-publish.

Source

pub fn text_revision(&self) -> Revision

The text-write epoch — salsa’s own global revision, which bumps on every source-file add, edit, or removal (any input write at all), unlike Self::index_generation which only bumps on file add/remove/declaration changes (a body-only edit deliberately leaves it unchanged, to avoid over-invalidating workspace-enumeration queries). A cache of a per-file or per-query result — as opposed to declaration-shape state — must key on this instead: any text write can move or add/remove a reference location within that file, even when no declaration changed. See MirDbStorage::current_revision’s doc comment for why this must be salsa’s own revision rather than a hand-rolled counter.

Source

pub fn index_batch( &self, files: &[(Arc<str>, Arc<str>)], parallelism: IndexParallelism, cancel: &IndexCancel, ) -> IndexBatchOutcome

Index one bounded chunk of (path, text) files — the chunked background indexing primitive.

For each chunk this: (1) registers the files as Durability::HIGH salsa inputs in one short write window, (2) parses them to prime the in-process and on-disk declaration caches (in parallel when parallelism == [IndexParallelism::Rayon]; sequentially for wasm / single-thread consumers), and (3) merges their declarations into the workspace symbol index singleton incrementally (no full rebuild) so partially-indexed symbols resolve immediately.

The library spawns no thread: the consumer pumps chunks from its own driver (LSP worker thread, or one chunk per wasm event-loop tick), re-checking higher-priority work between calls. cancel is honoured at chunk boundaries so an edit can abandon queued indexing cheaply.

Contract: index the workspace incrementally through this method; don’t bulk-register the entire file set up front and then index — the first call lazily seeds the singleton from the currently-registered set (built-in stubs + this chunk), so keeping that initial set small keeps the first call cheap. Call Self::finalize_index once after the last chunk to reconcile authoritatively.

Responsiveness: parsing / declaration collection happens off the salsa write lock (on a snapshot); only the cheap symbol-map merge runs under the lock, so the write window per chunk is short and an interactive read on another thread blocks at most that long. Note that, per salsa’s snapshot model, a cancellable query in flight on another thread (e.g. hover, definition_of, FileAnalyzer::analyze_diagnostics_only) when this batch takes the write lock may unwind with salsa::Cancelled; a multi-threaded consumer should catch that and retry the request (the rust-analyzer pattern). A single-threaded consumer that interleaves requests between index_batch calls never observes cancellation.

Source

pub fn finalize_index(&self)

Authoritative full rebuild of the workspace symbol index. Call once after the consumer has pumped every Self::index_batch chunk (end of warm-up) to reconcile the incrementally-merged index against the full registered set. Cheap after indexing — every file’s declarations are already cached.

Source

pub fn warm_start_files(&self, files: &[(Arc<str>, Arc<str>)]) -> Vec<Arc<str>>

Replay disk-cached reference-location postings and subtype-index class edges for files, so a returning session’s find-references / goto-implementation queries are answered from the index immediately instead of paying the on-demand analysis sweep the first time each file is queried (indexed_references_to/indexed_subtype_classes’s freshness pass already handles a miss correctly — this only shortens the common warm-start case).

A no-op (per file) unless the disk cache from a previous run has an entry whose content hash matches files’ current text: Self::with_cache/ Self::with_cache_dir must be attached, and each file’s reference locations ([AnalysisCache], populated by the CLI batch pipeline) or definitions ([crate::stub_cache::StubSliceCache], populated by Self::ingest_file/vendor ingestion) must already be on disk from some earlier run/tool invocation against this exact content. A first- ever run (nothing cached yet) is unaffected — every file simply falls through to the existing lazy on-demand paths, same as without this call.

Registers files as Durability::HIGH salsa inputs (like Self::index_batch) if not already registered. Safe to call alongside index_batch in any order; both merge into the same maintained indexes.

Returns every file whose replayed postings carry an unresolved name (live_analyzed: false in RefCommit terms) — these are seeded, but indexed_references_to’s freshness pass can never treat them as immune to growth, so the first query that touches one of them pays a full analyze_file synchronously on the request path (measured: ~1.3-1.5s on a distinctive static method in a 15K-file workspace). The subset is known right here, at warm-start time — a caller with idle time between warm-start and the first live query (e.g. an LSP server between indexReady and the user’s first request) can close most of that gap by handing this list to Self::reanalyze_files_cancellable on a background thread, the same pattern Self::prefetch_imports uses for lazy-loaded vendor FQCNs:

ⓘ
let unresolved = session.warm_start_files(&files);
let s = session.clone();
std::thread::spawn(move || {
    s.reanalyze_files_cancellable(&unresolved, &crate::IndexCancel::new());
});

This only relocates cost that would otherwise land on a user-facing query — it cannot eliminate the freshness check itself, since an unresolved posting is workspace-generation-sensitive by design.

Source

pub fn settle_workspace_index(&self)

Reconcile the symbol-index singleton with mirror-only text writes.

Plain upsert_source_file_with_durability calls (an LSP host mirroring watcher-driven external edits or new files) bypass ingest_file’s incremental index maintenance; those files accumulate in a pending set while a singleton exists. Query entry points call this first so the singleton is never consulted stale. Declaration memos are pre-warmed on a snapshot (parallel, off the write lock); the per-file merge under the lock is then a memo hit.

Source

pub fn invalidate_file(&self, file: &str)

Drop a file’s contribution to the session: codebase definitions, reference locations, salsa input handle, cache entry, and outgoing reverse-dependency edges. Cache entries of dependent files are also evicted (cross-file invalidation).

Use this when a file is closed by the consumer, or before a re-ingest of substantially changed content. (Plain re-ingest via Self::ingest_file also drops old definitions, but does not remove the salsa input handle — call this for full cleanup.)

Source

pub fn tracked_file_count(&self) -> usize

Number of files currently tracked in this session’s salsa input set. Stable across reads; useful for diagnostics and memory bounds checks.

Source§

impl AnalysisSession

Source

pub fn contains_function(&self, fqn: &str) -> bool

Returns true if a function with fqn is registered and active in the codebase. Case-insensitive lookup with optional leading backslash.

Source

pub fn contains_class(&self, fqcn: &str) -> bool

Returns true if a class / interface / trait / enum with fqcn is registered and active in the codebase.

Source

pub fn contains_method(&self, class: &str, name: &str) -> bool

Returns true if class has a method named name registered. Method names are matched case-insensitively (PHP method dispatch semantics).

Source

pub fn load_class(&self, fqcn: &str) -> LoadOutcome

Resolve fqcn via the configured crate::ClassResolver and ingest the mapped file. The session keeps a negative cache so repeated calls for an unresolvable name don’t re-hit the resolver; the cache is invalidated on any Self::ingest_file / Self::invalidate_file.

This is the LSP-friendly entry point: the analyzer never touches vendor/ on its own, but consumers can ask it to resolve individual symbols on demand. Designed to be called when a diagnostic would otherwise report UndefinedClass.

Returns a crate::LoadOutcome distinguishing already-loaded / freshly-loaded / not-resolvable. Use crate::LoadOutcome::is_loaded when only success matters.

Source§

impl AnalysisSession

Source

pub fn analyze_file_diagnostics(&self, file: &str, source: &str) -> FileAnalysis

Open-file diagnostics entrypoint for issue-only consumers.

Updates the session’s current text for file, reuses the tracked parse and definition queries for syntax/collector diagnostics, then runs the targeted open-file body-analysis path without retaining whole-file ResolvedSymbol payloads.

Source

pub fn name_at(&self, file: &str, byte_offset: u32) -> Option<Name>

Resolve the codebase-level symbol name at byte_offset in file.

This is the compact cursor-navigation helper for consumers that only need symbol identity (for example, references queries) and not the full ResolvedSymbol payload.

Source

pub fn symbol_at(&self, file: &str, byte_offset: u32) -> Option<ResolvedSymbol>

Resolve the symbol at byte_offset in file’s current ingested text.

Canonical open-file navigation entrypoint: unlike crate::FileAnalysis::symbol_at, this does not require the caller to retain a whole-file symbol list from diagnostics.

Source

pub fn resolve_at(&self, file: &str, byte_offset: u32) -> Option<ResolvedSymbol>

Resolve the symbol at byte_offset in file’s current ingested text.

This powers hover / go-to-definition without requiring the caller to retain a whole-file ResolvedSymbol list: the containing scope is re-analyzed on demand and only its symbol payload is searched.

Side effects: like Self::definition_of and Self::hover, this may fault in direct dependencies of file by running the open-file warm-up path (prepare_file_for_analysis) before snapshotting.

Source

pub fn hover_at( &self, file: &str, byte_offset: u32, ) -> Result<HoverInfo, SymbolLookupError>

Hover information for the symbol at byte_offset in file.

Uses the targeted Self::resolve_at navigation path, then resolves the resulting symbol’s hover payload.

Source

pub fn definition_at( &self, file: &str, byte_offset: u32, ) -> Result<Location, SymbolLookupError>

Definition location for the symbol at byte_offset in file.

Uses the targeted Self::resolve_at navigation path, then resolves the resulting symbol to its declaration site.

Source

pub fn references_at( &self, file: &str, byte_offset: u32, files: &[Arc<str>], include_declaration: bool, includes: ReferenceIncludes, ) -> Result<Vec<(Arc<str>, Range)>, SymbolLookupError>

Reference locations for the symbol at byte_offset in file.

This is the compact cursor-navigation entrypoint for find-references: resolve a typed symbol identity with Self::name_at, then answer the query from the maintained reference index.

Source

pub fn references_at_cancellable( &self, file: &str, byte_offset: u32, files: &[Arc<str>], include_declaration: bool, includes: ReferenceIncludes, should_cancel: &(dyn Fn() -> bool + Sync), ) -> Result<Option<Vec<(Arc<str>, Range)>>, SymbolLookupError>

Cancellable variant of Self::references_at.

Returns Err(NotFound) when no symbol exists at the cursor. Returns Ok(None) when should_cancel aborts the underlying indexed query.

Source

pub fn definition_of( &self, symbol: &Name, ) -> Result<Location, SymbolLookupError>

Resolve a top-level symbol (class or function) to its declaration location. Powers go-to-definition.

Side effects: if the symbol isn’t yet known, this may invoke the configured crate::SourceProvider to fault in additional files and mutate the salsa input set. Use Self::definition_of_cached for a pure variant that only consults already-loaded state.

Returns:

  • Ok(Location) — symbol found with a source location
  • Err(NotFound) — no such symbol in the codebase
  • Err(NoSourceLocation) — symbol exists but has no recorded span (e.g. some stub-only declarations)
Source

pub fn definition_of_cached( &self, symbol: &Name, ) -> Result<Location, SymbolLookupError>

Pure variant of Self::definition_of. Never invokes the crate::SourceProvider and never mutates salsa inputs; resolves only against state already loaded by set_file_text / ingest_file. Returns Err(NotFound) when the symbol isn’t in the loaded set, even if a resolver could in principle map it.

Source

pub fn hover(&self, symbol: &Name) -> Result<HoverInfo, SymbolLookupError>

Hover information for a symbol: type, docstring, and definition location.

For cursor-based editor navigation, prefer Self::hover_at, which uses the targeted Self::resolve_at path instead of requiring a retained whole-file symbol list. This method assembles hover data once the caller already has a typed crate::Name.

Side effects: when symbol’s owning class isn’t yet loaded, this may invoke the configured crate::SourceProvider to fault in dependencies. Use Self::hover_cached for a pure variant.

Returns Err(NotFound) if the symbol doesn’t exist. May still return Ok with docstring: None or definition: None if those specific pieces aren’t available.

Source

pub fn hover_cached( &self, symbol: &Name, ) -> Result<HoverInfo, SymbolLookupError>

Pure variant of Self::hover. Never invokes the crate::SourceProvider; consults only the already-loaded db.

Source

pub fn subtype_files(&self, class_fqn: &str) -> Vec<Arc<str>>

Files declaring transitive subclasses of class_fqn, backed by the maintained subtype index (see Self::indexed_subtype_classes). Excludes class_fqn’s own declaring file — the caller adds it.

Lets a reference-search caller scope a protected member to its class hierarchy without reconstructing that hierarchy from declaration text: subclasses are matched by resolved FQCN, so extends \Ns\Base and aliased use forms are all found. Read-only from the caller’s perspective; may trigger an on-demand commit of stale/uncommitted candidates’ class edges (same self-heal indexed_subtype_classes uses).

Source

pub fn indexed_use_import_locations( &self, symbol: &Name, files: &[Arc<str>], ) -> Vec<(Arc<str>, Range)>

Compatibility wrapper for callers that only want use import items. Delegates to Self::indexed_references_to so import references share the same freshness/self-heal path, scope filtering, and memoization boundary as every other reference query.

Source

pub fn indexed_references_to( &self, symbol: &Name, files: &[Arc<str>], include_declaration: bool, includes: ReferenceIncludes, should_cancel: &(dyn Fn() -> bool + Sync), ) -> Option<Vec<(Arc<str>, Range)>>

Inverted-index find-references: posting-list lookup plus an on-demand freshness/completeness pass over files (the host’s candidate scope — passing the whole workspace is fine; see the gate below).

A candidate whose postings were committed from its current input text (Arc identity) is answered from the index with no salsa work at all. Stale or never-committed candidates are analyzed via the memoized analyze_file query and committed, so each file pays that cost once per text change — after a background warm sweep the steady state is a pure lookup, O(results) instead of O(candidates). Never-committed candidates are additionally gated on their raw text mentioning the symbol’s name (whole-identifier, ASCII-case-insensitive), so hosts need no text prefilter of their own — and must not use one, since a host-side filter cannot know these matching semantics.

Results are filtered to files (the host controls scope — e.g. workspace files only, excluding stubs/vendor). With include_declaration, the symbol’s declaration name span is appended when it lies inside the scope.

Returned ranges use mir’s native coordinates: 1-based lines and 0-based Unicode code-point columns (UTF-32/LSP positionEncoding "utf-32"), not UTF-8 byte offsets or UTF-16 code units.

should_cancel is polled at phase boundaries and between cancellation retries; true aborts with None.

Memoized per (symbol, files, include_declaration, includes, text_revision) — see [RefQueryCacheKey]. The freshness scan below still costs O(candidates) even when every candidate is already committed (it has to check), so a caller re-running the same query against unchanged state (e.g. a host recomputing reference counts for a code-lens refresh) would otherwise re-pay that scan on every call; this makes the repeat a single hashmap lookup instead.

Source

pub fn declaration_name_range(&self, symbol: &Name) -> Option<(Arc<str>, Range)>

The symbol’s declaration site, narrowed from the collector’s whole-declaration span to the declared name’s own token (matching the span shape of recorded references).

Source

pub fn indexed_subtype_classes( &self, class_fqn: &str, files: &[Arc<str>], include_trait_users: bool, ) -> Vec<SubtypeClassSite>

Transitive subtypes of class_fqn (classes/interfaces/enums whose resolved ancestor chain reaches it), answered from the maintained subtype edge index.

files is the host’s candidate scope for the on-demand completeness pass: per BFS round, not-yet-committed files whose text mentions a frontier name get their definitions committed, so results are complete even before a background sweep has covered the workspace. Committed files answer from the index with no parsing at all.

include_trait_users also counts use Trait; composition as a subtype edge (visibility-scoping semantics); leave it off for goto-implementation semantics (extends/implements only).

Memoized per (class_fqn, include_trait_users, files, text_revision) — same shape and rationale as Self::indexed_references_to’s cache: commit_defs_for_matching’s freshness pass costs O(candidates) on every call regardless of outcome, so a repeat query (e.g. a host resolving a protected/static method’s reference scope on every code-lens refresh) would otherwise re-pay it every time.

Source

pub fn indexed_method_implementations( &self, class_fqn: &str, method: &str, files: &[Arc<str>], ) -> Vec<(Arc<str>, Arc<str>, Range)>

Concrete implementations of class_fqn::method across its transitive subtypes: the same-named non-abstract method available to each subtype (its own declaration, or one inherited/composed from a parent, trait, or mixin), as (subtype fqcn, file, name range). Subtypes resolving to the same declaring location collapse to a single entry.

Source

pub fn class_issues(&self, files: &[Arc<str>]) -> Vec<Issue>

Class-level issues (inheritance violations, abstract-method gaps, override incompatibilities) for the given set of files.

These checks are cross-file by nature and are not emitted by crate::FileAnalyzer::analyze. Call this after ingesting or re-analyzing a file and its dependents to get the full diagnostic picture.

Circular-inheritance checks always run against the full workspace graph regardless of the files filter — a cycle is a workspace-wide problem.

Source

pub fn collector_issues(&self, files: &[Arc<str>]) -> Vec<Issue>

Collector-phase issues (e.g. BackedEnumCaseTypeMismatch, InvalidReadonlyPropertyDeclaration, InvalidDocblock, and raw parse errors) for the given files.

These are found while building a file’s declaration slice ([crate::db::collect_file_definitions]), before body analysis or cross-file class checks ever run — neither crate::FileAnalyzer::analyze nor Self::class_issues reads them, so a caller merging just those two sources silently drops every collector-time diagnostic. Call this alongside them to get the full picture.

A plain snapshot read through [crate::db::collect_file_definitions], same as Self::document_symbols — correct regardless of which path put the file’s text into the db (ingest_file, set_file_text, lazy vendor load) or how many times. The parse-cache fast paths behind that query used to zero out issues on any hit, including re-collecting the same file after its salsa memo was invalidated; they now preserve (and, for a genuinely different file sharing content, re-point) the originally-computed issues instead.

Source

pub fn document_symbols(&self, file: &str) -> Vec<DocumentSymbol>

All declarations defined in file as a hierarchical tree.

Classes/interfaces/traits/enums are returned with their methods, properties, and constants nested in children. Top-level functions and constants are returned with empty children.

Source§

impl AnalysisSession

Source

pub fn ensure_essential_stubs(&self)

👎Deprecated:

use ensure_all_stubs() or ensure_stubs_for_ast() instead

Deprecated — stub loading is now fully lazy per-AST.

This is an alias for Self::ensure_all_stubs kept for API compatibility. Internal analysis paths use Self::prepare_ast_for_analysis which loads only the stubs referenced by the file under analysis.

Source

pub fn ensure_all_stubs(&self)

Load every embedded PHP stub plus any configured user stubs. Use for batch tools (CLI, full project analysis) where comprehensive symbol coverage matters more than cold-start latency.

Source

pub fn loaded_stub_count(&self) -> usize

Number of distinct embedded stubs currently ingested into the session. Useful for diagnostics and bench reporting.

Source

pub fn ensure_stubs_for_source(&self, source: &str)

Auto-discover and ingest the embedded stubs needed to cover every built-in PHP function / class / constant referenced by source.

Used by the open-file analysis paths (crate::FileAnalyzer::analyze and crate::FileAnalyzer::analyze_diagnostics_only) to keep essentials-only mode correct without forcing callers to enumerate which stubs they need. Idempotent — already-loaded stubs are skipped via [Self::loaded_stubs].

The discovery scan is a coarse identifier sweep (see [crate::stubs::collect_referenced_builtin_paths]) — it may pull in a slightly larger set than the file strictly needs, but never misses a referenced built-in. Cost is sub-millisecond per file.

Fast path: if every embedded stub is already loaded (e.g. after a batch tool called Self::ensure_all_stubs), the source scan is skipped entirely.

Source

pub fn ensure_stubs_for_ast(&self, program: &Program)

Discover and ingest stubs by walking the parsed AST of a PHP file.

Similar to Self::ensure_stubs_for_source, but takes an already-parsed AST instead of raw source text. Produces zero false positives since it only extracts identifiers from actual AST nodes (not from strings or comments). Preferred over ensure_stubs_for_source when the AST is already available (e.g., in crate::FileAnalyzer).

Idempotent and skips the scan if all stubs are already loaded.

Source

pub fn has_resolver(&self) -> bool

Returns true if this session has a configured class resolver (typically a PSR-4 / classmap autoloader chained with the stub resolver). Used by FileAnalyzer to skip the AST-scan preload when no resolver is wired up.

Source

pub fn prepare_ast_for_analysis(&self, program: &Program, file: &str)

Run all pre-passes (builtin-stub loading, vendor-eager-file loading, and PSR-4 class preloading) before body analysis of a single file.

Replaces the two separate ensure_stubs_for_ast / preload_psr4_classes_for_ast calls at every open-file analysis site.

Source

pub fn prepare_file_for_analysis(&self, path: &Arc<str>)

Run file’s Phase-1 warm-up — resolve its direct class references and lazy-load any not yet indexed — and record it as prepared against its current text. No-op when the file is unknown or already prepared.

This is the write-path home of the warm-up: hosts call it (via Self::ingest_file_prepared) when text lands, so read paths (indexed_references_to, reanalyze_files_cancellable) find every candidate prepared and stay pure. Loading mutates salsa inputs, so the parse snapshot is scoped and dropped before the warm-up runs — callers must not hold a live snapshot across this call.

Source

pub fn bump_prepare_generation(&self)

Invalidate every warm-up skip entry. Call when previously loaded declarations may have been removed outside Self::ingest_file / Self::invalidate_file — e.g. a host that writes file text straight into the salsa layer and detects a declaration-level change. A prepared file might then need its warm-up re-run to lazy-load a replacement (a vendor class shadowed by a since-deleted project class).

Source

pub fn priority_index_for_ast(&self, program: &Program, file: &str)

Priority-index the classes directly referenced by file’s AST.

In the eager-static-input model the background indexer (Self::index_batch) walks the whole vendor tree, but it may not have reached every file the open buffer references yet. To avoid a transient false UndefinedClass during the warm-up window, this reorders that static work: it resolves the buffer’s direct class references and loads any not-yet-indexed ones immediately, jumping them to the front of the queue.

This is bounded by the number of distinct direct references in one file — no transitive BFS, no depth/total budget, no pinning. Inheritance ancestors and signature types of those classes are picked up by the background walk (or, for navigation, by Self::hover / Self::definition_of). Because bump_workspace_revision no longer nulls the workspace index singleton, each Self::load_class here costs only a resolver lookup + parse (or cache hit) + one tier-aware merge, invalidating just the actively-analyzed file’s memo once — not the whole cache. Once background indexing completes this is a no-op (every reference already resolves).

Source§

impl AnalysisSession

Source

pub fn new(php_version: PhpVersion) -> Self

Create a session targeting the given PHP language version.

Source

pub fn ref_query_cache_hits(&self) -> u64

Hits served from Self::indexed_references_to’s memoization cache. Diagnostic only — see the field doc on ref_query_cache_hits.

Source

pub fn ref_query_cache_locations(&self) -> usize

Total cached reference-location count across ref_query_cache. Diagnostic only — lets tests/hosts assert the cache stays under its byte-proportional cap rather than trusting entry count alone.

Source

pub fn subtype_query_cache_hits(&self) -> u64

Hits served from Self::indexed_subtype_classes’s memoization cache. Diagnostic only.

Source

pub fn subtype_query_cache_sites(&self) -> usize

Total cached site count across subtype_query_cache. Diagnostic only — same purpose as ref_query_cache_locations.

Source

pub fn ref_index_lock_count(&self) -> u64

Times the reference index has been locked on this session’s db.

Source

pub fn class_mention_stats(&self) -> ClassMentionStats

Coverage/size counters for the class-mention gate index (host metrics and memory-bound checks).

Source

pub fn files_mentioning_class( &self, files: &[Arc<str>], class_name: &str, ) -> Vec<Arc<str>>

Which of files currently mention class_name as a whole identifier (case-insensitive) — the same persistent, incrementally- maintained mechanism Self::indexed_references_to’s own gate uses internally (see db::class_mention_index’s doc comment), exposed so a host doesn’t need to re-implement an equivalent from-scratch text scan for its own narrowing (e.g. a reachability pre-filter ahead of a references/code-lens query). A file already scanned against a universe that included class_name answers via an O(log n) lookup with no text pass at all; only a never-scanned or since-edited file pays a scan, and that scan is recorded for every other name already in the universe too, not just this one.

Scope: the universe is seeded from declared class-like names (classes/interfaces/traits/enums) — the same population mir’s own indexing already does as a side effect — but a class_name outside that set is not a reason to skip narrowing: it’s admitted verbatim on the spot (see Self::files_mentioning_any) and answered by a real scan, same as any other needle.

Source

pub fn files_mentioning_any( &self, files: &[Arc<str>], needles: &[&str], ) -> Vec<Arc<str>>

Which of files currently mention ANY of needles as a whole identifier (case-insensitive) — the multi-needle form of Self::files_mentioning_class, for a caller resolving several candidate names at once (e.g. an owner FQN plus its subtype closure). Every needle is admitted into the shared universe verbatim (no short-name stripping) before querying, so this call’s answer is never conservative on account of an unknown needle — a file only falls back to a raw scan when it was scanned before all of needles existed in the universe and hasn’t been touched since; that one scan then answers every needle in the universe at once, not just this call’s.

Source

pub fn flush_analysis_cache(&self)

Persist the attached [AnalysisCache] to disk. No-op without an attached cache or when nothing changed since the last flush. Reference postings committed by session sweeps and on-demand query freshness passes reach disk only here — a host should call this after its warm sweep completes and on shutdown so the next launch’s Self::warm_start_files finds them.

Source

pub fn workspace_symbol_index_ready(&self) -> bool

Whether the workspace symbol index singleton is populated (seeded by Self::warm_start_files or built by index_batch) — symbol lookups answer from the O(1) map instead of the tracked O(all-files) walk.

Source

pub fn workspace_index_walks(&self) -> u64

Executions of the tracked O(all-files) workspace_symbol_index walk (diagnostic; a warm-started session should keep this at zero).

Source

pub fn with_source_provider(self, provider: Arc<dyn SourceProvider>) -> Self

Swap in a custom crate::SourceProvider. LSPs install a VFS-backed provider here so the analyzer reads from unsaved editor buffers instead of disk.

Source

pub fn with_cache(self, cache: Arc<AnalysisCache>) -> Self

Attach a pre-built [AnalysisCache] (the body-analysis issue cache) and open a sibling definition StubSlice cache under the same root, so callers using this builder get the same speedup as with_cache_dir.

Rebuilds the shared database to attach the definition cache — call before any file is ingested. A debug assertion catches misuse.

Source

pub fn with_cache_dir(self, cache_dir: &Path) -> Self

Convenience: open a disk-backed cache at cache_dir and attach it.

Attaches both the body-analysis issue cache ([AnalysisCache]) and the definition StubSlice cache to the shared database. Builds a fresh [AnalyzerDb] internally — call before any file is ingested. A debug assertion catches misuse.

Source

pub fn with_psr4(self, map: Arc<Psr4Map>) -> Self

Attach a Composer autoload map (PSR-4, PSR-0, classmap, files). Sets the same map as the active crate::ClassResolver so Self::load_class works out of the box.

Source

pub fn with_class_resolver(self, resolver: Arc<dyn ClassResolver>) -> Self

Attach a generic class resolver for projects that don’t use Composer (WordPress, Drupal, custom autoloaders, workspace-walk indexes). Replaces any previously-set Composer-backed resolver. Automatically wrapped with stub awareness so PHP built-ins remain resolvable.

Source

pub fn with_user_stubs(self, files: Vec<PathBuf>, dirs: Vec<PathBuf>) -> Self

Source

pub fn php_version(&self) -> PhpVersion

Source

pub fn cache(&self) -> Option<&AnalysisCache>

Source

pub fn psr4(&self) -> Option<&Psr4Map>

Trait Implementations§

Source§

impl Clone for AnalysisSession

Source§

fn clone(&self) -> AnalysisSession

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Lookup<T> for T

Source§

fn into_owned(self) -> T

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more