1 //! ide crate provides "ide-centric" APIs for the rust-analyzer. That is,
2 //! it generally operates with files and text ranges, and returns results as
3 //! Strings, suitable for displaying to the human.
5 //! What powers this API are the `RootDatabase` struct, which defines a `salsa`
6 //! database, and the `hir` crate, where majority of the analysis happens.
7 //! However, IDE specific bits of the analysis (most notably completion) happen
10 // For proving that RootDatabase is RefUnwindSafe.
11 #![recursion_limit = "128"]
12 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
15 macro_rules
! eprintln
{
16 ($
($tt
:tt
)*) => { stdx::eprintln!($($tt)*) }
;
24 mod navigation_target
;
30 mod highlight_related
;
37 mod goto_implementation
;
38 mod goto_type_definition
;
53 mod syntax_highlighting
;
59 mod shuffle_crate_graph
;
66 salsa
::{self, ParallelDatabase}
,
67 CrateOrigin
, Env
, FileLoader
, FileSet
, SourceDatabase
, VfsPath
,
69 symbol_index
, LineIndexDatabase
,
71 use syntax
::SourceFile
;
73 use crate::navigation_target
::{ToNav, TryToNav}
;
76 annotations
::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}
,
77 call_hierarchy
::CallItem
,
78 expand_macro
::ExpandedMacro
,
79 file_structure
::{StructureNode, StructureNodeKind}
,
80 folding_ranges
::{Fold, FoldKind}
,
81 highlight_related
::{HighlightRelatedConfig, HighlightedRange}
,
82 hover
::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}
,
84 AdjustmentHints
, AdjustmentHintsMode
, ClosureReturnTypeHints
, DiscriminantHints
, InlayHint
,
85 InlayHintLabel
, InlayHintsConfig
, InlayKind
, InlayTooltip
, LifetimeElisionHints
,
87 join_lines
::JoinLinesConfig
,
89 moniker
::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation}
,
91 navigation_target
::NavigationTarget
,
92 prime_caches
::ParallelPrimeCachesProgress
,
93 references
::ReferenceSearchResult
,
95 runnables
::{Runnable, RunnableKind, TestId}
,
96 signature_help
::SignatureHelp
,
97 static_index
::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData}
,
98 syntax_highlighting
::{
99 tags
::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}
,
100 HighlightConfig
, HlRange
,
103 pub use hir
::{Documentation, Semantics}
;
104 pub use ide_assists
::{
105 Assist
, AssistConfig
, AssistId
, AssistKind
, AssistResolveStrategy
, SingleResolve
,
107 pub use ide_completion
::{
108 CallableSnippets
, CompletionConfig
, CompletionItem
, CompletionItemKind
, CompletionRelevance
,
109 Snippet
, SnippetScope
,
113 Cancelled
, Change
, CrateGraph
, CrateId
, Edition
, FileId
, FilePosition
, FileRange
,
114 SourceRoot
, SourceRootId
,
117 line_index
::{LineCol, LineColUtf16, LineIndex}
,
118 search
::{ReferenceCategory, SearchScope}
,
119 source_change
::{FileSystemEdit, SourceChange}
,
121 RootDatabase
, SymbolKind
,
123 pub use ide_diagnostics
::{Diagnostic, DiagnosticsConfig, ExprFillDefaultMode, Severity}
;
124 pub use ide_ssr
::SsrError
;
125 pub use syntax
::{TextRange, TextSize}
;
126 pub use text_edit
::{Indel, TextEdit}
;
128 pub type Cancellable
<T
> = Result
<T
, Cancelled
>;
130 /// Info associated with a text range.
132 pub struct RangeInfo
<T
> {
133 pub range
: TextRange
,
137 impl<T
> RangeInfo
<T
> {
138 pub fn new(range
: TextRange
, info
: T
) -> RangeInfo
<T
> {
139 RangeInfo { range, info }
143 /// `AnalysisHost` stores the current state of the world.
145 pub struct AnalysisHost
{
150 pub fn new(lru_capacity
: Option
<usize>) -> AnalysisHost
{
151 AnalysisHost { db: RootDatabase::new(lru_capacity) }
154 pub fn update_lru_capacity(&mut self, lru_capacity
: Option
<usize>) {
155 self.db
.update_lru_capacity(lru_capacity
);
158 /// Returns a snapshot of the current state, which you can query for
159 /// semantic information.
160 pub fn analysis(&self) -> Analysis
{
161 Analysis { db: self.db.snapshot() }
164 /// Applies changes to the current state of the world. If there are
165 /// outstanding snapshots, they will be canceled.
166 pub fn apply_change(&mut self, change
: Change
) {
167 self.db
.apply_change(change
)
170 /// NB: this clears the database
171 pub fn per_query_memory_usage(&mut self) -> Vec
<(String
, profile
::Bytes
)> {
172 self.db
.per_query_memory_usage()
174 pub fn request_cancellation(&mut self) {
175 self.db
.request_cancellation();
177 pub fn raw_database(&self) -> &RootDatabase
{
180 pub fn raw_database_mut(&mut self) -> &mut RootDatabase
{
184 pub fn shuffle_crate_graph(&mut self) {
185 shuffle_crate_graph
::shuffle_crate_graph(&mut self.db
);
189 impl Default
for AnalysisHost
{
190 fn default() -> AnalysisHost
{
191 AnalysisHost
::new(None
)
195 /// Analysis is a snapshot of a world state at a moment in time. It is the main
196 /// entry point for asking semantic information about the world. When the world
197 /// state is advanced using `AnalysisHost::apply_change` method, all existing
198 /// `Analysis` are canceled (most method return `Err(Canceled)`).
200 pub struct Analysis
{
201 db
: salsa
::Snapshot
<RootDatabase
>,
204 // As a general design guideline, `Analysis` API are intended to be independent
205 // from the language server protocol. That is, when exposing some functionality
206 // we should think in terms of "what API makes most sense" and not in terms of
207 // "what types LSP uses". Although currently LSP is the only consumer of the
208 // API, the API should in theory be usable as a library, or via a different
211 // Creates an analysis instance for a single file, without any external
212 // dependencies, stdlib support or ability to apply changes. See
213 // `AnalysisHost` for creating a fully-featured analysis.
214 pub fn from_single_file(text
: String
) -> (Analysis
, FileId
) {
215 let mut host
= AnalysisHost
::default();
216 let file_id
= FileId(0);
217 let mut file_set
= FileSet
::default();
218 file_set
.insert(file_id
, VfsPath
::new_virtual_path("/main.rs".to_string()));
219 let source_root
= SourceRoot
::new_local(file_set
);
221 let mut change
= Change
::new();
222 change
.set_roots(vec
![source_root
]);
223 let mut crate_graph
= CrateGraph
::default();
224 // FIXME: cfg options
225 // Default to enable test for single file.
226 let mut cfg_options
= CfgOptions
::default();
227 cfg_options
.insert_atom("test".into());
228 crate_graph
.add_crate_root(
238 CrateOrigin
::CratesIo { repo: None, name: None }
,
241 change
.change_file(file_id
, Some(Arc
::new(text
)));
242 change
.set_crate_graph(crate_graph
);
243 host
.apply_change(change
);
244 (host
.analysis(), file_id
)
247 /// Debug info about the current state of the analysis.
248 pub fn status(&self, file_id
: Option
<FileId
>) -> Cancellable
<String
> {
249 self.with_db(|db
| status
::status(&*db
, file_id
))
252 pub fn parallel_prime_caches
<F
>(&self, num_worker_threads
: u8, cb
: F
) -> Cancellable
<()>
254 F
: Fn(ParallelPrimeCachesProgress
) + Sync
+ std
::panic
::UnwindSafe
,
256 self.with_db(move |db
| prime_caches
::parallel_prime_caches(db
, num_worker_threads
, &cb
))
259 /// Gets the text of the source file.
260 pub fn file_text(&self, file_id
: FileId
) -> Cancellable
<Arc
<String
>> {
261 self.with_db(|db
| db
.file_text(file_id
))
264 /// Gets the syntax tree of the file.
265 pub fn parse(&self, file_id
: FileId
) -> Cancellable
<SourceFile
> {
266 self.with_db(|db
| db
.parse(file_id
).tree())
269 /// Returns true if this file belongs to an immutable library.
270 pub fn is_library_file(&self, file_id
: FileId
) -> Cancellable
<bool
> {
271 use ide_db
::base_db
::SourceDatabaseExt
;
272 self.with_db(|db
| db
.source_root(db
.file_source_root(file_id
)).is_library
)
275 /// Gets the file's `LineIndex`: data structure to convert between absolute
276 /// offsets and line/column representation.
277 pub fn file_line_index(&self, file_id
: FileId
) -> Cancellable
<Arc
<LineIndex
>> {
278 self.with_db(|db
| db
.line_index(file_id
))
281 /// Selects the next syntactic nodes encompassing the range.
282 pub fn extend_selection(&self, frange
: FileRange
) -> Cancellable
<TextRange
> {
283 self.with_db(|db
| extend_selection
::extend_selection(db
, frange
))
286 /// Returns position of the matching brace (all types of braces are
288 pub fn matching_brace(&self, position
: FilePosition
) -> Cancellable
<Option
<TextSize
>> {
290 let parse
= db
.parse(position
.file_id
);
291 let file
= parse
.tree();
292 matching_brace
::matching_brace(&file
, position
.offset
)
296 /// Returns a syntax tree represented as `String`, for debug purposes.
297 // FIXME: use a better name here.
301 text_range
: Option
<TextRange
>,
302 ) -> Cancellable
<String
> {
303 self.with_db(|db
| syntax_tree
::syntax_tree(db
, file_id
, text_range
))
306 pub fn view_hir(&self, position
: FilePosition
) -> Cancellable
<String
> {
307 self.with_db(|db
| view_hir
::view_hir(db
, position
))
310 pub fn view_item_tree(&self, file_id
: FileId
) -> Cancellable
<String
> {
311 self.with_db(|db
| view_item_tree
::view_item_tree(db
, file_id
))
314 /// Renders the crate graph to GraphViz "dot" syntax.
315 pub fn view_crate_graph(&self, full
: bool
) -> Cancellable
<Result
<String
, String
>> {
316 self.with_db(|db
| view_crate_graph
::view_crate_graph(db
, full
))
319 pub fn expand_macro(&self, position
: FilePosition
) -> Cancellable
<Option
<ExpandedMacro
>> {
320 self.with_db(|db
| expand_macro
::expand_macro(db
, position
))
323 /// Returns an edit to remove all newlines in the range, cleaning up minor
324 /// stuff like trailing commas.
325 pub fn join_lines(&self, config
: &JoinLinesConfig
, frange
: FileRange
) -> Cancellable
<TextEdit
> {
327 let parse
= db
.parse(frange
.file_id
);
328 join_lines
::join_lines(config
, &parse
.tree(), frange
.range
)
332 /// Returns an edit which should be applied when opening a new line, fixing
333 /// up minor stuff like continuing the comment.
334 /// The edit will be a snippet (with `$0`).
335 pub fn on_enter(&self, position
: FilePosition
) -> Cancellable
<Option
<TextEdit
>> {
336 self.with_db(|db
| typing
::on_enter(db
, position
))
339 /// Returns an edit which should be applied after a character was typed.
341 /// This is useful for some on-the-fly fixups, like adding `;` to `let =`
343 pub fn on_char_typed(
345 position
: FilePosition
,
348 ) -> Cancellable
<Option
<SourceChange
>> {
349 // Fast path to not even parse the file.
350 if !typing
::TRIGGER_CHARS
.contains(char_typed
) {
353 if char_typed
== '
<'
&& !autoclose
{
357 self.with_db(|db
| typing
::on_char_typed(db
, position
, char_typed
))
360 /// Returns a tree representation of symbols in the file. Useful to draw a
362 pub fn file_structure(&self, file_id
: FileId
) -> Cancellable
<Vec
<StructureNode
>> {
363 self.with_db(|db
| file_structure
::file_structure(&db
.parse(file_id
).tree()))
366 /// Returns a list of the places in the file where type hints can be displayed.
369 config
: &InlayHintsConfig
,
371 range
: Option
<TextRange
>,
372 ) -> Cancellable
<Vec
<InlayHint
>> {
373 self.with_db(|db
| inlay_hints
::inlay_hints(db
, file_id
, range
, config
))
376 /// Returns the set of folding ranges.
377 pub fn folding_ranges(&self, file_id
: FileId
) -> Cancellable
<Vec
<Fold
>> {
378 self.with_db(|db
| folding_ranges
::folding_ranges(&db
.parse(file_id
).tree()))
381 /// Fuzzy searches for a symbol.
382 pub fn symbol_search(&self, query
: Query
) -> Cancellable
<Vec
<NavigationTarget
>> {
384 symbol_index
::world_symbols(db
, query
)
385 .into_iter() // xx: should we make this a par iter?
386 .filter_map(|s
| s
.try_to_nav(db
))
391 /// Returns the definitions from the symbol at `position`.
392 pub fn goto_definition(
394 position
: FilePosition
,
395 ) -> Cancellable
<Option
<RangeInfo
<Vec
<NavigationTarget
>>>> {
396 self.with_db(|db
| goto_definition
::goto_definition(db
, position
))
399 /// Returns the declaration from the symbol at `position`.
400 pub fn goto_declaration(
402 position
: FilePosition
,
403 ) -> Cancellable
<Option
<RangeInfo
<Vec
<NavigationTarget
>>>> {
404 self.with_db(|db
| goto_declaration
::goto_declaration(db
, position
))
407 /// Returns the impls from the symbol at `position`.
408 pub fn goto_implementation(
410 position
: FilePosition
,
411 ) -> Cancellable
<Option
<RangeInfo
<Vec
<NavigationTarget
>>>> {
412 self.with_db(|db
| goto_implementation
::goto_implementation(db
, position
))
415 /// Returns the type definitions for the symbol at `position`.
416 pub fn goto_type_definition(
418 position
: FilePosition
,
419 ) -> Cancellable
<Option
<RangeInfo
<Vec
<NavigationTarget
>>>> {
420 self.with_db(|db
| goto_type_definition
::goto_type_definition(db
, position
))
423 /// Finds all usages of the reference at point.
424 pub fn find_all_refs(
426 position
: FilePosition
,
427 search_scope
: Option
<SearchScope
>,
428 ) -> Cancellable
<Option
<Vec
<ReferenceSearchResult
>>> {
429 self.with_db(|db
| references
::find_all_refs(&Semantics
::new(db
), position
, search_scope
))
432 /// Returns a short text describing element at position.
435 config
: &HoverConfig
,
437 ) -> Cancellable
<Option
<RangeInfo
<HoverResult
>>> {
438 self.with_db(|db
| hover
::hover(db
, range
, config
))
441 /// Returns moniker of symbol at position.
444 position
: FilePosition
,
445 ) -> Cancellable
<Option
<RangeInfo
<Vec
<moniker
::MonikerResult
>>>> {
446 self.with_db(|db
| moniker
::moniker(db
, position
))
449 /// Return URL(s) for the documentation of the symbol under the cursor.
450 pub fn external_docs(
452 position
: FilePosition
,
453 ) -> Cancellable
<Option
<doc_links
::DocumentationLink
>> {
454 self.with_db(|db
| doc_links
::external_docs(db
, &position
))
457 /// Computes parameter information at the given position.
458 pub fn signature_help(&self, position
: FilePosition
) -> Cancellable
<Option
<SignatureHelp
>> {
459 self.with_db(|db
| signature_help
::signature_help(db
, position
))
462 /// Computes call hierarchy candidates for the given file position.
463 pub fn call_hierarchy(
465 position
: FilePosition
,
466 ) -> Cancellable
<Option
<RangeInfo
<Vec
<NavigationTarget
>>>> {
467 self.with_db(|db
| call_hierarchy
::call_hierarchy(db
, position
))
470 /// Computes incoming calls for the given file position.
471 pub fn incoming_calls(&self, position
: FilePosition
) -> Cancellable
<Option
<Vec
<CallItem
>>> {
472 self.with_db(|db
| call_hierarchy
::incoming_calls(db
, position
))
475 /// Computes outgoing calls for the given file position.
476 pub fn outgoing_calls(&self, position
: FilePosition
) -> Cancellable
<Option
<Vec
<CallItem
>>> {
477 self.with_db(|db
| call_hierarchy
::outgoing_calls(db
, position
))
480 /// Returns a `mod name;` declaration which created the current module.
481 pub fn parent_module(&self, position
: FilePosition
) -> Cancellable
<Vec
<NavigationTarget
>> {
482 self.with_db(|db
| parent_module
::parent_module(db
, position
))
485 /// Returns crates this file belongs too.
486 pub fn crates_for(&self, file_id
: FileId
) -> Cancellable
<Vec
<CrateId
>> {
487 self.with_db(|db
| parent_module
::crates_for(db
, file_id
))
490 /// Returns crates this file belongs too.
491 pub fn transitive_rev_deps(&self, crate_id
: CrateId
) -> Cancellable
<Vec
<CrateId
>> {
492 self.with_db(|db
| db
.crate_graph().transitive_rev_deps(crate_id
).collect())
495 /// Returns crates this file *might* belong too.
496 pub fn relevant_crates_for(&self, file_id
: FileId
) -> Cancellable
<Vec
<CrateId
>> {
497 self.with_db(|db
| db
.relevant_crates(file_id
).iter().copied().collect())
500 /// Returns the edition of the given crate.
501 pub fn crate_edition(&self, crate_id
: CrateId
) -> Cancellable
<Edition
> {
502 self.with_db(|db
| db
.crate_graph()[crate_id
].edition
)
505 /// Returns the root file of the given crate.
506 pub fn crate_root(&self, crate_id
: CrateId
) -> Cancellable
<FileId
> {
507 self.with_db(|db
| db
.crate_graph()[crate_id
].root_file_id
)
510 /// Returns the set of possible targets to run for the current file.
511 pub fn runnables(&self, file_id
: FileId
) -> Cancellable
<Vec
<Runnable
>> {
512 self.with_db(|db
| runnables
::runnables(db
, file_id
))
515 /// Returns the set of tests for the given file position.
516 pub fn related_tests(
518 position
: FilePosition
,
519 search_scope
: Option
<SearchScope
>,
520 ) -> Cancellable
<Vec
<Runnable
>> {
521 self.with_db(|db
| runnables
::related_tests(db
, position
, search_scope
))
524 /// Computes syntax highlighting for the given file
527 highlight_config
: HighlightConfig
,
529 ) -> Cancellable
<Vec
<HlRange
>> {
530 self.with_db(|db
| syntax_highlighting
::highlight(db
, highlight_config
, file_id
, None
))
533 /// Computes all ranges to highlight for a given item in a file.
534 pub fn highlight_related(
536 config
: HighlightRelatedConfig
,
537 position
: FilePosition
,
538 ) -> Cancellable
<Option
<Vec
<HighlightedRange
>>> {
540 highlight_related
::highlight_related(&Semantics
::new(db
), config
, position
)
544 /// Computes syntax highlighting for the given file range.
545 pub fn highlight_range(
547 highlight_config
: HighlightConfig
,
549 ) -> Cancellable
<Vec
<HlRange
>> {
551 syntax_highlighting
::highlight(db
, highlight_config
, frange
.file_id
, Some(frange
.range
))
555 /// Computes syntax highlighting for the given file.
556 pub fn highlight_as_html(&self, file_id
: FileId
, rainbow
: bool
) -> Cancellable
<String
> {
557 self.with_db(|db
| syntax_highlighting
::highlight_as_html(db
, file_id
, rainbow
))
560 /// Computes completions at the given position.
563 config
: &CompletionConfig
,
564 position
: FilePosition
,
565 trigger_character
: Option
<char>,
566 ) -> Cancellable
<Option
<Vec
<CompletionItem
>>> {
568 ide_completion
::completions(db
, config
, position
, trigger_character
).map(Into
::into
)
572 /// Resolves additional completion data at the position given.
573 pub fn resolve_completion_edits(
575 config
: &CompletionConfig
,
576 position
: FilePosition
,
577 imports
: impl IntoIterator
<Item
= (String
, String
)> + std
::panic
::UnwindSafe
,
578 ) -> Cancellable
<Vec
<TextEdit
>> {
580 .with_db(|db
| ide_completion
::resolve_completion_edits(db
, config
, position
, imports
))?
581 .unwrap_or_default())
584 /// Computes the set of diagnostics for the given file.
587 config
: &DiagnosticsConfig
,
588 resolve
: AssistResolveStrategy
,
590 ) -> Cancellable
<Vec
<Diagnostic
>> {
591 self.with_db(|db
| ide_diagnostics
::diagnostics(db
, config
, &resolve
, file_id
))
594 /// Convenience function to return assists + quick fixes for diagnostics
595 pub fn assists_with_fixes(
597 assist_config
: &AssistConfig
,
598 diagnostics_config
: &DiagnosticsConfig
,
599 resolve
: AssistResolveStrategy
,
601 ) -> Cancellable
<Vec
<Assist
>> {
602 let include_fixes
= match &assist_config
.allowed
{
603 Some(it
) => it
.iter().any(|&it
| it
== AssistKind
::None
|| it
== AssistKind
::QuickFix
),
608 let diagnostic_assists
= if include_fixes
{
609 ide_diagnostics
::diagnostics(db
, diagnostics_config
, &resolve
, frange
.file_id
)
611 .flat_map(|it
| it
.fixes
.unwrap_or_default())
612 .filter(|it
| it
.target
.intersect(frange
.range
).is_some())
617 let ssr_assists
= ssr
::ssr_assists(db
, &resolve
, frange
);
618 let assists
= ide_assists
::assists(db
, assist_config
, resolve
, frange
);
620 let mut res
= diagnostic_assists
;
621 res
.extend(ssr_assists
.into_iter());
622 res
.extend(assists
.into_iter());
628 /// Returns the edit required to rename reference at the position to the new
632 position
: FilePosition
,
634 ) -> Cancellable
<Result
<SourceChange
, RenameError
>> {
635 self.with_db(|db
| rename
::rename(db
, position
, new_name
))
638 pub fn prepare_rename(
640 position
: FilePosition
,
641 ) -> Cancellable
<Result
<RangeInfo
<()>, RenameError
>> {
642 self.with_db(|db
| rename
::prepare_rename(db
, position
))
645 pub fn will_rename_file(
649 ) -> Cancellable
<Option
<SourceChange
>> {
650 self.with_db(|db
| rename
::will_rename_file(db
, file_id
, new_name_stem
))
653 pub fn structural_search_replace(
657 resolve_context
: FilePosition
,
658 selections
: Vec
<FileRange
>,
659 ) -> Cancellable
<Result
<SourceChange
, SsrError
>> {
661 let rule
: ide_ssr
::SsrRule
= query
.parse()?
;
662 let mut match_finder
=
663 ide_ssr
::MatchFinder
::in_context(db
, resolve_context
, selections
)?
;
664 match_finder
.add_rule(rule
)?
;
665 let edits
= if parse_only { Default::default() }
else { match_finder.edits() }
;
666 Ok(SourceChange
::from(edits
))
672 config
: &AnnotationConfig
,
674 ) -> Cancellable
<Vec
<Annotation
>> {
675 self.with_db(|db
| annotations
::annotations(db
, config
, file_id
))
678 pub fn resolve_annotation(&self, annotation
: Annotation
) -> Cancellable
<Annotation
> {
679 self.with_db(|db
| annotations
::resolve_annotation(db
, annotation
))
685 direction
: Direction
,
686 ) -> Cancellable
<Option
<TextEdit
>> {
687 self.with_db(|db
| move_item
::move_item(db
, range
, direction
))
690 /// Performs an operation on the database that may be canceled.
692 /// rust-analyzer needs to be able to answer semantic questions about the
693 /// code while the code is being modified. A common problem is that a
694 /// long-running query is being calculated when a new change arrives.
696 /// We can't just apply the change immediately: this will cause the pending
697 /// query to see inconsistent state (it will observe an absence of
698 /// repeatable read). So what we do is we **cancel** all pending queries
699 /// before applying the change.
701 /// Salsa implements cancellation by unwinding with a special value and
702 /// catching it on the API boundary.
703 fn with_db
<F
, T
>(&self, f
: F
) -> Cancellable
<T
>
705 F
: FnOnce(&RootDatabase
) -> T
+ std
::panic
::UnwindSafe
,
707 Cancelled
::catch(|| f(&self.db
))
712 fn analysis_is_send() {
713 fn is_send
<T
: Send
>() {}
714 is_send
::<Analysis
>();