From fbf48fa0ff0599bb7b8a09e55b11cb3a8345fcce Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 28 Dec 2022 20:47:40 -0800 Subject: [PATCH] compile error for circle/square shapes with different width and height --- d2compiler/compile.go | 14 +- d2compiler/compile_test.go | 56 ++++++ e2etests/todo_test.go | 2 - .../equal_dimensions_on_circle.exp.json | 16 ++ .../single_dimension_on_circle.exp.json | 178 ++++++++++++++++++ 5 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 testdata/d2compiler/TestCompile/equal_dimensions_on_circle.exp.json create mode 100644 testdata/d2compiler/TestCompile/single_dimension_on_circle.exp.json diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 5429bdea4..7737950e1 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -797,8 +797,18 @@ func (c *compiler) validateKey(obj *d2graph.Object, m *d2ast.Map, mk *d2ast.Key) return } - if reserved == "" && obj.Attributes.Shape.Value == d2target.ShapeImage { - c.errorf(mk.Range.Start, mk.Range.End, "image shapes cannot have children.") + switch strings.ToLower(obj.Attributes.Shape.Value) { + case d2target.ShapeImage: + if reserved == "" { + c.errorf(mk.Range.Start, mk.Range.End, "image shapes cannot have children.") + } + case d2target.ShapeCircle, d2target.ShapeSquare: + checkEqual := (reserved == "width" && obj.Attributes.Height != nil) || + (reserved == "height" && obj.Attributes.Width != nil) + + if checkEqual && obj.Attributes.Width.Value != obj.Attributes.Height.Value { + c.errorf(mk.Range.Start, mk.Range.End, fmt.Sprintf("width and height must be equal for %s shapes", obj.Attributes.Shape.Value)) + } } in := d2target.IsShape(obj.Attributes.Shape.Value) diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 207211e1d..f0f729be7 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -95,6 +95,62 @@ x: { height: 230 } `, + assertions: func(t *testing.T, g *d2graph.Graph) { + if len(g.Objects) != 1 { + t.Fatalf("expected 1 objects: %#v", g.Objects) + } + if g.Objects[0].ID != "hey" { + t.Fatalf("expected g.Objects[0].ID to be 'hey': %#v", g.Objects[0]) + } + if g.Objects[0].Attributes.Shape.Value != d2target.ShapeHexagon { + t.Fatalf("expected g.Objects[0].Attributes.Shape.Value to be hexagon: %#v", g.Objects[0].Attributes.Shape.Value) + } + if g.Objects[0].Attributes.Width.Value != "200" { + t.Fatalf("expected g.Objects[0].Attributes.Width.Value to be 200: %#v", g.Objects[0].Attributes.Width.Value) + } + if g.Objects[0].Attributes.Height.Value != "230" { + t.Fatalf("expected g.Objects[0].Attributes.Height.Value to be 230: %#v", g.Objects[0].Attributes.Height.Value) + } + }, + }, + { + name: "equal_dimensions_on_circle", + + text: `hey: "" { + shape: circle + width: 200 + height: 230 +} +`, + expErr: `d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2:3:2: width and height must be equal for circle shapes +d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2:4:2: width and height must be equal for circle shapes +`, + }, + { + name: "single_dimension_on_circle", + + text: `hey: "" { + shape: circle + height: 230 +} +`, + assertions: func(t *testing.T, g *d2graph.Graph) { + if len(g.Objects) != 1 { + t.Fatalf("expected 1 objects: %#v", g.Objects) + } + if g.Objects[0].ID != "hey" { + t.Fatalf("expected ID to be 'hey': %#v", g.Objects[0]) + } + if g.Objects[0].Attributes.Shape.Value != d2target.ShapeCircle { + t.Fatalf("expected Attributes.Shape.Value to be circle: %#v", g.Objects[0].Attributes.Shape.Value) + } + if g.Objects[0].Attributes.Width != nil { + t.Fatalf("expected Attributes.Width to be nil: %#v", g.Objects[0].Attributes.Width) + } + if g.Objects[0].Attributes.Height == nil { + t.Fatalf("Attributes.Height is nil") + } + }, }, { name: "basic_icon", diff --git a/e2etests/todo_test.go b/e2etests/todo_test.go index 925d791c8..3397dcb60 100644 --- a/e2etests/todo_test.go +++ b/e2etests/todo_test.go @@ -98,7 +98,6 @@ containers: { circle container: { shape: circle width: 512 - height: 256 diamond: { shape: diamond @@ -114,7 +113,6 @@ containers: { circle: { shape: circle width: 128 - height: 64 } } oval container: { diff --git a/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.exp.json b/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.exp.json new file mode 100644 index 000000000..dd0a210f2 --- /dev/null +++ b/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.exp.json @@ -0,0 +1,16 @@ +{ + "graph": null, + "err": { + "ioerr": null, + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2,2:1:26-2:11:36", + "errmsg": "d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2:3:2: width and height must be equal for circle shapes" + }, + { + "range": "d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2,3:1:38-3:12:49", + "errmsg": "d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2:4:2: width and height must be equal for circle shapes" + } + ] + } +} diff --git a/testdata/d2compiler/TestCompile/single_dimension_on_circle.exp.json b/testdata/d2compiler/TestCompile/single_dimension_on_circle.exp.json new file mode 100644 index 000000000..23a5ec0d3 --- /dev/null +++ b/testdata/d2compiler/TestCompile/single_dimension_on_circle.exp.json @@ -0,0 +1,178 @@ +{ + "graph": { + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-4:0:40", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-3:1:39", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-0:3:3", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-0:3:3", + "value": [ + { + "string": "hey", + "raw_string": "hey" + } + ] + } + } + ] + }, + "primary": { + "double_quoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:5:5-0:7:7", + "value": null + } + }, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:8:8-3:0:38", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,1:1:11-1:14:24", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,1:1:11-1:6:16", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,1:1:11-1:6:16", + "value": [ + { + "string": "shape", + "raw_string": "shape" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,1:8:18-1:14:24", + "value": [ + { + "string": "circle", + "raw_string": "circle" + } + ] + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,2:1:26-2:12:37", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,2:1:26-2:7:32", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,2:1:26-2:7:32", + "value": [ + { + "string": "height", + "raw_string": "height" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "number": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,2:9:34-2:12:37", + "raw": "230", + "value": "230" + } + } + } + } + ] + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "label_dimensions": { + "width": 0, + "height": 0 + }, + "attributes": { + "label": { + "value": "" + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + } + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "hey", + "id_val": "hey", + "label_dimensions": { + "width": 0, + "height": 0 + }, + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-0:3:3", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/single_dimension_on_circle.d2,0:0:0-0:3:3", + "value": [ + { + "string": "hey", + "raw_string": "hey" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "" + }, + "style": {}, + "height": { + "value": "230" + }, + "near_key": null, + "shape": { + "value": "circle" + }, + "direction": { + "value": "" + } + }, + "zIndex": 0 + } + ] + }, + "err": null +}