Skip to content

Commit

Permalink
Various improvements to the Go language frontend (#893)
Browse files Browse the repository at this point in the history
* Various improvements to the Go language frontend

Mainly crash fixes and some implementation improvements

* Fixes #892
* Fixes #891
* Fixes #890

* Added more Go improvements

* Fixes #894

* Added support for `ast.StarExpr`

Fixes #895
  • Loading branch information
oxisto authored Aug 27, 2022
1 parent fd0d3ec commit 216c32b
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -931,8 +931,7 @@ object NodeBuilder {
lang: LanguageFrontend? = null,
rawNode: Any? = null
): ProblemExpression {
val node = ProblemExpression()
node.problem = problem
val node = ProblemExpression(problem, type)
node.setCodeAndRegion(lang, rawNode, code)
log(node)
return node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ public void setSuperClasses(List<Type> superClasses) {
this.superClasses = superClasses;
}

/**
* Adds a type to the list of super classes for this record declaration.
*
* @param superClass the super class.
*/
public void addSuperClass(Type superClass) {
this.superClasses.add(superClass);
}

/**
* Interfaces implemented by this class. This concept is not present in C++
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ class ProblemExpression(
.toString()
}

override fun equals(o: Any?): Boolean {
if (this === o) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (o !is ProblemExpression) {
if (other !is ProblemExpression) {
return false
}
val that = o
return (super.equals(that) && problem == that.problem)

return (super.equals(other) && problem == other.problem)
}

override fun hashCode(): Int {
Expand Down
6 changes: 6 additions & 0 deletions cpg-language-go/src/main/golang/declarations.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ func (r *RecordDeclaration) AddMethod(m *MethodDeclaration) (err error) {
return
}

func (r *RecordDeclaration) AddSuperClass(t *Type) (err error) {
(*jnigi.ObjectRef)(r).CallMethod(env, "addSuperClass", nil, t)

return
}

func (r *RecordDeclaration) IsNil() bool {
return (*jnigi.ObjectRef)(r).IsNil()
}
Expand Down
22 changes: 22 additions & 0 deletions cpg-language-go/src/main/golang/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (e *Expression) IsArray() bool {
}

type CallExpression Expression
type CastExpression Expression
type NewExpression Expression
type ArrayCreationExpression Expression
type ArraySubscriptionExpression Expression
Expand All @@ -75,6 +76,19 @@ func NewCallExpression(fset *token.FileSet, astNode ast.Node) *CallExpression {
return (*CallExpression)(c)
}

func NewCastExpression(fset *token.FileSet, astNode ast.Node) *CastExpression {
c, err := env.NewObject("de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression")
if err != nil {
log.Fatal(err)

}

updateCode(fset, (*Node)(c), astNode)
updateLocation(fset, (*Node)(c), astNode)

return (*CastExpression)(c)
}

func NewMemberExpression(fset *token.FileSet, astNode ast.Node) *MemberExpression {
c, err := env.NewObject("de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression")
if err != nil {
Expand Down Expand Up @@ -243,6 +257,14 @@ func (c *CallExpression) SetFqn(s string) {
(*jnigi.ObjectRef)(c).SetField(env, "fqn", NewString(s))
}

func (c *CastExpression) SetExpression(e *Expression) {
(*jnigi.ObjectRef)(c).CallMethod(env, "setExpression", nil, (*jnigi.ObjectRef)(e).Cast("de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression"))
}

func (c *CastExpression) SetCastType(t *Type) {
(*jnigi.ObjectRef)(c).CallMethod(env, "setCastType", nil, t)
}

func (c *MemberCallExpression) SetName(s string) {
(*Node)(c).SetName(s)
}
Expand Down
130 changes: 102 additions & 28 deletions cpg-language-go/src/main/golang/frontend/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,25 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as

var recordType = this.handleType(recv.Type)

receiver = cpg.NewVariableDeclaration(fset, nil)

// TODO: should we use the FQN here? FQNs are a mess in the CPG...
receiver.SetName(recv.Names[0].Name)
receiver.SetType(recordType)

err := m.SetReceiver(receiver)
if err != nil {
log.Fatal(err)
// The name of the Go receiver is optional. In fact, if the name is not
// specified we probably do not need any receiver variable at all,
// because the syntax is only there to ensure that this method is part
// of the struct, but it is not modifying the receiver.
if len(recv.Names) > 0 {
receiver = cpg.NewVariableDeclaration(fset, nil)

// TODO: should we use the FQN here? FQNs are a mess in the CPG...
receiver.SetName(recv.Names[0].Name)
receiver.SetType(recordType)

err := m.SetReceiver(receiver)
if err != nil {
log.Fatal(err)
}
}

if recordType != nil {
var recordName = (*cpg.Node)(recordType).GetName()
var recordName = recordType.GetName()

// TODO: this will only find methods within the current translation unit
// this is a limitation that we have for C++ as well
Expand Down Expand Up @@ -275,21 +281,37 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as
}
}

this.LogDebug("Function has return type %s", (*cpg.Node)(t).GetName())
this.LogDebug("Function has return type %s", t.GetName())

f.SetType(t)

// TODO: for other languages, we would enter the record declaration, if
// this is a method; however I am not quite sure if this makes sense for
// go, since we do not have a 'this', but rather a named receiver

this.LogDebug("Parsing function body")

for _, param := range funcDecl.Type.Params.List {
p := cpg.NewParamVariableDeclaration(fset, param)

// TODO: more than one name?
p.SetName(param.Names[0].Name)
this.LogDebug("Parsing param: %+v", param)

// Somehow parameters end up having no name sometimes, have not fully understood why.
if len(param.Names) > 0 {
// TODO: more than one name?
var name = param.Names[0].Name

// If the name is an underscore, it means that the parameter is
// unnamed. In order to avoid confusing and some compatibility with
// other languages, we are just setting the name to an empty string
// in this case.
if name == "_" {
name = ""
}

p.SetName(name)
} else {
this.LogError("Some param has no name, which is a bit weird: %+v", param)
}

p.SetType(this.handleType(param.Type))

// add parameter to scope
Expand All @@ -298,6 +320,8 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as
this.handleComments((*cpg.Node)(p), param)
}

this.LogDebug("Parsing function body of %s", (*cpg.Node)(f).GetName())

// parse body
s := this.handleBlockStmt(fset, funcDecl.Body)

Expand Down Expand Up @@ -430,7 +454,7 @@ func (this *GoLanguageFrontend) handleStructTypeSpec(fset *token.FileSet, typeDe

if field.Names == nil {
// retrieve the root type name
var typeName = (*cpg.Node)(t.GetRoot()).GetName()
var typeName = t.GetRoot().GetName()

this.LogDebug("Handling embedded field of type %s", typeName)

Expand Down Expand Up @@ -465,14 +489,25 @@ func (this *GoLanguageFrontend) handleInterfaceTypeSpec(fset *token.FileSet, typ

if !interfaceType.Incomplete {
for _, method := range interfaceType.Methods.List {
m := cpg.NewMethodDeclaration(fset, method)

t := this.handleType(method.Type)

m.SetType(t)
m.SetName(method.Names[0].Name)
// Even though this list is called "Methods", it contains all kinds
// of things, so we need to proceed with caution. Only if the
// "method" actually has a name, we declare a new method
// declaration.
if len(method.Names) > 0 {
m := cpg.NewMethodDeclaration(fset, method)
m.SetType(t)
m.SetName(method.Names[0].Name)

scope.AddDeclaration((*cpg.Declaration)(m))
scope.AddDeclaration((*cpg.Declaration)(m))
} else {
this.LogDebug("Adding %s as super class of interface %s", t.GetName(), (*cpg.Node)(r).GetName())
// Otherwise, it contains either types or interfaces. For now we
// hope that it only has interfaces. We consider embedded
// interfaces as sort of super types for this interface.
r.AddSuperClass(t)
}
}
}

Expand Down Expand Up @@ -626,6 +661,8 @@ func (this *GoLanguageFrontend) handleExpr(fset *token.FileSet, expr ast.Expr) (
e = (*cpg.Expression)(this.handleBinaryExpr(fset, v))
case *ast.UnaryExpr:
e = (*cpg.Expression)(this.handleUnaryExpr(fset, v))
case *ast.StarExpr:
e = (*cpg.Expression)(this.handleStarExpr(fset, v))
case *ast.SelectorExpr:
e = (*cpg.Expression)(this.handleSelectorExpr(fset, v))
case *ast.KeyValueExpr:
Expand All @@ -636,6 +673,10 @@ func (this *GoLanguageFrontend) handleExpr(fset *token.FileSet, expr ast.Expr) (
e = (*cpg.Expression)(this.handleCompositeLit(fset, v))
case *ast.Ident:
e = (*cpg.Expression)(this.handleIdent(fset, v))
case *ast.TypeAssertExpr:
e = (*cpg.Expression)(this.handleTypeAssertExpr(fset, v))
case *ast.ParenExpr:
e = this.handleExpr(fset, v.X)
default:
this.LogError("Could not parse expression of type %T: %+v", v, v)
// TODO: return an error instead?
Expand Down Expand Up @@ -991,6 +1032,23 @@ func (this *GoLanguageFrontend) handleUnaryExpr(fset *token.FileSet, unaryExpr *
return u
}

func (this *GoLanguageFrontend) handleStarExpr(fset *token.FileSet, unaryExpr *ast.StarExpr) *cpg.UnaryOperator {
u := cpg.NewUnaryOperator(fset, unaryExpr)

input := this.handleExpr(fset, unaryExpr.X)

err := u.SetOperatorCode("*")
if err != nil {
log.Fatal(err)
}

if input != nil {
u.SetInput(input)
}

return u
}

func (this *GoLanguageFrontend) handleSelectorExpr(fset *token.FileSet, selectorExpr *ast.SelectorExpr) *cpg.DeclaredReferenceExpression {
base := this.handleExpr(fset, selectorExpr.X)

Expand Down Expand Up @@ -1102,7 +1160,7 @@ func (this *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast
var typ = this.handleType(lit.Type)

if typ != nil {
(*cpg.Node)(c).SetName((*cpg.Node)(typ).GetName())
(*cpg.Node)(c).SetName(typ.GetName())
(*cpg.Expression)(c).SetType(typ)
}

Expand Down Expand Up @@ -1144,6 +1202,21 @@ func (this *GoLanguageFrontend) handleIdent(fset *token.FileSet, ident *ast.Iden
return ref
}

func (this *GoLanguageFrontend) handleTypeAssertExpr(fset *token.FileSet, assert *ast.TypeAssertExpr) *cpg.CastExpression {
cast := cpg.NewCastExpression(fset, assert)

// Parse the inner expression
expr := this.handleExpr(fset, assert.X)

// Parse the type
typ := this.handleType(assert.Type)

cast.SetExpression(expr)
cast.SetCastType(typ)

return cast
}

func (this *GoLanguageFrontend) handleType(typeExpr ast.Expr) *cpg.Type {
this.LogDebug("Parsing type %T: %+v", typeExpr, typeExpr)

Expand All @@ -1168,7 +1241,7 @@ func (this *GoLanguageFrontend) handleType(typeExpr ast.Expr) *cpg.Type {
log.Fatal(err)
}

this.LogDebug("Pointer to %s", (*cpg.Node)(t).GetName())
this.LogDebug("Pointer to %s", t.GetName())

return t.Reference(i)
case *ast.ArrayType:
Expand All @@ -1180,7 +1253,7 @@ func (this *GoLanguageFrontend) handleType(typeExpr ast.Expr) *cpg.Type {
log.Fatal(err)
}

this.LogDebug("Array of %s", (*cpg.Node)(t).GetName())
this.LogDebug("Array of %s", t.GetName())

return t.Reference(i)
case *ast.MapType:
Expand All @@ -1190,16 +1263,17 @@ func (this *GoLanguageFrontend) handleType(typeExpr ast.Expr) *cpg.Type {
keyType := this.handleType(v.Key)
valueType := this.handleType(v.Value)

(*cpg.ObjectType)(t).AddGeneric(keyType)
(*cpg.ObjectType)(t).AddGeneric(valueType)
// TODO(oxisto): Find a better way to represent casts
(&(cpg.ObjectType{Type: *t})).AddGeneric(keyType)
(&(cpg.ObjectType{Type: *t})).AddGeneric(valueType)

return t
case *ast.ChanType:
// handle them similar to maps
t := cpg.TypeParser_createFrom("chan", false)
chanType := this.handleType(v.Value)

(*cpg.ObjectType)(t).AddGeneric(chanType)
(&(cpg.ObjectType{Type: *t})).AddGeneric(chanType)

return t
case *ast.FuncType:
Expand All @@ -1211,7 +1285,7 @@ func (this *GoLanguageFrontend) handleType(typeExpr ast.Expr) *cpg.Type {
}
}

return cpg.UnknownType_getUnknown()
return &cpg.UnknownType_getUnknown().Type
}

func (this *GoLanguageFrontend) isBuiltinType(s string) bool {
Expand Down
Loading

0 comments on commit 216c32b

Please sign in to comment.