aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2018-12-24 18:07:48 +0000
committerFlorian Diebold <[email protected]>2018-12-25 14:16:42 +0000
commitb5b68f2094d49cacde6d7f0c49f521a0b25f34bd (patch)
tree5439bb80896c211b3cadb2c037383243fe1fc140 /crates
parent5fb426cb9eeefa69a53d7c8c3367f7c6b714b9b8 (diff)
Add basic HIR and types for structs/enums
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/db.rs2
-rw-r--r--crates/ra_hir/src/adt.rs114
-rw-r--r--crates/ra_hir/src/db.rs11
-rw-r--r--crates/ra_hir/src/lib.rs18
-rw-r--r--crates/ra_hir/src/mock.rs2
-rw-r--r--crates/ra_hir/src/query_definitions.rs21
-rw-r--r--crates/ra_hir/src/ty.rs72
-rw-r--r--crates/ra_hir/src/ty/tests.rs23
-rw-r--r--crates/ra_hir/src/ty/tests/data/0004_struct.txt10
9 files changed, 244 insertions, 29 deletions
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 780a84291..7043a0f4d 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -95,6 +95,8 @@ salsa::database_storage! {
95 fn submodules() for hir::db::SubmodulesQuery; 95 fn submodules() for hir::db::SubmodulesQuery;
96 fn infer() for hir::db::InferQuery; 96 fn infer() for hir::db::InferQuery;
97 fn type_for_def() for hir::db::TypeForDefQuery; 97 fn type_for_def() for hir::db::TypeForDefQuery;
98 fn struct_data() for db::StructDataQuery;
99 fn enum_data() for db::EnumDataQuery;
98 } 100 }
99 } 101 }
100} 102}
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
new file mode 100644
index 000000000..a2d228593
--- /dev/null
+++ b/crates/ra_hir/src/adt.rs
@@ -0,0 +1,114 @@
1use ra_syntax::{SmolStr, ast::{self, NameOwner}};
2
3use crate::{
4 DefId, Cancelable,
5 db::{HirDatabase},
6 ty::{Ty},
7};
8
9pub struct Struct {
10 def_id: DefId,
11}
12
13impl Struct {
14 pub(crate) fn new(def_id: DefId) -> Self {
15 Struct { def_id }
16 }
17
18 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
19 Ok(db.struct_data(self.def_id)?.name.clone())
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct StructData {
25 name: SmolStr,
26 variant_data: VariantData,
27}
28
29impl StructData {
30 pub(crate) fn new(struct_def: ast::StructDef) -> StructData {
31 let name = struct_def
32 .name()
33 .map(|n| n.text())
34 .unwrap_or(SmolStr::new("[error]"));
35 let variant_data = VariantData::Unit; // TODO implement this
36 StructData { name, variant_data }
37 }
38}
39
40pub struct Enum {
41 def_id: DefId,
42}
43
44impl Enum {
45 pub(crate) fn new(def_id: DefId) -> Self {
46 Enum { def_id }
47 }
48
49 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
50 Ok(db.enum_data(self.def_id)?.name.clone())
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct EnumData {
56 name: SmolStr,
57 variants: Vec<(SmolStr, VariantData)>,
58}
59
60impl EnumData {
61 pub(crate) fn new(enum_def: ast::EnumDef) -> Self {
62 let name = enum_def
63 .name()
64 .map(|n| n.text())
65 .unwrap_or(SmolStr::new("[error]"));
66 let variants = Vec::new(); // TODO implement this
67 EnumData { name, variants }
68 }
69}
70
71/// A single field of an enum variant or struct
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct StructField {
74 name: SmolStr,
75 ty: Ty,
76}
77
78/// Fields of an enum variant or struct
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum VariantData {
81 Struct(Vec<StructField>),
82 Tuple(Vec<StructField>),
83 Unit,
84}
85
86impl VariantData {
87 pub fn fields(&self) -> &[StructField] {
88 match *self {
89 VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
90 _ => &[],
91 }
92 }
93 pub fn is_struct(&self) -> bool {
94 if let VariantData::Struct(..) = *self {
95 true
96 } else {
97 false
98 }
99 }
100 pub fn is_tuple(&self) -> bool {
101 if let VariantData::Tuple(..) = *self {
102 true
103 } else {
104 false
105 }
106 }
107 pub fn is_unit(&self) -> bool {
108 if let VariantData::Unit = *self {
109 true
110 } else {
111 false
112 }
113 }
114}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index d94f75857..113790ee9 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -15,6 +15,7 @@ use crate::{
15 module::{ModuleId, ModuleTree, ModuleSource, 15 module::{ModuleId, ModuleTree, ModuleSource,
16 nameres::{ItemMap, InputModuleItems}}, 16 nameres::{ItemMap, InputModuleItems}},
17 ty::{InferenceResult, Ty}, 17 ty::{InferenceResult, Ty},
18 adt::{StructData, EnumData},
18}; 19};
19 20
20salsa::query_group! { 21salsa::query_group! {
@@ -31,6 +32,16 @@ pub trait HirDatabase: SyntaxDatabase
31 use fn query_definitions::fn_syntax; 32 use fn query_definitions::fn_syntax;
32 } 33 }
33 34
35 fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> {
36 type StructDataQuery;
37 use fn query_definitions::struct_data;
38 }
39
40 fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> {
41 type EnumDataQuery;
42 use fn query_definitions::enum_data;
43 }
44
34 fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { 45 fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
35 type InferQuery; 46 type InferQuery;
36 use fn query_definitions::infer; 47 use fn query_definitions::infer;
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index a0d99a84d..7e9824de9 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -25,6 +25,7 @@ pub mod source_binder;
25mod krate; 25mod krate;
26mod module; 26mod module;
27mod function; 27mod function;
28mod adt;
28mod ty; 29mod ty;
29 30
30use std::ops::Index; 31use std::ops::Index;
@@ -42,6 +43,7 @@ pub use self::{
42 krate::Crate, 43 krate::Crate,
43 module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, 44 module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
44 function::{Function, FnScopes}, 45 function::{Function, FnScopes},
46 adt::{Struct, Enum},
45}; 47};
46 48
47pub use self::function::FnSignatureInfo; 49pub use self::function::FnSignatureInfo;
@@ -56,6 +58,8 @@ ra_db::impl_numeric_id!(DefId);
56pub(crate) enum DefKind { 58pub(crate) enum DefKind {
57 Module, 59 Module,
58 Function, 60 Function,
61 Struct,
62 Enum,
59 Item, 63 Item,
60} 64}
61 65
@@ -73,8 +77,8 @@ impl DefKind {
73 SyntaxKind::FN_DEF => Some(DefKind::Function), 77 SyntaxKind::FN_DEF => Some(DefKind::Function),
74 SyntaxKind::MODULE => Some(DefKind::Module), 78 SyntaxKind::MODULE => Some(DefKind::Module),
75 // These define items, but don't have their own DefKinds yet: 79 // These define items, but don't have their own DefKinds yet:
76 SyntaxKind::STRUCT_DEF => Some(DefKind::Item), 80 SyntaxKind::STRUCT_DEF => Some(DefKind::Struct),
77 SyntaxKind::ENUM_DEF => Some(DefKind::Item), 81 SyntaxKind::ENUM_DEF => Some(DefKind::Enum),
78 SyntaxKind::TRAIT_DEF => Some(DefKind::Item), 82 SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
79 SyntaxKind::TYPE_DEF => Some(DefKind::Item), 83 SyntaxKind::TYPE_DEF => Some(DefKind::Item),
80 SyntaxKind::CONST_DEF => Some(DefKind::Item), 84 SyntaxKind::CONST_DEF => Some(DefKind::Item),
@@ -99,6 +103,8 @@ impl DefLoc {
99pub enum Def { 103pub enum Def {
100 Module(Module), 104 Module(Module),
101 Function(Function), 105 Function(Function),
106 Struct(Struct),
107 Enum(Enum),
102 Item, 108 Item,
103} 109}
104 110
@@ -114,6 +120,14 @@ impl DefId {
114 let function = Function::new(self); 120 let function = Function::new(self);
115 Def::Function(function) 121 Def::Function(function)
116 } 122 }
123 DefKind::Struct => {
124 let struct_def = Struct::new(self);
125 Def::Struct(struct_def)
126 }
127 DefKind::Enum => {
128 let enum_def = Enum::new(self);
129 Def::Enum(enum_def)
130 }
117 DefKind::Item => Def::Item, 131 DefKind::Item => Def::Item,
118 }; 132 };
119 Ok(res) 133 Ok(res)
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index b5a997170..ead2b8414 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -193,6 +193,8 @@ salsa::database_storage! {
193 fn submodules() for db::SubmodulesQuery; 193 fn submodules() for db::SubmodulesQuery;
194 fn infer() for db::InferQuery; 194 fn infer() for db::InferQuery;
195 fn type_for_def() for db::TypeForDefQuery; 195 fn type_for_def() for db::TypeForDefQuery;
196 fn struct_data() for db::StructDataQuery;
197 fn enum_data() for db::EnumDataQuery;
196 } 198 }
197 } 199 }
198} 200}
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index b654af920..72440d1d6 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -19,7 +19,8 @@ use crate::{
19 imp::Submodule, 19 imp::Submodule,
20 nameres::{InputModuleItems, ItemMap, Resolver}, 20 nameres::{InputModuleItems, ItemMap, Resolver},
21 }, 21 },
22 ty::{self, InferenceResult, Ty} 22 ty::{self, InferenceResult, Ty},
23 adt::{StructData, EnumData},
23}; 24};
24 25
25/// Resolve `FnId` to the corresponding `SyntaxNode` 26/// Resolve `FnId` to the corresponding `SyntaxNode`
@@ -45,6 +46,24 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
45 ty::type_for_def(db, def_id) 46 ty::type_for_def(db, def_id)
46} 47}
47 48
49pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
50 let def_loc = def_id.loc(db);
51 assert!(def_loc.kind == DefKind::Struct);
52 let syntax = db.file_item(def_loc.source_item_id);
53 let struct_def =
54 ast::StructDef::cast(syntax.borrowed()).expect("struct def should point to StructDef node");
55 Ok(Arc::new(StructData::new(struct_def.borrowed())))
56}
57
58pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> {
59 let def_loc = def_id.loc(db);
60 assert!(def_loc.kind == DefKind::Enum);
61 let syntax = db.file_item(def_loc.source_item_id);
62 let enum_def =
63 ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node");
64 Ok(Arc::new(EnumData::new(enum_def.borrowed())))
65}
66
48pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 67pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
49 let mut res = SourceFileItems::new(file_id); 68 let mut res = SourceFileItems::new(file_id);
50 let source_file = db.source_file(file_id); 69 let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index c759d4c8b..f86b749ec 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -17,7 +17,7 @@ use ra_syntax::{
17 17
18use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; 18use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
19 19
20#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 20#[derive(Clone, PartialEq, Eq, Hash, Debug)]
21pub enum Ty { 21pub enum Ty {
22 /// The primitive boolean type. Written as `bool`. 22 /// The primitive boolean type. Written as `bool`.
23 Bool, 23 Bool,
@@ -35,8 +35,15 @@ pub enum Ty {
35 /// A primitive floating-point type. For example, `f64`. 35 /// A primitive floating-point type. For example, `f64`.
36 Float(primitive::FloatTy), 36 Float(primitive::FloatTy),
37 37
38 // Structures, enumerations and unions. 38 /// Structures, enumerations and unions.
39 // Adt(AdtDef, Substs), 39 Adt {
40 /// The DefId of the struct/enum.
41 def_id: DefId,
42 /// The name, for displaying.
43 name: SmolStr,
44 // later we'll need generic substitutions here
45 },
46
40 /// The pointee of a string slice. Written as `str`. 47 /// The pointee of a string slice. Written as `str`.
41 Str, 48 Str,
42 49
@@ -107,45 +114,48 @@ pub enum Ty {
107 114
108type TyRef = Arc<Ty>; 115type TyRef = Arc<Ty>;
109 116
110#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 117#[derive(Clone, PartialEq, Eq, Hash, Debug)]
111pub struct FnSig { 118pub struct FnSig {
112 input: Vec<Ty>, 119 input: Vec<Ty>,
113 output: Ty, 120 output: Ty,
114} 121}
115 122
116impl Ty { 123impl Ty {
117 pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> { 124 pub(crate) fn new(
125 db: &impl HirDatabase,
126 module: &Module,
127 node: ast::TypeRef,
128 ) -> Cancelable<Self> {
118 use ra_syntax::ast::TypeRef::*; 129 use ra_syntax::ast::TypeRef::*;
119 Ok(match node { 130 Ok(match node {
120 ParenType(_inner) => Ty::Unknown, // TODO 131 ParenType(_inner) => Ty::Unknown, // TODO
121 TupleType(_inner) => Ty::Unknown, // TODO 132 TupleType(_inner) => Ty::Unknown, // TODO
122 NeverType(..) => Ty::Never, 133 NeverType(..) => Ty::Never,
123 PathType(inner) => { 134 PathType(inner) => {
124 let path = if let Some(p) = inner.path() { 135 let path = if let Some(p) = inner.path().and_then(Path::from_ast) {
125 p 136 p
126 } else { 137 } else {
127 return Ok(Ty::Unknown); 138 return Ok(Ty::Unknown);
128 }; 139 };
129 if path.qualifier().is_none() { 140 if path.is_ident() {
130 let name = path 141 let name = &path.segments[0];
131 .segment()
132 .and_then(|s| s.name_ref())
133 .map(|n| n.text())
134 .unwrap_or(SmolStr::new(""));
135 if let Some(int_ty) = primitive::IntTy::from_string(&name) { 142 if let Some(int_ty) = primitive::IntTy::from_string(&name) {
136 Ty::Int(int_ty) 143 return Ok(Ty::Int(int_ty));
137 } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { 144 } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) {
138 Ty::Uint(uint_ty) 145 return Ok(Ty::Uint(uint_ty));
139 } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { 146 } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) {
140 Ty::Float(float_ty) 147 return Ok(Ty::Float(float_ty));
141 } else {
142 // TODO
143 Ty::Unknown
144 } 148 }
145 } else {
146 // TODO
147 Ty::Unknown
148 } 149 }
150
151 // Resolve in module (in type namespace)
152 let resolved = if let Some(r) = module.resolve_path(db, path)? {
153 r
154 } else {
155 return Ok(Ty::Unknown);
156 };
157 let ty = db.type_for_def(resolved)?;
158 ty
149 } 159 }
150 PointerType(_inner) => Ty::Unknown, // TODO 160 PointerType(_inner) => Ty::Unknown, // TODO
151 ArrayType(_inner) => Ty::Unknown, // TODO 161 ArrayType(_inner) => Ty::Unknown, // TODO
@@ -189,6 +199,7 @@ impl fmt::Display for Ty {
189 } 199 }
190 write!(f, ") -> {}", sig.output) 200 write!(f, ") -> {}", sig.output)
191 } 201 }
202 Ty::Adt { name, .. } => write!(f, "{}", name),
192 Ty::Unknown => write!(f, "[unknown]"), 203 Ty::Unknown => write!(f, "[unknown]"),
193 } 204 }
194 } 205 }
@@ -196,6 +207,7 @@ impl fmt::Display for Ty {
196 207
197pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { 208pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
198 let syntax = f.syntax(db); 209 let syntax = f.syntax(db);
210 let module = f.module(db)?;
199 let node = syntax.borrowed(); 211 let node = syntax.borrowed();
200 // TODO we ignore type parameters for now 212 // TODO we ignore type parameters for now
201 let input = node 213 let input = node
@@ -204,7 +216,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
204 pl.params() 216 pl.params()
205 .map(|p| { 217 .map(|p| {
206 p.type_ref() 218 p.type_ref()
207 .map(|t| Ty::new(db, t)) 219 .map(|t| Ty::new(db, &module, t))
208 .unwrap_or(Ok(Ty::Unknown)) 220 .unwrap_or(Ok(Ty::Unknown))
209 }) 221 })
210 .collect() 222 .collect()
@@ -213,7 +225,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
213 let output = node 225 let output = node
214 .ret_type() 226 .ret_type()
215 .and_then(|rt| rt.type_ref()) 227 .and_then(|rt| rt.type_ref())
216 .map(|t| Ty::new(db, t)) 228 .map(|t| Ty::new(db, &module, t))
217 .unwrap_or(Ok(Ty::Unknown))?; 229 .unwrap_or(Ok(Ty::Unknown))?;
218 let sig = FnSig { input, output }; 230 let sig = FnSig { input, output };
219 Ok(Ty::FnPtr(Arc::new(sig))) 231 Ok(Ty::FnPtr(Arc::new(sig)))
@@ -232,6 +244,14 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
232 Ok(Ty::Unknown) 244 Ok(Ty::Unknown)
233 } 245 }
234 Def::Function(f) => type_for_fn(db, f), 246 Def::Function(f) => type_for_fn(db, f),
247 Def::Struct(s) => Ok(Ty::Adt {
248 def_id,
249 name: s.name(db)?,
250 }),
251 Def::Enum(e) => Ok(Ty::Adt {
252 def_id,
253 name: e.name(db)?,
254 }),
235 Def::Item => { 255 Def::Item => {
236 log::debug!("trying to get type for item of unknown type {:?}", def_id); 256 log::debug!("trying to get type for item of unknown type {:?}", def_id);
237 Ok(Ty::Unknown) 257 Ok(Ty::Unknown)
@@ -492,7 +512,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
492 }; 512 };
493 let cast_ty = e 513 let cast_ty = e
494 .type_ref() 514 .type_ref()
495 .map(|t| Ty::new(self.db, t)) 515 .map(|t| Ty::new(self.db, &self.module, t))
496 .unwrap_or(Ok(Ty::Unknown))?; 516 .unwrap_or(Ok(Ty::Unknown))?;
497 // TODO do the coercion... 517 // TODO do the coercion...
498 cast_ty 518 cast_ty
@@ -526,7 +546,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
526 match stmt { 546 match stmt {
527 ast::Stmt::LetStmt(stmt) => { 547 ast::Stmt::LetStmt(stmt) => {
528 let decl_ty = if let Some(type_ref) = stmt.type_ref() { 548 let decl_ty = if let Some(type_ref) = stmt.type_ref() {
529 Ty::new(self.db, type_ref)? 549 Ty::new(self.db, &self.module, type_ref)?
530 } else { 550 } else {
531 Ty::Unknown 551 Ty::Unknown
532 }; 552 };
@@ -576,7 +596,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
576 continue; 596 continue;
577 }; 597 };
578 if let Some(type_ref) = param.type_ref() { 598 if let Some(type_ref) = param.type_ref() {
579 let ty = Ty::new(db, type_ref)?; 599 let ty = Ty::new(db, &ctx.module, type_ref)?;
580 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); 600 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
581 } else { 601 } else {
582 // TODO self param 602 // TODO self param
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index b6c02cd80..170eef147 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -68,6 +68,29 @@ fn test() {
68 ); 68 );
69} 69}
70 70
71#[test]
72fn infer_struct() {
73 check_inference(
74 r#"
75struct A {
76 b: B,
77 c: C,
78}
79struct B;
80struct C(usize);
81
82fn test() {
83 let c = C(1);
84 B;
85 let a: A = A { b: B, c: C() };
86 a.b;
87 a.c;
88}
89"#,
90 "0004_struct.txt",
91 );
92}
93
71fn infer(content: &str) -> String { 94fn infer(content: &str) -> String {
72 let (db, _, file_id) = MockDatabase::with_single_file(content); 95 let (db, _, file_id) = MockDatabase::with_single_file(content);
73 let source_file = db.source_file(file_id); 96 let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
new file mode 100644
index 000000000..70ad055ff
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
@@ -0,0 +1,10 @@
1[86; 90) 'C(1)': [unknown]
2[72; 153) '{ ...a.c; }': ()
3[86; 87) 'C': C
4[107; 108) 'a': A
5[114; 132) 'A { b:... C() }': [unknown]
6[138; 141) 'a.b': [unknown]
7[147; 150) 'a.c': [unknown]
8[96; 97) 'B': B
9[88; 89) '1': [unknown]
10[82; 83) 'c': [unknown]