diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 9c413894ac..cbc406562c 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -13,3 +13,4 @@ - Fixes `d2 fmt` to format all files passed as arguments rather than first non-formatted only [#1523](https://github.com/terrastruct/d2/issues/1523) - Fixes Markdown cropping last element in mixed-element blocks (e.g. em and strong) [#1543](https://github.com/terrastruct/d2/issues/1543) +- Fixes missing compile error for non-blockstring empty labels [#1590](https://github.com/terrastruct/d2/issues/1590) diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 88db84d7e7..9a9337585f 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -82,6 +82,7 @@ func (c *compiler) compileBoard(g *d2graph.Graph, ir *d2ir.Map) *d2graph.Graph { if len(c.err.Errors) == 0 { c.validateKeys(g.Root, ir) } + c.validateLabels(g) c.validateNear(g) c.validateEdges(g) @@ -998,6 +999,22 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) { } } +func (c *compiler) validateLabels(g *d2graph.Graph) { + for _, obj := range g.Objects { + if obj.Shape.Value != d2target.ShapeText { + continue + } + if obj.Attributes.Language != "" { + // blockstrings have already been validated + continue + } + if strings.TrimSpace(obj.Label.Value) == "" { + c.errorf(obj.Label.MapKey, "shape text must have a non-empty label") + continue + } + } +} + func (c *compiler) validateNear(g *d2graph.Graph) { for _, obj := range g.Objects { if obj.NearKey != nil { diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index a8788e95e5..608d92c97f 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -2712,6 +2712,31 @@ object: { `, expErr: `d2/testdata/d2compiler/TestCompile/reserved-composite.d2:1:1: reserved field shape does not accept composite`, }, + { + name: "text_no_label", + text: `a: "ok" { + shape: text +} +b: " \n " { + shape: text +} +c: "" { + shape: text +} +d: "" { + shape: circle +} +e: " \n " +f: |md | +g: |md + +| +`, + expErr: `d2/testdata/d2compiler/TestCompile/text_no_label.d2:14:1: block string cannot be empty +d2/testdata/d2compiler/TestCompile/text_no_label.d2:15:1: block string cannot be empty +d2/testdata/d2compiler/TestCompile/text_no_label.d2:4:1: shape text must have a non-empty label +d2/testdata/d2compiler/TestCompile/text_no_label.d2:7:1: shape text must have a non-empty label`, + }, } for _, tc := range testCases { diff --git a/testdata/d2compiler/TestCompile/text_no_label.exp.json b/testdata/d2compiler/TestCompile/text_no_label.exp.json new file mode 100644 index 0000000000..d0a3956b0c --- /dev/null +++ b/testdata/d2compiler/TestCompile/text_no_label.exp.json @@ -0,0 +1,23 @@ +{ + "graph": null, + "err": { + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/text_no_label.d2,13:0:110-13:9:119", + "errmsg": "d2/testdata/d2compiler/TestCompile/text_no_label.d2:14:1: block string cannot be empty" + }, + { + "range": "d2/testdata/d2compiler/TestCompile/text_no_label.d2,14:0:120-16:1:129", + "errmsg": "d2/testdata/d2compiler/TestCompile/text_no_label.d2:15:1: block string cannot be empty" + }, + { + "range": "d2/testdata/d2compiler/TestCompile/text_no_label.d2,3:0:25-5:1:51", + "errmsg": "d2/testdata/d2compiler/TestCompile/text_no_label.d2:4:1: shape text must have a non-empty label" + }, + { + "range": "d2/testdata/d2compiler/TestCompile/text_no_label.d2,6:0:52-8:1:74", + "errmsg": "d2/testdata/d2compiler/TestCompile/text_no_label.d2:7:1: shape text must have a non-empty label" + } + ] + } +}