Skip to content

Commit afaf10e

Browse files
author
mehmet-yalcinkaya_volvo
committed
feat: add is_mutable_raw_ptr and as_raw_ptr to hir::Type
1 parent 4eac290 commit afaf10e

4 files changed

Lines changed: 248 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ expect-test.workspace = true
4141
tracing.workspace = true
4242
tracing-subscriber.workspace = true
4343
tracing-tree.workspace = true
44+
salsa.workspace = true
45+
salsa-macros.workspace = true
46+
triomphe.workspace = true
4447

4548
# local deps
4649
test-utils.workspace = true

crates/hir/src/lib.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ pub mod term_search;
3535

3636
mod display;
3737

38+
#[cfg(test)]
39+
mod test_db;
40+
3841
#[doc(hidden)]
3942
pub use hir_def::ModuleId;
4043

@@ -5693,6 +5696,16 @@ impl<'db> Type<'db> {
56935696
matches!(self.ty.kind(), TyKind::RawPtr(..))
56945697
}
56955698

5699+
pub fn is_mutable_raw_ptr(&self) -> bool {
5700+
matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut))
5701+
}
5702+
5703+
pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> {
5704+
let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None };
5705+
let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut));
5706+
Some((self.derived(ty), m))
5707+
}
5708+
56965709
pub fn remove_raw_ptr(&self) -> Option<Type<'db>> {
56975710
if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None }
56985711
}
@@ -7159,3 +7172,99 @@ fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> {
71597172

71607173
pub use hir_ty::next_solver;
71617174
pub use hir_ty::setup_tracing;
7175+
7176+
#[cfg(test)]
7177+
mod tests {
7178+
use expect_test::{Expect, expect};
7179+
use syntax::AstNode;
7180+
use test_fixture::WithFixture;
7181+
7182+
use crate::{Semantics, test_db::TestDB};
7183+
7184+
fn check_raw_ptr_api(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
7185+
let (db, file_id) = TestDB::with_single_file(ra_fixture);
7186+
let sema = Semantics::new(&db);
7187+
let source_file = sema.parse(file_id);
7188+
7189+
let mut buf = String::new();
7190+
for stmt in source_file.syntax().descendants().filter_map(syntax::ast::LetStmt::cast) {
7191+
let pat = stmt.pat().unwrap();
7192+
let ty = sema.type_of_pat(&pat).unwrap().original;
7193+
let name = pat.syntax().text().to_string();
7194+
7195+
buf += &format!("{name}: {{\n");
7196+
buf += &format!(" is_raw_ptr: {:?}\n", ty.is_raw_ptr());
7197+
buf += &format!(" is_mutable_raw_ptr: {:?}\n", ty.is_mutable_raw_ptr());
7198+
buf += &format!(" as_raw_ptr: {:?}\n", ty.as_raw_ptr().map(|(_, m)| m));
7199+
// Show the symmetry: reference API for comparison
7200+
buf += &format!(" is_reference: {:?}\n", ty.is_reference());
7201+
buf += &format!(" is_mutable_reference: {:?}\n", ty.is_mutable_reference());
7202+
buf += &format!(" as_reference: {:?}\n", ty.as_reference().map(|(_, m)| m));
7203+
buf += "}\n";
7204+
}
7205+
expect.assert_eq(buf.trim_end());
7206+
}
7207+
7208+
// Demonstrates the API gap that existed before this PR:
7209+
// `is_raw_ptr` could detect raw pointers, but there was no way to query
7210+
// their mutability — unlike references which had full `is_mutable_reference`
7211+
// and `as_reference` support. Users had to parse display strings as a workaround.
7212+
//
7213+
// After this PR, `is_mutable_raw_ptr` and `as_raw_ptr` fill the gap,
7214+
// making the raw pointer API symmetric with the reference API.
7215+
#[test]
7216+
fn raw_ptr_mutability_api() {
7217+
check_raw_ptr_api(
7218+
r#"
7219+
fn test() {
7220+
let a: *const u32 = 0 as *const u32;
7221+
let b: *mut u32 = 0 as *mut u32;
7222+
let c: &u32 = &0u32;
7223+
let d: &mut u32 = &mut 0u32;
7224+
let e: u32 = 0u32;
7225+
}
7226+
"#,
7227+
expect![[r#"
7228+
a: {
7229+
is_raw_ptr: true
7230+
is_mutable_raw_ptr: false
7231+
as_raw_ptr: Some(Shared)
7232+
is_reference: false
7233+
is_mutable_reference: false
7234+
as_reference: None
7235+
}
7236+
b: {
7237+
is_raw_ptr: true
7238+
is_mutable_raw_ptr: true
7239+
as_raw_ptr: Some(Mut)
7240+
is_reference: false
7241+
is_mutable_reference: false
7242+
as_reference: None
7243+
}
7244+
c: {
7245+
is_raw_ptr: false
7246+
is_mutable_raw_ptr: false
7247+
as_raw_ptr: None
7248+
is_reference: true
7249+
is_mutable_reference: false
7250+
as_reference: Some(Shared)
7251+
}
7252+
d: {
7253+
is_raw_ptr: false
7254+
is_mutable_raw_ptr: false
7255+
as_raw_ptr: None
7256+
is_reference: true
7257+
is_mutable_reference: true
7258+
as_reference: Some(Mut)
7259+
}
7260+
e: {
7261+
is_raw_ptr: false
7262+
is_mutable_raw_ptr: false
7263+
as_raw_ptr: None
7264+
is_reference: false
7265+
is_mutable_reference: false
7266+
as_reference: None
7267+
}"#]],
7268+
);
7269+
}
7270+
}

crates/hir/src/test_db.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//! Database used for testing `hir`.
2+
3+
use std::{fmt, panic, sync::Mutex};
4+
5+
use base_db::{
6+
CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb,
7+
SourceDatabase, SourceRoot, SourceRootId, SourceRootInput,
8+
};
9+
use hir_def::db::DefDatabase;
10+
#[allow(unused_imports)]
11+
use hir_ty::db::HirDatabase;
12+
use salsa::Durability;
13+
use triomphe::Arc;
14+
15+
#[salsa_macros::db]
16+
pub(crate) struct TestDB {
17+
storage: salsa::Storage<Self>,
18+
files: Arc<base_db::Files>,
19+
crates_map: Arc<CratesMap>,
20+
events: Arc<Mutex<Option<Vec<salsa::Event>>>>,
21+
nonce: Nonce,
22+
}
23+
24+
impl Default for TestDB {
25+
fn default() -> Self {
26+
let events = <Arc<Mutex<Option<Vec<salsa::Event>>>>>::default();
27+
let mut this = Self {
28+
storage: salsa::Storage::new(Some(Box::new({
29+
let events = events.clone();
30+
move |event| {
31+
let mut events = events.lock().unwrap();
32+
if let Some(events) = &mut *events {
33+
events.push(event);
34+
}
35+
}
36+
}))),
37+
events,
38+
files: Default::default(),
39+
crates_map: Default::default(),
40+
nonce: Nonce::new(),
41+
};
42+
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
43+
this.set_all_crates(Arc::new(Box::new([])));
44+
_ = base_db::LibraryRoots::builder(Default::default())
45+
.durability(Durability::MEDIUM)
46+
.new(&this);
47+
_ = base_db::LocalRoots::builder(Default::default())
48+
.durability(Durability::MEDIUM)
49+
.new(&this);
50+
CrateGraphBuilder::default().set_in_db(&mut this);
51+
this
52+
}
53+
}
54+
55+
impl Clone for TestDB {
56+
fn clone(&self) -> Self {
57+
Self {
58+
storage: self.storage.clone(),
59+
files: self.files.clone(),
60+
crates_map: self.crates_map.clone(),
61+
events: self.events.clone(),
62+
nonce: Nonce::new(),
63+
}
64+
}
65+
}
66+
67+
impl fmt::Debug for TestDB {
68+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69+
f.debug_struct("TestDB").finish()
70+
}
71+
}
72+
73+
#[salsa_macros::db]
74+
impl SourceDatabase for TestDB {
75+
fn file_text(&self, file_id: base_db::FileId) -> FileText {
76+
self.files.file_text(file_id)
77+
}
78+
79+
fn set_file_text(&mut self, file_id: base_db::FileId, text: &str) {
80+
let files = Arc::clone(&self.files);
81+
files.set_file_text(self, file_id, text);
82+
}
83+
84+
fn set_file_text_with_durability(
85+
&mut self,
86+
file_id: base_db::FileId,
87+
text: &str,
88+
durability: Durability,
89+
) {
90+
let files = Arc::clone(&self.files);
91+
files.set_file_text_with_durability(self, file_id, text, durability);
92+
}
93+
94+
fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
95+
self.files.source_root(source_root_id)
96+
}
97+
98+
fn set_source_root_with_durability(
99+
&mut self,
100+
source_root_id: SourceRootId,
101+
source_root: Arc<SourceRoot>,
102+
durability: Durability,
103+
) {
104+
let files = Arc::clone(&self.files);
105+
files.set_source_root_with_durability(self, source_root_id, source_root, durability);
106+
}
107+
108+
fn file_source_root(&self, id: base_db::FileId) -> FileSourceRootInput {
109+
self.files.file_source_root(id)
110+
}
111+
112+
fn set_file_source_root_with_durability(
113+
&mut self,
114+
id: base_db::FileId,
115+
source_root_id: SourceRootId,
116+
durability: Durability,
117+
) {
118+
let files = Arc::clone(&self.files);
119+
files.set_file_source_root_with_durability(self, id, source_root_id, durability);
120+
}
121+
122+
fn crates_map(&self) -> Arc<CratesMap> {
123+
self.crates_map.clone()
124+
}
125+
126+
fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) {
127+
(self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision())
128+
}
129+
}
130+
131+
#[salsa_macros::db]
132+
impl salsa::Database for TestDB {}
133+
134+
impl panic::RefUnwindSafe for TestDB {}

0 commit comments

Comments
 (0)