Move LSP semantics into typed analysis
This commit is contained in:
parent
f4cd4f4458
commit
bac1183593
26 changed files with 1232 additions and 637 deletions
|
|
@ -48,26 +48,48 @@ func (scope *Scope) Lookup(name string) (*Symbol, bool) {
|
|||
}
|
||||
|
||||
type ClassSymbol struct {
|
||||
Name string
|
||||
Decl *ClassDecl
|
||||
Fields map[string]*Symbol
|
||||
Methods map[string]*Symbol
|
||||
Name string
|
||||
TypeParams []string
|
||||
Decl *ClassDecl
|
||||
Fields map[string]*Symbol
|
||||
Methods map[string]*Symbol
|
||||
}
|
||||
|
||||
type SemanticProgram struct {
|
||||
Syntax *Program
|
||||
Global *Scope
|
||||
Classes map[string]ClassDecl
|
||||
ClassInfo map[string]*ClassSymbol
|
||||
Functions map[string]FunctionDecl
|
||||
Enums map[string]EnumDecl
|
||||
Imports map[string]bool
|
||||
GoPackages map[string]*gotypes.Package
|
||||
HIR *HIRProgram
|
||||
Mappings *mappingState
|
||||
Syntax *Program
|
||||
Global *Scope
|
||||
Classes map[string]ClassDecl
|
||||
ClassInfo map[string]*ClassSymbol
|
||||
Functions map[string]FunctionDecl
|
||||
Enums map[string]EnumDecl
|
||||
Imports map[string]bool
|
||||
GoPackages map[string]*gotypes.Package
|
||||
HIR *HIRProgram
|
||||
Mappings *mappingState
|
||||
Diagnostics []SemanticDiagnostic
|
||||
}
|
||||
|
||||
func Analyze(program *Program) (*SemanticProgram, error) {
|
||||
semantic, diagnostics := AnalyzeWithContext(program, nil)
|
||||
if len(diagnostics) > 0 {
|
||||
return nil, diagnostics[0]
|
||||
}
|
||||
return semantic, nil
|
||||
}
|
||||
|
||||
func AnalyzeWithContext(program *Program, additional []*Program) (*SemanticProgram, []SemanticDiagnostic) {
|
||||
semantic, err := analyzeProgram(program, additional)
|
||||
if err != nil {
|
||||
return semantic, []SemanticDiagnostic{diagnosticForError("semantic-error", err)}
|
||||
}
|
||||
diagnostics := semantic.Diagnostics
|
||||
if len(diagnostics) > 0 {
|
||||
return semantic, diagnostics
|
||||
}
|
||||
return semantic, nil
|
||||
}
|
||||
|
||||
func analyzeProgram(program *Program, additional []*Program) (*SemanticProgram, error) {
|
||||
semantic := &SemanticProgram{
|
||||
Syntax: program,
|
||||
Global: NewScope(nil),
|
||||
|
|
@ -79,67 +101,94 @@ func Analyze(program *Program) (*SemanticProgram, error) {
|
|||
GoPackages: map[string]*gotypes.Package{},
|
||||
Mappings: &mappingState{functions: map[string]string{}},
|
||||
}
|
||||
for index := range program.Classes {
|
||||
decl := &program.Classes[index]
|
||||
class := &ClassSymbol{Name: decl.Name, Decl: decl, Fields: map[string]*Symbol{}, Methods: map[string]*Symbol{}}
|
||||
semantic.Classes[decl.Name] = *decl
|
||||
semantic.ClassInfo[decl.Name] = class
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: ClassSymbolKind, Type: ClassType{Class: class}, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for index := range program.Enums {
|
||||
decl := &program.Enums[index]
|
||||
semantic.Enums[decl.Name] = *decl
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: EnumSymbolKind, Type: NamedType{Name: decl.Name}, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, imported := range program.Imports {
|
||||
name := imported.Alias
|
||||
if name == "" {
|
||||
name = defaultImportAlias(imported)
|
||||
}
|
||||
semantic.Imports[name] = true
|
||||
if importedPackage, err := importGoPackage(imported); err == nil {
|
||||
semantic.GoPackages[name] = importedPackage
|
||||
}
|
||||
if existing, ok := semantic.Global.Lookup(name); ok && existing.Kind == ImportSymbolKind {
|
||||
continue
|
||||
}
|
||||
if err := semantic.Global.Define(&Symbol{Name: name, Kind: ImportSymbolKind, Type: NamedType{Name: name}, Decl: imported}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for index := range program.Functions {
|
||||
decl := &program.Functions[index]
|
||||
semantic.Functions[decl.Name] = *decl
|
||||
typ, err := semantic.functionType(decl.Params, decl.ReturnType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function %s: %w", decl.Name, err)
|
||||
}
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: FunctionSymbolKind, Type: typ, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for index := range program.Classes {
|
||||
decl := &program.Classes[index]
|
||||
class := semantic.ClassInfo[decl.Name]
|
||||
for fieldIndex := range decl.Fields {
|
||||
field := &decl.Fields[fieldIndex]
|
||||
typ, err := semantic.ResolveType(field.Type)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("field %s.%s: %w", decl.Name, field.Name, err)
|
||||
programs := append([]*Program{program}, additional...)
|
||||
for _, source := range programs {
|
||||
for index := range source.Classes {
|
||||
decl := &source.Classes[index]
|
||||
class := &ClassSymbol{Name: decl.Name, TypeParams: decl.TypeParams, Decl: decl, Fields: map[string]*Symbol{}, Methods: map[string]*Symbol{}}
|
||||
semantic.Classes[decl.Name] = *decl
|
||||
semantic.ClassInfo[decl.Name] = class
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: ClassSymbolKind, Type: ClassType{Class: class}, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
class.Fields[field.Name] = &Symbol{Name: field.Name, Kind: VariableSymbol, Type: typ, Mutable: field.Mutable, Decl: field}
|
||||
}
|
||||
for methodIndex := range decl.Methods {
|
||||
method := &decl.Methods[methodIndex]
|
||||
typ, err := semantic.functionType(method.Params, method.ReturnType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("method %s.%s: %w", decl.Name, method.Name, err)
|
||||
}
|
||||
for _, source := range programs {
|
||||
for index := range source.Enums {
|
||||
decl := &source.Enums[index]
|
||||
semantic.Enums[decl.Name] = *decl
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: EnumSymbolKind, Type: NamedType{Name: decl.Name}, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, source := range programs {
|
||||
for _, imported := range source.Imports {
|
||||
name := imported.Alias
|
||||
if name == "" {
|
||||
name = defaultImportAlias(imported)
|
||||
}
|
||||
semantic.Imports[name] = true
|
||||
if importedPackage, err := importGoPackage(imported); err == nil {
|
||||
semantic.GoPackages[name] = importedPackage
|
||||
}
|
||||
if existing, ok := semantic.Global.Lookup(name); ok && existing.Kind == ImportSymbolKind {
|
||||
continue
|
||||
}
|
||||
if err := semantic.Global.Define(&Symbol{Name: name, Kind: ImportSymbolKind, Type: NamedType{Name: name}, Decl: imported}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, source := range programs {
|
||||
for index := range source.Embeds {
|
||||
embedded := &source.Embeds[index]
|
||||
typ, err := semantic.ResolveType(embedded.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, exists := semantic.Global.Lookup(embedded.Name); !exists {
|
||||
_ = semantic.Global.Define(&Symbol{Name: embedded.Name, Kind: VariableSymbol, Type: typ, Decl: embedded})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, source := range programs {
|
||||
for index := range source.Functions {
|
||||
decl := &source.Functions[index]
|
||||
semantic.Functions[decl.Name] = *decl
|
||||
typ, err := semantic.functionType(decl.TypeParams, decl.Params, decl.ReturnType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function %s: %w", decl.Name, err)
|
||||
}
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: FunctionSymbolKind, Type: typ, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, source := range programs {
|
||||
for index := range source.Classes {
|
||||
decl := &source.Classes[index]
|
||||
class := semantic.ClassInfo[decl.Name]
|
||||
for fieldIndex := range decl.Fields {
|
||||
field := &decl.Fields[fieldIndex]
|
||||
typ, err := semantic.ResolveTypeRefWithParams(field.TypeRef, decl.TypeParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("field %s.%s: %w", decl.Name, field.Name, err)
|
||||
}
|
||||
class.Fields[field.Name] = &Symbol{Name: field.Name, Kind: VariableSymbol, Type: typ, Mutable: field.Mutable, Decl: field}
|
||||
}
|
||||
for methodIndex := range decl.Methods {
|
||||
method := &decl.Methods[methodIndex]
|
||||
if len(method.TypeParams) > 0 {
|
||||
return nil, fmt.Errorf("generic methods are not supported; declare type parameters on class %s", decl.Name)
|
||||
}
|
||||
methodParams := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
||||
typ, err := semantic.functionType(methodParams, method.Params, method.ReturnType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("method %s.%s: %w", decl.Name, method.Name, err)
|
||||
}
|
||||
class.Methods[method.Name] = &Symbol{Name: method.Name, Kind: FunctionSymbolKind, Type: typ, Decl: method}
|
||||
}
|
||||
class.Methods[method.Name] = &Symbol{Name: method.Name, Kind: FunctionSymbolKind, Type: typ, Decl: method}
|
||||
}
|
||||
}
|
||||
if err := validateMutability(semantic); err != nil {
|
||||
|
|
@ -164,6 +213,30 @@ func (semantic *SemanticProgram) ResolveType(text string) (Type, error) {
|
|||
return resolved, nil
|
||||
}
|
||||
|
||||
func (semantic *SemanticProgram) ResolveTypeRef(ref TypeRef) (Type, error) {
|
||||
return semantic.ResolveTypeRefWithParams(ref, nil)
|
||||
}
|
||||
|
||||
func (semantic *SemanticProgram) ResolveTypeRefWithParams(ref TypeRef, params []string) (Type, error) {
|
||||
typ := ref.Syntax
|
||||
if typ == nil {
|
||||
parsed, err := ParseType(ref.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ = parsed
|
||||
}
|
||||
paramSet := map[string]bool{}
|
||||
for _, param := range params {
|
||||
paramSet[param] = true
|
||||
}
|
||||
resolved := resolveClassTypes(resolveTypeParameters(typ, paramSet), semantic.ClassInfo)
|
||||
if err := validateNoClassPointer(resolved); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func validateNoClassPointer(typ Type) error {
|
||||
switch value := typ.(type) {
|
||||
case GoPointerType:
|
||||
|
|
@ -190,20 +263,24 @@ func validateNoClassPointer(typ Type) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (semantic *SemanticProgram) functionType(params []Param, result string) (Type, error) {
|
||||
func (semantic *SemanticProgram) functionType(typeParams []string, params []Param, result string) (Type, error) {
|
||||
paramTypes := make([]Type, len(params))
|
||||
for index, param := range params {
|
||||
typ, err := semantic.ResolveType(param.Type)
|
||||
typ, err := semantic.ResolveTypeRefWithParams(param.TypeRef, typeParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paramTypes[index] = typ
|
||||
}
|
||||
resultType, err := semantic.ResolveType(result)
|
||||
resultRef, err := ParseTypeRef(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FunctionType{Params: paramTypes, Result: resultType}, nil
|
||||
resultType, err := semantic.ResolveTypeRefWithParams(resultRef, typeParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FunctionType{TypeParams: typeParams, Params: paramTypes, Result: resultType}, nil
|
||||
}
|
||||
|
||||
func (semantic *SemanticProgram) GoType(text string) string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue