Skip to content

Commit 5c6f098

Browse files
Resolve offset_of!() in IDE
1 parent 5e544ed commit 5c6f098

File tree

5 files changed

+169
-3
lines changed

5 files changed

+169
-3
lines changed

src/tools/rust-analyzer/crates/hir/src/semantics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,13 @@ impl<'db> SemanticsImpl<'db> {
16221622
self.analyze(name.syntax())?.resolve_use_type_arg(name)
16231623
}
16241624

1625+
pub fn resolve_offset_of_field(
1626+
&self,
1627+
name_ref: &ast::NameRef,
1628+
) -> Option<(Either<Variant, Field>, GenericSubstitution)> {
1629+
self.analyze_no_infer(name_ref.syntax())?.resolve_offset_of_field(self.db, name_ref)
1630+
}
1631+
16251632
pub fn resolve_mod_path(
16261633
&self,
16271634
scope: &SyntaxNode,

src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
};
1515
use either::Either;
1616
use hir_def::{
17-
AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId,
17+
AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId,
1818
ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
1919
expr_store::{
2020
Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId,
@@ -34,8 +34,8 @@ use hir_expand::{
3434
name::{AsName, Name},
3535
};
3636
use hir_ty::{
37-
Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, TyExt, TyKind,
38-
TyLoweringContext,
37+
Adjustment, AliasTy, InferenceResult, Interner, ProjectionTy, Substitution, TraitEnvironment,
38+
Ty, TyExt, TyKind, TyLoweringContext,
3939
diagnostics::{
4040
InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields,
4141
unsafe_operations,
@@ -47,6 +47,7 @@ use hir_ty::{
4747
use intern::sym;
4848
use itertools::Itertools;
4949
use smallvec::SmallVec;
50+
use stdx::never;
5051
use syntax::{
5152
SyntaxKind, SyntaxNode, TextRange, TextSize,
5253
ast::{self, AstNode, RangeItem, RangeOp},
@@ -791,6 +792,78 @@ impl SourceAnalyzer {
791792
.map(crate::TypeParam::from)
792793
}
793794

795+
pub(crate) fn resolve_offset_of_field(
796+
&self,
797+
db: &dyn HirDatabase,
798+
name_ref: &ast::NameRef,
799+
) -> Option<(Either<crate::Variant, crate::Field>, GenericSubstitution)> {
800+
let offset_of_expr = ast::OffsetOfExpr::cast(name_ref.syntax().parent()?)?;
801+
let container = offset_of_expr.ty()?;
802+
let container = self.type_of_type(db, &container)?;
803+
804+
let trait_env = container.env;
805+
let mut container = Either::Right(container.ty);
806+
for field_name in offset_of_expr.fields() {
807+
if let Some(
808+
TyKind::Alias(AliasTy::Projection(ProjectionTy { associated_ty_id, substitution }))
809+
| TyKind::AssociatedType(associated_ty_id, substitution),
810+
) = container.as_ref().right().map(|it| it.kind(Interner))
811+
{
812+
let projection = ProjectionTy {
813+
associated_ty_id: *associated_ty_id,
814+
substitution: substitution.clone(),
815+
};
816+
container = Either::Right(db.normalize_projection(projection, trait_env.clone()));
817+
}
818+
let handle_variants = |variant, subst: &Substitution, container: &mut _| {
819+
let fields = db.variant_fields(variant);
820+
let field = fields.field(&field_name.as_name())?;
821+
let field_types = db.field_types(variant);
822+
*container = Either::Right(field_types[field].clone().substitute(Interner, subst));
823+
let generic_def = match variant {
824+
VariantId::EnumVariantId(it) => it.loc(db).parent.into(),
825+
VariantId::StructId(it) => it.into(),
826+
VariantId::UnionId(it) => it.into(),
827+
};
828+
Some((
829+
Either::Right(Field { parent: variant.into(), id: field }),
830+
generic_def,
831+
subst.clone(),
832+
))
833+
};
834+
let temp_ty = TyKind::Error.intern(Interner);
835+
let (field_def, generic_def, subst) =
836+
match std::mem::replace(&mut container, Either::Right(temp_ty.clone())) {
837+
Either::Left((variant_id, subst)) => {
838+
handle_variants(VariantId::from(variant_id), &subst, &mut container)?
839+
}
840+
Either::Right(container_ty) => match container_ty.kind(Interner) {
841+
TyKind::Adt(adt_id, subst) => match adt_id.0 {
842+
AdtId::StructId(id) => {
843+
handle_variants(id.into(), subst, &mut container)?
844+
}
845+
AdtId::UnionId(id) => {
846+
handle_variants(id.into(), subst, &mut container)?
847+
}
848+
AdtId::EnumId(id) => {
849+
let variants = db.enum_variants(id);
850+
let variant = variants.variant(&field_name.as_name())?;
851+
container = Either::Left((variant, subst.clone()));
852+
(Either::Left(Variant { id: variant }), id.into(), subst.clone())
853+
}
854+
},
855+
_ => return None,
856+
},
857+
};
858+
859+
if field_name.syntax().text_range() == name_ref.syntax().text_range() {
860+
return Some((field_def, GenericSubstitution::new(generic_def, subst, trait_env)));
861+
}
862+
}
863+
never!("the `NameRef` is a child of the `OffsetOfExpr`, we should've visited it");
864+
None
865+
}
866+
794867
pub(crate) fn resolve_path(
795868
&self,
796869
db: &dyn HirDatabase,

src/tools/rust-analyzer/crates/ide-db/src/defs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,14 @@ impl NameRefClass {
839839
ast::AsmRegSpec(_) => {
840840
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None))
841841
},
842+
ast::OffsetOfExpr(_) => {
843+
let (def, subst) = sema.resolve_offset_of_field(name_ref)?;
844+
let def = match def {
845+
Either::Left(variant) => Definition::Variant(variant),
846+
Either::Right(field) => Definition::Field(field),
847+
};
848+
Some(NameRefClass::Definition(def, Some(subst)))
849+
},
842850
_ => None
843851
}
844852
}

src/tools/rust-analyzer/crates/ide/src/goto_definition.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,4 +3476,74 @@ fn main() {
34763476
"#,
34773477
);
34783478
}
3479+
3480+
#[test]
3481+
fn offset_of() {
3482+
check(
3483+
r#"
3484+
//- minicore: offset_of
3485+
struct Foo {
3486+
field: i32,
3487+
// ^^^^^
3488+
}
3489+
3490+
fn foo() {
3491+
let _ = core::mem::offset_of!(Foo, fiel$0d);
3492+
}
3493+
"#,
3494+
);
3495+
3496+
check(
3497+
r#"
3498+
//- minicore: offset_of
3499+
struct Bar(Foo);
3500+
struct Foo {
3501+
field: i32,
3502+
// ^^^^^
3503+
}
3504+
3505+
fn foo() {
3506+
let _ = core::mem::offset_of!(Bar, 0.fiel$0d);
3507+
}
3508+
"#,
3509+
);
3510+
3511+
check(
3512+
r#"
3513+
//- minicore: offset_of
3514+
struct Bar(Baz);
3515+
enum Baz {
3516+
Abc(Foo),
3517+
None,
3518+
}
3519+
struct Foo {
3520+
field: i32,
3521+
// ^^^^^
3522+
}
3523+
3524+
fn foo() {
3525+
let _ = core::mem::offset_of!(Bar, 0.Abc.0.fiel$0d);
3526+
}
3527+
"#,
3528+
);
3529+
3530+
check(
3531+
r#"
3532+
//- minicore: offset_of
3533+
struct Bar(Baz);
3534+
enum Baz {
3535+
Abc(Foo),
3536+
// ^^^
3537+
None,
3538+
}
3539+
struct Foo {
3540+
field: i32,
3541+
}
3542+
3543+
fn foo() {
3544+
let _ = core::mem::offset_of!(Bar, 0.Ab$0c.0.field);
3545+
}
3546+
"#,
3547+
);
3548+
}
34793549
}

src/tools/rust-analyzer/crates/test-utils/src/minicore.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
//! unimplemented: panic
7171
//! column:
7272
//! addr_of:
73+
//! offset_of:
7374
7475
#![rustc_coherence_is_core]
7576

@@ -414,6 +415,13 @@ pub mod mem {
414415
use crate::marker::DiscriminantKind;
415416
pub struct Discriminant<T>(<T as DiscriminantKind>::Discriminant);
416417
// endregion:discriminant
418+
419+
// region:offset_of
420+
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
421+
// The `{}` is for better error messages
422+
{builtin # offset_of($Container, $($fields)+)}
423+
}
424+
// endregion:offset_of
417425
}
418426

419427
pub mod ptr {

0 commit comments

Comments
 (0)