Unnamed repository; edit this file 'description' to name the repository.
Fix a bug when synthetic AST node were searched in the AST ID map and caused panics
Chayim Refael Friedman 2024-11-25
parent 9224ec4 · commit b66bc91
-rw-r--r--crates/hir-def/src/lower.rs26
-rw-r--r--crates/hir/src/semantics.rs13
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs45
3 files changed, 80 insertions, 4 deletions
diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs
index 6d1a3d1744..350bb8d517 100644
--- a/crates/hir-def/src/lower.rs
+++ b/crates/hir-def/src/lower.rs
@@ -2,7 +2,7 @@
use std::{cell::OnceCell, mem};
use hir_expand::{span_map::SpanMap, AstId, HirFileId, InFile};
-use span::{AstIdMap, AstIdNode};
+use span::{AstIdMap, AstIdNode, Edition, EditionedFileId, FileId, RealSpanMap};
use stdx::thin_vec::ThinVec;
use syntax::ast;
use triomphe::Arc;
@@ -63,6 +63,30 @@ impl<'a> LowerCtx<'a> {
}
}
+ /// Prepares a `LowerCtx` for synthetic AST that needs to be lowered. This is intended for IDE things.
+ pub fn for_synthetic_ast(
+ db: &'a dyn DefDatabase,
+ ast_id_map: Arc<AstIdMap>,
+ types_map: &'a mut TypesMap,
+ types_source_map: &'a mut TypesSourceMap,
+ ) -> Self {
+ let file_id = EditionedFileId::new(
+ FileId::from_raw(EditionedFileId::MAX_FILE_ID),
+ Edition::Edition2015,
+ );
+ LowerCtx {
+ db,
+ // Make up an invalid file id, so that if we will try to actually access it salsa will panic.
+ file_id: file_id.into(),
+ span_map: SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(file_id))).into(),
+ ast_id_map: ast_id_map.into(),
+ impl_trait_bounds: Vec::new(),
+ outer_impl_trait: false,
+ types_map,
+ types_source_map,
+ }
+ }
+
pub(crate) fn span_map(&self) -> &SpanMap {
self.span_map.get_or_init(|| self.db.span_map(self.file_id))
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9d3f8e5fba..46766fcc5b 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -34,7 +34,7 @@ use intern::Symbol;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
-use span::{EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
+use span::{AstIdMap, EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
use stdx::TupleExt;
use syntax::{
algo::skip_trivia_token,
@@ -42,6 +42,7 @@ use syntax::{
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
TextSize,
};
+use triomphe::Arc;
use crate::{
db::HirDatabase,
@@ -1973,10 +1974,16 @@ impl SemanticsScope<'_> {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> {
+ let root = ast_path.syntax().ancestors().last().unwrap();
+ let ast_id_map = Arc::new(AstIdMap::from_source(&root));
let (mut types_map, mut types_source_map) =
(TypesMap::default(), TypesSourceMap::default());
- let mut ctx =
- LowerCtx::new(self.db.upcast(), self.file_id, &mut types_map, &mut types_source_map);
+ let mut ctx = LowerCtx::for_synthetic_ast(
+ self.db.upcast(),
+ ast_id_map,
+ &mut types_map,
+ &mut types_source_map,
+ );
let path = Path::from_src(&mut ctx, ast_path.clone())?;
resolve_hir_path(
self.db,
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 7f8ea44fb1..57df39d541 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -2318,4 +2318,49 @@ impl<'a> Test<'a, i32> for bool {
"#,
);
}
+
+ #[test]
+ fn issue_17321() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+fn main() {}
+
+mod other_file_1 {
+ pub const SOME_CONSTANT: usize = 8;
+}
+
+mod other_file_2 {
+ use crate::other_file_1::SOME_CONSTANT;
+
+ pub trait Trait {
+ type Iter: Iterator<Item = [u8; SOME_CONSTANT]>;
+ }
+}
+
+pub struct MyStruct;
+
+impl other_file_2::Trait for MyStruct$0 {}"#,
+ r#"
+fn main() {}
+
+mod other_file_1 {
+ pub const SOME_CONSTANT: usize = 8;
+}
+
+mod other_file_2 {
+ use crate::other_file_1::SOME_CONSTANT;
+
+ pub trait Trait {
+ type Iter: Iterator<Item = [u8; SOME_CONSTANT]>;
+ }
+}
+
+pub struct MyStruct;
+
+impl other_file_2::Trait for MyStruct {
+ $0type Iter;
+}"#,
+ );
+ }
}