// scopeAssignedValue scopes a named assigned value exported into the context. func (sb *scopeBuilder) scopeAssignedValue(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // If the assigned value's name is _, then it is anonymous scope. if node.Get(parser.NodeNamedValueName) == ANONYMOUS_REFERENCE { return newScope().ForAnonymousScope(sb.sg.tdg).GetScope() } // If the assigned value is under a rejection, then it is always an error (but nullable, as it // may not be present always). if _, ok := node.TryGetIncomingNode(parser.NodeAssignedRejection); ok { return newScope().Valid().Assignable(sb.sg.tdg.ErrorTypeReference().AsNullable()).GetScope() } // Otherwise, the value is the assignment of the parent statement's expression. parentNode := node.GetIncomingNode(parser.NodeAssignedDestination) switch parentNode.Kind() { case parser.NodeTypeResolveStatement: // The assigned value exported by a resolve statement has the type of its expression. exprScope := sb.getScope(parentNode.GetNode(parser.NodeResolveStatementSource), context) if !exprScope.GetIsValid() { return newScope().Invalid().GetScope() } // If the parent node has a rejection, then the expression may be null. exprType := exprScope.ResolvedTypeRef(sb.sg.tdg) if _, ok := parentNode.TryGetNode(parser.NodeAssignedRejection); ok { exprType = exprType.AsNullable() } return newScope().Valid().Assignable(exprType).GetScope() default: panic(fmt.Sprintf("Unknown node exporting an assigned value: %v", parentNode.Kind())) return newScope().Invalid().GetScope() } }
// scopeStructuralNewExpressionEntry scopes a single entry in a structural new expression. func (sb *scopeBuilder) scopeStructuralNewExpressionEntry(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { parentNode := node.GetIncomingNode(parser.NodeStructuralNewExpressionChildEntry) parentExprScope := sb.getScope(parentNode.GetNode(parser.NodeStructuralNewTypeExpression), context) parentType := parentExprScope.StaticTypeRef(sb.sg.tdg) if parentExprScope.GetKind() == proto.ScopeKind_VALUE { parentType = parentExprScope.ResolvedTypeRef(sb.sg.tdg) } entryName := node.Get(parser.NodeStructuralNewEntryKey) // Get the scope for the value. valueScope := sb.getScope(node.GetNode(parser.NodeStructuralNewEntryValue), context) if !valueScope.GetIsValid() { return newScope().Invalid().GetScope() } // Lookup the member associated with the entry name. module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) member, rerr := parentType.ResolveAccessibleMember(entryName, module, typegraph.MemberResolutionInstance) if rerr != nil { sb.decorateWithError(node, "%v", rerr) return newScope().Invalid().GetScope() } // Ensure the member is assignable. if member.IsReadOnly() { sb.decorateWithError(node, "%v %v under type %v is read-only", member.Title(), member.Name(), parentType) return newScope().Invalid().GetScope() } // Get the member's assignable type, transformed under the parent type, and ensure it is assignable // from the type of the value. assignableType := member.AssignableType().TransformUnder(parentType) valueType := valueScope.ResolvedTypeRef(sb.sg.tdg) if aerr := valueType.CheckSubTypeOf(assignableType); aerr != nil { sb.decorateWithError(node, "Cannot assign value of type %v to %v %v: %v", valueType, member.Title(), member.Name(), aerr) return newScope().Invalid().GetScope() } return newScope().ForNamedScope(sb.getNamedScopeForMember(member), context).Valid().GetScope() }
// getPackageForImport returns the package information for the package imported by the given import // package node. func (g *SRG) getPackageForImport(importPackageNode compilergraph.GraphNode) importedPackage { importNode := importPackageNode.GetIncomingNode(parser.NodeImportPredicatePackageRef) // Note: There may not be a kind, in which case this will return empty string, which is the // default kind. packageKind, _ := importNode.TryGet(parser.NodeImportPredicateKind) packageLocation := importNode.Get(parser.NodeImportPredicateLocation) packageInfo, ok := g.packageMap.Get(packageKind, packageLocation) if !ok { source := importNode.Get(parser.NodeImportPredicateSource) subsource, _ := importPackageNode.TryGet(parser.NodeImportPredicateSubsource) panic(fmt.Sprintf("Missing package info for import %s %s (reference %v) (node %v)\nPackage Map: %v", source, subsource, packageLocation, importNode, g.packageMap)) } return importedPackage{ srg: g, packageInfo: packageInfo, importSource: compilercommon.InputSource(importPackageNode.Get(parser.NodePredicateSource)), } }
// scopeNamedValue scopes a named value exported by a with or loop statement into context. func (sb *scopeBuilder) scopeNamedValue(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // If the value's name is _, then it is anonymous scope. if node.Get(parser.NodeNamedValueName) == ANONYMOUS_REFERENCE { return newScope().ForAnonymousScope(sb.sg.tdg).GetScope() } // Find the parent node creating this named value. parentNode := node.GetIncomingNode(parser.NodeStatementNamedValue) switch parentNode.Kind() { case parser.NodeTypeWithStatement: // The named value exported by a with statement has the type of its expression. exprScope := sb.getScope(parentNode.GetNode(parser.NodeWithStatementExpression), context) if !exprScope.GetIsValid() { return newScope().Invalid().GetScope() } return newScope().Valid().AssignableResolvedTypeOf(exprScope).GetScope() case parser.NodeTypeMatchStatement: // The named value exported by a match statement has the type of its expression (although // this is then overridden with specific hints under each case). exprScope := sb.getScope(parentNode.GetNode(parser.NodeMatchStatementExpression), context) if !exprScope.GetIsValid() { return newScope().Invalid().GetScope() } return newScope().Valid().AssignableResolvedTypeOf(exprScope).GetScope() case parser.NodeTypeLoopExpression: fallthrough case parser.NodeTypeLoopStatement: // The named value exported by a loop statement or expression has the type of the generic of the // Stream<T> interface implemented. var predicate compilergraph.Predicate = parser.NodeLoopStatementExpression if parentNode.Kind() == parser.NodeTypeLoopExpression { predicate = parser.NodeLoopExpressionStreamExpression } exprScope := sb.getScope(parentNode.GetNode(predicate), context) if !exprScope.GetIsValid() { return newScope().Invalid().GetScope() } loopExprType := exprScope.ResolvedTypeRef(sb.sg.tdg) // Check for a Streamable. generics, lerr := loopExprType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamableType()) if lerr == nil { return newScope().Valid().WithLabel(proto.ScopeLabel_STREAMABLE_LOOP).Assignable(generics[0]).GetScope() } else { generics, serr := loopExprType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamType()) if serr != nil { sb.decorateWithError(parentNode, "Loop iterable expression must implement type 'stream' or 'streamable': %v", serr) return newScope().Invalid().GetScope() } return newScope().WithLabel(proto.ScopeLabel_STREAM_LOOP).Valid().Assignable(generics[0]).GetScope() } default: panic(fmt.Sprintf("Unknown node exporting a named value: %v", parentNode.Kind())) return newScope().Invalid().GetScope() } }