-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstruct.go
More file actions
107 lines (96 loc) · 3.21 KB
/
Copy pathstruct.go
File metadata and controls
107 lines (96 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package analysis
import (
"github.com/ProCode-Software/klar/internal/ast"
"github.com/ProCode-Software/klar/internal/klarerrs"
)
type Struct struct {
Inherited map[Type]struct{} // Structs, interfaces, and tags
Fields []*Object // Type is [*StructField]
fieldMap map[string]*Object // Contains fields and methods
Initializers []*Object // Type is [*Overload]
MethodSet
fmset *FieldMethodSet // Lazy-computed
}
var _ SupportsMethods = &Struct{}
func (s *Struct) String() string { return "<struct>" }
func (s *Struct) Kind() Kind { return KindStruct }
type FieldMethodSet struct {
All map[string]Type
Fields map[string]Type
Methods map[string]*Function
// TODO: Should we add a map of ambiguous field/methods?
}
type StructField struct {
*Variable
Optional bool // Has default param or Optional type
Attributes *Attributes
}
// checkStructDecl checks a struct declaration and sets o's underlying type
// to a [*Struct]. o's Type should be [*TypeName].
func (c *Checker) checkStructDecl(o *Object, node *ast.StructDeclaration) {
str := &Struct{}
str.nonMethodMap = &str.fieldMap
fctx := o.LookupContext()
o.TypeName().Type = str
// We're just checking their kinds for now. TODO: Add the fields and methods later.
str.Inherited = c.checkInheritedTypes(node.InheritedTypes, KindStruct, fctx)
if len(node.Fields) == 0 {
// TODO: Remove when fmset is implemented
str.fieldMap = make(map[string]*Object, 0)
return
}
str.fieldMap = make(map[string]*Object)
str.Fields = make([]*Object, 0, len(node.Fields))
for _, field := range node.Fields {
var (
typ = c.parseType(field.Type, fctx)
attrs = c.parseAttributes(
field.Attributes, attrTargetKindOf(field, true),
field.Range, o.File,
)
)
for _, id := range field.Names {
f := &StructField{
Variable: &Variable{VarKind: StructFieldVar, Type: typ},
Attributes: attrs,
}
obj := NewObject(id.Name, o.File, field.Range, o.Module, f)
f.Variable.Object = obj
str.Fields = append(str.Fields, obj)
if _, ok := str.fieldMap[id.Name]; ok {
// Duplicate struct fields should have already been checked by the parser
panic("field '" + id.Name + "' already exists in struct " + o.Name)
}
str.fieldMap[id.Name] = obj
c.queue(func() {
// TODO: look into this again
// The type may not be initialized by the time we initialize this struct
if Underlying(typ) != nil {
f.Optional = typ.Kind() == KindOptional || field.Value != nil
}
// TODO: default values
}, false)
}
}
}
func (s *Struct) Index(f string, t *Expr) *klarerrs.Error {
// TODO: use fmset to also add inherited fields/methods
if obj, ok := s.fieldMap[f]; ok {
t.Type = obj
return nil
}
err := fieldNotFound(f)
if len(s.fieldMap) == 0 {
err.Hint("The struct has no fields.")
}
return err
}
// If the initialization represents a type cast, the type of the first argument is
// returned, and cast is true. Otherwise, typ is returned, possibly wrapped in an optional
// or result, following the rules of custom initializers.
func (c *Checker) checkStructInitializer(
s *Struct, name string, args []*ast.CallParam,
) (res Type, cast bool) {
// var i int // Index of unlabelled params
return s, false
}