Improve fixture support
Support more features beside highlighting, and support items from minicore.
diff --git a/Cargo.lock b/Cargo.lock
index 55e5bdc..539f8cf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -930,6 +930,7 @@
"ide-diagnostics",
"ide-ssr",
"itertools",
+ "macros",
"nohash-hasher",
"oorandom",
"profile",
@@ -976,6 +977,7 @@
"hir",
"ide-db",
"itertools",
+ "macros",
"smallvec",
"stdx",
"syntax",
@@ -1000,6 +1002,7 @@
"indexmap",
"itertools",
"line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "macros",
"memchr",
"nohash-hasher",
"parser",
@@ -1009,6 +1012,7 @@
"rustc-hash 2.1.1",
"salsa",
"salsa-macros",
+ "smallvec",
"span",
"stdx",
"syntax",
diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml
index 9bad21f..277d5df 100644
--- a/crates/ide-completion/Cargo.toml
+++ b/crates/ide-completion/Cargo.toml
@@ -28,6 +28,7 @@
# completions crate should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
+macros.workspace = true
[dev-dependencies]
expect-test = "1.5.1"
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b822f53..ed58e86 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -16,6 +16,7 @@
pub(crate) mod mod_;
pub(crate) mod pattern;
pub(crate) mod postfix;
+pub(crate) mod ra_fixture;
pub(crate) mod record;
pub(crate) mod snippet;
pub(crate) mod r#type;
@@ -74,6 +75,10 @@
self.buf.push(item)
}
+ fn add_many(&mut self, items: impl IntoIterator<Item = CompletionItem>) {
+ self.buf.extend(items)
+ }
+
fn add_opt(&mut self, item: Option<CompletionItem>) {
if let Some(item) = item {
self.buf.push(item)
diff --git a/crates/ide-completion/src/completions/ra_fixture.rs b/crates/ide-completion/src/completions/ra_fixture.rs
new file mode 100644
index 0000000..b44c907
--- /dev/null
+++ b/crates/ide-completion/src/completions/ra_fixture.rs
@@ -0,0 +1,113 @@
+//! Injected completions for `#[rust_analyzer::rust_fixture]`.
+
+use hir::FilePositionWrapper;
+use ide_db::{
+ impl_empty_upmap_from_ra_fixture,
+ ra_fixture::{RaFixtureAnalysis, UpmapFromRaFixture},
+};
+use syntax::ast;
+
+use crate::{
+ CompletionItemKind, CompletionItemRefMode, CompletionRelevance, completions::Completions,
+ context::CompletionContext, item::CompletionItemLabel,
+};
+
+pub(crate) fn complete_ra_fixture(
+ acc: &mut Completions,
+ ctx: &CompletionContext<'_>,
+ original: &ast::String,
+ expanded: &ast::String,
+) -> Option<()> {
+ let analysis = RaFixtureAnalysis::analyze_ra_fixture(
+ &ctx.sema,
+ original.clone(),
+ expanded,
+ ctx.config.minicore,
+ &mut |_| {},
+ )?;
+ let (virtual_file_id, virtual_offset) = analysis.map_offset_down(ctx.position.offset)?;
+ let completions = hir::attach_db_allow_change(&analysis.db, || {
+ crate::completions(
+ &analysis.db,
+ ctx.config,
+ FilePositionWrapper { file_id: virtual_file_id, offset: virtual_offset },
+ ctx.trigger_character,
+ )
+ })?;
+ let completions =
+ completions.upmap_from_ra_fixture(&analysis, virtual_file_id, ctx.position.file_id).ok()?;
+ acc.add_many(completions);
+ Some(())
+}
+
+impl_empty_upmap_from_ra_fixture!(
+ CompletionItemLabel,
+ CompletionItemKind,
+ CompletionRelevance,
+ CompletionItemRefMode,
+);
+
+#[cfg(test)]
+mod tests {
+ use expect_test::expect;
+
+ use crate::tests::check;
+
+ #[test]
+ fn it_works() {
+ check(
+ r##"
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
+
+fn foo() {
+ fixture(r#"
+fn complete_me() {}
+
+fn baz() {
+ let foo_bar_baz = 123;
+ f$0
+}
+ "#);
+}
+ "##,
+ expect![[r#"
+ fn baz() fn()
+ fn complete_me() fn()
+ lc foo_bar_baz i32
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+ }
+}
diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs
index b7367cb..5623257 100644
--- a/crates/ide-completion/src/config.rs
+++ b/crates/ide-completion/src/config.rs
@@ -6,13 +6,13 @@
use hir::FindPathConfig;
use ide_db::{
- SnippetCap,
+ MiniCore, SnippetCap,
imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig},
};
use crate::{CompletionFieldsToResolve, snippet::Snippet};
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug)]
pub struct CompletionConfig<'a> {
pub enable_postfix_completions: bool,
pub enable_imports_on_the_fly: bool,
@@ -35,6 +35,7 @@
pub fields_to_resolve: CompletionFieldsToResolve,
pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>,
pub exclude_traits: &'a [String],
+ pub minicore: MiniCore<'a>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 4032329..fc2cc3b 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -440,6 +440,7 @@
pub(crate) config: &'a CompletionConfig<'a>,
pub(crate) position: FilePosition,
+ pub(crate) trigger_character: Option<char>,
/// The token before the cursor, in the original file.
pub(crate) original_token: SyntaxToken,
/// The token before the cursor, in the macro-expanded file.
@@ -703,6 +704,7 @@
db: &'db RootDatabase,
position @ FilePosition { file_id, offset }: FilePosition,
config: &'db CompletionConfig<'db>,
+ trigger_character: Option<char>,
) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
let _p = tracing::info_span!("CompletionContext::new").entered();
let sema = Semantics::new(db);
@@ -871,6 +873,7 @@
db,
config,
position,
+ trigger_character,
original_token,
token,
krate,
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index e798f3b..51d28bd 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -10,7 +10,7 @@
let (db, pos) = position(ra_fixture);
let config = TEST_CONFIG;
let (completion_context, _analysis) =
- hir::attach_db(&db, || CompletionContext::new(&db, pos, &config).unwrap());
+ hir::attach_db(&db, || CompletionContext::new(&db, pos, &config, None).unwrap());
let ty = completion_context
.expected_type
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 5fb9dc9..303c712 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -9,6 +9,7 @@
imports::import_assets::LocatedImport,
};
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
use smallvec::SmallVec;
use stdx::{format_to, impl_from, never};
use syntax::{Edition, SmolStr, TextRange, TextSize, format_smolstr};
@@ -23,7 +24,7 @@
///
/// It is basically a POD with various properties. To construct a [`CompletionItem`],
/// use [`Builder::new`] method and the [`Builder`] struct.
-#[derive(Clone)]
+#[derive(Clone, UpmapFromRaFixture)]
#[non_exhaustive]
pub struct CompletionItem {
/// Label in the completion pop up which identifies completion.
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index a70a113..8a0aaf3 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -187,7 +187,7 @@
position: FilePosition,
trigger_character: Option<char>,
) -> Option<Vec<CompletionItem>> {
- let (ctx, analysis) = &CompletionContext::new(db, position, config)?;
+ let (ctx, analysis) = &CompletionContext::new(db, position, config, trigger_character)?;
let mut completions = Completions::default();
// prevent `(` from triggering unwanted completion noise
@@ -241,6 +241,7 @@
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
completions::format_string::format_string(acc, ctx, original, expanded);
completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded);
+ completions::ra_fixture::complete_ra_fixture(acc, ctx, original, expanded);
}
CompletionAnalysis::UnexpandedAttrTT {
colon_prefix,
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index ec9cd9f..b32a895 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -29,7 +29,7 @@
use hir::db::HirDatabase;
use hir::{PrefixKind, setup_tracing};
use ide_db::{
- FilePosition, RootDatabase, SnippetCap,
+ FilePosition, MiniCore, RootDatabase, SnippetCap,
imports::insert_use::{ImportGranularity, InsertUseConfig},
};
use itertools::Itertools;
@@ -90,6 +90,7 @@
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
+ minicore: MiniCore::default(),
};
pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 2d3ebad..0cd4208 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -16,7 +16,8 @@
expect: Expect,
) {
let (db, position) = crate::tests::position(ra_fixture);
- let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
+ let (ctx, analysis) =
+ crate::context::CompletionContext::new(&db, position, &config, None).unwrap();
let mut acc = crate::completions::Completions::default();
hir::attach_db(ctx.db, || {
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index e065adb..b714816 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -30,6 +30,7 @@
triomphe.workspace = true
nohash-hasher.workspace = true
bitflags.workspace = true
+smallvec.workspace = true
# local deps
base-db.workspace = true
@@ -42,15 +43,15 @@
# ide should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
+macros.workspace = true
+
+test-utils.workspace = true
+test-fixture.workspace = true
line-index.workspace = true
[dev-dependencies]
expect-test = "1.5.1"
-# local deps
-test-utils.workspace = true
-test-fixture.workspace = true
-
[lints]
workspace = true
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 44bccd8..7efa97b 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -2,6 +2,8 @@
//!
//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
+extern crate self as ide_db;
+
mod apply_change;
pub mod active_parameter;
@@ -14,6 +16,8 @@
pub mod label;
pub mod path_transform;
pub mod prime_caches;
+pub mod ra_fixture;
+pub mod range_mapper;
pub mod rename;
pub mod rust_doc;
pub mod search;
@@ -364,3 +368,25 @@
WeakWarning,
Allow,
}
+
+#[derive(Debug, Clone, Copy)]
+pub struct MiniCore<'a>(&'a str);
+
+impl<'a> MiniCore<'a> {
+ #[inline]
+ pub fn new(minicore: &'a str) -> Self {
+ Self(minicore)
+ }
+
+ #[inline]
+ pub const fn default() -> Self {
+ Self(test_utils::MiniCore::RAW_SOURCE)
+ }
+}
+
+impl<'a> Default for MiniCore<'a> {
+ #[inline]
+ fn default() -> Self {
+ Self::default()
+ }
+}
diff --git a/crates/ide-db/src/ra_fixture.rs b/crates/ide-db/src/ra_fixture.rs
new file mode 100644
index 0000000..1f056a8
--- /dev/null
+++ b/crates/ide-db/src/ra_fixture.rs
@@ -0,0 +1,532 @@
+//! Working with the fixtures in r-a tests, and providing IDE services for them.
+
+use std::hash::{BuildHasher, Hash};
+
+use hir::{CfgExpr, FilePositionWrapper, FileRangeWrapper, Semantics};
+use smallvec::SmallVec;
+use span::{TextRange, TextSize};
+use syntax::{
+ AstToken, SmolStr,
+ ast::{self, IsString},
+};
+
+use crate::{
+ MiniCore, RootDatabase, SymbolKind, active_parameter::ActiveParameter,
+ documentation::Documentation, range_mapper::RangeMapper, search::ReferenceCategory,
+};
+
+pub use span::FileId;
+
+impl RootDatabase {
+ fn from_ra_fixture(
+ text: &str,
+ minicore: MiniCore<'_>,
+ ) -> Result<(RootDatabase, Vec<(FileId, usize)>, Vec<FileId>), ()> {
+ // We don't want a mistake in the fixture to crash r-a, so we wrap this in `catch_unwind()`.
+ std::panic::catch_unwind(|| {
+ let mut db = RootDatabase::default();
+ let fixture = test_fixture::ChangeFixture::parse_with_proc_macros(
+ &db,
+ text,
+ minicore.0,
+ Vec::new(),
+ );
+ db.apply_change(fixture.change);
+ let files = fixture
+ .files
+ .into_iter()
+ .zip(fixture.file_lines)
+ .map(|(file_id, range)| (file_id.file_id(&db), range))
+ .collect();
+ (db, files, fixture.sysroot_files)
+ })
+ .map_err(|error| {
+ tracing::error!(
+ "cannot crate the crate graph: {}\nCrate graph:\n{}\n",
+ if let Some(&s) = error.downcast_ref::<&'static str>() {
+ s
+ } else if let Some(s) = error.downcast_ref::<String>() {
+ s.as_str()
+ } else {
+ "Box<dyn Any>"
+ },
+ text,
+ );
+ })
+ }
+}
+
+pub struct RaFixtureAnalysis {
+ pub db: RootDatabase,
+ tmp_file_ids: Vec<(FileId, usize)>,
+ line_offsets: Vec<TextSize>,
+ virtual_file_id_to_line: Vec<usize>,
+ mapper: RangeMapper,
+ literal: ast::String,
+ // `minicore` etc..
+ sysroot_files: Vec<FileId>,
+ combined_len: TextSize,
+}
+
+impl RaFixtureAnalysis {
+ pub fn analyze_ra_fixture(
+ sema: &Semantics<'_, RootDatabase>,
+ literal: ast::String,
+ expanded: &ast::String,
+ minicore: MiniCore<'_>,
+ on_cursor: &mut dyn FnMut(TextRange),
+ ) -> Option<RaFixtureAnalysis> {
+ if !literal.is_raw() {
+ return None;
+ }
+
+ let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?;
+ let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
+ attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
+ path.segments()
+ .zip(["rust_analyzer", "rust_fixture"])
+ .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
+ })
+ });
+ if !has_rust_fixture_attr {
+ return None;
+ }
+ let value = literal.value().ok()?;
+
+ let mut mapper = RangeMapper::default();
+
+ // This is used for the `Injector`, to resolve precise location in the string literal,
+ // which will then be used to resolve precise location in the enclosing file.
+ let mut offset_with_indent = TextSize::new(0);
+ // This is used to resolve the location relative to the virtual file into a location
+ // relative to the indentation-trimmed file which will then (by the `Injector`) used
+ // to resolve to a location in the actual file.
+ // Besides indentation, we also skip `$0` cursors for this, since they are not included
+ // in the virtual files.
+ let mut offset_without_indent = TextSize::new(0);
+
+ let mut text = &*value;
+ if let Some(t) = text.strip_prefix('\n') {
+ offset_with_indent += TextSize::of("\n");
+ text = t;
+ }
+ // This stores the offsets of each line, **after we remove indentation**.
+ let mut line_offsets = Vec::new();
+ for mut line in text.split_inclusive('\n') {
+ line_offsets.push(offset_without_indent);
+
+ if line.starts_with("@@") {
+ // Introducing `//` into a fixture inside fixture causes all sorts of problems,
+ // so for testing purposes we escape it as `@@` and replace it here.
+ mapper.add("//", TextRange::at(offset_with_indent, TextSize::of("@@")));
+ line = &line["@@".len()..];
+ offset_with_indent += TextSize::of("@@");
+ offset_without_indent += TextSize::of("@@");
+ }
+
+ // Remove indentation to simplify the mapping with fixture (which de-indents).
+ // Removing indentation shouldn't affect highlighting.
+ let mut unindented_line = line.trim_start();
+ if unindented_line.is_empty() {
+ // The whole line was whitespaces, but we need the newline.
+ unindented_line = "\n";
+ }
+ offset_with_indent += TextSize::of(line) - TextSize::of(unindented_line);
+
+ let marker = "$0";
+ match unindented_line.find(marker) {
+ Some(marker_pos) => {
+ let (before_marker, after_marker) = unindented_line.split_at(marker_pos);
+ let after_marker = &after_marker[marker.len()..];
+
+ mapper.add(
+ before_marker,
+ TextRange::at(offset_with_indent, TextSize::of(before_marker)),
+ );
+ offset_with_indent += TextSize::of(before_marker);
+ offset_without_indent += TextSize::of(before_marker);
+
+ if let Some(marker_range) = literal
+ .map_range_up(TextRange::at(offset_with_indent, TextSize::of(marker)))
+ {
+ on_cursor(marker_range);
+ }
+ offset_with_indent += TextSize::of(marker);
+
+ mapper.add(
+ after_marker,
+ TextRange::at(offset_with_indent, TextSize::of(after_marker)),
+ );
+ offset_with_indent += TextSize::of(after_marker);
+ offset_without_indent += TextSize::of(after_marker);
+ }
+ None => {
+ mapper.add(
+ unindented_line,
+ TextRange::at(offset_with_indent, TextSize::of(unindented_line)),
+ );
+ offset_with_indent += TextSize::of(unindented_line);
+ offset_without_indent += TextSize::of(unindented_line);
+ }
+ }
+ }
+
+ let combined = mapper.take_text();
+ let combined_len = TextSize::of(&combined);
+ let (analysis, tmp_file_ids, sysroot_files) =
+ RootDatabase::from_ra_fixture(&combined, minicore).ok()?;
+
+ // We use a `Vec` because we know the `FileId`s will always be close.
+ let mut virtual_file_id_to_line = Vec::new();
+ for &(file_id, line) in &tmp_file_ids {
+ virtual_file_id_to_line.resize(file_id.index() as usize + 1, usize::MAX);
+ virtual_file_id_to_line[file_id.index() as usize] = line;
+ }
+
+ Some(RaFixtureAnalysis {
+ db: analysis,
+ tmp_file_ids,
+ line_offsets,
+ virtual_file_id_to_line,
+ mapper,
+ literal,
+ sysroot_files,
+ combined_len,
+ })
+ }
+
+ pub fn files(&self) -> impl Iterator<Item = FileId> {
+ self.tmp_file_ids.iter().map(|(file, _)| *file)
+ }
+
+ /// This returns `None` for minicore or other sysroot files.
+ fn virtual_file_id_to_line(&self, file_id: FileId) -> Option<usize> {
+ if self.is_sysroot_file(file_id) {
+ None
+ } else {
+ Some(self.virtual_file_id_to_line[file_id.index() as usize])
+ }
+ }
+
+ pub fn map_offset_down(&self, offset: TextSize) -> Option<(FileId, TextSize)> {
+ let inside_literal_range = self.literal.map_offset_down(offset)?;
+ let combined_offset = self.mapper.map_offset_down(inside_literal_range)?;
+ // There is usually a small number of files, so a linear search is smaller and faster.
+ let (_, &(file_id, file_line)) =
+ self.tmp_file_ids.iter().enumerate().find(|&(idx, &(_, file_line))| {
+ let file_start = self.line_offsets[file_line];
+ let file_end = self
+ .tmp_file_ids
+ .get(idx + 1)
+ .map(|&(_, next_file_line)| self.line_offsets[next_file_line])
+ .unwrap_or_else(|| self.combined_len);
+ TextRange::new(file_start, file_end).contains(combined_offset)
+ })?;
+ let file_line_offset = self.line_offsets[file_line];
+ let file_offset = combined_offset - file_line_offset;
+ Some((file_id, file_offset))
+ }
+
+ pub fn map_range_down(&self, range: TextRange) -> Option<(FileId, TextRange)> {
+ let (start_file_id, start_offset) = self.map_offset_down(range.start())?;
+ let (end_file_id, end_offset) = self.map_offset_down(range.end())?;
+ if start_file_id != end_file_id {
+ None
+ } else {
+ Some((start_file_id, TextRange::new(start_offset, end_offset)))
+ }
+ }
+
+ pub fn map_range_up(
+ &self,
+ virtual_file: FileId,
+ range: TextRange,
+ ) -> impl Iterator<Item = TextRange> {
+ // This could be `None` if the file is empty.
+ self.virtual_file_id_to_line(virtual_file)
+ .and_then(|line| self.line_offsets.get(line))
+ .into_iter()
+ .flat_map(move |&tmp_file_offset| {
+ // Resolve the offset relative to the virtual file to an offset relative to the combined indentation-trimmed file
+ let range = range + tmp_file_offset;
+ // Then resolve that to an offset relative to the real file.
+ self.mapper.map_range_up(range)
+ })
+ // And finally resolve the offset relative to the literal to relative to the file.
+ .filter_map(|range| self.literal.map_range_up(range))
+ }
+
+ pub fn map_offset_up(&self, virtual_file: FileId, offset: TextSize) -> Option<TextSize> {
+ self.map_range_up(virtual_file, TextRange::empty(offset)).next().map(|range| range.start())
+ }
+
+ pub fn is_sysroot_file(&self, file_id: FileId) -> bool {
+ self.sysroot_files.contains(&file_id)
+ }
+}
+
+pub trait UpmapFromRaFixture: Sized {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()>;
+}
+
+trait IsEmpty {
+ fn is_empty(&self) -> bool;
+}
+
+impl<T> IsEmpty for Vec<T> {
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
+}
+
+impl<T, const N: usize> IsEmpty for SmallVec<[T; N]> {
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
+}
+
+#[allow(clippy::disallowed_types)]
+impl<K, V, S> IsEmpty for std::collections::HashMap<K, V, S> {
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
+}
+
+fn upmap_collection<T, Collection>(
+ collection: Collection,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+) -> Result<Collection, ()>
+where
+ T: UpmapFromRaFixture,
+ Collection: IntoIterator<Item = T> + FromIterator<T> + IsEmpty,
+{
+ if collection.is_empty() {
+ // The collection was already empty, don't mark it as failing just because of that.
+ return Ok(collection);
+ }
+ let result = collection
+ .into_iter()
+ .filter_map(|item| item.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok())
+ .collect::<Collection>();
+ if result.is_empty() {
+ // The collection was emptied by the upmapping - all items errored, therefore mark it as erroring as well.
+ Err(())
+ } else {
+ Ok(result)
+ }
+}
+
+impl<T: UpmapFromRaFixture> UpmapFromRaFixture for Option<T> {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ Ok(match self {
+ Some(it) => Some(it.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?),
+ None => None,
+ })
+ }
+}
+
+impl<T: UpmapFromRaFixture> UpmapFromRaFixture for Vec<T> {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ upmap_collection(self, analysis, virtual_file_id, real_file_id)
+ }
+}
+
+impl<T: UpmapFromRaFixture, const N: usize> UpmapFromRaFixture for SmallVec<[T; N]> {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ upmap_collection(self, analysis, virtual_file_id, real_file_id)
+ }
+}
+
+#[allow(clippy::disallowed_types)]
+impl<K: UpmapFromRaFixture + Hash + Eq, V: UpmapFromRaFixture, S: BuildHasher + Default>
+ UpmapFromRaFixture for std::collections::HashMap<K, V, S>
+{
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ upmap_collection(self, analysis, virtual_file_id, real_file_id)
+ }
+}
+
+// A map of `FileId`s is treated as associating the ranges in the values with the keys.
+#[allow(clippy::disallowed_types)]
+impl<V: UpmapFromRaFixture, S: BuildHasher + Default> UpmapFromRaFixture
+ for std::collections::HashMap<FileId, V, S>
+{
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ _virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ if self.is_empty() {
+ return Ok(self);
+ }
+ let result = self
+ .into_iter()
+ .filter_map(|(virtual_file_id, value)| {
+ Some((
+ real_file_id,
+ value.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok()?,
+ ))
+ })
+ .collect::<std::collections::HashMap<_, _, _>>();
+ if result.is_empty() { Err(()) } else { Ok(result) }
+ }
+}
+
+macro_rules! impl_tuple {
+ () => {}; // Base case.
+ ( $first:ident, $( $rest:ident, )* ) => {
+ impl<
+ $first: UpmapFromRaFixture,
+ $( $rest: UpmapFromRaFixture, )*
+ > UpmapFromRaFixture for ( $first, $( $rest, )* ) {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ #[allow(non_snake_case)]
+ let ( $first, $($rest,)* ) = self;
+ Ok((
+ $first.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
+ $( $rest.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, )*
+ ))
+ }
+ }
+
+ impl_tuple!( $($rest,)* );
+ };
+}
+impl_tuple!(A, B, C, D, E,);
+
+impl UpmapFromRaFixture for TextSize {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ _real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ analysis.map_offset_up(virtual_file_id, self).ok_or(())
+ }
+}
+
+impl UpmapFromRaFixture for TextRange {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ virtual_file_id: FileId,
+ _real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ analysis.map_range_up(virtual_file_id, self).next().ok_or(())
+ }
+}
+
+// Deliberately do not implement that, as it's easy to get things misbehave and be treated with the wrong FileId:
+//
+// impl UpmapFromRaFixture for FileId {
+// fn upmap_from_ra_fixture(
+// self,
+// _analysis: &RaFixtureAnalysis,
+// _virtual_file_id: FileId,
+// real_file_id: FileId,
+// ) -> Result<Self, ()> {
+// Ok(real_file_id)
+// }
+// }
+
+impl UpmapFromRaFixture for FilePositionWrapper<FileId> {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ _virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ Ok(FilePositionWrapper {
+ file_id: real_file_id,
+ offset: self.offset.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?,
+ })
+ }
+}
+
+impl UpmapFromRaFixture for FileRangeWrapper<FileId> {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &RaFixtureAnalysis,
+ _virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ Ok(FileRangeWrapper {
+ file_id: real_file_id,
+ range: self.range.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?,
+ })
+ }
+}
+
+#[macro_export]
+macro_rules! impl_empty_upmap_from_ra_fixture {
+ ( $( $ty:ty ),* $(,)? ) => {
+ $(
+ impl $crate::ra_fixture::UpmapFromRaFixture for $ty {
+ fn upmap_from_ra_fixture(
+ self,
+ _analysis: &$crate::ra_fixture::RaFixtureAnalysis,
+ _virtual_file_id: $crate::ra_fixture::FileId,
+ _real_file_id: $crate::ra_fixture::FileId,
+ ) -> Result<Self, ()> {
+ Ok(self)
+ }
+ }
+ )*
+ };
+}
+
+impl_empty_upmap_from_ra_fixture!(
+ bool,
+ i8,
+ i16,
+ i32,
+ i64,
+ i128,
+ u8,
+ u16,
+ u32,
+ u64,
+ u128,
+ f32,
+ f64,
+ &str,
+ String,
+ SmolStr,
+ Documentation,
+ SymbolKind,
+ CfgExpr,
+ ReferenceCategory,
+);
diff --git a/crates/ide-db/src/range_mapper.rs b/crates/ide-db/src/range_mapper.rs
new file mode 100644
index 0000000..ef84888
--- /dev/null
+++ b/crates/ide-db/src/range_mapper.rs
@@ -0,0 +1,65 @@
+//! Maps between ranges in documents.
+
+use std::cmp::Ordering;
+
+use stdx::equal_range_by;
+use syntax::{TextRange, TextSize};
+
+#[derive(Default)]
+pub struct RangeMapper {
+ buf: String,
+ ranges: Vec<(TextRange, Option<TextRange>)>,
+}
+
+impl RangeMapper {
+ pub fn add(&mut self, text: &str, source_range: TextRange) {
+ let len = TextSize::of(text);
+ assert_eq!(len, source_range.len());
+ self.add_impl(text, Some(source_range.start()));
+ }
+
+ pub fn add_unmapped(&mut self, text: &str) {
+ self.add_impl(text, None);
+ }
+
+ fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
+ let len = TextSize::of(text);
+ let target_range = TextRange::at(TextSize::of(&self.buf), len);
+ self.ranges.push((target_range, source.map(|it| TextRange::at(it, len))));
+ self.buf.push_str(text);
+ }
+
+ pub fn take_text(&mut self) -> String {
+ std::mem::take(&mut self.buf)
+ }
+
+ pub fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
+ equal_range_by(&self.ranges, |&(r, _)| {
+ if range.is_empty() && r.contains(range.start()) {
+ Ordering::Equal
+ } else {
+ TextRange::ordering(r, range)
+ }
+ })
+ .filter_map(move |i| {
+ let (target_range, source_range) = self.ranges[i];
+ let intersection = target_range.intersect(range).unwrap();
+ let source_range = source_range?;
+ Some(intersection - target_range.start() + source_range.start())
+ })
+ }
+
+ pub fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> {
+ // Using a binary search here is a bit complicated because of the `None` entries.
+ // But the number of lines in fixtures is usually low.
+ let (target_range, source_range) =
+ self.ranges.iter().find_map(|&(target_range, source_range)| {
+ let source_range = source_range?;
+ if !source_range.contains(offset) {
+ return None;
+ }
+ Some((target_range, source_range))
+ })?;
+ Some(offset - source_range.start() + target_range.start())
+ }
+}
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 16c0d8d..57072bb 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -10,6 +10,7 @@
use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff};
use base_db::AnchoredPathBuf;
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
use nohash_hasher::IntMap;
use rustc_hash::FxHashMap;
use span::FileId;
@@ -20,7 +21,7 @@
};
/// An annotation ID associated with an indel, to describe changes.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, UpmapFromRaFixture)]
pub struct ChangeAnnotationId(u32);
impl fmt::Display for ChangeAnnotationId {
diff --git a/crates/ide-db/src/text_edit.rs b/crates/ide-db/src/text_edit.rs
index 6e9bd7b..d2a7371 100644
--- a/crates/ide-db/src/text_edit.rs
+++ b/crates/ide-db/src/text_edit.rs
@@ -5,6 +5,7 @@
//! rust-analyzer.
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
pub use span::{TextRange, TextSize};
use std::cmp::max;
@@ -13,14 +14,14 @@
/// `InsertDelete` -- a single "atomic" change to text
///
/// Must not overlap with other `InDel`s
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, UpmapFromRaFixture)]
pub struct Indel {
pub insert: String,
/// Refers to offsets in the original text
pub delete: TextRange,
}
-#[derive(Default, Debug, Clone)]
+#[derive(Default, Debug, Clone, UpmapFromRaFixture)]
pub struct TextEdit {
/// Invariant: disjoint and sorted by `delete`.
indels: Vec<Indel>,
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 37af05e..3dc155e 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -311,7 +311,7 @@
}
fn check(minicore: MiniCore) {
- let source = minicore.source_code();
+ let source = minicore.source_code(MiniCore::RAW_SOURCE);
let mut config = DiagnosticsConfig::test_sample();
// This should be ignored since we conditionally remove code which creates single item use with braces
config.disabled.insert("unused_braces".to_owned());
@@ -321,7 +321,7 @@
}
// Checks that there is no diagnostic in minicore for each flag.
- for flag in MiniCore::available_flags() {
+ for flag in MiniCore::available_flags(MiniCore::RAW_SOURCE) {
if flag == "clone" {
// Clone without copy has `moved-out-of-ref`, so ignoring.
// FIXME: Maybe we should merge copy and clone in a single flag?
@@ -332,5 +332,5 @@
}
// And one time for all flags, to check codes which are behind multiple flags + prevent name collisions
eprintln!("Checking all minicore flags");
- check(MiniCore::from_flags(MiniCore::available_flags()))
+ check(MiniCore::from_flags(MiniCore::available_flags(MiniCore::RAW_SOURCE)))
}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 06d2776..08ffd39 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -42,6 +42,7 @@
# ide should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
+macros.workspace = true
[target.'cfg(not(any(target_arch = "wasm32", target_os = "emscripten")))'.dependencies]
toolchain.workspace = true
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index dec1889..36c4404 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -1,6 +1,6 @@
use hir::{HasSource, InFile, InRealFile, Semantics};
use ide_db::{
- FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition,
+ FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition,
helpers::visit_file_defs,
};
use itertools::Itertools;
@@ -11,7 +11,7 @@
annotations::fn_references::find_all_methods,
goto_implementation::goto_implementation,
navigation_target,
- references::find_all_refs,
+ references::{FindAllRefsConfig, find_all_refs},
runnables::{Runnable, runnables},
};
@@ -36,7 +36,7 @@
HasReferences { pos: FilePosition, data: Option<Vec<FileRange>> },
}
-pub struct AnnotationConfig {
+pub struct AnnotationConfig<'a> {
pub binary_target: bool,
pub annotate_runnables: bool,
pub annotate_impls: bool,
@@ -44,6 +44,7 @@
pub annotate_method_references: bool,
pub annotate_enum_variant_references: bool,
pub location: AnnotationLocation,
+ pub minicore: MiniCore<'a>,
}
pub enum AnnotationLocation {
@@ -53,7 +54,7 @@
pub(crate) fn annotations(
db: &RootDatabase,
- config: &AnnotationConfig,
+ config: &AnnotationConfig<'_>,
file_id: FileId,
) -> Vec<Annotation> {
let mut annotations = FxIndexSet::default();
@@ -196,13 +197,22 @@
.collect()
}
-pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
+pub(crate) fn resolve_annotation(
+ db: &RootDatabase,
+ config: &AnnotationConfig<'_>,
+ mut annotation: Annotation,
+) -> Annotation {
match annotation.kind {
AnnotationKind::HasImpls { pos, ref mut data } => {
*data = goto_implementation(db, pos).map(|range| range.info);
}
AnnotationKind::HasReferences { pos, ref mut data } => {
- *data = find_all_refs(&Semantics::new(db), pos, None).map(|result| {
+ *data = find_all_refs(
+ &Semantics::new(db),
+ pos,
+ &FindAllRefsConfig { search_scope: None, minicore: config.minicore },
+ )
+ .map(|result| {
result
.into_iter()
.flat_map(|res| res.references)
@@ -228,12 +238,13 @@
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
+ use ide_db::MiniCore;
use crate::{Annotation, AnnotationConfig, fixture};
use super::AnnotationLocation;
- const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig {
+ const DEFAULT_CONFIG: AnnotationConfig<'_> = AnnotationConfig {
binary_target: true,
annotate_runnables: true,
annotate_impls: true,
@@ -241,12 +252,13 @@
annotate_method_references: true,
annotate_enum_variant_references: true,
location: AnnotationLocation::AboveName,
+ minicore: MiniCore::default(),
};
fn check_with_config(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
- config: &AnnotationConfig,
+ config: &AnnotationConfig<'_>,
) {
let (analysis, file_id) = fixture::file(ra_fixture);
@@ -254,7 +266,7 @@
.annotations(config, file_id)
.unwrap()
.into_iter()
- .map(|annotation| analysis.resolve_annotation(annotation).unwrap())
+ .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap())
.collect();
expect.assert_debug_eq(&annotations);
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index f42cead..aded911 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -4,14 +4,16 @@
use hir::Semantics;
use ide_db::{
- FileRange, FxIndexMap, RootDatabase,
+ FileRange, FxIndexMap, MiniCore, RootDatabase,
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
search::FileReference,
};
use syntax::{AstNode, SyntaxKind::IDENT, ast};
-use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav, goto_definition};
+use crate::{
+ FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo, TryToNav, goto_definition,
+};
#[derive(Debug, Clone)]
pub struct CallItem {
@@ -19,22 +21,28 @@
pub ranges: Vec<FileRange>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct CallHierarchyConfig {
+#[derive(Debug, Clone, Copy)]
+pub struct CallHierarchyConfig<'a> {
/// Whether to exclude tests from the call hierarchy
pub exclude_tests: bool,
+ pub minicore: MiniCore<'a>,
}
pub(crate) fn call_hierarchy(
db: &RootDatabase,
position: FilePosition,
+ config: &CallHierarchyConfig<'_>,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
- goto_definition::goto_definition(db, position)
+ goto_definition::goto_definition(
+ db,
+ position,
+ &GotoDefinitionConfig { minicore: config.minicore },
+ )
}
pub(crate) fn incoming_calls(
db: &RootDatabase,
- CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
+ config: &CallHierarchyConfig<'_>,
FilePosition { file_id, offset }: FilePosition,
) -> Option<Vec<CallItem>> {
let sema = &Semantics::new(db);
@@ -71,7 +79,7 @@
});
if let Some((def, nav)) = def_nav {
- if exclude_tests && def.is_test(db) {
+ if config.exclude_tests && def.is_test(db) {
continue;
}
@@ -89,7 +97,7 @@
pub(crate) fn outgoing_calls(
db: &RootDatabase,
- CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
+ config: &CallHierarchyConfig<'_>,
FilePosition { file_id, offset }: FilePosition,
) -> Option<Vec<CallItem>> {
let sema = Semantics::new(db);
@@ -119,7 +127,7 @@
let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?;
match callable.kind() {
hir::CallableKind::Function(it) => {
- if exclude_tests && it.is_test(db) {
+ if config.exclude_tests && it.is_test(db) {
return None;
}
it.try_to_nav(&sema)
@@ -132,7 +140,7 @@
}
ast::CallableExpr::MethodCall(expr) => {
let function = sema.resolve_method_call(&expr)?;
- if exclude_tests && function.is_test(db) {
+ if config.exclude_tests && function.is_test(db) {
return None;
}
function
@@ -166,7 +174,7 @@
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
- use ide_db::FilePosition;
+ use ide_db::{FilePosition, MiniCore};
use itertools::Itertools;
use crate::fixture;
@@ -189,21 +197,20 @@
)
}
+ let config = crate::CallHierarchyConfig { exclude_tests, minicore: MiniCore::default() };
let (analysis, pos) = fixture::position(ra_fixture);
- let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
+ let mut navs = analysis.call_hierarchy(pos, &config).unwrap().unwrap().info;
assert_eq!(navs.len(), 1);
let nav = navs.pop().unwrap();
expected_nav.assert_eq(&nav.debug_render());
- let config = crate::CallHierarchyConfig { exclude_tests };
-
let item_pos =
FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
- let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap();
+ let incoming_calls = analysis.incoming_calls(&config, item_pos).unwrap().unwrap();
expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n"));
- let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap();
+ let outgoing_calls = analysis.outgoing_calls(&config, item_pos).unwrap().unwrap();
expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n"));
}
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index 686dbe2..375ce94 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -6,8 +6,8 @@
use syntax::{AstNode, SyntaxKind::*, T, ast, match_ast};
use crate::{
- FilePosition, NavigationTarget, RangeInfo, goto_definition::goto_definition,
- navigation_target::TryToNav,
+ FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo,
+ goto_definition::goto_definition, navigation_target::TryToNav,
};
// Feature: Go to Declaration
@@ -21,6 +21,7 @@
pub(crate) fn goto_declaration(
db: &RootDatabase,
position @ FilePosition { file_id, offset }: FilePosition,
+ config: &GotoDefinitionConfig<'_>,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db);
let file = sema.parse_guess_edition(file_id).syntax().clone();
@@ -69,20 +70,27 @@
.flatten()
.collect();
- if info.is_empty() { goto_definition(db, position) } else { Some(RangeInfo::new(range, info)) }
+ if info.is_empty() {
+ goto_definition(db, position, config)
+ } else {
+ Some(RangeInfo::new(range, info))
+ }
}
#[cfg(test)]
mod tests {
- use ide_db::FileRange;
+ use ide_db::{FileRange, MiniCore};
use itertools::Itertools;
- use crate::fixture;
+ use crate::{GotoDefinitionConfig, fixture};
+
+ const TEST_CONFIG: GotoDefinitionConfig<'_> =
+ GotoDefinitionConfig { minicore: MiniCore::default() };
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis
- .goto_declaration(position)
+ .goto_declaration(position, &TEST_CONFIG)
.unwrap()
.expect("no declaration or definition found")
.info;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 2dcb13d..e335989 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,5 +1,6 @@
use std::{iter, mem::discriminant};
+use crate::Analysis;
use crate::{
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
doc_links::token_as_doc_comment,
@@ -8,6 +9,7 @@
use hir::{
AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym,
};
+use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture};
use ide_db::{
RootDatabase, SymbolKind,
base_db::{AnchoredPath, SourceDatabase},
@@ -25,6 +27,11 @@
match_ast,
};
+#[derive(Debug)]
+pub struct GotoDefinitionConfig<'a> {
+ pub minicore: MiniCore<'a>,
+}
+
// Feature: Go to Definition
//
// Navigates to the definition of an identifier.
@@ -39,6 +46,7 @@
pub(crate) fn goto_definition(
db: &RootDatabase,
FilePosition { file_id, offset }: FilePosition,
+ config: &GotoDefinitionConfig<'_>,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = &Semantics::new(db);
let file = sema.parse_guess_edition(file_id).syntax().clone();
@@ -83,52 +91,64 @@
return Some(RangeInfo::new(original_token.text_range(), navs));
}
- let navs = sema
- .descend_into_macros_no_opaque(original_token.clone(), false)
- .into_iter()
- .filter_map(|token| {
- if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
- return Some(navs);
- }
+ let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);
+ let mut navs = Vec::new();
+ for token in tokens {
+ if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
+ navs.extend(n);
+ continue;
+ }
- let parent = token.value.parent()?;
+ if let Some(token) = ast::String::cast(token.value.clone())
+ && let Some(original_token) = ast::String::cast(original_token.clone())
+ && let Some((analysis, fixture_analysis)) =
+ Analysis::from_ra_fixture(sema, original_token, &token, config.minicore)
+ && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)
+ {
+ return hir::attach_db_allow_change(&analysis.db, || {
+ goto_definition(
+ &analysis.db,
+ FilePosition { file_id: virtual_file_id, offset: file_offset },
+ config,
+ )
+ })
+ .and_then(|navs| {
+ navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok()
+ });
+ }
- let token_file_id = token.file_id;
- if let Some(token) = ast::String::cast(token.value.clone())
- && let Some(x) =
- try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
- {
- return Some(vec![x]);
- }
+ let parent = token.value.parent()?;
- if ast::TokenTree::can_cast(parent.kind())
- && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
- {
- return Some(vec![x]);
- }
+ let token_file_id = token.file_id;
+ if let Some(token) = ast::String::cast(token.value.clone())
+ && let Some(x) =
+ try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
+ {
+ navs.push(x);
+ continue;
+ }
- Some(
- IdentClass::classify_node(sema, &parent)?
- .definitions()
+ if ast::TokenTree::can_cast(parent.kind())
+ && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
+ {
+ navs.push(x);
+ continue;
+ }
+
+ let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue };
+ navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| {
+ if let Definition::ExternCrateDecl(crate_def) = def {
+ return crate_def
+ .resolved_crate(db)
+ .map(|it| it.root_module().to_nav(sema.db))
.into_iter()
- .flat_map(|(def, _)| {
- if let Definition::ExternCrateDecl(crate_def) = def {
- return crate_def
- .resolved_crate(db)
- .map(|it| it.root_module().to_nav(sema.db))
- .into_iter()
- .flatten()
- .collect();
- }
- try_filter_trait_item_definition(sema, &def)
- .unwrap_or_else(|| def_to_nav(sema, def))
- })
- .collect(),
- )
- })
- .flatten()
- .unique()
- .collect::<Vec<NavigationTarget>>();
+ .flatten()
+ .collect();
+ }
+ try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def))
+ }));
+ }
+ let navs = navs.into_iter().unique().collect();
Some(RangeInfo::new(original_token.text_range(), navs))
}
@@ -584,15 +604,22 @@
#[cfg(test)]
mod tests {
- use crate::fixture;
- use ide_db::FileRange;
+ use crate::{GotoDefinitionConfig, fixture};
+ use ide_db::{FileRange, MiniCore};
use itertools::Itertools;
use syntax::SmolStr;
+ const TEST_CONFIG: GotoDefinitionConfig<'_> =
+ GotoDefinitionConfig { minicore: MiniCore::default() };
+
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
let navs = navs
@@ -611,14 +638,22 @@
fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
}
fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, _) = fixture::annotations(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
let Some(target) = navs.into_iter().next() else {
panic!("expected single navigation target but encountered none");
@@ -3961,4 +3996,23 @@
"#,
);
}
+
+ #[test]
+ fn ra_fixture() {
+ check(
+ r##"
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
+
+fn foo() {
+ fixture(r#"
+fn foo() {}
+// ^^^
+fn bar() {
+ f$0oo();
+}
+ "#)
+}
+ "##,
+ );
+ }
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c4fb6d1..e1d18b0 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,29 +11,32 @@
db::DefDatabase,
};
use ide_db::{
- FileRange, FxIndexSet, Ranker, RootDatabase,
+ FileRange, FxIndexSet, MiniCore, Ranker, RootDatabase,
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
+ ra_fixture::UpmapFromRaFixture,
};
use itertools::{Itertools, multizip};
-use span::Edition;
+use macros::UpmapFromRaFixture;
+use span::{Edition, TextRange};
use syntax::{
- AstNode,
+ AstNode, AstToken,
SyntaxKind::{self, *},
SyntaxNode, T, ast,
};
use crate::{
- FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
+ Analysis, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
doc_links::token_as_doc_comment,
markdown_remove::remove_markdown,
markup::Markup,
navigation_target::UpmappingResult,
runnables::{runnable_fn, runnable_mod},
};
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct HoverConfig {
+
+#[derive(Clone, Debug)]
+pub struct HoverConfig<'a> {
pub links_in_hover: bool,
pub memory_layout: Option<MemoryLayoutHoverConfig>,
pub documentation: bool,
@@ -44,6 +47,7 @@
pub max_enum_variants_count: Option<usize>,
pub max_subst_ty_len: SubstTyLen,
pub show_drop_glue: bool,
+ pub minicore: MiniCore<'a>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -75,7 +79,7 @@
PlainText,
}
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)]
pub enum HoverAction {
Runnable(Runnable),
Implementation(FilePosition),
@@ -108,14 +112,14 @@
}
}
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Clone, Eq, PartialEq, Hash, UpmapFromRaFixture)]
pub struct HoverGotoTypeData {
pub mod_path: String,
pub nav: NavigationTarget,
}
/// Contains the results when hovering over an item
-#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, UpmapFromRaFixture)]
pub struct HoverResult {
pub markup: Markup,
pub actions: Vec<HoverAction>,
@@ -130,7 +134,7 @@
pub(crate) fn hover(
db: &RootDatabase,
frange @ FileRange { file_id, range }: FileRange,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
) -> Option<RangeInfo<HoverResult>> {
let sema = &hir::Semantics::new(db);
let file = sema.parse_guess_edition(file_id).syntax().clone();
@@ -161,7 +165,7 @@
sema: &Semantics<'_, RootDatabase>,
FilePosition { file_id, offset }: FilePosition,
file: SyntaxNode,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
edition: Edition,
display_target: DisplayTarget,
) -> Option<RangeInfo<HoverResult>> {
@@ -219,6 +223,21 @@
return Some(RangeInfo::new(range, res));
}
+ if let Some(literal) = ast::String::cast(original_token.clone())
+ && let Some((analysis, fixture_analysis)) =
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
+ {
+ let (virtual_file_id, virtual_offset) = fixture_analysis.map_offset_down(offset)?;
+ return analysis
+ .hover(
+ config,
+ FileRange { file_id: virtual_file_id, range: TextRange::empty(virtual_offset) },
+ )
+ .ok()??
+ .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
+ .ok();
+ }
+
// prefer descending the same token kind in attribute expansions, in normal macros text
// equivalency is more important
let mut descended = sema.descend_into_macros(original_token.clone());
@@ -383,9 +402,9 @@
fn hover_ranged(
sema: &Semantics<'_, RootDatabase>,
- FileRange { range, .. }: FileRange,
+ FileRange { file_id, range }: FileRange,
file: SyntaxNode,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
edition: Edition,
display_target: DisplayTarget,
) -> Option<RangeInfo<HoverResult>> {
@@ -404,6 +423,20 @@
{
render::deref_expr(sema, config, prefix_expr, edition, display_target)
}
+ Either::Left(ast::Expr::Literal(literal)) => {
+ if let Some(literal) = ast::String::cast(literal.token())
+ && let Some((analysis, fixture_analysis)) =
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
+ {
+ let (virtual_file_id, virtual_range) = fixture_analysis.map_range_down(range)?;
+ return analysis
+ .hover(config, FileRange { file_id: virtual_file_id, range: virtual_range })
+ .ok()??
+ .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
+ .ok();
+ }
+ None
+ }
_ => None,
};
let res =
@@ -426,7 +459,7 @@
scope_node: &SyntaxNode,
macro_arm: Option<u32>,
render_extras: bool,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
edition: Edition,
display_target: DisplayTarget,
) -> HoverResult {
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index f29ccc9..a1eff3a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -35,7 +35,7 @@
pub(super) fn type_info_of(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
+ _config: &HoverConfig<'_>,
expr_or_pat: &Either<ast::Expr, ast::Pat>,
edition: Edition,
display_target: DisplayTarget,
@@ -49,7 +49,7 @@
pub(super) fn closure_expr(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
c: ast::ClosureExpr,
edition: Edition,
display_target: DisplayTarget,
@@ -60,7 +60,7 @@
pub(super) fn try_expr(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
+ _config: &HoverConfig<'_>,
try_expr: &ast::TryExpr,
edition: Edition,
display_target: DisplayTarget,
@@ -155,7 +155,7 @@
pub(super) fn deref_expr(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
+ _config: &HoverConfig<'_>,
deref_expr: &ast::PrefixExpr,
edition: Edition,
display_target: DisplayTarget,
@@ -219,7 +219,7 @@
pub(super) fn underscore(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
token: &SyntaxToken,
edition: Edition,
display_target: DisplayTarget,
@@ -263,7 +263,7 @@
pub(super) fn keyword(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
token: &SyntaxToken,
edition: Edition,
display_target: DisplayTarget,
@@ -290,7 +290,7 @@
/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
pub(super) fn struct_rest_pat(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
+ _config: &HoverConfig<'_>,
pattern: &ast::RecordPat,
edition: Edition,
display_target: DisplayTarget,
@@ -371,7 +371,7 @@
def: Definition,
markup: &Markup,
markup_range_map: Option<DocsRangeMap>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
) -> Markup {
let markup = markup.as_str();
let markup = if config.links_in_hover {
@@ -481,7 +481,7 @@
macro_arm: Option<u32>,
render_extras: bool,
subst_types: Option<&Vec<(Symbol, Type<'_>)>>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
edition: Edition,
display_target: DisplayTarget,
) -> (Markup, Option<DocsRangeMap>) {
@@ -979,7 +979,7 @@
fn type_info(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
ty: TypeInfo<'_>,
edition: Edition,
display_target: DisplayTarget,
@@ -1038,7 +1038,7 @@
fn closure_ty(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
TypeInfo { original, adjusted }: &TypeInfo<'_>,
edition: Edition,
display_target: DisplayTarget,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index df18006..91fb4d0 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1,5 +1,5 @@
use expect_test::{Expect, expect};
-use ide_db::{FileRange, base_db::SourceDatabase};
+use ide_db::{FileRange, MiniCore, base_db::SourceDatabase};
use syntax::TextRange;
use crate::{
@@ -8,7 +8,7 @@
use hir::setup_tracing;
-const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
+const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig {
links_in_hover: false,
memory_layout: Some(MemoryLayoutHoverConfig {
size: Some(MemoryLayoutHoverRenderKind::Both),
@@ -25,6 +25,7 @@
max_enum_variants_count: Some(5),
max_subst_ty_len: super::SubstTyLen::Unlimited,
show_drop_glue: true,
+ minicore: MiniCore::default(),
};
fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index f7b09b4..21550d5 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -8,9 +8,12 @@
ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError,
HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
};
-use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder};
+use ide_db::{
+ FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder,
+};
use ide_db::{FxHashSet, text_edit::TextEdit};
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
use smallvec::{SmallVec, smallvec};
use stdx::never;
use syntax::{
@@ -37,6 +40,7 @@
mod implied_dyn_trait;
mod lifetime;
mod param_name;
+mod ra_fixture;
mod range_exclusive;
// Feature: Inlay Hints
@@ -80,7 +84,7 @@
db: &RootDatabase,
file_id: FileId,
range_limit: Option<TextRange>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
) -> Vec<InlayHint> {
let _p = tracing::info_span!("inlay_hints").entered();
let sema = Semantics::new(db);
@@ -132,7 +136,7 @@
file_id: FileId,
resolve_range: TextRange,
hash: u64,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
hasher: impl Fn(&InlayHint) -> u64,
) -> Option<InlayHint> {
let _p = tracing::info_span!("inlay_hints_resolve").entered();
@@ -208,7 +212,7 @@
hints: &mut Vec<InlayHint>,
ctx: &mut InlayHintCtx,
famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
file_id: EditionedFileId,
display_target: DisplayTarget,
node: SyntaxNode,
@@ -239,6 +243,7 @@
closure_ret::hints(hints, famous_defs, config, display_target, it)
},
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
+ ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it),
_ => Some(()),
}
},
@@ -294,8 +299,8 @@
};
}
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct InlayHintsConfig {
+#[derive(Clone, Debug)]
+pub struct InlayHintsConfig<'a> {
pub render_colons: bool,
pub type_hints: bool,
pub sized_bound: bool,
@@ -321,9 +326,10 @@
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
+ pub minicore: MiniCore<'a>,
}
-impl InlayHintsConfig {
+impl InlayHintsConfig<'_> {
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
if self.fields_to_resolve.resolve_text_edits {
LazyProperty::Lazy
@@ -466,7 +472,7 @@
After,
}
-#[derive(Debug)]
+#[derive(Debug, UpmapFromRaFixture)]
pub struct InlayHint {
/// The text range this inlay hint applies to.
pub range: TextRange,
@@ -485,9 +491,10 @@
}
/// A type signaling that a value is either computed, or is available for computation.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default, UpmapFromRaFixture)]
pub enum LazyProperty<T> {
Computed(T),
+ #[default]
Lazy,
}
@@ -537,7 +544,7 @@
Markdown(String),
}
-#[derive(Default, Hash)]
+#[derive(Default, Hash, UpmapFromRaFixture)]
pub struct InlayHintLabel {
pub parts: SmallVec<[InlayHintLabelPart; 1]>,
}
@@ -623,6 +630,7 @@
}
}
+#[derive(UpmapFromRaFixture)]
pub struct InlayHintLabelPart {
pub text: String,
/// Source location represented by this label part. The client will use this to fetch the part's
@@ -724,7 +732,7 @@
fn label_of_ty(
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
ty: &hir::Type<'_>,
display_target: DisplayTarget,
) -> Option<InlayHintLabel> {
@@ -734,7 +742,7 @@
mut max_length: Option<usize>,
ty: &hir::Type<'_>,
label_builder: &mut InlayHintLabelBuilder<'_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
) -> Result<(), HirDisplayError> {
hir::attach_db(sema.db, || {
@@ -829,7 +837,7 @@
fn ty_to_text_edit(
sema: &Semantics<'_, RootDatabase>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
node_for_hint: &SyntaxNode,
ty: &hir::Type<'_>,
offset_to_insert_ty: TextSize,
@@ -860,6 +868,7 @@
use expect_test::Expect;
use hir::ClosureStyle;
+ use ide_db::MiniCore;
use itertools::Itertools;
use test_utils::extract_annotations;
@@ -869,7 +878,7 @@
use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve};
- pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
+ pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never,
render_colons: false,
type_hints: false,
@@ -899,8 +908,9 @@
fields_to_resolve: InlayFieldsToResolve::empty(),
implicit_drop_hints: false,
range_exclusive_hints: false,
+ minicore: MiniCore::default(),
};
- pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
+ pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
type_hints: true,
parameter_hints: true,
chaining_hints: true,
@@ -917,7 +927,7 @@
#[track_caller]
pub(super) fn check_with_config(
- config: InlayHintsConfig,
+ config: InlayHintsConfig<'_>,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
) {
let (analysis, file_id) = fixture::file(ra_fixture);
@@ -936,7 +946,7 @@
#[track_caller]
pub(super) fn check_expect(
- config: InlayHintsConfig,
+ config: InlayHintsConfig<'_>,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
@@ -951,7 +961,7 @@
/// expect test.
#[track_caller]
pub(super) fn check_edit(
- config: InlayHintsConfig,
+ config: InlayHintsConfig<'_>,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
@@ -974,7 +984,7 @@
#[track_caller]
pub(super) fn check_no_edit(
- config: InlayHintsConfig,
+ config: InlayHintsConfig<'_>,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
) {
let (analysis, file_id) = fixture::file(ra_fixture);
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 7231a31..ebb0d57 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -23,7 +23,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
expr: &ast::Expr,
) -> Option<()> {
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 121b16b..de207c7 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -20,7 +20,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
pat: &ast::IdentPat,
) -> Option<()> {
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index 169ab92..e8d305a 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -15,7 +15,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
pat: &ast::Pat,
) -> Option<()> {
if !config.binding_mode_hints {
diff --git a/crates/ide/src/inlay_hints/bounds.rs b/crates/ide/src/inlay_hints/bounds.rs
index 4abd67b..c9fbdf3 100644
--- a/crates/ide/src/inlay_hints/bounds.rs
+++ b/crates/ide/src/inlay_hints/bounds.rs
@@ -13,7 +13,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
params: ast::GenericParamList,
) -> Option<()> {
if !config.sized_bound {
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index a8bb652..cf3149c 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -13,7 +13,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
expr: &ast::Expr,
) -> Option<()> {
@@ -93,7 +93,7 @@
#[track_caller]
pub(super) fn check_expect_clear_loc(
- config: InlayHintsConfig,
+ config: InlayHintsConfig<'_>,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index 9d246ed..ab3ce5b 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -19,7 +19,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
InRealFile { file_id, value: node }: InRealFile<SyntaxNode>,
) -> Option<()> {
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index 3186a56..f8d4ddc 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -13,7 +13,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
closure: ast::ClosureExpr,
) -> Option<()> {
if !config.closure_capture_hints {
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
index fef1cb8..7765dc4 100644
--- a/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -13,7 +13,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
closure: ast::ClosureExpr,
) -> Option<()> {
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index a2a7028..5b92671 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -17,7 +17,7 @@
pub(super) fn enum_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
enum_: ast::Enum,
) -> Option<()> {
if let DiscriminantHints::Never = config.discriminant_hints {
@@ -41,7 +41,7 @@
fn variant_hints(
acc: &mut Vec<InlayHint>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
sema: &Semantics<'_, RootDatabase>,
enum_: &ast::Enum,
variant: &ast::Variant,
diff --git a/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs
index 491018a..8dd6c4d 100644
--- a/crates/ide/src/inlay_hints/extern_block.rs
+++ b/crates/ide/src/inlay_hints/extern_block.rs
@@ -7,7 +7,7 @@
pub(super) fn extern_block_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
extern_block: ast::ExternBlock,
) -> Option<()> {
if extern_block.unsafe_token().is_some() {
@@ -33,7 +33,7 @@
pub(super) fn fn_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
fn_: &ast::Fn,
extern_block: &ast::ExternBlock,
) -> Option<()> {
@@ -51,7 +51,7 @@
pub(super) fn static_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
static_: &ast::Static,
extern_block: &ast::ExternBlock,
) -> Option<()> {
@@ -67,7 +67,7 @@
}
fn item_hint(
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
extern_block: &ast::ExternBlock,
token: SyntaxToken,
) -> InlayHint {
diff --git a/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs
index 1fddb6f..27d14f7 100644
--- a/crates/ide/src/inlay_hints/generic_param.rs
+++ b/crates/ide/src/inlay_hints/generic_param.rs
@@ -16,7 +16,7 @@
pub(crate) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
node: AnyHasGenericArgs,
) -> Option<()> {
let GenericParameterHints { type_hints, lifetime_hints, const_hints } =
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 1e272fe..951a672 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -23,7 +23,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
display_target: hir::DisplayTarget,
node: &ast::Fn,
) -> Option<()> {
@@ -147,7 +147,7 @@
inlay_hints::tests::{DISABLED_CONFIG, check_with_config},
};
- const ONLY_DROP_CONFIG: InlayHintsConfig =
+ const ONLY_DROP_CONFIG: InlayHintsConfig<'_> =
InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG };
#[test]
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
index bddce90..0492991 100644
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -15,7 +15,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
statik_or_const: Either<ast::Static, ast::Const>,
) -> Option<()> {
if config.lifetime_elision_hints != LifetimeElisionHints::Always {
diff --git a/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/crates/ide/src/inlay_hints/implied_dyn_trait.rs
index 0da1785..562eb1e 100644
--- a/crates/ide/src/inlay_hints/implied_dyn_trait.rs
+++ b/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -11,7 +11,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
path: Either<ast::PathType, ast::DynTraitType>,
) -> Option<()> {
let parent = path.syntax().parent()?;
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index a89c53e..4982b60 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -21,7 +21,7 @@
acc: &mut Vec<InlayHint>,
ctx: &mut InlayHintCtx,
fd: &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
func: ast::Fn,
) -> Option<()> {
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -70,7 +70,7 @@
acc: &mut Vec<InlayHint>,
ctx: &mut InlayHintCtx,
fd: &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
func: ast::FnPtrType,
) -> Option<()> {
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -135,7 +135,7 @@
acc: &mut Vec<InlayHint>,
ctx: &mut InlayHintCtx,
fd: &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
func: &ast::PathType,
) -> Option<()> {
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -196,7 +196,7 @@
acc: &mut Vec<InlayHint>,
ctx: &mut InlayHintCtx,
FamousDefs(_, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
params: impl Iterator<Item = (Option<ast::Name>, ast::Type)>,
generic_param_list: Option<ast::GenericParamList>,
ret_type: Option<ast::RetType>,
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 7547077..3e555e8 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -18,7 +18,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
file_id: EditionedFileId,
expr: ast::Expr,
) -> Option<()> {
diff --git a/crates/ide/src/inlay_hints/ra_fixture.rs b/crates/ide/src/inlay_hints/ra_fixture.rs
new file mode 100644
index 0000000..bee1841
--- /dev/null
+++ b/crates/ide/src/inlay_hints/ra_fixture.rs
@@ -0,0 +1,32 @@
+//! Injected inlay hints for `#[rust_analyzer::rust_fixture]`.
+
+use hir::{EditionedFileId, Semantics};
+use ide_db::{RootDatabase, impl_empty_upmap_from_ra_fixture, ra_fixture::UpmapFromRaFixture};
+use syntax::{AstToken, ast};
+
+use crate::{Analysis, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip};
+
+pub(super) fn hints(
+ acc: &mut Vec<InlayHint>,
+ sema: &Semantics<'_, RootDatabase>,
+ file_id: EditionedFileId,
+ config: &InlayHintsConfig<'_>,
+ literal: ast::Literal,
+) -> Option<()> {
+ let file_id = file_id.file_id(sema.db);
+ let literal = ast::String::cast(literal.token())?;
+ let (analysis, fixture_analysis) =
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)?;
+ for virtual_file_id in fixture_analysis.files() {
+ acc.extend(
+ analysis
+ .inlay_hints(config, virtual_file_id, None)
+ .ok()?
+ .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
+ .ok()?,
+ );
+ }
+ Some(())
+}
+
+impl_empty_upmap_from_ra_fixture!(InlayHintPosition, InlayKind, InlayTooltip);
diff --git a/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs
index 47bd6d7..a446908 100644
--- a/crates/ide/src/inlay_hints/range_exclusive.rs
+++ b/crates/ide/src/inlay_hints/range_exclusive.rs
@@ -11,7 +11,7 @@
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
range: impl ast::RangeItem,
) -> Option<()> {
(config.range_exclusive_hints && range.end().is_some())
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f7d21c9..8572528 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -62,7 +62,7 @@
use cfg::CfgOptions;
use fetch_crates::CrateInfo;
-use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, db::HirDatabase, sym};
+use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
use ide_db::{
FxHashMap, FxIndexSet, LineIndexDatabase,
base_db::{
@@ -71,7 +71,9 @@
},
prime_caches, symbol_index,
};
-use syntax::SourceFile;
+use ide_db::{MiniCore, ra_fixture::RaFixtureAnalysis};
+use macros::UpmapFromRaFixture;
+use syntax::{SourceFile, ast};
use triomphe::Arc;
use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout};
@@ -83,6 +85,7 @@
expand_macro::ExpandedMacro,
file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
folding_ranges::{Fold, FoldKind},
+ goto_definition::GotoDefinitionConfig,
highlight_related::{HighlightRelatedConfig, HighlightedRange},
hover::{
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
@@ -102,7 +105,7 @@
},
move_item::Direction,
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
- references::ReferenceSearchResult,
+ references::{FindAllRefsConfig, ReferenceSearchResult},
rename::RenameError,
runnables::{Runnable, RunnableKind, TestId, UpdateTest},
signature_help::SignatureHelp,
@@ -144,7 +147,7 @@
pub type Cancellable<T> = Result<T, Cancelled>;
/// Info associated with a text range.
-#[derive(Debug)]
+#[derive(Debug, UpmapFromRaFixture)]
pub struct RangeInfo<T> {
pub range: TextRange,
pub info: T,
@@ -274,6 +277,28 @@
(host.analysis(), file_id)
}
+ pub(crate) fn from_ra_fixture(
+ sema: &Semantics<'_, RootDatabase>,
+ literal: ast::String,
+ expanded: &ast::String,
+ minicore: MiniCore<'_>,
+ ) -> Option<(Analysis, RaFixtureAnalysis)> {
+ Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, minicore, &mut |_| {})
+ }
+
+ /// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position.
+ pub(crate) fn from_ra_fixture_with_on_cursor(
+ sema: &Semantics<'_, RootDatabase>,
+ literal: ast::String,
+ expanded: &ast::String,
+ minicore: MiniCore<'_>,
+ on_cursor: &mut dyn FnMut(TextRange),
+ ) -> Option<(Analysis, RaFixtureAnalysis)> {
+ let analysis =
+ RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, minicore, on_cursor)?;
+ Some((Analysis { db: analysis.db.clone() }, analysis))
+ }
+
/// Debug info about the current state of the analysis.
pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
self.with_db(|db| status::status(db, file_id))
@@ -446,7 +471,7 @@
/// Returns a list of the places in the file where type hints can be displayed.
pub fn inlay_hints(
&self,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
file_id: FileId,
range: Option<TextRange>,
) -> Cancellable<Vec<InlayHint>> {
@@ -454,7 +479,7 @@
}
pub fn inlay_hints_resolve(
&self,
- config: &InlayHintsConfig,
+ config: &InlayHintsConfig<'_>,
file_id: FileId,
resolve_range: TextRange,
hash: u64,
@@ -495,16 +520,18 @@
pub fn goto_definition(
&self,
position: FilePosition,
+ config: &GotoDefinitionConfig<'_>,
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
- self.with_db(|db| goto_definition::goto_definition(db, position))
+ self.with_db(|db| goto_definition::goto_definition(db, position, config))
}
/// Returns the declaration from the symbol at `position`.
pub fn goto_declaration(
&self,
position: FilePosition,
+ config: &GotoDefinitionConfig<'_>,
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
- self.with_db(|db| goto_declaration::goto_declaration(db, position))
+ self.with_db(|db| goto_declaration::goto_declaration(db, position, config))
}
/// Returns the impls from the symbol at `position`.
@@ -526,19 +553,16 @@
pub fn find_all_refs(
&self,
position: FilePosition,
- search_scope: Option<SearchScope>,
+ config: &FindAllRefsConfig<'_>,
) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
- let search_scope = AssertUnwindSafe(search_scope);
- self.with_db(|db| {
- let _ = &search_scope;
- references::find_all_refs(&Semantics::new(db), position, search_scope.0)
- })
+ let config = AssertUnwindSafe(config);
+ self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, &config))
}
/// Returns a short text describing element at position.
pub fn hover(
&self,
- config: &HoverConfig,
+ config: &HoverConfig<'_>,
range: FileRange,
) -> Cancellable<Option<RangeInfo<HoverResult>>> {
self.with_db(|db| hover::hover(db, range, config))
@@ -576,14 +600,15 @@
pub fn call_hierarchy(
&self,
position: FilePosition,
+ config: &CallHierarchyConfig<'_>,
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
- self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
+ self.with_db(|db| call_hierarchy::call_hierarchy(db, position, config))
}
/// Computes incoming calls for the given file position.
pub fn incoming_calls(
&self,
- config: CallHierarchyConfig,
+ config: &CallHierarchyConfig<'_>,
position: FilePosition,
) -> Cancellable<Option<Vec<CallItem>>> {
self.with_db(|db| call_hierarchy::incoming_calls(db, config, position))
@@ -592,7 +617,7 @@
/// Computes outgoing calls for the given file position.
pub fn outgoing_calls(
&self,
- config: CallHierarchyConfig,
+ config: &CallHierarchyConfig<'_>,
position: FilePosition,
) -> Cancellable<Option<Vec<CallItem>>> {
self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position))
@@ -675,28 +700,22 @@
/// Computes syntax highlighting for the given file
pub fn highlight(
&self,
- highlight_config: HighlightConfig,
+ highlight_config: HighlightConfig<'_>,
file_id: FileId,
) -> Cancellable<Vec<HlRange>> {
- // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
- // highlighting instead sets up the attach hook where neceesary for the trait solver
- Cancelled::catch(|| {
- syntax_highlighting::highlight(&self.db, highlight_config, file_id, None)
- })
+ self.with_db(|db| syntax_highlighting::highlight(db, &highlight_config, file_id, None))
}
/// Computes syntax highlighting for the given file range.
pub fn highlight_range(
&self,
- highlight_config: HighlightConfig,
+ highlight_config: HighlightConfig<'_>,
frange: FileRange,
) -> Cancellable<Vec<HlRange>> {
- // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
- // highlighting instead sets up the attach hook where neceesary for the trait solver
- Cancelled::catch(|| {
+ self.with_db(|db| {
syntax_highlighting::highlight(
- &self.db,
- highlight_config,
+ db,
+ &highlight_config,
frange.file_id,
Some(frange.range),
)
@@ -706,22 +725,18 @@
/// Computes syntax highlighting for the given file.
pub fn highlight_as_html_with_config(
&self,
- config: HighlightConfig,
+ config: HighlightConfig<'_>,
file_id: FileId,
rainbow: bool,
) -> Cancellable<String> {
- // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
- // highlighting instead sets up the attach hook where neceesary for the trait solver
- Cancelled::catch(|| {
- syntax_highlighting::highlight_as_html_with_config(&self.db, config, file_id, rainbow)
+ self.with_db(|db| {
+ syntax_highlighting::highlight_as_html_with_config(db, &config, file_id, rainbow)
})
}
/// Computes syntax highlighting for the given file.
pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
- // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
- // highlighting instead sets up the attach hook where neceesary for the trait solver
- Cancelled::catch(|| syntax_highlighting::highlight_as_html(&self.db, file_id, rainbow))
+ self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
}
/// Computes completions at the given position.
@@ -853,14 +868,18 @@
pub fn annotations(
&self,
- config: &AnnotationConfig,
+ config: &AnnotationConfig<'_>,
file_id: FileId,
) -> Cancellable<Vec<Annotation>> {
self.with_db(|db| annotations::annotations(db, config, file_id))
}
- pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
- self.with_db(|db| annotations::resolve_annotation(db, annotation))
+ pub fn resolve_annotation(
+ &self,
+ config: &AnnotationConfig<'_>,
+ annotation: Annotation,
+ ) -> Cancellable<Annotation> {
+ self.with_db(|db| annotations::resolve_annotation(db, config, annotation))
}
pub fn move_item(
@@ -899,12 +918,8 @@
where
F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
{
- hir::attach_db(&self.db, || {
- // the trait solver code may invoke `as_view<HirDatabase>` outside of queries,
- // so technically we might run into a panic in salsa if the downcaster has not yet been registered.
- HirDatabase::zalsa_register_downcaster(&self.db);
- Cancelled::catch(|| f(&self.db))
- })
+ // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database.
+ hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db)))
}
}
diff --git a/crates/ide/src/markup.rs b/crates/ide/src/markup.rs
index 750d125..3eb9986 100644
--- a/crates/ide/src/markup.rs
+++ b/crates/ide/src/markup.rs
@@ -5,6 +5,8 @@
//! what is used by LSP, so let's keep it simple.
use std::fmt;
+use ide_db::impl_empty_upmap_from_ra_fixture;
+
#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)]
pub struct Markup {
text: String,
@@ -39,3 +41,5 @@
format!("```text\n{contents}\n```").into()
}
}
+
+impl_empty_upmap_from_ra_fixture!(Markup);
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index db12983..4058008 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -14,6 +14,7 @@
defs::{Definition, find_std_module},
documentation::{Documentation, HasDocs},
famous_defs::FamousDefs,
+ ra_fixture::UpmapFromRaFixture,
};
use span::Edition;
use stdx::never;
@@ -78,6 +79,44 @@
}
}
+impl UpmapFromRaFixture for NavigationTarget {
+ fn upmap_from_ra_fixture(
+ self,
+ analysis: &ide_db::ra_fixture::RaFixtureAnalysis,
+ _virtual_file_id: FileId,
+ real_file_id: FileId,
+ ) -> Result<Self, ()> {
+ let virtual_file_id = self.file_id;
+ Ok(NavigationTarget {
+ file_id: real_file_id,
+ full_range: self.full_range.upmap_from_ra_fixture(
+ analysis,
+ virtual_file_id,
+ real_file_id,
+ )?,
+ focus_range: self.focus_range.upmap_from_ra_fixture(
+ analysis,
+ virtual_file_id,
+ real_file_id,
+ )?,
+ name: self.name.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
+ kind: self.kind.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
+ container_name: self.container_name.upmap_from_ra_fixture(
+ analysis,
+ virtual_file_id,
+ real_file_id,
+ )?,
+ description: self.description.upmap_from_ra_fixture(
+ analysis,
+ virtual_file_id,
+ real_file_id,
+ )?,
+ docs: self.docs.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
+ alias: self.alias.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
+ })
+ }
+}
+
pub(crate) trait ToNav {
fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget>;
}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 0189939..a53a192 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -19,14 +19,17 @@
use hir::{PathResolution, Semantics};
use ide_db::{
- FileId, RootDatabase,
+ FileId, MiniCore, RootDatabase,
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
+ ra_fixture::UpmapFromRaFixture,
search::{ReferenceCategory, SearchScope, UsageSearchResult},
};
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
use nohash_hasher::IntMap;
use span::Edition;
+use syntax::AstToken;
use syntax::{
AstNode,
SyntaxKind::*,
@@ -35,10 +38,12 @@
match_ast,
};
-use crate::{FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related};
+use crate::{
+ Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related,
+};
/// Result of a reference search operation.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, UpmapFromRaFixture)]
pub struct ReferenceSearchResult {
/// Information about the declaration site of the searched item.
/// For ADTs (structs/enums), this points to the type definition.
@@ -54,7 +59,7 @@
}
/// Information about the declaration site of a searched item.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, UpmapFromRaFixture)]
pub struct Declaration {
/// Navigation information to jump to the declaration
pub nav: NavigationTarget,
@@ -82,6 +87,12 @@
//
// 
+#[derive(Debug)]
+pub struct FindAllRefsConfig<'a> {
+ pub search_scope: Option<SearchScope>,
+ pub minicore: MiniCore<'a>,
+}
+
/// Find all references to the item at the given position.
///
/// # Arguments
@@ -110,14 +121,14 @@
pub(crate) fn find_all_refs(
sema: &Semantics<'_, RootDatabase>,
position: FilePosition,
- search_scope: Option<SearchScope>,
+ config: &FindAllRefsConfig<'_>,
) -> Option<Vec<ReferenceSearchResult>> {
let _p = tracing::info_span!("find_all_refs").entered();
let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();
let make_searcher = |literal_search: bool| {
move |def: Definition| {
let mut usages =
- def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all();
+ def.usages(sema).set_scope(config.search_scope.as_ref()).include_self_refs().all();
if literal_search {
retain_adt_literal_usages(&mut usages, def, sema);
}
@@ -165,6 +176,20 @@
return Some(vec![res]);
}
+ if let Some(token) = syntax.token_at_offset(position.offset).left_biased()
+ && let Some(token) = ast::String::cast(token.clone())
+ && let Some((analysis, fixture_analysis)) =
+ Analysis::from_ra_fixture(sema, token.clone(), &token, config.minicore)
+ && let Some((virtual_file_id, file_offset)) =
+ fixture_analysis.map_offset_down(position.offset)
+ {
+ return analysis
+ .find_all_refs(FilePosition { file_id: virtual_file_id, offset: file_offset }, config)
+ .ok()??
+ .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, position.file_id)
+ .ok();
+ }
+
match name_for_constructor_search(&syntax, position) {
Some(name) => {
let def = match NameClass::classify(sema, &name)? {
@@ -433,10 +458,10 @@
mod tests {
use expect_test::{Expect, expect};
use hir::EditionedFileId;
- use ide_db::{FileId, RootDatabase};
+ use ide_db::{FileId, MiniCore, RootDatabase};
use stdx::format_to;
- use crate::{SearchScope, fixture};
+ use crate::{SearchScope, fixture, references::FindAllRefsConfig};
#[test]
fn exclude_tests() {
@@ -1513,8 +1538,11 @@
expect: Expect,
) {
let (analysis, pos) = fixture::position(ra_fixture);
- let refs =
- analysis.find_all_refs(pos, search_scope.map(|it| it(&analysis.db))).unwrap().unwrap();
+ let config = FindAllRefsConfig {
+ search_scope: search_scope.map(|it| it(&analysis.db)),
+ minicore: MiniCore::default(),
+ };
+ let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap();
let mut actual = String::new();
for mut refs in refs {
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index cc1bbfb..494701d 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -8,6 +8,7 @@
sym,
};
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
+use ide_db::impl_empty_upmap_from_ra_fixture;
use ide_db::{
FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind,
base_db::RootQueryDb,
@@ -17,6 +18,7 @@
search::{FileReferenceNode, SearchScope},
};
use itertools::Itertools;
+use macros::UpmapFromRaFixture;
use smallvec::SmallVec;
use span::{Edition, TextSize};
use stdx::format_to;
@@ -28,7 +30,7 @@
use crate::{FileId, NavigationTarget, ToNav, TryToNav, references};
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)]
pub struct Runnable {
pub use_name_in_title: bool,
pub nav: NavigationTarget,
@@ -37,6 +39,8 @@
pub update_test: UpdateTest,
}
+impl_empty_upmap_from_ra_fixture!(RunnableKind, UpdateTest);
+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum TestId {
Name(SmolStr),
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 453d6f5..e261928 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -4,7 +4,7 @@
use arrayvec::ArrayVec;
use hir::{Crate, Module, Semantics, db::HirDatabase};
use ide_db::{
- FileId, FileRange, FxHashMap, FxHashSet, RootDatabase,
+ FileId, FileRange, FxHashMap, FxHashSet, MiniCore, RootDatabase,
base_db::{RootQueryDb, SourceDatabase, VfsPath},
defs::{Definition, IdentClass},
documentation::Documentation,
@@ -184,6 +184,7 @@
closing_brace_hints_min_lines: Some(25),
fields_to_resolve: InlayFieldsToResolve::empty(),
range_exclusive_hints: false,
+ minicore: MiniCore::default(),
},
file_id,
None,
@@ -215,6 +216,7 @@
max_enum_variants_count: Some(5),
max_subst_ty_len: SubstTyLen::Unlimited,
show_drop_glue: true,
+ minicore: MiniCore::default(),
};
let tokens = tokens.filter(|token| {
matches!(
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 0da9ee0..66895cb 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -1,7 +1,6 @@
pub(crate) mod tags;
mod highlights;
-mod injector;
mod escape;
mod format;
@@ -16,7 +15,7 @@
use either::Either;
use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics};
-use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind};
+use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind};
use syntax::{
AstNode, AstToken, NodeOrToken,
SyntaxKind::*,
@@ -44,8 +43,8 @@
pub binding_hash: Option<u64>,
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct HighlightConfig {
+#[derive(Copy, Clone, Debug)]
+pub struct HighlightConfig<'a> {
/// Whether to highlight strings
pub strings: bool,
/// Whether to highlight comments
@@ -64,6 +63,7 @@
pub macro_bang: bool,
/// Whether to highlight unresolved things be their syntax
pub syntactic_name_ref_highlighting: bool,
+ pub minicore: MiniCore<'a>,
}
// Feature: Semantic Syntax Highlighting
@@ -191,7 +191,7 @@
// 
pub(crate) fn highlight(
db: &RootDatabase,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
file_id: FileId,
range_to_highlight: Option<TextRange>,
) -> Vec<HlRange> {
@@ -226,7 +226,7 @@
fn traverse(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
InRealFile { file_id, value: root }: InRealFile<&SyntaxNode>,
krate: Option<hir::Crate>,
range_to_highlight: TextRange,
@@ -426,12 +426,9 @@
let edition = descended_element.file_id.edition(sema.db);
let (unsafe_ops, bindings_shadow_count) = match current_body {
Some(current_body) => {
- let (ops, bindings) = per_body_cache.entry(current_body).or_insert_with(|| {
- (
- hir::attach_db(sema.db, || sema.get_unsafe_ops(current_body)),
- Default::default(),
- )
- });
+ let (ops, bindings) = per_body_cache
+ .entry(current_body)
+ .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default()));
(&*ops, Some(bindings))
}
None => (&empty, None),
@@ -494,7 +491,7 @@
fn string_injections(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
file_id: EditionedFileId,
krate: Option<hir::Crate>,
token: SyntaxToken,
@@ -591,7 +588,7 @@
})
}
-fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool {
+fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool {
match &mut highlight.tag {
HlTag::StringLiteral if !config.strings => return false,
HlTag::Comment if !config.comments => return false,
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 358ac9b..75e46b8 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -1,6 +1,7 @@
//! Renders a bit of code as HTML.
use hir::{EditionedFileId, Semantics};
+use ide_db::MiniCore;
use oorandom::Rand32;
use stdx::format_to;
use syntax::AstNode;
@@ -12,7 +13,7 @@
pub(crate) fn highlight_as_html_with_config(
db: &RootDatabase,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
file_id: FileId,
rainbow: bool,
) -> String {
@@ -60,7 +61,7 @@
pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
highlight_as_html_with_config(
db,
- HighlightConfig {
+ &HighlightConfig {
strings: true,
comments: true,
punctuation: true,
@@ -70,6 +71,7 @@
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
+ minicore: MiniCore::default(),
},
file_id,
rainbow,
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index efc7782..7955f5a 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -4,9 +4,9 @@
use either::Either;
use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym};
+use ide_db::range_mapper::RangeMapper;
use ide_db::{
- SymbolKind, active_parameter::ActiveParameter, defs::Definition,
- documentation::docs_with_rangemap, rust_doc::is_rust_fence,
+ SymbolKind, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence,
};
use syntax::{
AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize,
@@ -16,85 +16,56 @@
use crate::{
Analysis, HlMod, HlRange, HlTag, RootDatabase,
doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
- syntax_highlighting::{HighlightConfig, highlights::Highlights, injector::Injector},
+ syntax_highlighting::{HighlightConfig, highlights::Highlights},
};
pub(super) fn ra_fixture(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
literal: &ast::String,
expanded: &ast::String,
) -> Option<()> {
- let active_parameter =
- hir::attach_db(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?;
- let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
- attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
- path.segments()
- .zip(["rust_analyzer", "rust_fixture"])
- .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
- })
- });
- if !has_rust_fixture_attr {
- return None;
- }
- let value = literal.value().ok()?;
+ let (analysis, fixture_analysis) = Analysis::from_ra_fixture_with_on_cursor(
+ sema,
+ literal.clone(),
+ expanded,
+ config.minicore,
+ &mut |range| {
+ hl.add(HlRange {
+ range,
+ highlight: HlTag::Keyword | HlMod::Injected,
+ binding_hash: None,
+ });
+ },
+ )?;
if let Some(range) = literal.open_quote_text_range() {
hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
}
- let mut inj = Injector::default();
-
- let mut text = &*value;
- let mut offset: TextSize = 0.into();
-
- while !text.is_empty() {
- let marker = "$0";
- let idx = text.find(marker).unwrap_or(text.len());
- let (chunk, next) = text.split_at(idx);
- inj.add(chunk, TextRange::at(offset, TextSize::of(chunk)));
-
- text = next;
- offset += TextSize::of(chunk);
-
- if let Some(next) = text.strip_prefix(marker) {
- if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
- hl.add(HlRange {
- range,
- highlight: HlTag::Keyword | HlMod::Injected,
- binding_hash: None,
- });
- }
-
- text = next;
-
- let marker_len = TextSize::of(marker);
- offset += marker_len;
- }
- }
-
- let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
-
- for mut hl_range in analysis
- .highlight(
- HighlightConfig {
- syntactic_name_ref_highlighting: false,
- comments: true,
- punctuation: true,
- operator: true,
- strings: true,
- specialize_punctuation: config.specialize_punctuation,
- specialize_operator: config.operator,
- inject_doc_comment: config.inject_doc_comment,
- macro_bang: config.macro_bang,
- },
- tmp_file_id,
- )
- .unwrap()
- {
- for range in inj.map_range_up(hl_range.range) {
- if let Some(range) = literal.map_range_up(range) {
+ for tmp_file_id in fixture_analysis.files() {
+ for mut hl_range in analysis
+ .highlight(
+ HighlightConfig {
+ syntactic_name_ref_highlighting: false,
+ comments: true,
+ punctuation: true,
+ operator: true,
+ strings: true,
+ specialize_punctuation: config.specialize_punctuation,
+ specialize_operator: config.operator,
+ inject_doc_comment: config.inject_doc_comment,
+ macro_bang: config.macro_bang,
+ // What if there is a fixture inside a fixture? It's fixtures all the way down.
+ // (In fact, we have a fixture inside a fixture in our test suite!)
+ minicore: config.minicore,
+ },
+ tmp_file_id,
+ )
+ .unwrap()
+ {
+ for range in fixture_analysis.map_range_up(tmp_file_id, hl_range.range) {
hl_range.range = range;
hl_range.highlight |= HlMod::Injected;
hl.add(hl_range);
@@ -116,7 +87,7 @@
pub(super) fn doc_comment(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
- config: HighlightConfig,
+ config: &HighlightConfig<'_>,
src_file_id: EditionedFileId,
node: &SyntaxNode,
) {
@@ -128,39 +99,37 @@
// Extract intra-doc links and emit highlights for them.
if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
- hir::attach_db(sema.db, || {
- extract_definitions_from_docs(&docs)
- .into_iter()
- .filter_map(|(range, link, ns)| {
- doc_mapping
- .map(range)
- .filter(|(mapping, _)| mapping.file_id == src_file_id)
- .and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
- Some(mapped_range).zip(resolve_doc_path_for_def(
- sema.db,
- def,
- &link,
- ns,
- attr_id.is_inner_attr(),
- ))
- })
- })
- .for_each(|(range, def)| {
- hl.add(HlRange {
- range,
- highlight: module_def_to_hl_tag(def)
- | HlMod::Documentation
- | HlMod::Injected
- | HlMod::IntraDocLink,
- binding_hash: None,
+ extract_definitions_from_docs(&docs)
+ .into_iter()
+ .filter_map(|(range, link, ns)| {
+ doc_mapping
+ .map(range)
+ .filter(|(mapping, _)| mapping.file_id == src_file_id)
+ .and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
+ Some(mapped_range).zip(resolve_doc_path_for_def(
+ sema.db,
+ def,
+ &link,
+ ns,
+ attr_id.is_inner_attr(),
+ ))
})
+ })
+ .for_each(|(range, def)| {
+ hl.add(HlRange {
+ range,
+ highlight: module_def_to_hl_tag(def)
+ | HlMod::Documentation
+ | HlMod::Injected
+ | HlMod::IntraDocLink,
+ binding_hash: None,
})
- });
+ })
}
// Extract doc-test sources from the docs and calculate highlighting for them.
- let mut inj = Injector::default();
+ let mut inj = RangeMapper::default();
inj.add_unmapped("fn doctest() {\n");
let attrs_source_map = attributes.source_map(sema.db);
@@ -249,7 +218,7 @@
if let Ok(ranges) = analysis.with_db(|db| {
super::highlight(
db,
- HighlightConfig {
+ &HighlightConfig {
syntactic_name_ref_highlighting: true,
comments: true,
punctuation: true,
@@ -259,6 +228,7 @@
specialize_operator: config.operator,
inject_doc_comment: config.inject_doc_comment,
macro_bang: config.macro_bang,
+ minicore: config.minicore,
},
tmp_file_id,
None,
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs
deleted file mode 100644
index c30f797..0000000
--- a/crates/ide/src/syntax_highlighting/injector.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-//! Extracts a subsequence of a text document, remembering the mapping of ranges
-//! between original and extracted texts.
-use std::ops::{self, Sub};
-
-use stdx::equal_range_by;
-use syntax::{TextRange, TextSize};
-
-#[derive(Default)]
-pub(super) struct Injector {
- buf: String,
- ranges: Vec<(TextRange, Option<Delta<TextSize>>)>,
-}
-
-impl Injector {
- pub(super) fn add(&mut self, text: &str, source_range: TextRange) {
- let len = TextSize::of(text);
- assert_eq!(len, source_range.len());
- self.add_impl(text, Some(source_range.start()));
- }
-
- pub(super) fn add_unmapped(&mut self, text: &str) {
- self.add_impl(text, None);
- }
-
- fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
- let len = TextSize::of(text);
- let target_range = TextRange::at(TextSize::of(&self.buf), len);
- self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it))));
- self.buf.push_str(text);
- }
-
- pub(super) fn take_text(&mut self) -> String {
- std::mem::take(&mut self.buf)
- }
-
- pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
- equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
- let (target_range, delta) = self.ranges[i];
- let intersection = target_range.intersect(range).unwrap();
- Some(intersection + delta?)
- })
- }
-}
-
-#[derive(Clone, Copy)]
-enum Delta<T> {
- Add(T),
- Sub(T),
-}
-
-impl<T> Delta<T> {
- fn new(from: T, to: T) -> Delta<T>
- where
- T: Ord + Sub<Output = T>,
- {
- if to >= from { Delta::Add(to - from) } else { Delta::Sub(from - to) }
- }
-}
-
-impl ops::Add<Delta<TextSize>> for TextSize {
- type Output = TextSize;
-
- fn add(self, rhs: Delta<TextSize>) -> TextSize {
- match rhs {
- Delta::Add(it) => self + it,
- Delta::Sub(it) => self - it,
- }
- }
-}
-
-impl ops::Add<Delta<TextSize>> for TextRange {
- type Output = TextRange;
-
- fn add(self, rhs: Delta<TextSize>) -> TextRange {
- TextRange::at(self.start() + rhs, self.len())
- }
-}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 3b468ab..579c6ce 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -43,18 +43,19 @@
<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
-</span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
- </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
- </span><span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="string_literal injected">"2 + 2 = {}"</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">4</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
- </span><span class="brace injected">}</span><span class="none injected">
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
+@@- minicore: sized
+<span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="trait default_library injected library">Sized</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="string_literal injected">"2 + 2 = {}"</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">4</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
+</span> <span class="brace injected">}</span><span class="none injected">
</span><span class="brace injected">}</span><span class="string_literal">"#</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span><span class="none injected">
-</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
- </span><span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
- </span><span class="numeric_literal injected">92</span><span class="none injected">
- </span><span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
+<span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="numeric_literal injected">92</span><span class="none injected">
+</span> <span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
</span><span class="brace injected">}</span><span class="string_literal">"</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html
new file mode 100644
index 0000000..fc2d9a3
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html
@@ -0,0 +1,61 @@
+
+<style>
+body { margin: 0; }
+pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+
+.lifetime { color: #DFAF8F; font-style: italic; }
+.label { color: #DFAF8F; font-style: italic; }
+.comment { color: #7F9F7F; }
+.documentation { color: #629755; }
+.intra_doc_link { font-style: italic; }
+.injected { opacity: 0.65 ; }
+.struct, .enum { color: #7CB8BB; }
+.enum_variant { color: #BDE0F3; }
+.string_literal { color: #CC9393; }
+.field { color: #94BFF3; }
+.function { color: #93E0E3; }
+.parameter { color: #94BFF3; }
+.text { color: #DCDCCC; }
+.type { color: #7CB8BB; }
+.builtin_type { color: #8CD0D3; }
+.type_param { color: #DFAF8F; }
+.attribute { color: #94BFF3; }
+.numeric_literal { color: #BFEBBF; }
+.bool_literal { color: #BFE6EB; }
+.macro { color: #94BFF3; }
+.proc_macro { color: #94BFF3; text-decoration: underline; }
+.derive { color: #94BFF3; font-style: italic; }
+.module { color: #AFD8AF; }
+.value_param { color: #DCDCCC; }
+.variable { color: #DCDCCC; }
+.format_specifier { color: #CC696B; }
+.mutable { text-decoration: underline; }
+.escape_sequence { color: #94BFF3; }
+.keyword { color: #F0DFAF; font-weight: bold; }
+.control { font-style: italic; }
+.reference { font-style: italic; font-weight: bold; }
+.const { font-weight: bolder; }
+.unsafe { color: #BC8383; }
+
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+</style>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+
+<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
+@@- /main.rs crate:main deps:other_crate
+<span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">x</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="module crate_root injected library">other_crate</span><span class="operator injected">::</span><span class="module injected library">foo</span><span class="operator injected">::</span><span class="struct injected library">S</span><span class="operator injected">::</span><span class="function associated injected library static">thing</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
+</span> <span class="variable injected">x</span><span class="semicolon injected">;</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="none injected"> </span><span class="comment injected">//^ i128</span><span class="none injected">
+</span><span class="none injected">
+</span>@@- /lib.rs crate:other_crate
+<span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">mod</span><span class="none injected"> </span><span class="module declaration injected public">foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">struct</span><span class="none injected"> </span><span class="struct declaration injected public">S</span><span class="semicolon injected">;</span><span class="none injected">
+</span> <span class="keyword injected">impl</span><span class="none injected"> </span><span class="struct injected public">S</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+</span> <span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected public static">thing</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">-></span><span class="none injected"> </span><span class="builtin_type injected">i128</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="numeric_literal injected">0</span><span class="none injected"> </span><span class="brace injected">}</span><span class="none injected">
+</span> <span class="brace injected">}</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="none injected">
+</span> <span class="none injected"> </span><span class="string_literal">"#</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 8198701..4e84127 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1,13 +1,13 @@
use std::time::Instant;
use expect_test::{ExpectFile, expect_file};
-use ide_db::SymbolKind;
+use ide_db::{MiniCore, SymbolKind};
use span::Edition;
use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests};
use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture};
-const HL_CONFIG: HighlightConfig = HighlightConfig {
+const HL_CONFIG: HighlightConfig<'_> = HighlightConfig {
strings: true,
comments: true,
punctuation: true,
@@ -17,6 +17,7 @@
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
+ minicore: MiniCore::default(),
};
#[test]
@@ -1017,6 +1018,35 @@
}
#[test]
+fn test_injection_2() {
+ check_highlighting(
+ r##"
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
+
+fn main() {
+ fixture(r#"
+@@- /main.rs crate:main deps:other_crate
+fn test() {
+ let x = other_crate::foo::S::thing();
+ x;
+} //^ i128
+
+@@- /lib.rs crate:other_crate
+pub mod foo {
+ pub struct S;
+ impl S {
+ pub fn thing() -> i128 { 0 }
+ }
+}
+ "#);
+}
+"##,
+ expect_file!["./test_data/highlight_injection_2.html"],
+ false,
+ );
+}
+
+#[test]
fn test_injection() {
check_highlighting(
r##"
@@ -1024,7 +1054,8 @@
fn main() {
fixture(r#"
-trait Foo {
+@@- minicore: sized
+trait Foo: Sized {
fn foo() {
println!("2 + 2 = {}", 4);
}
@@ -1223,7 +1254,7 @@
/// Note that the `snapshot` file is overwritten by the rendered HTML.
fn check_highlighting_with_config(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
- config: HighlightConfig,
+ config: HighlightConfig<'_>,
expect: ExpectFile,
rainbow: bool,
) {
diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs
index 8bafcf4..3f90ecc 100644
--- a/crates/macros/src/lib.rs
+++ b/crates/macros/src/lib.rs
@@ -162,3 +162,42 @@
ignored
}
+
+decl_derive!(
+ [UpmapFromRaFixture] => upmap_from_ra_fixture
+);
+
+fn upmap_from_ra_fixture(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+ if let syn::Data::Union(_) = s.ast().data {
+ panic!("cannot derive on union")
+ }
+
+ s.add_bounds(synstructure::AddBounds::Generics);
+ s.bind_with(|_| synstructure::BindStyle::Move);
+ let body = s.each_variant(|vi| {
+ let bindings = vi.bindings();
+ vi.construct(|_, index| {
+ let bind = &bindings[index];
+
+ quote! {
+ ::ide_db::ra_fixture::UpmapFromRaFixture::upmap_from_ra_fixture(
+ #bind, __analysis, __virtual_file_id, __real_file_id,
+ )?
+ }
+ })
+ });
+
+ s.bound_impl(
+ quote!(::ide_db::ra_fixture::UpmapFromRaFixture),
+ quote! {
+ fn upmap_from_ra_fixture(
+ self,
+ __analysis: &::ide_db::ra_fixture::RaFixtureAnalysis,
+ __virtual_file_id: ::ide_db::ra_fixture::FileId,
+ __real_file_id: ::ide_db::ra_fixture::FileId,
+ ) -> Result<Self, ()> {
+ Ok(match self { #body })
+ }
+ },
+ )
+}
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 2a9ef98..717bd23 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -25,7 +25,7 @@
InlayHintsConfig, LineCol, RootDatabase,
};
use ide_db::{
- EditionedFileId, LineIndexDatabase, SnippetCap,
+ EditionedFileId, LineIndexDatabase, MiniCore, SnippetCap,
base_db::{SourceDatabase, salsa::Database},
};
use itertools::Itertools;
@@ -1194,6 +1194,7 @@
closing_brace_hints_min_lines: Some(20),
fields_to_resolve: InlayFieldsToResolve::empty(),
range_exclusive_hints: true,
+ minicore: MiniCore::default(),
},
analysis.editioned_file_id_to_vfs(file_id),
None,
@@ -1203,26 +1204,25 @@
bar.finish_and_clear();
let mut bar = create_bar();
+ let annotation_config = AnnotationConfig {
+ binary_target: true,
+ annotate_runnables: true,
+ annotate_impls: true,
+ annotate_references: false,
+ annotate_method_references: false,
+ annotate_enum_variant_references: false,
+ location: ide::AnnotationLocation::AboveName,
+ minicore: MiniCore::default(),
+ };
for &file_id in file_ids {
let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db)));
bar.set_message(move || msg.clone());
analysis
- .annotations(
- &AnnotationConfig {
- binary_target: true,
- annotate_runnables: true,
- annotate_impls: true,
- annotate_references: false,
- annotate_method_references: false,
- annotate_enum_variant_references: false,
- location: ide::AnnotationLocation::AboveName,
- },
- analysis.editioned_file_id_to_vfs(file_id),
- )
+ .annotations(&annotation_config, analysis.editioned_file_id_to_vfs(file_id))
.unwrap()
.into_iter()
.for_each(|annotation| {
- _ = analysis.resolve_annotation(annotation);
+ _ = analysis.resolve_annotation(&annotation_config, annotation);
});
bar.inc(1);
}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 96b6583..652c2e3 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -8,14 +8,14 @@
use cfg::{CfgAtom, CfgDiff};
use hir::Symbol;
use ide::{
- AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
- CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, HighlightConfig,
- HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig,
- JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
- SourceRootId,
+ AnnotationConfig, AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
+ CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig,
+ HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
+ InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+ Snippet, SnippetScope, SourceRootId,
};
use ide_db::{
- SnippetCap,
+ MiniCore, SnippetCap,
assists::ExprFillDefaultMode,
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
};
@@ -1454,6 +1454,23 @@
pub fn references(&self) -> bool {
self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs
}
+
+ pub fn into_annotation_config<'a>(
+ self,
+ binary_target: bool,
+ minicore: MiniCore<'a>,
+ ) -> AnnotationConfig<'a> {
+ AnnotationConfig {
+ binary_target,
+ annotate_runnables: self.runnable(),
+ annotate_impls: self.implementations,
+ annotate_references: self.refs_adt,
+ annotate_method_references: self.method_refs,
+ annotate_enum_variant_references: self.enum_variant_refs,
+ location: self.location.into(),
+ minicore,
+ }
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1688,11 +1705,15 @@
}
}
- pub fn call_hierarchy(&self) -> CallHierarchyConfig {
- CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() }
+ pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> {
+ CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore }
}
- pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig<'_> {
+ pub fn completion<'a>(
+ &'a self,
+ source_root: Option<SourceRootId>,
+ minicore: MiniCore<'a>,
+ ) -> CompletionConfig<'a> {
let client_capability_fields = self.completion_resolve_support_properties();
CompletionConfig {
enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
@@ -1746,6 +1767,7 @@
})
.collect(),
exclude_traits: self.completion_excludeTraits(source_root),
+ minicore,
}
}
@@ -1820,7 +1842,7 @@
}
}
- pub fn hover(&self) -> HoverConfig {
+ pub fn hover<'a>(&self, minicore: MiniCore<'a>) -> HoverConfig<'a> {
let mem_kind = |kind| match kind {
MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
@@ -1853,10 +1875,15 @@
None => ide::SubstTyLen::Unlimited,
},
show_drop_glue: *self.hover_dropGlue_enable(),
+ minicore,
}
}
- pub fn inlay_hints(&self) -> InlayHintsConfig {
+ pub fn goto_definition<'a>(&self, minicore: MiniCore<'a>) -> GotoDefinitionConfig<'a> {
+ GotoDefinitionConfig { minicore }
+ }
+
+ pub fn inlay_hints<'a>(&self, minicore: MiniCore<'a>) -> InlayHintsConfig<'a> {
let client_capability_fields = self.inlay_hint_resolve_support_properties();
InlayHintsConfig {
@@ -1938,6 +1965,7 @@
),
implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(),
range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(),
+ minicore,
}
}
@@ -1975,7 +2003,7 @@
self.semanticHighlighting_nonStandardTokens().to_owned()
}
- pub fn highlighting_config(&self) -> HighlightConfig {
+ pub fn highlighting_config<'a>(&self, minicore: MiniCore<'a>) -> HighlightConfig<'a> {
HighlightConfig {
strings: self.semanticHighlighting_strings_enable().to_owned(),
comments: self.semanticHighlighting_comments_enable().to_owned(),
@@ -1990,6 +2018,7 @@
.to_owned(),
inject_doc_comment: self.semanticHighlighting_doc_comment_inject_enable().to_owned(),
syntactic_name_ref_highlighting: false,
+ minicore,
}
}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index ce6644f..f557dd5 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -13,7 +13,10 @@
use crossbeam_channel::{Receiver, Sender, unbounded};
use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
-use ide_db::base_db::{Crate, ProcMacroPaths, SourceDatabase};
+use ide_db::{
+ MiniCore,
+ base_db::{Crate, ProcMacroPaths, SourceDatabase},
+};
use itertools::Itertools;
use load_cargo::SourceRootConfig;
use lsp_types::{SemanticTokens, Url};
@@ -188,6 +191,14 @@
/// This is marked true if we failed to load a crate root file at crate graph creation,
/// which will usually end up causing a bunch of incorrect diagnostics on startup.
pub(crate) incomplete_crate_graph: bool,
+
+ pub(crate) minicore: MiniCoreRustAnalyzerInternalOnly,
+}
+
+// FIXME: This should move to the VFS once the rewrite is done.
+#[derive(Debug, Clone, Default)]
+pub(crate) struct MiniCoreRustAnalyzerInternalOnly {
+ pub(crate) minicore_text: Option<String>,
}
/// An immutable snapshot of the world's state at a point in time.
@@ -204,6 +215,7 @@
// FIXME: Can we derive this from somewhere else?
pub(crate) proc_macros_loaded: bool,
pub(crate) flycheck: Arc<[FlycheckHandle]>,
+ minicore: MiniCoreRustAnalyzerInternalOnly,
}
impl std::panic::UnwindSafe for GlobalStateSnapshot {}
@@ -304,6 +316,8 @@
deferred_task_queue: task_queue,
incomplete_crate_graph: false,
+
+ minicore: MiniCoreRustAnalyzerInternalOnly::default(),
};
// Apply any required database inputs from the config.
this.update_configuration(config);
@@ -550,6 +564,7 @@
workspaces: Arc::clone(&self.workspaces),
analysis: self.analysis_host.analysis(),
vfs: Arc::clone(&self.vfs),
+ minicore: self.minicore.clone(),
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
@@ -838,6 +853,14 @@
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
self.vfs.read().0.exists(file_id)
}
+
+ #[inline]
+ pub(crate) fn minicore(&self) -> MiniCore<'_> {
+ match &self.minicore.minicore_text {
+ Some(minicore) => MiniCore::new(minicore),
+ None => MiniCore::default(),
+ }
+ }
}
pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 6cb28ae..55d092f 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -7,8 +7,8 @@
use base64::{Engine, prelude::BASE64_STANDARD};
use ide::{
- AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
- FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData,
+ AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition,
+ FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData,
InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
SingleResolve, SourceChange, TextEdit,
};
@@ -811,7 +811,8 @@
let _p = tracing::info_span!("handle_goto_definition").entered();
let position =
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
- let nav_info = match snap.analysis.goto_definition(position)? {
+ let config = snap.config.goto_definition(snap.minicore());
+ let nav_info = match snap.analysis.goto_definition(position, &config)? {
None => return Ok(None),
Some(it) => it,
};
@@ -829,7 +830,8 @@
&snap,
params.text_document_position_params.clone()
)?);
- let nav_info = match snap.analysis.goto_declaration(position)? {
+ let config = snap.config.goto_definition(snap.minicore());
+ let nav_info = match snap.analysis.goto_declaration(position, &config)? {
None => return handle_goto_definition(snap, params),
Some(it) => it,
};
@@ -1106,7 +1108,7 @@
context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
let source_root = snap.analysis.source_root_id(position.file_id)?;
- let completion_config = &snap.config.completion(Some(source_root));
+ let completion_config = &snap.config.completion(Some(source_root), snap.minicore());
// FIXME: We should fix up the position when retrying the cancelled request instead
position.offset = position.offset.min(line_index.index.len());
let items = match snap.analysis.completions(
@@ -1160,7 +1162,8 @@
};
let source_root = snap.analysis.source_root_id(file_id)?;
- let mut forced_resolve_completions_config = snap.config.completion(Some(source_root));
+ let mut forced_resolve_completions_config =
+ snap.config.completion(Some(source_root), snap.minicore());
forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty();
let position = FilePosition { file_id, offset };
@@ -1274,7 +1277,7 @@
};
let file_range = try_default!(from_proto::file_range(&snap, ¶ms.text_document, range)?);
- let hover = snap.config.hover();
+ let hover = snap.config.hover(snap.minicore());
let info = match snap.analysis.hover(&hover, file_range)? {
None => return Ok(None),
Some(info) => info,
@@ -1360,7 +1363,11 @@
let exclude_imports = snap.config.find_all_refs_exclude_imports();
let exclude_tests = snap.config.find_all_refs_exclude_tests();
- let Some(refs) = snap.analysis.find_all_refs(position, None)? else {
+ let Some(refs) = snap.analysis.find_all_refs(
+ position,
+ &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
+ )?
+ else {
return Ok(None);
};
@@ -1615,8 +1622,8 @@
let target_spec = TargetSpec::for_file(&snap, file_id)?;
let annotations = snap.analysis.annotations(
- &AnnotationConfig {
- binary_target: target_spec
+ &lens_config.into_annotation_config(
+ target_spec
.map(|spec| {
matches!(
spec.target_kind(),
@@ -1624,13 +1631,8 @@
)
})
.unwrap_or(false),
- annotate_runnables: lens_config.runnable(),
- annotate_impls: lens_config.implementations,
- annotate_references: lens_config.refs_adt,
- annotate_method_references: lens_config.method_refs,
- annotate_enum_variant_references: lens_config.enum_variant_refs,
- location: lens_config.location.into(),
- },
+ snap.minicore(),
+ ),
file_id,
)?;
@@ -1653,7 +1655,8 @@
let Some(annotation) = from_proto::annotation(&snap, code_lens.range, resolve)? else {
return Ok(code_lens);
};
- let annotation = snap.analysis.resolve_annotation(annotation)?;
+ let config = snap.config.lens().into_annotation_config(false, snap.minicore());
+ let annotation = snap.analysis.resolve_annotation(&config, annotation)?;
let mut acc = Vec::new();
to_proto::code_lens(&mut acc, &snap, annotation)?;
@@ -1736,7 +1739,7 @@
range.end().min(line_index.index.len()),
);
- let inlay_hints_config = snap.config.inlay_hints();
+ let inlay_hints_config = snap.config.inlay_hints(snap.minicore());
Ok(Some(
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
@@ -1777,7 +1780,7 @@
let line_index = snap.file_line_index(file_id)?;
let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?;
- let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
+ let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(snap.minicore());
forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
let resolve_hints = snap.analysis.inlay_hints_resolve(
&forced_resolve_inlay_hints_config,
@@ -1816,7 +1819,8 @@
let position =
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
- let nav_info = match snap.analysis.call_hierarchy(position)? {
+ let config = snap.config.call_hierarchy(snap.minicore());
+ let nav_info = match snap.analysis.call_hierarchy(position, &config)? {
None => return Ok(None),
Some(it) => it,
};
@@ -1842,8 +1846,8 @@
let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
- let config = snap.config.call_hierarchy();
- let call_items = match snap.analysis.incoming_calls(config, fpos)? {
+ let config = snap.config.call_hierarchy(snap.minicore());
+ let call_items = match snap.analysis.incoming_calls(&config, fpos)? {
None => return Ok(None),
Some(it) => it,
};
@@ -1881,8 +1885,8 @@
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let line_index = snap.file_line_index(fpos.file_id)?;
- let config = snap.config.call_hierarchy();
- let call_items = match snap.analysis.outgoing_calls(config, fpos)? {
+ let config = snap.config.call_hierarchy(snap.minicore());
+ let call_items = match snap.analysis.outgoing_calls(&config, fpos)? {
None => return Ok(None),
Some(it) => it,
};
@@ -1916,7 +1920,7 @@
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
- let mut highlight_config = snap.config.highlighting_config();
+ let mut highlight_config = snap.config.highlighting_config(snap.minicore());
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1946,7 +1950,7 @@
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
- let mut highlight_config = snap.config.highlighting_config();
+ let mut highlight_config = snap.config.highlighting_config(snap.minicore());
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -1988,7 +1992,7 @@
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
- let mut highlight_config = snap.config.highlighting_config();
+ let mut highlight_config = snap.config.highlighting_config(snap.minicore());
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
highlight_config.syntactic_name_ref_highlighting =
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
@@ -2156,7 +2160,13 @@
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover_actions().references
&& snap.config.client_commands().show_reference
- && let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None)
+ && let Some(ref_search_res) = snap
+ .analysis
+ .find_all_refs(
+ *position,
+ &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
+ )
+ .unwrap_or(None)
{
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 84b7888..38ee9cb 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -16,7 +16,7 @@
FilePosition, TextSize,
};
use ide_db::{
- SnippetCap,
+ MiniCore, SnippetCap,
imports::insert_use::{ImportGranularity, InsertUseConfig},
};
use project_model::CargoConfig;
@@ -186,6 +186,7 @@
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
+ minicore: MiniCore::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -240,6 +241,7 @@
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
+ minicore: MiniCore::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -292,6 +294,7 @@
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
+ minicore: MiniCore::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index d51ddb8..cd384ca 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -16,7 +16,9 @@
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
UpdateTest,
};
-use ide_db::{FxHasher, assists, rust_doc::format_docs, source_change::ChangeAnnotationId};
+use ide_db::{
+ FxHasher, MiniCore, assists, rust_doc::format_docs, source_change::ChangeAnnotationId,
+};
use itertools::Itertools;
use paths::{Utf8Component, Utf8Prefix};
use semver::VersionReq;
@@ -270,7 +272,7 @@
);
}
- if let Some(limit) = config.completion(None).limit {
+ if let Some(limit) = config.completion(None, MiniCore::default()).limit {
res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
res.truncate(limit);
}
@@ -400,16 +402,17 @@
set_score(&mut lsp_item, max_relevance, item.relevance);
- let imports =
- if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
- item.import_to_add
- .clone()
- .into_iter()
- .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
- .collect()
- } else {
- Vec::new()
- };
+ let imports = if config.completion(None, MiniCore::default()).enable_imports_on_the_fly
+ && !item.import_to_add.is_empty()
+ {
+ item.import_to_add
+ .clone()
+ .into_iter()
+ .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
+ .collect()
+ } else {
+ Vec::new()
+ };
let (ref_resolve_data, resolve_data) = if something_to_resolve || !imports.is_empty() {
let ref_resolve_data = if ref_match.is_some() {
let ref_resolve_data = lsp_ext::CompletionResolveData {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 3e80e8b..c0947b2 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -847,6 +847,13 @@
self.debounce_workspace_fetch();
let vfs = &mut self.vfs.write().0;
for (path, contents) in files {
+ if matches!(path.name_and_extension(), Some(("minicore", Some("rs")))) {
+ // Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that.
+ self.minicore.minicore_text = contents
+ .as_ref()
+ .and_then(|contents| String::from_utf8(contents.clone()).ok());
+ }
+
let path = VfsPath::from(path);
// if the file is in mem docs, it's managed by the client via notifications
// so only set it if its not in there
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index d9223e8..e1a9f3a 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -201,6 +201,10 @@
None
}
}
+ fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> {
+ let contents_range = self.text_range_between_quotes()?;
+ offset.checked_sub(contents_range.start())
+ }
}
impl IsString for ast::String {
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index a454979..aefe81f 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -24,7 +24,7 @@
use span::{Edition, FileId, Span};
use stdx::itertools::Itertools;
use test_utils::{
- CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, RangeOrOffset,
+ CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, MiniCore, RangeOrOffset,
extract_range_or_offset,
};
use triomphe::Arc;
@@ -69,7 +69,12 @@
proc_macros: Vec<(String, ProcMacro)>,
) -> Self {
let mut db = Self::default();
- let fixture = ChangeFixture::parse_with_proc_macros(&db, ra_fixture, proc_macros);
+ let fixture = ChangeFixture::parse_with_proc_macros(
+ &db,
+ ra_fixture,
+ MiniCore::RAW_SOURCE,
+ proc_macros,
+ );
fixture.change.apply(&mut db);
assert!(fixture.file_position.is_none());
db
@@ -112,8 +117,10 @@
pub struct ChangeFixture {
pub file_position: Option<(EditionedFileId, RangeOrOffset)>,
+ pub file_lines: Vec<usize>,
pub files: Vec<EditionedFileId>,
pub change: ChangeWithProcMacros,
+ pub sysroot_files: Vec<FileId>,
}
const SOURCE_ROOT_PREFIX: &str = "/";
@@ -123,12 +130,13 @@
db: &dyn salsa::Database,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
) -> ChangeFixture {
- Self::parse_with_proc_macros(db, ra_fixture, Vec::new())
+ Self::parse_with_proc_macros(db, ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
}
pub fn parse_with_proc_macros(
db: &dyn salsa::Database,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
+ minicore_raw: &str,
mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture {
let FixtureWithProjectMeta {
@@ -149,6 +157,8 @@
let mut source_change = FileChange::default();
let mut files = Vec::new();
+ let mut sysroot_files = Vec::new();
+ let mut file_lines = Vec::new();
let mut crate_graph = CrateGraphBuilder::default();
let mut crates = FxIndexMap::default();
let mut crate_deps = Vec::new();
@@ -173,6 +183,8 @@
let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()));
for entry in fixture {
+ file_lines.push(entry.line);
+
let mut range_or_offset = None;
let text = if entry.text.contains(CURSOR_MARKER) {
if entry.text.contains(ESCAPED_CURSOR_MARKER) {
@@ -259,7 +271,9 @@
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
roots.push(SourceRoot::new_library(fs));
- source_change.change_file(core_file, Some(mini_core.source_code()));
+ sysroot_files.push(core_file);
+
+ source_change.change_file(core_file, Some(mini_core.source_code(minicore_raw)));
let core_crate = crate_graph.add_crate_root(
core_file,
@@ -348,6 +362,8 @@
);
roots.push(SourceRoot::new_library(fs));
+ sysroot_files.push(proc_lib_file);
+
source_change.change_file(proc_lib_file, Some(source));
let all_crates = crate_graph.iter().collect::<Vec<_>>();
@@ -396,7 +412,7 @@
change.source_change.set_roots(roots);
change.source_change.set_crate_graph(crate_graph);
- ChangeFixture { file_position, files, change }
+ ChangeFixture { file_position, file_lines, files, change, sysroot_files }
}
}
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index c024089..559894e 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -132,13 +132,17 @@
pub library: bool,
/// Actual file contents. All meta comments are stripped.
pub text: String,
+ /// The line number in the original fixture of the beginning of this fixture.
+ pub line: usize,
}
+#[derive(Debug)]
pub struct MiniCore {
activated_flags: Vec<String>,
valid_flags: Vec<String>,
}
+#[derive(Debug)]
pub struct FixtureWithProjectMeta {
pub fixture: Vec<Fixture>,
pub mini_core: Option<MiniCore>,
@@ -184,40 +188,49 @@
let mut mini_core = None;
let mut res: Vec<Fixture> = Vec::new();
let mut proc_macro_names = vec![];
+ let mut first_row = 0;
if let Some(meta) = fixture.strip_prefix("//- toolchain:") {
+ first_row += 1;
let (meta, remain) = meta.split_once('\n').unwrap();
toolchain = Some(meta.trim().to_owned());
fixture = remain;
}
if let Some(meta) = fixture.strip_prefix("//- target_data_layout:") {
+ first_row += 1;
let (meta, remain) = meta.split_once('\n').unwrap();
meta.trim().clone_into(&mut target_data_layout);
fixture = remain;
}
if let Some(meta) = fixture.strip_prefix("//- target_arch:") {
+ first_row += 1;
let (meta, remain) = meta.split_once('\n').unwrap();
meta.trim().clone_into(&mut target_arch);
fixture = remain;
}
if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
+ first_row += 1;
let (meta, remain) = meta.split_once('\n').unwrap();
proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect();
fixture = remain;
}
if let Some(meta) = fixture.strip_prefix("//- minicore:") {
+ first_row += 1;
let (meta, remain) = meta.split_once('\n').unwrap();
mini_core = Some(MiniCore::parse(meta));
fixture = remain;
}
- let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
+ let default =
+ if fixture.contains("//- /") { None } else { Some((first_row - 1, "//- /main.rs")) };
- for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
+ for (ix, line) in
+ default.into_iter().chain((first_row..).zip(fixture.split_inclusive('\n')))
+ {
if line.contains("//-") {
assert!(
line.starts_with("//-"),
@@ -228,7 +241,7 @@
}
if let Some(line) = line.strip_prefix("//-") {
- let meta = Self::parse_meta_line(line);
+ let meta = Self::parse_meta_line(line, (ix + 1).try_into().unwrap());
res.push(meta);
} else {
if matches!(line.strip_prefix("// "), Some(l) if l.trim().starts_with('/')) {
@@ -252,7 +265,7 @@
}
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
- fn parse_meta_line(meta: &str) -> Fixture {
+ fn parse_meta_line(meta: &str, line: usize) -> Fixture {
let meta = meta.trim();
let mut components = meta.split_ascii_whitespace();
@@ -317,6 +330,7 @@
Fixture {
path,
text: String::new(),
+ line,
krate,
deps,
extern_prelude,
@@ -330,7 +344,7 @@
}
impl MiniCore {
- const RAW_SOURCE: &'static str = include_str!("./minicore.rs");
+ pub const RAW_SOURCE: &'static str = include_str!("./minicore.rs");
fn has_flag(&self, flag: &str) -> bool {
self.activated_flags.iter().any(|it| it == flag)
@@ -363,8 +377,8 @@
res
}
- pub fn available_flags() -> impl Iterator<Item = &'static str> {
- let lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
+ pub fn available_flags(raw_source: &str) -> impl Iterator<Item = &str> {
+ let lines = raw_source.split_inclusive('\n');
lines
.map_while(|x| x.strip_prefix("//!"))
.skip_while(|line| !line.contains("Available flags:"))
@@ -375,9 +389,9 @@
/// Strips parts of minicore.rs which are flagged by inactive flags.
///
/// This is probably over-engineered to support flags dependencies.
- pub fn source_code(mut self) -> String {
+ pub fn source_code(mut self, raw_source: &str) -> String {
let mut buf = String::new();
- let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
+ let mut lines = raw_source.split_inclusive('\n');
let mut implications = Vec::new();