From 16478fd8942fabaa28a6561213f2076140e0f133 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 26 Apr 2023 12:38:41 -0700 Subject: [PATCH] fix classes panic --- ci/release/changelogs/next.md | 1 + d2compiler/compile.go | 18 +- d2compiler/compile_test.go | 14 + d2ir/d2ir.go | 18 ++ .../TestCompile/class-shape-class.exp.json | 286 ++++++++++++++++++ 5 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 testdata/d2compiler/TestCompile/class-shape-class.exp.json diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 69d056047..8451bba92 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -11,3 +11,4 @@ - Fixes an issue with markdown labels that are empty when rendered [#1223](https://github.com/terrastruct/d2/issues/1223) - ELK self loops always have enough space for long labels [#1232](https://github.com/terrastruct/d2/pull/1232) +- Fixes panic when setting `shape` to be `class` or `sql_table` within a class [#1251](https://github.com/terrastruct/d2/pull/1251) diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 4b07a6684..7cec4208a 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -150,15 +150,17 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) { c.compileField(obj, f) } - switch obj.Shape.Value { - case d2target.ShapeClass: - c.compileClass(obj) - case d2target.ShapeSQLTable: - c.compileSQLTable(obj) - } + if !m.IsClass() { + switch obj.Shape.Value { + case d2target.ShapeClass: + c.compileClass(obj) + case d2target.ShapeSQLTable: + c.compileSQLTable(obj) + } - for _, e := range m.Edges { - c.compileEdge(obj, e) + for _, e := range m.Edges { + c.compileEdge(obj, e) + } } } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 0f615566c..628e03e80 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -2397,6 +2397,20 @@ classes.x.shape: diamond tassert.Equal(t, "diamond", g.Objects[0].Shape.Value) }, }, + { + name: "class-shape-class", + text: `classes: { + classClass: { + shape: class + } +} + +object: { + class: classClass + length(): int +} +`, + }, { name: "no-class-primary", text: `x.class diff --git a/d2ir/d2ir.go b/d2ir/d2ir.go index 56a5f7550..26f60c57d 100644 --- a/d2ir/d2ir.go +++ b/d2ir/d2ir.go @@ -1201,3 +1201,21 @@ func (m *Map) InClass(key *d2ast.Key) bool { } return false } + +func (m *Map) IsClass() bool { + parentBoard := ParentBoard(m) + if parentBoard.Map() == nil { + return false + } + classes := parentBoard.Map().GetField("classes") + if classes == nil || classes.Map() == nil { + return false + } + + for _, class := range classes.Map().Fields { + if class.Map() == m { + return true + } + } + return false +} diff --git a/testdata/d2compiler/TestCompile/class-shape-class.exp.json b/testdata/d2compiler/TestCompile/class-shape-class.exp.json new file mode 100644 index 000000000..a787bab08 --- /dev/null +++ b/testdata/d2compiler/TestCompile/class-shape-class.exp.json @@ -0,0 +1,286 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,0:0:0-10:0:99", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,0:0:0-4:1:49", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,0:0:0-0:7:7", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,0:0:0-0:7:7", + "value": [ + { + "string": "classes", + "raw_string": "classes" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,0:9:9-4:0:48", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,1:2:13-3:3:47", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,1:2:13-1:12:23", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,1:2:13-1:12:23", + "value": [ + { + "string": "classClass", + "raw_string": "classClass" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,1:14:25-3:2:46", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,2:4:31-2:16:43", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,2:4:31-2:9:36", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,2:4:31-2:9:36", + "value": [ + { + "string": "shape", + "raw_string": "shape" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,2:11:38-2:16:43", + "value": [ + { + "string": "class", + "raw_string": "class" + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:0:51-9:1:98", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:0:51-6:6:57", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:0:51-6:6:57", + "value": [ + { + "string": "object", + "raw_string": "object" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:8:59-9:0:97", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,7:2:63-7:19:80", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,7:2:63-7:7:68", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,7:2:63-7:7:68", + "value": [ + { + "string": "class", + "raw_string": "class" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,7:9:70-7:19:80", + "value": [ + { + "string": "classClass", + "raw_string": "classClass" + } + ] + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,8:2:83-8:15:96", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,8:2:83-8:10:91", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,8:2:83-8:10:91", + "value": [ + { + "string": "length()", + "raw_string": "length()" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,8:12:93-8:15:96", + "value": [ + { + "string": "int", + "raw_string": "int" + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": { + "value": "" + } + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "object", + "id_val": "object", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:0:51-6:6:57", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/class-shape-class.d2,6:0:51-6:6:57", + "value": [ + { + "string": "object", + "raw_string": "object" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "class": { + "fields": null, + "methods": [ + { + "name": "length()", + "return": "int", + "visibility": "public" + } + ] + }, + "attributes": { + "label": { + "value": "object" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "class" + }, + "direction": { + "value": "" + }, + "constraint": { + "value": "" + }, + "classes": [ + "classClass" + ] + }, + "zIndex": 0 + } + ] + }, + "err": null +}