Move LSP semantics into typed analysis
This commit is contained in:
parent
f4cd4f4458
commit
bac1183593
26 changed files with 1232 additions and 637 deletions
|
|
@ -28,6 +28,7 @@ const (
|
|||
type ExprMeta struct{ Semantic *HIRExpr }
|
||||
|
||||
type HIRExpr struct {
|
||||
ID int
|
||||
Type Type
|
||||
Meaning ExprMeaning
|
||||
Symbol *Symbol
|
||||
|
|
@ -111,8 +112,9 @@ type HIRFunction struct {
|
|||
}
|
||||
|
||||
type HIRProgram struct {
|
||||
Functions []*HIRFunction
|
||||
Methods []*HIRFunction
|
||||
Functions []*HIRFunction
|
||||
Methods []*HIRFunction
|
||||
Expressions []*HIRExpr
|
||||
}
|
||||
|
||||
func exprMeta(expr Expr) *HIRExpr {
|
||||
|
|
@ -240,10 +242,10 @@ func (resolver *semanticResolver) resolve() error {
|
|||
symbol, _ := resolver.program.Global.Lookup(decl.Name)
|
||||
scope := NewScope(resolver.program.Global)
|
||||
for _, param := range decl.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
typ, _ := resolver.program.ResolveTypeRefWithParams(param.TypeRef, decl.TypeParams)
|
||||
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
result, _ := resolver.program.ResolveType(decl.ReturnType)
|
||||
result, _ := resolver.program.ResolveTypeRefWithParams(decl.ReturnRef, decl.TypeParams)
|
||||
resolver.resolveStmts(decl.Body, scope, nil, result)
|
||||
resolver.program.HIR.Functions = append(resolver.program.HIR.Functions, &HIRFunction{Symbol: symbol, Decl: decl, Scope: scope})
|
||||
}
|
||||
|
|
@ -255,10 +257,12 @@ func (resolver *semanticResolver) resolve() error {
|
|||
scope := NewScope(resolver.program.Global)
|
||||
_ = scope.Define(&Symbol{Name: "this", Kind: VariableSymbol, Type: ClassType{Class: class}})
|
||||
for _, param := range method.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
params := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
||||
typ, _ := resolver.program.ResolveTypeRefWithParams(param.TypeRef, params)
|
||||
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
result, _ := resolver.program.ResolveType(method.ReturnType)
|
||||
params := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
||||
result, _ := resolver.program.ResolveTypeRefWithParams(method.ReturnRef, params)
|
||||
resolver.resolveStmts(method.Body, scope, class, result)
|
||||
resolver.program.HIR.Methods = append(resolver.program.HIR.Methods, &HIRFunction{Symbol: class.Methods[method.Name], Decl: method, Scope: scope})
|
||||
}
|
||||
|
|
@ -289,6 +293,8 @@ func (resolver *semanticResolver) resolveStmts(stmts []Stmt, scope *Scope, class
|
|||
if result, ok := ResolvedType(value.Value).(GenericType); ok && result.Base.String() == "Result" && len(result.Args) == 2 && len(valueTypes) == 2 {
|
||||
valueTypes[0] = result.Args[0]
|
||||
valueTypes[1] = NullableType{Element: result.Args[1]}
|
||||
} else if tuple, ok := ResolvedType(value.Value).(TupleType); ok && len(tuple.Elements) == len(valueTypes) {
|
||||
copy(valueTypes, tuple.Elements)
|
||||
}
|
||||
for index, name := range value.Names {
|
||||
_ = scope.Define(&Symbol{Name: name, Kind: VariableSymbol, Type: valueTypes[index], Mutable: value.Mutable})
|
||||
|
|
@ -298,13 +304,34 @@ func (resolver *semanticResolver) resolveStmts(stmts []Stmt, scope *Scope, class
|
|||
expected := Type(UnknownType{})
|
||||
if symbol, ok := scope.Lookup(value.Name); ok {
|
||||
expected = symbol.Type
|
||||
} else if class != nil && class.Fields[value.Name] != nil {
|
||||
expected = class.Fields[value.Name].Type
|
||||
} else {
|
||||
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + value.Name, Severity: DiagnosticError, Span: SourceSpan{Start: value.Pos, End: value.Pos + len(value.Name)}})
|
||||
}
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
|
||||
stmts[index] = value
|
||||
case AddAssignStmt:
|
||||
_, local := scope.Lookup(value.Name)
|
||||
field := false
|
||||
if class != nil {
|
||||
_, field = class.Fields[value.Name]
|
||||
}
|
||||
if !local && !field {
|
||||
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + value.Name, Severity: DiagnosticError, Span: SourceSpan{Start: value.Pos, End: value.Pos + len(value.Name)}})
|
||||
}
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
stmts[index] = value
|
||||
case MultiAssignStmt:
|
||||
for index, name := range value.Names {
|
||||
if _, ok := scope.Lookup(name); !ok {
|
||||
position := 0
|
||||
if index < len(value.Positions) {
|
||||
position = value.Positions[index]
|
||||
}
|
||||
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + name, Severity: DiagnosticError, Span: SourceSpan{Start: position, End: position + len(name)}})
|
||||
}
|
||||
}
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
stmts[index] = value
|
||||
case ReturnStmt:
|
||||
|
|
@ -421,8 +448,11 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
|||
expr = value
|
||||
case LambdaExpr:
|
||||
lambdaScope := NewScope(scope)
|
||||
if value.ImplicitIt {
|
||||
_ = lambdaScope.Define(&Symbol{Name: "it", Kind: VariableSymbol, Type: UnknownType{}})
|
||||
}
|
||||
for _, param := range value.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
typ, _ := resolver.program.ResolveTypeRef(param.TypeRef)
|
||||
_ = lambdaScope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
resolver.resolveStmts(value.Body, lambdaScope, class, functionResult(expected))
|
||||
|
|
@ -443,15 +473,20 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
|||
}
|
||||
}
|
||||
meaning, symbol := resolver.meaning(expr, scope)
|
||||
if ident, ok := expr.(IdentExpr); ok && meaning == UnresolvedExpr && isUnknownType(typ) && !isSemanticBuiltin(ident.Name) {
|
||||
resolver.addDiagnostic(undefinedDiagnostic(ident.Name, ident.Pos))
|
||||
}
|
||||
resolver.validateEnumExpression(expr, meaning)
|
||||
resolver.validateGenericCall(expr)
|
||||
if meaning == GoCallExpr && isResultType(expected) {
|
||||
typ = expected
|
||||
}
|
||||
if nullable, ok := typ.(NullableType); ok && typeEqual(nullable.Element, expected) {
|
||||
typ = expected
|
||||
}
|
||||
semantic := &HIRExpr{Type: typ, Meaning: meaning, Symbol: symbol}
|
||||
semantic := &HIRExpr{ID: len(resolver.program.HIR.Expressions) + 1, Type: typ, Meaning: meaning, Symbol: symbol}
|
||||
semantic.Node = resolver.hirNode(expr, semantic)
|
||||
resolver.program.HIR.Expressions = append(resolver.program.HIR.Expressions, semantic)
|
||||
return withExprMeta(expr, semantic), typ
|
||||
}
|
||||
|
||||
|
|
@ -699,6 +734,31 @@ func (resolver *semanticResolver) fail(err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) addDiagnostic(diagnostic SemanticDiagnostic) {
|
||||
for _, existing := range resolver.program.Diagnostics {
|
||||
if existing.Code == diagnostic.Code && existing.Span.Start == diagnostic.Span.Start && existing.Message == diagnostic.Message {
|
||||
return
|
||||
}
|
||||
}
|
||||
resolver.program.Diagnostics = append(resolver.program.Diagnostics, diagnostic)
|
||||
}
|
||||
|
||||
func isSemanticBuiltin(name string) bool {
|
||||
return semanticBuiltins[name]
|
||||
}
|
||||
|
||||
var semanticBuiltins = map[string]bool{
|
||||
"println": true, "runCatching": true, "Channel": true,
|
||||
"listOf": true, "mutableListOf": true, "mapOf": true, "mutableMapOf": true,
|
||||
"append": true, "keys": true, "goAssert": true, "len": true, "cap": true,
|
||||
"make": true, "new": true, "copy": true, "delete": true, "close": true,
|
||||
"panic": true, "recover": true, "string": true, "int": true, "float64": true, "bool": true,
|
||||
"sql": true, "set": true, "now": true, "Result": true, "ByteSlice": true,
|
||||
"runBlocking": true, "coroutineScope": true, "launch": true, "async": true,
|
||||
"delay": true, "withTimeout": true, "isActive": true,
|
||||
"continue": true, "break": true,
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateEnumExpression(expr Expr, meaning ExprMeaning) {
|
||||
if meaning != EnumConstructionExpr {
|
||||
return
|
||||
|
|
@ -745,6 +805,30 @@ func (resolver *semanticResolver) validateEnumExpression(expr Expr, meaning Expr
|
|||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateGenericCall(expr Expr) {
|
||||
call, ok := expr.(CallExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ident, ok := call.Callee.(IdentExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var params []string
|
||||
found := false
|
||||
if function, ok := resolver.program.Functions[ident.Name]; ok {
|
||||
params = function.TypeParams
|
||||
found = true
|
||||
}
|
||||
if class, ok := resolver.program.ClassInfo[ident.Name]; ok {
|
||||
params = class.TypeParams
|
||||
found = true
|
||||
}
|
||||
if found && len(call.TypeArgs) > 0 && len(call.TypeArgs) != len(params) {
|
||||
resolver.fail(fmt.Errorf("%s expects %d type arguments, got %d", ident.Name, len(params), len(call.TypeArgs)))
|
||||
}
|
||||
}
|
||||
|
||||
func collectionElement(typ Type) Type {
|
||||
if generic, ok := typ.(GenericType); ok && len(generic.Args) > 0 {
|
||||
return generic.Args[len(generic.Args)-1]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue