func TestParser(t *testing.T) { for _, test := range parserTests { if os.Getenv("FILTER") != "" { if !strings.Contains(test.name, os.Getenv("FILTER")) { continue } else { fmt.Printf("Matched Test: %v\n", test.name) } } moduleNode := createAstNode(compilercommon.InputSource(test.name), NodeTypeGlobalModule) Parse(moduleNode, createAstNode, compilercommon.InputSource(test.name), test.input()) parseTree := getParseTree((moduleNode).(*testNode), 0) assert := assert.New(t) expected := strings.TrimSpace(test.tree()) found := strings.TrimSpace(parseTree) if os.Getenv("REGEN") == "true" { test.writeTree(found) } else { if !assert.Equal(expected, found, test.name) { t.Log(parseTree) } } } }
// conductParsing performs parsing of a source file found at the given path. func (p *PackageLoader) conductParsing(sourceFile pathInformation) { inputSource := compilercommon.InputSource(sourceFile.path) // Add the file to the package map as a package of one file. p.packageMap.Add(sourceFile.sourceKind, sourceFile.referenceId, PackageInfo{ kind: sourceFile.sourceKind, referenceId: sourceFile.referenceId, modulePaths: []compilercommon.InputSource{inputSource}, }) // Ensure the file exists. if ok, _ := exists(sourceFile.path); !ok { p.errors <- compilercommon.SourceErrorf(sourceFile.sal, "Could not find source file '%s'", sourceFile.path) return } // Load the source file's contents. contents, err := ioutil.ReadFile(sourceFile.path) if err != nil { p.errors <- compilercommon.SourceErrorf(sourceFile.sal, "Could not load source file '%s'", sourceFile.path) return } // Parse the source file. handler, hasHandler := p.handlers[sourceFile.sourceKind] if !hasHandler { log.Fatalf("Missing handler for source file of kind: [%v]", sourceFile.sourceKind) } handler.Parse(inputSource, string(contents), p.handleImport) }
// scopeUnaryExpression scopes a unary expression in the SRG. func (sb *scopeBuilder) scopeUnaryExpression(node compilergraph.GraphNode, opName string, predicate compilergraph.Predicate, context scopeContext) *scopeInfoBuilder { // Get the scope of the sub expression. childScope := sb.getScope(node.GetNode(predicate), context) // Ensure that the child scope is valid. if !childScope.GetIsValid() { return newScope().Invalid() } // Ensure that the operator exists under the resolved type. childType := childScope.ResolvedTypeRef(sb.sg.tdg) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) operator, rerr := childType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator) if rerr != nil { sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, childType) return newScope().Invalid() } returnType, _ := operator.ReturnType() // Check for nullable values. if childType.NullValueAllowed() { sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, childType) return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(childType)) } return newScope().Valid().CallsOperator(operator).Resolving(returnType.TransformUnder(childType)) }
// scopeInCollectionExpression scopes an 'in' collection expression in the SRG. func (sb *scopeBuilder) scopeInCollectionExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // Get the scope of the left and right expressions. leftScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionLeftExpr), context) rightScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionRightExpr), context) // Ensure that both scopes are valid. if !leftScope.GetIsValid() || !rightScope.GetIsValid() { return newScope().Invalid().GetScope() } // Ensure that the right side has a 'contains' operator defined. rightType := rightScope.ResolvedTypeRef(sb.sg.tdg) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) operator, rerr := rightType.ResolveAccessibleMember("contains", module, typegraph.MemberResolutionOperator) if rerr != nil { sb.decorateWithError(node, "Operator 'contains' is not defined on type '%v'", rightType) return newScope().Invalid().GetScope() } // Ensure the right side is not nullable. if rightType.NullValueAllowed() { sb.decorateWithError(node, "Cannot invoke operator 'in' on nullable value of type '%v'", rightType) return newScope().Invalid().GetScope() } // Ensure that the left side can be used as the operator's parameter. parameterType := operator.ParameterTypes()[0].TransformUnder(rightType) leftType := leftScope.ResolvedTypeRef(sb.sg.tdg) if serr := leftType.CheckSubTypeOf(parameterType); serr != nil { sb.decorateWithError(node, "Cannot invoke operator 'in' with value of type '%v': %v", leftType, serr) return newScope().Invalid().GetScope() } return newScope().Valid().CallsOperator(operator).Resolving(sb.sg.tdg.BoolTypeReference()).GetScope() }
// salForValues returns a SourceAndLocation for the given string predicate values. func salForValues(sourceStr string, bytePositionStr string) compilercommon.SourceAndLocation { source := compilercommon.InputSource(sourceStr) bytePosition, err := strconv.Atoi(bytePositionStr) if err != nil { panic(fmt.Sprintf("Expected int value for byte position, found: %v", bytePositionStr)) } return compilercommon.NewSourceAndLocation(source, bytePosition) }
func conductParsing(t *testing.T, test goldenTest, source []byte) (*parseTree, formatterNode) { parseTree := newParseTree(source) inputSource := compilercommon.InputSource(test.filename) rootNode := parser.Parse(parseTree.createAstNode, nil, inputSource, string(source)) if !assert.Empty(t, parseTree.errors, "Expected no parse errors for test %s", test.name) { return nil, formatterNode{} } return parseTree, rootNode.(formatterNode) }
func TestNameScoping(t *testing.T) { for _, test := range nameScopeTests { if os.Getenv("FILTER") != "" && !strings.Contains(test.name, os.Getenv("FILTER")) { continue } source := fmt.Sprintf("tests/namescope/%s.seru", test.source) testSRG := getSRG(t, source, "tests/testlib") _, found := testSRG.FindModuleBySource(compilercommon.InputSource(source)) if !assert.True(t, found, "Test module not found") { continue } // Find the commented node. commentedNode, commentFound := testSRG.FindCommentedNode("/* " + test.comment + " */") if !assert.True(t, commentFound, "Comment %v for test %v not found", test.comment, test.name) { continue } // Resolve the name from the node. result, nameFound := testSRG.FindNameInScope(test.queryName, commentedNode) if !test.result.isValid { assert.False(t, nameFound, "Test %v expected name %v to not be found. Found: %v", test.name, test.queryName, result) continue } if !assert.Equal(t, result.IsNamedScope(), !test.result.isExternal, "External scope mismatch on test %s", test.name) { continue } if result.IsNamedScope() { resolved := result.AsNamedScope() if !assert.True(t, nameFound, "Test %v expected name %v to be found", test.name, test.queryName) { continue } if !assert.Equal(t, test.result.expectedNodeType, resolved.Kind(), "Test %v expected node of kind %v", test.name, test.result.expectedNodeType) { continue } if !assert.Equal(t, test.result.expectedName, resolved.Name(), "Test %v expected name %v", test.name, test.result.expectedName) { continue } if !assert.Equal(t, test.result.expectedKind, resolved.ScopeKind(), "Test %v expected kind %v", test.name, test.result.expectedKind) { continue } } else { if !assert.Equal(t, test.result.expectedName, result.AsPackageImport().name, "Mismatch name on imported package for test %s", test.name) { continue } } } }
func TestComplexResolveTypePath(t *testing.T) { testSRG := getSRG(t, "tests/complexresolve/entrypoint.seru") // Lookup the entrypoint module. entrypointModule, ok := testSRG.FindModuleBySource(compilercommon.InputSource("tests/complexresolve/entrypoint.seru")) assert.True(t, ok, "Could not find entrypoint module") // Lookup all the expected types. // In module. assertResolveTypePath(t, entrypointModule, "SomeClass", "SomeClass") // from ... import ... on module assertResolveTypePath(t, entrypointModule, "AnotherClass", "AnotherClass") assertResolveTypePath(t, entrypointModule, "BestClass", "AThirdClass") // from ... import ... on package assertResolveTypePath(t, entrypointModule, "FirstClass", "FirstClass") assertResolveTypePath(t, entrypointModule, "SecondClass", "SecondClass") assertResolveTypePath(t, entrypointModule, "BusinessClass", "FirstClass") // Direct imports. assertResolveTypePath(t, entrypointModule, "anothermodule.AnotherClass", "AnotherClass") assertResolveTypePath(t, entrypointModule, "subpackage.FirstClass", "FirstClass") assertResolveTypePath(t, entrypointModule, "subpackage.SecondClass", "SecondClass") // Ensure that an non-exported type is still accessible inside the package.. assertResolveTypePath(t, entrypointModule, "anothermodule.localClass", "localClass") // Other package. assertResolveTypePath(t, entrypointModule, "anotherpackage.ExportedPackageClass", "ExportedPackageClass") // Ensure that an non-exported type is not accessible from another package. _, found := entrypointModule.ResolveTypePath("anotherpackage.otherPackageClass") assert.False(t, found, "Expected otherPackageClass to not be exported") // Lookup another module. anotherModule, ok := testSRG.FindModuleBySource(compilercommon.InputSource("tests/complexresolve/anothermodule.seru")) assert.True(t, ok, "Could not find another module") assertResolveTypePath(t, anotherModule, "localClass", "localClass") }
// collect gathers the emitted tokens into a slice. func collect(t *lexerTest) (tokens []lexeme) { l := lex(compilercommon.InputSource(t.name), t.input) for { token := l.nextToken() tokens = append(tokens, token) if token.kind == tokenTypeEOF || token.kind == tokenTypeError { break } } return }
func TestBasicResolveTypePath(t *testing.T) { testSRG := getSRG(t, "tests/basic/basic.seru") // Lookup the basic module. basicModule, ok := testSRG.FindModuleBySource(compilercommon.InputSource("tests/basic/basic.seru")) assert.True(t, ok, "Could not find basic module") // Lookup both expected types. assertResolveTypePath(t, basicModule, "SomeClass", "SomeClass") assertResolveTypePath(t, basicModule, "AnotherClass", "AnotherClass") }
// scopeMemberAccessExpression scopes a member access expression in the SRG. func (sb *scopeBuilder) scopeMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // Get the scope of the child expression. childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context) if !childScope.GetIsValid() { return newScope().Invalid().GetScope() } memberName := node.Get(parser.NodeMemberAccessIdentifier) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) switch childScope.GetKind() { case proto.ScopeKind_VALUE: childType := childScope.ResolvedTypeRef(sb.sg.tdg) if childType.IsNullable() { sb.decorateWithError(node, "Cannot access name '%v' under nullable type '%v'. Please use the ?. operator to ensure type safety.", memberName, childType) return newScope().Invalid().GetScope() } typeMember, rerr := childType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstance) if rerr != nil { sb.decorateWithError(node, "%v", rerr) return newScope().Invalid().GetScope() } memberScope := sb.getNamedScopeForMember(typeMember) context.staticDependencyCollector.checkNamedScopeForDependency(memberScope) return newScope().ForNamedScopeUnderType(memberScope, childType, context).GetScope() case proto.ScopeKind_GENERIC: namedScope, _ := sb.getNamedScopeForScope(childScope) sb.decorateWithError(node, "Cannot attempt member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name()) return newScope().Invalid().GetScope() case proto.ScopeKind_STATIC: staticType := childScope.StaticTypeRef(sb.sg.tdg) namedScope, _ := sb.getNamedScopeForScope(childScope) memberScope, rerr := namedScope.ResolveStaticMember(memberName, module, staticType) if rerr != nil { sb.decorateWithError(node, "%v", rerr) return newScope().Invalid().GetScope() } return newScope().ForNamedScopeUnderType(memberScope, staticType, context).GetScope() default: panic("Unknown scope kind") } return newScope().Invalid().GetScope() }
func TestModuleMembers(t *testing.T) { testSRG := getSRG(t, "tests/members/module.seru") // Ensure both module-level members are found. module, _ := testSRG.FindModuleBySource(compilercommon.InputSource("tests/members/module.seru")) members := module.GetMembers() if !assert.Equal(t, 2, len(members), "Expected 2 members found") { return } assert.Equal(t, members[0].MemberKind(), VarMember) assert.Equal(t, members[1].MemberKind(), FunctionMember) }
// scopeStreamMemberAccessExpression scopes a stream member access expression in the SRG. func (sb *scopeBuilder) scopeStreamMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // Get the scope of the child expression. childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context) if !childScope.GetIsValid() { return newScope().Invalid().GetScope() } memberName := node.Get(parser.NodeMemberAccessIdentifier) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) switch childScope.GetKind() { case proto.ScopeKind_VALUE: childType := childScope.ResolvedTypeRef(sb.sg.tdg) // Ensure the child type is a stream. generics, serr := childType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamType()) if serr != nil { sb.decorateWithError(node, "Cannot attempt stream access of name '%v' under non-stream type '%v': %v", memberName, childType, serr) return newScope().Invalid().GetScope() } valueType := generics[0] typeMember, rerr := valueType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstance) if rerr != nil { sb.decorateWithError(node, "%v", rerr) return newScope().Invalid().GetScope() } memberScope := sb.getNamedScopeForMember(typeMember) context.staticDependencyCollector.checkNamedScopeForDependency(memberScope) return newScope().ForNamedScopeUnderModifiedType(memberScope, valueType, makeStream, context).GetScope() case proto.ScopeKind_GENERIC: namedScope, _ := sb.getNamedScopeForScope(childScope) sb.decorateWithError(node, "Cannot attempt stream member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name()) return newScope().Invalid().GetScope() case proto.ScopeKind_STATIC: namedScope, _ := sb.getNamedScopeForScope(childScope) sb.decorateWithError(node, "Cannot attempt stream member access of '%v' under %v %v, as it is a static type", memberName, namedScope.Title(), namedScope.Name()) return newScope().Invalid().GetScope() default: panic("Unknown scope kind") } return newScope().Invalid().GetScope() }
// getModulesMap returns the modules map for the modules in the SRG. func (g *SRG) getModulesMap() map[compilercommon.InputSource]SRGModule { if g.modulePathMap == nil { moduleMap := map[compilercommon.InputSource]SRGModule{} it := g.findAllNodes(parser.NodeTypeFile).BuildNodeIterator(parser.NodePredicateSource) for it.Next() { moduleMap[compilercommon.InputSource(it.GetPredicate(parser.NodePredicateSource).String())] = SRGModule{it.Node(), g} } // Cache the map. g.modulePathMap = moduleMap return moduleMap } return g.modulePathMap }
// ResolveType attempts to resolve the type path referenced by this type ref. // Panics if this is not a RefKind of TypeRefPath. func (t SRGTypeRef) ResolveType() (TypeResolutionResult, bool) { // Find the parent module. source := compilercommon.InputSource(t.GraphNode.Get(parser.NodePredicateSource)) srgModule, found := t.srg.FindModuleBySource(source) if !found { panic(fmt.Sprintf("Unknown parent module: %s", source)) } // Resolve the type path under the module. resolutionPath := t.ResolutionPath() resolvedType, typeFound := srgModule.ResolveTypePath(resolutionPath) if typeFound { return resolvedType, true } // If not found and the path is a single name, try to resolve as a generic // under a parent function or type. if strings.ContainsRune(resolutionPath, '.') { // Not a single name. return TypeResolutionResult{}, false } containingFilter := func(q compilergraph.GraphQuery) compilergraph.Query { // For this filter, we check if the defining type (or type member) if the // generic is the same type (or type member) containing the typeref. To do so, // we perform a check that the start rune and end rune of the definition // contains the range of the start and end rune, respectively, of the typeref. Since // we know both nodes are in the same module, and the SRG is a tree, this validates // that we are in the correct scope without having to walk the tree upward. startRune := t.GraphNode.GetValue(parser.NodePredicateStartRune).Int() endRune := t.GraphNode.GetValue(parser.NodePredicateEndRune).Int() return q. In(parser.NodeTypeDefinitionGeneric, parser.NodePredicateTypeMemberGeneric). HasWhere(parser.NodePredicateStartRune, compilergraph.WhereLTE, startRune). HasWhere(parser.NodePredicateEndRune, compilergraph.WhereGTE, endRune) } resolvedGenericNode, genericFound := t.srg.layer. StartQuery(). // Find a node... Has(parser.NodeGenericPredicateName, resolutionPath). // With the generic name.. Has(parser.NodePredicateSource, string(source)). // That is in this module... IsKind(parser.NodeTypeGeneric). // That is a generic... FilterBy(containingFilter). // Filter by whether its defining type or member contains this typeref. TryGetNode() return resultForTypeOrGeneric(SRGTypeOrGeneric{resolvedGenericNode, t.srg}), genericFound }
// FindNameInScope finds the given name accessible from the scope under which the given node exists, if any. func (g *SRG) FindNameInScope(name string, node compilergraph.GraphNode) (SRGScopeOrImport, bool) { // Attempt to resolve the name as pointing to a parameter, var statement, loop var or with var. srgNode, srgNodeFound := g.findAddedNameInScope(name, node) if srgNodeFound { return SRGNamedScope{srgNode, g}, true } // If still not found, try to resolve as a type or import. nodeSource := node.Get(parser.NodePredicateSource) parentModule, parentModuleFound := g.FindModuleBySource(compilercommon.InputSource(nodeSource)) if !parentModuleFound { panic(fmt.Sprintf("Missing module for source %v", nodeSource)) } // Try to resolve as a local member. srgTypeOrMember, typeOrMemberFound := parentModule.FindTypeOrMemberByName(name, ModuleResolveAll) if typeOrMemberFound { return SRGNamedScope{srgTypeOrMember.GraphNode, g}, true } // Try to resolve as an imported member. localImportNode, localImportFound := parentModule.findImportWithLocalName(name) if localImportFound { // Retrieve the package for the imported member. packageInfo := g.getPackageForImport(localImportNode) resolutionName := localImportNode.Get(parser.NodeImportPredicateSubsource) // If an SRG package, then continue with the resolution. Otherwise, // we return a named scope that says that the name needs to be furthered // resolved in the package by the type graph. if packageInfo.IsSRGPackage() { packageTypeOrMember, packagetypeOrMemberFound := packageInfo.FindTypeOrMemberByName(resolutionName) if packagetypeOrMemberFound { return SRGNamedScope{packageTypeOrMember.GraphNode, g}, true } } return SRGExternalPackageImport{packageInfo.packageInfo, resolutionName, g}, true } // Try to resolve as an imported package. importNode, importFound := parentModule.findImportByPackageName(name) if importFound { return SRGNamedScope{importNode, g}, true } return SRGNamedScope{}, false }
// loadLocalPackage loads the package found at the path relative to the package directory. func (p *PackageLoader) loadLocalPackage(packagePath pathInformation) { // Ensure the directory exists. if ok, _ := exists(packagePath.path); !ok { p.errors <- compilercommon.SourceErrorf(packagePath.sal, "Could not find directory '%s'", packagePath.path) return } // Read the contents of the directory. directoryContents, err := ioutil.ReadDir(packagePath.path) if err != nil { p.errors <- compilercommon.SourceErrorf(packagePath.sal, "Could not load directory '%s'", packagePath.path) return } // Add the package information to the map. packageInfo := &PackageInfo{ kind: packagePath.sourceKind, referenceId: packagePath.referenceId, modulePaths: make([]compilercommon.InputSource, 0), } // Load the handler for the package. handler, hasHandler := p.handlers[packagePath.sourceKind] if !hasHandler { log.Fatalf("Missing handler for source file of kind: [%v]", packagePath.sourceKind) } // Find all source files in the directory and add them to the paths list. var fileFound bool for _, fileInfo := range directoryContents { if path.Ext(fileInfo.Name()) == handler.PackageFileExtension() { fileFound = true filePath := path.Join(packagePath.path, fileInfo.Name()) p.pushPath(pathSourceFile, packagePath.sourceKind, filePath, packagePath.sal) // Add the source file to the package information. packageInfo.modulePaths = append(packageInfo.modulePaths, compilercommon.InputSource(filePath)) } } p.packageMap.Add(packagePath.sourceKind, packagePath.referenceId, *packageInfo) if !fileFound { p.warnings <- compilercommon.SourceWarningf(packagePath.sal, "Package '%s' has no source files", packagePath.path) return } }
func TestLexerPositioning(t *testing.T) { text := `this.foo // some comment class SomeClass` l := lex(compilercommon.InputSource("position test"), text) checkNext(t, l, text, lexeme{tokenTypeKeyword, 0, "this"}, 0, 0) checkNext(t, l, text, lexeme{tokenTypeDotAccessOperator, 4, "."}, 0, 4) checkNext(t, l, text, lexeme{tokenTypeIdentifer, 5, "foo"}, 0, 5) checkNext(t, l, text, lexeme{tokenTypeWhitespace, 8, " "}, 0, 8) checkNext(t, l, text, lexeme{tokenTypeSinglelineComment, 9, "// some comment"}, 0, 9) checkNext(t, l, text, lexeme{tokenTypeSyntheticSemicolon, 24, "\n"}, 0, 24) checkNext(t, l, text, lexeme{tokenTypeKeyword, 25, "class"}, 1, 0) checkNext(t, l, text, lexeme{tokenTypeWhitespace, 30, " "}, 1, 5) checkNext(t, l, text, lexeme{tokenTypeIdentifer, 31, "SomeClass"}, 1, 6) checkNext(t, l, text, lexeme{tokenTypeEOF, 40, ""}, 1, 15) }
// 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() }
// scopeBinaryExpression scopes a binary expression in the SRG. func (sb *scopeBuilder) scopeBinaryExpression(node compilergraph.GraphNode, opName string, context scopeContext) *scopeInfoBuilder { // Get the scope of the left and right expressions. leftScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionLeftExpr), context) rightScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionRightExpr), context) // Ensure that both scopes are valid. if !leftScope.GetIsValid() || !rightScope.GetIsValid() { return newScope().Invalid() } // Ensure that both scopes have the same type. leftType := leftScope.ResolvedTypeRef(sb.sg.tdg) rightType := rightScope.ResolvedTypeRef(sb.sg.tdg) if leftType != rightType { sb.decorateWithError(node, "Operator '%v' requires operands of the same type. Found: '%v' and '%v'", opName, leftType, rightType) return newScope().Invalid() } // Ensure that the operator exists under the resolved type. module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) operator, rerr := leftType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator) if rerr != nil { sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, leftType) return newScope().Invalid() } returnType, _ := operator.ReturnType() // Check for nullable values. if leftType.NullValueAllowed() { sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, leftType) return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType)) } if rightType.NullValueAllowed() { sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, rightType) return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType)) } return newScope().Valid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType)) }
// scopeStructuralNewTypeExpression scopes a structural new expression for constructing a new instance // of a structural or class type. func (sb *scopeBuilder) scopeStructuralNewTypeExpression(node compilergraph.GraphNode, childScope *proto.ScopeInfo, context scopeContext) proto.ScopeInfo { // Retrieve the static type. staticTypeRef := childScope.StaticTypeRef(sb.sg.tdg) // Ensure that the static type is a struct OR it is a class with an accessible 'new'. staticType := staticTypeRef.ReferredType() switch staticType.TypeKind() { case typegraph.ClassType: // Classes can only be constructed structurally if they are in the same module as this call. // Otherwise, an exported constructor must be used. module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) _, rerr := staticTypeRef.ResolveAccessibleMember("new", module, typegraph.MemberResolutionStatic) if rerr != nil { sb.decorateWithError(node, "Cannot structurally construct type %v, as it is imported from another module", staticTypeRef) return newScope().Invalid().Resolving(staticTypeRef).GetScope() } case typegraph.StructType: // Structs can be constructed by anyone, assuming that their members are all exported. // That check occurs below. break default: sb.decorateWithError(node, "Cannot structurally construct type %v", staticTypeRef) return newScope().Invalid().Resolving(staticTypeRef).GetScope() } encountered, isValid := sb.scopeStructuralNewEntries(node, context) if !isValid { return newScope().Invalid().Resolving(staticTypeRef).GetScope() } // Ensure that all required entries are present. for _, field := range staticType.RequiredFields() { if _, ok := encountered[field.Name()]; !ok { isValid = false sb.decorateWithError(node, "Non-nullable %v '%v' is required to construct type %v", field.Title(), field.Name(), staticTypeRef) } } return newScope().IsValid(isValid).Resolving(staticTypeRef).GetScope() }
func getMapping(dom codedom.StatementOrExpression, positionMapper *compilercommon.PositionMapper) (sourcemap.SourceMapping, bool) { basisNode := dom.BasisNode() inputSource, hasInputSource := basisNode.TryGet(parser.NodePredicateSource) if !hasInputSource { return sourcemap.SourceMapping{}, false } startRune := basisNode.GetValue(parser.NodePredicateStartRune).Int() originalLine, originalCol, err := positionMapper.Map(compilercommon.InputSource(inputSource), startRune) if err != nil { panic(err) } var name = "" if named, ok := dom.(codedom.Named); ok { name = named.ExprName() } return sourcemap.SourceMapping{inputSource, originalLine, originalCol, name}, true }
// scopeSmlNormalAttribute scopes an SML expression attribute under a declaration. func (sb *scopeBuilder) scopeSmlAttribute(node compilergraph.GraphNode, propsType typegraph.TypeReference, context scopeContext) (string, bool) { attributeName := node.Get(parser.NodeSmlAttributeName) // If the props type is a struct or class, ensure that the attribute name exists. var allowedValueType = sb.sg.tdg.AnyTypeReference() if propsType.IsRefToStruct() || propsType.IsRefToClass() { module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) resolvedMember, rerr := propsType.ResolveAccessibleMember(attributeName, module, typegraph.MemberResolutionInstance) if rerr != nil { sb.decorateWithError(node, "%v", rerr) return attributeName, false } allowedValueType = resolvedMember.AssignableType() } else { // The props type must be a mapping, so the value must match it value type. allowedValueType = propsType.Generics()[0] } // Scope the attribute value (if any). If none, then we default to a boolean value. var attributeValueType = sb.sg.tdg.BoolTypeReference() valueNode, hasValueNode := node.TryGetNode(parser.NodeSmlAttributeValue) if hasValueNode { attributeValueScope := sb.getScope(valueNode, context) if !attributeValueScope.GetIsValid() { return attributeName, false } attributeValueType = attributeValueScope.ResolvedTypeRef(sb.sg.tdg) } // Ensure it matches the assignable value type. if serr := attributeValueType.CheckSubTypeOf(allowedValueType); serr != nil { sb.decorateWithError(node, "Cannot assign value of type %v for attribute %v: %v", attributeValueType, attributeName, serr) return attributeName, false } return attributeName, true }
// parseAndFormatSourceFile parses the source file at the given path (with associated file info), // formats it and, if changed, writes it back to that path. func parseAndFormatSourceFile(sourceFilePath string, info os.FileInfo, importHandling importHandlingInfo) error { // Load the source from the file. source, err := ioutil.ReadFile(sourceFilePath) if err != nil { return err } // Conduct the parsing. parseTree := newParseTree(source) inputSource := compilercommon.InputSource(sourceFilePath) rootNode := parser.Parse(parseTree.createAstNode, nil, inputSource, string(source)) // Report any errors found. if len(parseTree.errors) > 0 { for _, err := range parseTree.errors { startRune, _ := strconv.Atoi(err.properties[parser.NodePredicateStartRune]) sal := compilercommon.NewSourceAndLocation(inputSource, startRune) location := sal.Location() fmt.Printf("%v: line %v, column %v: %s\n", sourceFilePath, location.LineNumber()+1, location.ColumnPosition()+1, err.properties[parser.NodePredicateErrorMessage]) } return fmt.Errorf("Parsing errors found in file %s", sourceFilePath) } // Create the formatted source. formattedSource := buildFormattedSource(parseTree, rootNode.(formatterNode), importHandling) if string(formattedSource) == string(source) { // Nothing changed. return nil } // Overwrite the file with the formatted source. return ioutil.WriteFile(sourceFilePath, formattedSource, info.Mode()) }
// 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)), } }
// ResolveAccessibleMember looks for an member with the given name under the referred type and returns it (if any). func (tr TypeReference) ResolveAccessibleMember(memberName string, modulePath compilercommon.InputSource, kind MemberResolutionKind) (TGMember, error) { member, found := tr.ResolveMember(memberName, kind) if !found { adjusted := tr.tdg.adjustedName(memberName) _, otherSpellingFound := tr.ResolveMember(adjusted, kind) if otherSpellingFound { return TGMember{}, fmt.Errorf("Could not find %v name '%v' under %v; Did you mean '%v'?", kind.Title(), memberName, tr.TitledString(), adjusted) } return TGMember{}, fmt.Errorf("Could not find %v name '%v' under %v", kind.Title(), memberName, tr.TitledString()) } // If the member is exported, then always return it. Otherwise, only return it if the asking module's package // is the same as the declaring module's package. if !member.IsExported() { memberModulePath := compilercommon.InputSource(member.Node().Get(NodePredicateModulePath)) if !srg.InSamePackage(memberModulePath, modulePath) { return TGMember{}, fmt.Errorf("%v %v is not exported under %v", member.Title(), member.Name(), tr.TitledString()) } } return member, nil }
// scopeSliceChildExpression scopes the child expression of a slice expression, returning whether it // is valid and the associated operator found, if any. func (sb *scopeBuilder) scopeSliceChildExpression(node compilergraph.GraphNode, opName string, context scopeContext) (typegraph.TGMember, typegraph.TypeReference, bool) { // Scope the child expression of the slice. childScope := sb.getScope(node.GetNode(parser.NodeSliceExpressionChildExpr), context) if !childScope.GetIsValid() { return typegraph.TGMember{}, sb.sg.tdg.AnyTypeReference(), false } childType := childScope.ResolvedTypeRef(sb.sg.tdg) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) operator, rerr := childType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator) if rerr != nil { sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, childType) return typegraph.TGMember{}, childType, false } // Ensure that the child expression is not nullable. if childType.NullValueAllowed() { sb.decorateWithError(node, "Operator '%v' cannot be called on nullable type '%v'", opName, childType) return typegraph.TGMember{}, childType, false } return operator, childType, true }
// scopeDynamicMemberAccessExpression scopes a dynamic member access expression in the SRG. func (sb *scopeBuilder) scopeDynamicMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // Get the scope of the child expression. childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context) if !childScope.GetIsValid() { return newScope().Invalid().GetScope() } memberName := node.Get(parser.NodeMemberAccessIdentifier) module := compilercommon.InputSource(node.Get(parser.NodePredicateSource)) scopeMemberAccess := func(childType typegraph.TypeReference, expectStatic bool) proto.ScopeInfo { // If the child type is any, then this operator returns another value of any, regardless of name. if childType.IsAny() { return newScope().Valid().Resolving(sb.sg.tdg.AnyTypeReference()).GetScope() } var lookupType = childType if childType.IsNullable() { lookupType = childType.AsNonNullable() } // Look for the matching type member, either instance or static. If not found, then the access // returns an "any" type. typeMember, rerr := lookupType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstanceOrStatic) if rerr != nil { return newScope().Valid().Resolving(sb.sg.tdg.AnyTypeReference()).GetScope() } // Ensure static isn't accessed under instance and vice versa. if typeMember.IsStatic() != expectStatic { if typeMember.IsStatic() { sb.decorateWithError(node, "Member '%v' is static but accessed under an instance value", typeMember.Name()) } else { sb.decorateWithError(node, "Member '%v' is non-static but accessed under a static value", typeMember.Name()) } return newScope().Invalid().GetScope() } // The resulting type (if matching a named scope) is the named scope, but also nullable (since the operator) // allows for nullable types. memberScope := sb.getNamedScopeForMember(typeMember) context.staticDependencyCollector.checkNamedScopeForDependency(memberScope) if childType.IsNullable() { sb.decorateWithWarning(node, "Dynamic access of known member '%v' under type %v. The ?. operator is suggested.", typeMember.Name(), childType) return newScope().ForNamedScopeUnderModifiedType(memberScope, lookupType, makeNullable, context).GetScope() } else { sb.decorateWithWarning(node, "Dynamic access of known member '%v' under type %v. The . operator is suggested.", typeMember.Name(), childType) return newScope().ForNamedScopeUnderType(memberScope, lookupType, context).GetScope() } } switch childScope.GetKind() { case proto.ScopeKind_VALUE: childType := childScope.ResolvedTypeRef(sb.sg.tdg) return scopeMemberAccess(childType, false) case proto.ScopeKind_STATIC: namedScope, _ := sb.getNamedScopeForScope(childScope) if !namedScope.IsType() { sb.decorateWithError(node, "Cannot attempt dynamic member access of '%v' under %v %v, as it is not a type", memberName, namedScope.Title(), namedScope.Name()) return newScope().Invalid().GetScope() } childType := namedScope.StaticType(context) return scopeMemberAccess(childType, true) case proto.ScopeKind_GENERIC: namedScope, _ := sb.getNamedScopeForScope(childScope) sb.decorateWithError(node, "Cannot attempt dynamic member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name()) return newScope().Invalid().GetScope() default: panic("Unknown scope kind") } return newScope().Invalid().GetScope() }
func TestLookupReturnType(t *testing.T) { graph, err := compilergraph.NewGraph("tests/returntype/returntype.seru") if !assert.Nil(t, err, "Got graph creation error: %v", err) { return } testSRG := srg.NewSRG(graph) testIDL := webidl.NewIRG(graph) loader := packageloader.NewPackageLoader(graph.RootSourceFilePath, []string{}, testSRG.PackageLoaderHandler(), testIDL.PackageLoaderHandler()) srgResult := loader.Load(packageloader.Library{TESTLIB_PATH, false, ""}) if !assert.True(t, srgResult.Status, "Got error for SRG construction: %v", srgResult.Errors) { return } // Construct the type graph. result := typegraph.BuildTypeGraph(testSRG.Graph, GetConstructor(testSRG), webidltc.GetConstructor(testIDL)) if !assert.True(t, result.Status, "Got error for TypeGraph construction: %v", result.Errors) { return } // Ensure that the function and the property getter have return types. module, found := testSRG.FindModuleBySource(compilercommon.InputSource("tests/returntype/returntype.seru")) if !assert.True(t, found, "Could not find source module") { return } resolvedclass, foundClass := module.ResolveTypePath("SomeClass") if !assert.True(t, foundClass, "Could not find SomeClass") { return } someclass := resolvedclass.ResolvedType.AsType() // Check the function. dosomethingFunc, foundFunc := someclass.FindMember("DoSomething") if !assert.True(t, foundFunc, "Could not find DoSomething") { return } dosomethingFuncReturnType, hasReturnType := result.Graph.LookupReturnType(dosomethingFunc.Node()) if !assert.True(t, hasReturnType, "Could not find return type for DoSomething") { return } assert.Equal(t, "Integer", dosomethingFuncReturnType.String(), "Expected int for DoSomething return type, found: %v", dosomethingFuncReturnType) // Check the property getter. someProp, foundProp := someclass.FindMember("SomeProp") if !assert.True(t, foundProp, "Could not find SomeProp") { return } getter, _ := someProp.Getter() somePropReturnType, hasPropReturnType := result.Graph.LookupReturnType(getter.GraphNode) if !assert.True(t, hasPropReturnType, "Could not find return type for SomeProp getter") { return } assert.Equal(t, "SomeClass", somePropReturnType.String(), "Expected SomeClass for SomeProp return type, found: %v", somePropReturnType) }
// salForNode returns a SourceAndLocation for the given graph node. func salForNode(node compilergraph.GraphNode) compilercommon.SourceAndLocation { return compilercommon.NewSourceAndLocation( compilercommon.InputSource(node.Get(parser.NodePredicateSource)), node.GetValue(parser.NodePredicateStartRune).Int()) }