diff --git a/cmd/sysml/convert_branch_test.go b/cmd/sysml/convert_branch_test.go index d909ef4f8..ea020891a 100644 --- a/cmd/sysml/convert_branch_test.go +++ b/cmd/sysml/convert_branch_test.go @@ -37,17 +37,17 @@ func TestConvertReadsABranchAsNotationAndTurtle(t *testing.T) { func TestConvertReadsABranchIntoAFileAndRecordsTheHead(t *testing.T) { binary := buildCLI(t) stack := newFakeStack(t, liveGraph(t, syncedModel)) - out_path := filepath.Join(t.TempDir(), "out.sysml") + outPath := filepath.Join(t.TempDir(), "out.sysml") - out, code := exitCode(t, branchCommand(stack, binary, stack.server.URL+"/projects/proj-1/branches/main", "-convert", "sysml", "-o", out_path)) + out, code := exitCode(t, branchCommand(stack, binary, stack.server.URL+"/projects/proj-1/branches/main", "-convert", "sysml", "-o", outPath)) if code != 0 { t.Fatalf("read a branch to a file: exit %d:\n%s", code, out) } - written, err := os.ReadFile(out_path) + written, err := os.ReadFile(outPath) if err != nil || !strings.Contains(string(written), "part def Vehicle") { t.Fatalf("the output file: %v\n%s", err, written) } - state, err := os.ReadFile(out_path + ".sync.json") + state, err := os.ReadFile(outPath + ".sync.json") if err != nil || !strings.Contains(string(state), `"lastSeenCommit": "commit-0"`) || !strings.Contains(string(state), `"projectId": "proj-1"`) { t.Fatalf("sync state after a branch read: %v\n%s", err, state) } @@ -250,13 +250,13 @@ func TestConvertReadRefusesOutputOverTheSyncState(t *testing.T) { binary := buildCLI(t) stack := newFakeStack(t, liveGraph(t, syncedModel)) dir := t.TempDir() - out_path := writeModel(t, dir, "saved.sysml", "sentinel") + outPath := writeModel(t, dir, "saved.sysml", "sentinel") - out, code := exitCode(t, branchCommand(stack, binary, "flexo://proj-1/main", "-convert", "sysml", "-o", out_path, "-sync-state", out_path)) + out, code := exitCode(t, branchCommand(stack, binary, "flexo://proj-1/main", "-convert", "sysml", "-o", outPath, "-sync-state", outPath)) if code != 2 || !strings.Contains(out, "-o and -sync-state both name") { t.Fatalf("-o over the sync state: exit %d:\n%s", code, out) } - if content, _ := os.ReadFile(out_path); string(content) != "sentinel" { + if content, _ := os.ReadFile(outPath); string(content) != "sentinel" { t.Errorf("the refused read still replaced the file") } } @@ -266,16 +266,16 @@ func TestConvertReadRefusesAStatePinnedElsewhereBeforeWriting(t *testing.T) { stack := newFakeStack(t, liveGraph(t, syncedModel)) dir := t.TempDir() state := writeModel(t, dir, "s.sync.json", `{"org":"other","projectId":"proj-1","branch":"main"}`) - out_path := filepath.Join(dir, "out.sysml") + outPath := filepath.Join(dir, "out.sysml") - cmd := branchCommand(stack, binary, "flexo://proj-1/main", "-convert", "sysml", "-o", out_path, "-sync-state", state) + cmd := branchCommand(stack, binary, "flexo://proj-1/main", "-convert", "sysml", "-o", outPath, "-sync-state", state) cmd.Env = append(cmd.Env, flexo.EnvOrg+"=acme") out, code := exitCode(t, cmd) if code != 2 || !strings.Contains(out, "org other") { t.Fatalf("a state pinned to another org: exit %d:\n%s", code, out) } - if _, err := os.Stat(out_path); !os.IsNotExist(err) { - t.Errorf("the refused read still wrote %s", out_path) + if _, err := os.Stat(outPath); !os.IsNotExist(err) { + t.Errorf("the refused read still wrote %s", outPath) } } diff --git a/cmd/sysml/render_test.go b/cmd/sysml/render_test.go index 0259d74d9..581719971 100644 --- a/cmd/sysml/render_test.go +++ b/cmd/sysml/render_test.go @@ -683,7 +683,7 @@ func TestRenderFilenameFitsAPathComponent(t *testing.T) { if got != renderFilename(long+"first", view.FormDot, false) { t.Errorf("renderFilename(long) is not deterministic") } - if other := renderFilename(long+"second", view.FormDot, false); other == got { + if renderFilename(long+"second", view.FormDot, false) == got { t.Errorf("two long names that differ only past the cut share %q", got) } a := strings.Repeat("a", 230) diff --git a/cmd/sysml/usage.go b/cmd/sysml/usage.go index 8551626cc..81484d3a9 100644 --- a/cmd/sysml/usage.go +++ b/cmd/sysml/usage.go @@ -10,6 +10,7 @@ import ( // The help-text placeholders and check flag names the help repeats. const ( + callArg = "" nameArg = "" fileArg = "" featureArg = "" @@ -658,9 +659,9 @@ func optionGroups() []usage.OptionGroup { usage.Opt("constraint", nameArg), usage.Opt("requirement", nameArg), usage.Opt("satisfy", "[=]"), - usage.Opt("calc", ""), - usage.Opt("analysis", ""), - usage.Opt("record-run", ""), + usage.Opt("calc", callArg), + usage.Opt("analysis", callArg), + usage.Opt("record-run", callArg), usage.Opt("record-into", ""), usage.Opt("run-query", ""), usage.Opt("instantiate", nameArg), diff --git a/editors/cameo/tools/src/test/java/org/openmbee/opensysml/cameo/tools/BinaryStagerTest.java b/editors/cameo/tools/src/test/java/org/openmbee/opensysml/cameo/tools/BinaryStagerTest.java index c7c39dd57..7bd3f34b2 100644 --- a/editors/cameo/tools/src/test/java/org/openmbee/opensysml/cameo/tools/BinaryStagerTest.java +++ b/editors/cameo/tools/src/test/java/org/openmbee/opensysml/cameo/tools/BinaryStagerTest.java @@ -1,7 +1,7 @@ package org.openmbee.opensysml.cameo.tools; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import com.sun.net.httpserver.HttpServer; import java.io.IOException; @@ -28,7 +28,7 @@ void stagesAllAssetsWithGoodDigests() throws IOException, InterruptedException { Files.writeString(pin, json); Map written = BinaryStager.stage("v1.2.3", Files.createTempDirectory("staged"), base(server), pin); - assertTrue(written.size() == 5); + assertEquals(5, written.size()); } finally { server.stop(0); } @@ -42,17 +42,19 @@ void rejectsTamperedAsset() throws IOException, InterruptedException { try { Path pin = Files.createTempFile("digests", ".json"); Files.writeString(pin, json); - assertThrows(Exception.class, () -> BinaryStager.stage("v1.2.3", Files.createTempDirectory("staged"), base(server), pin)); + Path staged = Files.createTempDirectory("staged"); + assertThrows(Exception.class, () -> BinaryStager.stage("v1.2.3", staged, base(server), pin)); } finally { server.stop(0); } } @Test - void rejectsUnpinnedVersion() throws IOException, InterruptedException { + void rejectsUnpinnedVersion() throws IOException { Path pin = Files.createTempFile("digests", ".json"); Files.writeString(pin, "{\"v1.2.3\":{}}"); - assertThrows(IllegalArgumentException.class, () -> BinaryStager.stage("v9.9.9", Files.createTempDirectory("staged"), "http://127.0.0.1", pin)); + Path staged = Files.createTempDirectory("staged"); + assertThrows(IllegalArgumentException.class, () -> BinaryStager.stage("v9.9.9", staged, "http://127.0.0.1", pin)); } private static HttpServer server(byte[] payload) throws IOException { diff --git a/editors/vscode/src/webview/autolayout.ts b/editors/vscode/src/webview/autolayout.ts index 6a13b6ca2..880a2ea84 100644 --- a/editors/vscode/src/webview/autolayout.ts +++ b/editors/vscode/src/webview/autolayout.ts @@ -203,7 +203,7 @@ function reconcile( if (node.height !== undefined) { geometry.height = node.height; } - for (const [index, points] of [...routes]) { + for (const [index, points] of routes) { const edge = result.edges![index]; const from = inside.has(edge.from); const to = inside.has(edge.to); @@ -242,7 +242,7 @@ function reconcile( geometry.width = right - left; geometry.height = bottom - top; // The box moved, so routes anchored on its old border are dropped; they are drawn straight. - for (const [index] of [...routes]) { + for (const [index] of routes) { const edge = result.edges![index]; if (edge.from === node.id || edge.to === node.id) { routes.delete(index); diff --git a/internal/exec/analysis/record/record.go b/internal/exec/analysis/record/record.go index e5ac813f1..2c0eafb34 100644 --- a/internal/exec/analysis/record/record.go +++ b/internal/exec/analysis/record/record.go @@ -197,6 +197,14 @@ const ( kindQuantity ) +const ( + scalarValuesString = "ScalarValues::String" + scalarValuesReal = "ScalarValues::Real" + scalarValuesInteger = "ScalarValues::Integer" + scalarValuesBoolean = "ScalarValues::Boolean" + scalarValuesScalarValue = "ScalarValues::ScalarValue" +) + // shape is how a value is recorded: its feature kind, the literal spelling, // and — for a quantity — the unit text its companion feature records. type shape struct { @@ -226,12 +234,12 @@ func classify(v runtime.Value, r *Run) shape { } // A constant without a literal spelling, Infinity included, is // recorded as a string of its text. - return shape{kind: kindString, typ: "ScalarValues::String", literal: source.StringText(semantics.FormatConst(v.Const))} + return shape{kind: kindString, typ: scalarValuesString, literal: source.StringText(semantics.FormatConst(v.Const))} case runtime.ValString: - return shape{kind: kindString, typ: "ScalarValues::String", literal: source.StringText(v.Str())} + return shape{kind: kindString, typ: scalarValuesString, literal: source.StringText(v.Str())} case runtime.ValQuantity: q := v.Quantity() - return shape{kind: kindQuantity, typ: "ScalarValues::Real", literal: semantics.FormatConst(q.Num), unit: q.Unit.String()} + return shape{kind: kindQuantity, typ: scalarValuesReal, literal: semantics.FormatConst(q.Num), unit: q.Unit.String()} case runtime.ValInstance, runtime.ValVariant: if r.Spell.ObjectUsage != nil { if usage := r.Spell.ObjectUsage(v); usage != "" { @@ -241,7 +249,7 @@ func classify(v runtime.Value, r *Run) shape { } // Everything else — a structured value, or an object naming no usage — is // recorded by its text. - return shape{kind: kindString, typ: "ScalarValues::String", literal: source.StringText(spellText(v, r))} + return shape{kind: kindString, typ: scalarValuesString, literal: source.StringText(spellText(v, r))} } // constScalar is the feature kind and ScalarValues type a scalar literal's @@ -250,11 +258,11 @@ func classify(v runtime.Value, r *Run) shape { func constScalar(k semantics.ValueKind) (valueKind, string, bool) { switch k { case semantics.ValInt: - return kindInteger, "ScalarValues::Integer", true + return kindInteger, scalarValuesInteger, true case semantics.ValReal: - return kindReal, "ScalarValues::Real", true + return kindReal, scalarValuesReal, true case semantics.ValBool: - return kindBoolean, "ScalarValues::Boolean", true + return kindBoolean, scalarValuesBoolean, true } return 0, "", false } @@ -388,7 +396,7 @@ func buildFeatures(req *Request) ([]feature, error) { // definition: either way the member settles to Real, an Integer // literal remaining valid under it. if numericPair(cur.typ, sh.typ) { - cur = shape{kind: kindReal, typ: "ScalarValues::Real"} + cur = shape{kind: kindReal, typ: scalarValuesReal} shapes[m.name] = cur continue } @@ -410,8 +418,8 @@ func buildFeatures(req *Request) ([]feature, error) { case c.kind == kindQuantity && (o.kind == kindInteger || o.kind == kindReal): shapes[owner] = c case numericPair(o.typ, c.typ): - shapes[owner] = shape{kind: kindReal, typ: "ScalarValues::Real"} - shapes[companion] = shape{kind: kindReal, typ: "ScalarValues::Real"} + shapes[owner] = shape{kind: kindReal, typ: scalarValuesReal} + shapes[companion] = shape{kind: kindReal, typ: scalarValuesReal} default: f := feature{name: owner} applyShape(&f, o) @@ -437,7 +445,7 @@ func buildFeatures(req *Request) ([]feature, error) { applyShape(&f, shapes[name]) feats = append(feats, f) if shapes[name].kind == kindQuantity { - feats = append(feats, feature{name: name + "Unit", typ: "ScalarValues::String", unitOf: name}) + feats = append(feats, feature{name: name + "Unit", typ: scalarValuesString, unitOf: name}) } } return feats, nil @@ -446,8 +454,8 @@ func buildFeatures(req *Request) ([]feature, error) { // numericPair reports whether the types are Integer and Real in either order: // one numeric family for the record definition, settling to Real. func numericPair(a, b string) bool { - return (a == "ScalarValues::Integer" && b == "ScalarValues::Real") || - (a == "ScalarValues::Real" && b == "ScalarValues::Integer") + return (a == scalarValuesInteger && b == scalarValuesReal) || + (a == scalarValuesReal && b == scalarValuesInteger) } // applyShape gives a feature the declared shape a value's first supply asks for. @@ -455,7 +463,7 @@ func applyShape(f *feature, sh shape) { f.ref = sh.kind == kindRef switch sh.kind { case kindUnset: - f.typ = "ScalarValues::ScalarValue" + f.typ = scalarValuesScalarValue case kindRef: f.typ = "" default: @@ -474,7 +482,7 @@ func compatible(f *feature, sh shape) error { if f.ref || sh.kind == kindRef { return nil } - if f.typ == "ScalarValues::ScalarValue" { + if f.typ == scalarValuesScalarValue { // The first supply was unset; a settled value gives the member its type. f.typ = source.QualifiedNameText(sh.typ) return nil @@ -596,11 +604,11 @@ func checkExisting(req *Request, feats []feature, defName string) error { return fmt.Errorf("record definition %s declares %s as %s but the run values need %s; record into another package with `into`", def, f.name, kind, want) } if !f.ref && f.typ != "" && decl.TypeFQN != "" && decl.TypeFQN != f.typ && - f.typ != "ScalarValues::ScalarValue" && decl.TypeFQN != "ScalarValues::ScalarValue" { + f.typ != scalarValuesScalarValue && decl.TypeFQN != scalarValuesScalarValue { // An Integer literal is valid under a declared Real; the // reverse would widen a definition the model owns, so it stays // refused. - if decl.TypeFQN == "ScalarValues::Real" && f.typ == "ScalarValues::Integer" { + if decl.TypeFQN == scalarValuesReal && f.typ == scalarValuesInteger { continue } return fmt.Errorf("record definition %s declares %s : %s but the run values need %s : %s; record into another package with `into`", def, f.name, decl.TypeFQN, f.name, f.typ) diff --git a/internal/exec/analysis/record/record_test.go b/internal/exec/analysis/record/record_test.go index 683792cf0..cfd93631c 100644 --- a/internal/exec/analysis/record/record_test.go +++ b/internal/exec/analysis/record/record_test.go @@ -20,7 +20,7 @@ import ( var update = flag.Bool("update", false, "rewrite the golden files from the current generator") -func real(f float64) runtime.Value { +func realValue(f float64) runtime.Value { return runtime.Value{Kind: runtime.ValConst, Const: semantics.Value{Kind: semantics.ValReal, Real: f}} } @@ -108,7 +108,7 @@ func TestGenerateSingleRun(t *testing.T) { Spell: sp, Subject: Subject{Usage: "P::scout", Text: "P::scout"}, Inputs: []runtime.InputBinding{ - {Name: "burnTime", Value: real(3.0)}, + {Name: "burnTime", Value: realValue(3.0)}, {Name: "trials", Value: integer(8)}, {Name: "crewOk", Value: boolean(true)}, {Name: "label", Value: runtime.NewStringValue(`a "b"`)}, @@ -120,7 +120,7 @@ func TestGenerateSingleRun(t *testing.T) { {Name: "target", Value: obj}, }, Outputs: []runtime.CalcOutputValue{ - {Name: "fuelUsed", Value: real(12.5)}, + {Name: "fuelUsed", Value: realValue(12.5)}, {Name: "memo", Value: runtime.Value{Kind: runtime.ValNull}}, {Name: "plan", Value: runtime.NewSequenceValue(nil)}, }, @@ -146,11 +146,11 @@ func TestGenerateTradeRun(t *testing.T) { Runs: []Run{{ Spell: spell(), Subject: Subject{Text: "P::fleet"}, - Outputs: []runtime.CalcOutputValue{{Name: "best", Value: real(1.0)}}, + Outputs: []runtime.CalcOutputValue{{Name: "best", Value: realValue(1.0)}}, Evaluations: []runtime.AnalysisEvaluation{ - {Function: "P::score", Arguments: []runtime.Value{real(1.0)}, Result: real(0.75), Selected: true}, - {Function: "P::score", Arguments: []runtime.Value{real(2.0)}, Result: real(0.75), Tied: true}, - {Function: "P::score", Arguments: []runtime.Value{real(3.0)}, Result: runtime.Value{Kind: runtime.ValNull}, Error: failed}, + {Function: "P::score", Arguments: []runtime.Value{realValue(1.0)}, Result: realValue(0.75), Selected: true}, + {Function: "P::score", Arguments: []runtime.Value{realValue(2.0)}, Result: realValue(0.75), Tied: true}, + {Function: "P::score", Arguments: []runtime.Value{realValue(3.0)}, Result: runtime.Value{Kind: runtime.ValNull}, Error: failed}, }, }}, }) @@ -171,9 +171,9 @@ func TestGenerateSweepRuns(t *testing.T) { }, }, Runs: []Run{ - {Iteration: 1, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: real(1.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}}, - {Iteration: 2, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: real(3.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(false)}}}, - {Iteration: 3, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: real(5.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}}, + {Iteration: 1, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: realValue(1.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}}, + {Iteration: 2, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: realValue(3.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(false)}}}, + {Iteration: 3, Spell: spell(), Inputs: []runtime.InputBinding{{Name: "load", Value: realValue(5.0)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}}, }, }) want := []string{"Records::check_run4", "Records::check_run5", "Records::check_run6"} @@ -192,19 +192,19 @@ func TestGenerateErrors(t *testing.T) { t.Error("no runs: want an error") } req := base() - req.Runs = []Run{{Spell: spell(), Inputs: []runtime.InputBinding{{Name: "x", Value: real(1)}}}} + req.Runs = []Run{{Spell: spell(), Inputs: []runtime.InputBinding{{Name: "x", Value: realValue(1)}}}} if _, err := Generate(req); err == nil { t.Error("a run with no outputs, verdicts or evaluations: want an error") } req = base() - req.Runs = []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "kind", Value: real(1)}}}} + req.Runs = []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "kind", Value: realValue(1)}}}} if _, err := Generate(req); err == nil { t.Error("member colliding with an AnalysisRun feature: want an error") } req = base() req.Runs = []Run{{ Spell: spell(), - Outputs: []runtime.CalcOutputValue{{Name: "x", Value: real(2)}, {Name: "x", Value: real(3)}}, + Outputs: []runtime.CalcOutputValue{{Name: "x", Value: realValue(2)}, {Name: "x", Value: realValue(3)}}, }} if _, err := Generate(req); err == nil { t.Error("one side of the run listing a name twice: want an error") @@ -212,8 +212,8 @@ func TestGenerateErrors(t *testing.T) { req = base() req.Runs = []Run{{ Spell: spell(), - Inputs: []runtime.InputBinding{{Name: "x", Value: real(1)}, {Name: "xIn", Value: real(0)}}, - Outputs: []runtime.CalcOutputValue{{Name: "x", Value: real(2)}}, + Inputs: []runtime.InputBinding{{Name: "x", Value: realValue(1)}, {Name: "xIn", Value: realValue(0)}}, + Outputs: []runtime.CalcOutputValue{{Name: "x", Value: realValue(2)}}, }} if _, err := Generate(req); err == nil { t.Error("member colliding with an inout's in companion: want an error") @@ -224,7 +224,7 @@ func TestGenerateErrors(t *testing.T) { }} req.Runs = []Run{{ Spell: spell(), - Inputs: []runtime.InputBinding{{Name: "load", Value: real(1)}}, + Inputs: []runtime.InputBinding{{Name: "load", Value: realValue(1)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}, }} if _, err := Generate(req); err == nil { @@ -237,7 +237,7 @@ func TestGenerateErrors(t *testing.T) { }} req.Runs = []Run{{ Spell: spell(), - Inputs: []runtime.InputBinding{{Name: "load", Value: real(1)}}, + Inputs: []runtime.InputBinding{{Name: "load", Value: realValue(1)}}, Outputs: []runtime.CalcOutputValue{{Name: "done", Value: boolean(true)}}, }} if _, err := Generate(req); err == nil { @@ -250,14 +250,14 @@ func TestGenerateErrors(t *testing.T) { req = base() req.Runs = []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{ {Name: "dose", Value: dose}, - {Name: "doseUnit", Value: real(1)}, + {Name: "doseUnit", Value: realValue(1)}, }}} if _, err := Generate(req); err == nil { t.Error("a member named as a quantity's unit companion: want an error") } req = base() req.Runs = []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{ - {Name: "doseUnit", Value: real(1)}, + {Name: "doseUnit", Value: realValue(1)}, {Name: "dose", Value: dose}, }}} if _, err := Generate(req); err == nil { @@ -273,15 +273,15 @@ func TestGenerateSettlesAnUnsetMember(t *testing.T) { Runs: []Run{ {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{ {Name: "x", Value: runtime.Value{Kind: runtime.ValNull}}, - {Name: "a", Value: real(1)}, - {Name: "b", Value: real(2)}, - {Name: "c", Value: real(3)}, + {Name: "a", Value: realValue(1)}, + {Name: "b", Value: realValue(2)}, + {Name: "c", Value: realValue(3)}, }}, {Iteration: 2, Spell: spell(), Outputs: []runtime.CalcOutputValue{ - {Name: "x", Value: real(3.0)}, - {Name: "a", Value: real(1)}, - {Name: "b", Value: real(2)}, - {Name: "c", Value: real(3)}, + {Name: "x", Value: realValue(3.0)}, + {Name: "a", Value: realValue(1)}, + {Name: "b", Value: realValue(2)}, + {Name: "c", Value: realValue(3)}, }}, }, }) @@ -341,16 +341,16 @@ func TestGenerateSettlesAnUnsetMemberToQuantity(t *testing.T) { Runs: []Run{ {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{ {Name: "x", Value: runtime.Value{Kind: runtime.ValNull}}, - {Name: "a", Value: real(1)}, - {Name: "b", Value: real(2)}, + {Name: "a", Value: realValue(1)}, + {Name: "b", Value: realValue(2)}, }}, {Iteration: 2, Spell: spell(), Outputs: []runtime.CalcOutputValue{ {Name: "x", Value: runtime.NewQuantityValue(&runtime.Quantity{ Num: semantics.Value{Kind: semantics.ValReal, Real: 2.0}, Unit: semantics.Unit{Text: "kg"}, })}, - {Name: "a", Value: real(1)}, - {Name: "b", Value: real(2)}, + {Name: "a", Value: realValue(1)}, + {Name: "b", Value: realValue(2)}, }}, }, }) @@ -378,7 +378,7 @@ func TestGenerateSettlesARealMemberToQuantity(t *testing.T) { res, err := Generate(Request{ Package: "Records", Case: "P::check", Provenance: provenance(KindSweep), Runs: []Run{ - {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "x", Value: real(1)}}}, + {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "x", Value: realValue(1)}}}, {Iteration: 2, Spell: spell(), Outputs: []runtime.CalcOutputValue{ {Name: "x", Value: runtime.NewQuantityValue(&runtime.Quantity{ Num: semantics.Value{Kind: semantics.ValReal, Real: 2.0}, @@ -417,8 +417,8 @@ func TestGenerateNumbersIntoTheGaps(t *testing.T) { Attributes: map[string]Feature{"load": {TypeFQN: "ScalarValues::Real"}}, }, Runs: []Run{ - {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "load", Value: real(1)}}}, - {Iteration: 2, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "load", Value: real(2)}}}, + {Iteration: 1, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "load", Value: realValue(1)}}}, + {Iteration: 2, Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "load", Value: realValue(2)}}}, }, }) if err != nil { @@ -436,7 +436,7 @@ func TestGenerateQuotedName(t *testing.T) { res, err := Generate(Request{ Package: "Records", Case: "Demo::fuel budget", CaseName: "fuel budget", Provenance: provenance(KindRun), - Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "y", Value: real(3)}}}}, + Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "y", Value: realValue(3)}}}}, }) if err != nil { t.Fatalf("Generate: %v", err) @@ -462,7 +462,7 @@ func TestGenerateOwnerStem(t *testing.T) { Package: "Records", Case: "Demo::B::check", Provenance: provenance(KindRun), Existing: Existing{Stem: "B_check"}, - Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "y", Value: real(2)}}}}, + Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "y", Value: realValue(2)}}}}, }) if err != nil { t.Fatalf("Generate: %v", err) @@ -487,7 +487,7 @@ func TestGenerateExistingScalarValueAcceptsASettledType(t *testing.T) { Attributes: map[string]Feature{"x": {TypeFQN: "ScalarValues::ScalarValue"}}, Stem: "check", }, - Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "x", Value: real(2)}}}}, + Runs: []Run{{Spell: spell(), Outputs: []runtime.CalcOutputValue{{Name: "x", Value: realValue(2)}}}}, }) if err != nil { t.Fatalf("Generate: %v", err) @@ -503,7 +503,7 @@ func TestGenerateVerificationRun(t *testing.T) { res, err := Generate(Request{ Package: "P::Records", Case: "P::fire", Provenance: provenance(KindRun), Runs: []Run{{ - Outputs: []runtime.CalcOutputValue{{Name: "margin", Value: real(-200.0)}}, + Outputs: []runtime.CalcOutputValue{{Name: "margin", Value: realValue(-200.0)}}, Verdicts: []runtime.AnalysisVerdict{{Kind: "objective", Name: "thrust", Status: runtime.VerdictNotSatisfied}}, Verifications: []runtime.VerificationVerdict{ {Case: "P::fire", Kind: runtime.VerdictFail}, @@ -613,7 +613,7 @@ func TestGenerateScalarValuedEnumLiteral(t *testing.T) { Runs: []Run{{ Outputs: []runtime.CalcOutputValue{ {Name: "g", Value: runtime.EnumeratedValue(high, integer(3))}, - {Name: "half", Value: real(1.5)}, + {Name: "half", Value: realValue(1.5)}, }, Spell: spell(), }}, @@ -638,7 +638,7 @@ func TestGenerateNumericFamilySettlesToReal(t *testing.T) { Package: "P::Records", Case: "P::mix", Provenance: provenance(KindSweep), Runs: []Run{ {Outputs: []runtime.CalcOutputValue{{Name: "half", Value: integer(3)}}, Spell: spell()}, - {Outputs: []runtime.CalcOutputValue{{Name: "half", Value: real(3.5)}}, Spell: spell()}, + {Outputs: []runtime.CalcOutputValue{{Name: "half", Value: realValue(3.5)}}, Spell: spell()}, }, }) if err != nil { @@ -673,7 +673,7 @@ func TestGenerateExistingRealAcceptsAnInteger(t *testing.T) { t.Fatalf("an Integer under a declared Real: %v", err) } req.Existing.Attributes["half"] = Feature{TypeFQN: "ScalarValues::Integer"} - req.Runs[0].Outputs[0].Value = real(3.5) + req.Runs[0].Outputs[0].Value = realValue(3.5) if _, err := Generate(req); err == nil { t.Error("a Real under a declared Integer: want an error") } @@ -730,9 +730,9 @@ func TestGenerateInoutQuantityAndPlainNumber(t *testing.T) { }) sides := map[string][2]runtime.Value{ "integer out, quantity in": {integer(2), kg}, - "real out, quantity in": {real(2.5), kg}, + "real out, quantity in": {realValue(2.5), kg}, "quantity out, integer in": {kg, integer(2)}, - "quantity out, real in": {kg, real(2.5)}, + "quantity out, real in": {kg, realValue(2.5)}, } for name, v := range sides { res, err := Generate(Request{ diff --git a/internal/frontend/repl/compare_test.go b/internal/frontend/repl/compare_test.go index 59a8b7e22..8e51710c1 100644 --- a/internal/frontend/repl/compare_test.go +++ b/internal/frontend/repl/compare_test.go @@ -583,13 +583,13 @@ func TestComparisonTableComparesTheDeclaredStatistics(t *testing.T) { } // Nor is a run's or a summary's offset from the pooled mean taken raw: runs at // the ends of the Real range are pooled to the deviation they have. - max := math.MaxFloat64 + maxF := math.MaxFloat64 huge.Snapshots = []simresults.Snapshot{ - {ID: "_r1", Values: map[string]float64{"total": -max}}, - {ID: "_s1", Statistics: &simresults.Statistics{Observable: "total", Runs: 3, Mean: max, Deviation: simresults.Real(0)}}, + {ID: "_r1", Values: map[string]float64{"total": -maxF}}, + {ID: "_s1", Statistics: &simresults.Statistics{Observable: "total", Runs: 3, Mean: maxF, Deviation: simresults.Real(0)}}, } - if dev, ok := storedDeviation(huge, "total"); !ok || math.Abs(dev-max) > 1e293 { - t.Errorf("storedDeviation over -max and a summary at max = %v, %v; want %v", dev, ok, max) + if dev, ok := storedDeviation(huge, "total"); !ok || math.Abs(dev-maxF) > 1e293 { + t.Errorf("storedDeviation over -max and a summary at max = %v, %v; want %v", dev, ok, maxF) } // A summary that kept no deviation leaves the tool's blank and says so. diff --git a/internal/frontend/repl/record_test.go b/internal/frontend/repl/record_test.go index 78690bed4..7677e67aa 100644 --- a/internal/frontend/repl/record_test.go +++ b/internal/frontend/repl/record_test.go @@ -651,7 +651,7 @@ func TestRecordMergesIntoTheFileHoldingTheTargetPackage(t *testing.T) { t.Fatalf("model has errors: %v", errs) } wants(t, run(t, s, "%record A::Cases::check"), "recorded A::Records::check_run1") - if n := strings.Count(s.text(), "package Records"); n != 1 { + if strings.Count(s.text(), "package Records") != 1 { t.Fatalf("the record made a second A::Records:\n%s", s.text()) } if errs := errorDiagnostics(s.diagnostics()); len(errs) > 0 { diff --git a/internal/ir/lower/probability.go b/internal/ir/lower/probability.go index c54feba4f..64d4138eb 100644 --- a/internal/ir/lower/probability.go +++ b/internal/ir/lower/probability.go @@ -26,6 +26,8 @@ const ( // sum from 1.0 and still be taken as summing to it. const ProbabilityTolerance = 1e-6 +const acceptPrefix = "accept " + // Probability is the weight a succession's `@Probability { p = ...; }` states for // its branch: the expression bound to p, read where the succession's guard is. type Probability struct { @@ -307,7 +309,7 @@ func TriggerName(trigger ast.Node) string { case nil: return "" case *ast.AcceptEvent: - return "accept " + orAnyName(ast.SimpleName(t.SignalType)) + return acceptPrefix + orAnyName(ast.SimpleName(t.SignalType)) case *ast.CallEvent: return "call " + orAnyName(ast.SimpleName(t.Operation)) case *ast.TimeEvent: @@ -369,7 +371,7 @@ func triggerKey(trans *Transition, resolver *resolve.Resolver) string { if t.Subsets != nil { key = "accept :> " + operand(t.Subsets) } else { - key = "accept " + operand(t.SignalType) + key = acceptPrefix + operand(t.SignalType) } case *ast.CallEvent: key = "call " + operand(t.Operation) @@ -378,7 +380,7 @@ func triggerKey(trans *Transition, resolver *resolve.Resolver) string { if t.Absolute { keyword = "at" } - key = "accept " + keyword + " " + exprKeyOrUnique(t.Duration) + key = acceptPrefix + keyword + " " + exprKeyOrUnique(t.Duration) case *ast.ChangeEvent: key = "accept when " + exprKeyOrUnique(t.Condition) } @@ -408,7 +410,7 @@ func triggerSpelling(trans *Transition) string { if t.Absolute { keyword = "at" } - key = "accept " + keyword + " " + writtenValue(t.Duration) + key = acceptPrefix + keyword + " " + writtenValue(t.Duration) case *ast.ChangeEvent: key = "accept when " + writtenValue(t.Condition) } diff --git a/internal/ir/lower/state_footprint_test.go b/internal/ir/lower/state_footprint_test.go index 602d5ce44..cc6adff82 100644 --- a/internal/ir/lower/state_footprint_test.go +++ b/internal/ir/lower/state_footprint_test.go @@ -45,26 +45,26 @@ func TestTransitionFootprintsProjectGuardEffectAndActivity(t *testing.T) { if err != nil { t.Fatalf("ToStateGraph: %v", err) } - go_ := graph.TransitionFootprints()[transitionOut(t, graph, "idle", 0)] + goT := graph.TransitionFootprints()[transitionOut(t, graph, "idle", 0)] stop := graph.TransitionFootprints()[transitionOut(t, graph, "idle", 1)] - if !hasPlace(go_.Reads, "y") || !hasPlace(go_.Reads, "idle") { - t.Fatalf("Go reads %v, want the guard's y and the source's activity", placeNames(go_.Reads)) + if !hasPlace(goT.Reads, "y") || !hasPlace(goT.Reads, "idle") { + t.Fatalf("Go reads %v, want the guard's y and the source's activity", placeNames(goT.Reads)) } - if !hasPlace(go_.Writes, "x") || !hasPlace(go_.Writes, "idle") || !hasPlace(go_.Writes, "busy") { - t.Fatalf("Go writes %v, want busy's entry x and the activity of idle and busy", placeNames(go_.Writes)) + if !hasPlace(goT.Writes, "x") || !hasPlace(goT.Writes, "idle") || !hasPlace(goT.Writes, "busy") { + t.Fatalf("Go writes %v, want busy's entry x and the activity of idle and busy", placeNames(goT.Writes)) } // The event comes off the machine's own queue: the trigger is not a bus accept. - if len(go_.Accepts) != 0 { - t.Fatalf("Go accepts %v, want none", go_.Accepts) + if len(goT.Accepts) != 0 { + t.Fatalf("Go accepts %v, want none", goT.Accepts) } if !hasPlace(stop.Writes, "z") || hasPlace(stop.Writes, "x") { t.Fatalf("Stop writes %v, want its effect's z and not busy's x", placeNames(stop.Writes)) } - if !strings.Contains(go_.String(), "active idle") { - t.Fatalf("footprint renders as %q, want the activity spelt `active idle`", go_.String()) + if !strings.Contains(goT.String(), "active idle") { + t.Fatalf("footprint renders as %q, want the activity spelt `active idle`", goT.String()) } // Both leave idle: dependent through its activity, as two reactions of one leaf are. - if !go_.Dependent(stop) { + if !goT.Dependent(stop) { t.Fatal("two transitions out of one state must be dependent") } } @@ -94,12 +94,12 @@ func TestTransitionFootprintsCoverEnteredDoBehaviors(t *testing.T) { if err != nil { t.Fatalf("ToStateGraph: %v", err) } - go_ := graph.TransitionFootprints()[transitionOut(t, graph, "idle", 0)] - if !hasPlace(go_.Writes, "x") || !hasPlace(go_.Writes, "y") { - t.Fatalf("Go writes %v, want busy's entry x and its do behavior's y", placeNames(go_.Writes)) + goT := graph.TransitionFootprints()[transitionOut(t, graph, "idle", 0)] + if !hasPlace(goT.Writes, "x") || !hasPlace(goT.Writes, "y") { + t.Fatalf("Go writes %v, want busy's entry x and its do behavior's y", placeNames(goT.Writes)) } - if hasPlace(go_.Writes, "z") || hasPlace(go_.Reads, "z") { - t.Fatalf("Go touches %v / %v, want nothing of the do behavior of the state it leaves", placeNames(go_.Reads), placeNames(go_.Writes)) + if hasPlace(goT.Writes, "z") || hasPlace(goT.Reads, "z") { + t.Fatalf("Go touches %v / %v, want nothing of the do behavior of the state it leaves", placeNames(goT.Reads), placeNames(goT.Writes)) } } diff --git a/internal/ir/view/dot.go b/internal/ir/view/dot.go index 985a60ccb..9c8f38022 100644 --- a/internal/ir/view/dot.go +++ b/internal/ir/view/dot.go @@ -290,9 +290,12 @@ type dotWriter struct { // The Standard B&W style, after the sysmlbw PlantUML skin: Helvetica text, // white fills, thin #181818 lines, edge text a point smaller than node text. const ( - dotFontName = "Helvetica" - dotLineColor = "#181818" - dotEdgeFontPts = 13 + dotFontName = "Helvetica" + dotLineColor = "#181818" + dotEdgeFontPts = 13 + dotPenWidthOne = "penwidth=1" + dotFillBlack = "fillcolor=black" + dotArrowheadNone = "arrowhead=none" ) // dotNodeDefaults and dotEdgeDefaults are the `node` and `edge` statements the @@ -301,7 +304,7 @@ var ( dotNodeDefaults = []string{"shape=box", "style=filled", "fillcolor=white", dotColorAttr(dotLineColor), dotFontAttr(dotFontName), fmt.Sprintf("fontsize=%d", dotFontSize), "penwidth=0.5"} dotEdgeDefaults = []string{dotColorAttr(dotLineColor), dotFontAttr(dotFontName), - fmt.Sprintf("fontsize=%d", dotEdgeFontPts), "penwidth=1"} + fmt.Sprintf("fontsize=%d", dotEdgeFontPts), dotPenWidthOne} ) // dotColorAttr and dotFontAttr are the quoted `color` and `fontname` attributes. @@ -585,7 +588,7 @@ func (w *dotWriter) dotNodeAttributes(node *Node) []string { stated := node.Geometry != nil && node.Geometry.HasSize switch { case node.Kind == startKind: - attrs = []string{"shape=point", "fillcolor=black", `label=""`} + attrs = []string{"shape=point", dotFillBlack, `label=""`} case stated && isSymbolKind(node.Kind): attrs = w.dotSymbolAttributes(node) case node.Kind == "initial" || node.Kind == "final": @@ -595,7 +598,7 @@ func (w *dotWriter) dotNodeAttributes(node *Node) []string { attrs = append(attrs, `style="rounded,filled"`) } if w.fills.filled(node) { - attrs = append(attrs, "fillcolor="+dotQuote(w.fills.fill(node)), dotColorAttr(w.fills.color(node)), "penwidth=1") + attrs = append(attrs, "fillcolor="+dotQuote(w.fills.fill(node)), dotColorAttr(w.fills.color(node)), dotPenWidthOne) } if stated { attrs = append(attrs, w.dotStatedLabel(node)...) @@ -697,14 +700,14 @@ func (w *dotWriter) dotSymbolAttributes(node *Node) []string { case "decision", "merge", "choice": attrs = []string{"shape=diamond"} case "fork", "join": - attrs = []string{"fillcolor=black"} + attrs = []string{dotFillBlack} case "initial", "junction": - attrs = []string{"shape=circle", "fillcolor=black"} + attrs = []string{"shape=circle", dotFillBlack} case "final", terminateKind: - attrs = []string{"shape=doublecircle", "fillcolor=black"} + attrs = []string{"shape=doublecircle", dotFillBlack} default: if w.fills.filled(node) { - attrs = append(attrs, "fillcolor="+dotQuote(w.fills.fill(node)), dotColorAttr(w.fills.color(node)), "penwidth=1") + attrs = append(attrs, "fillcolor="+dotQuote(w.fills.fill(node)), dotColorAttr(w.fills.color(node)), dotPenWidthOne) } } attrs = append(attrs, `label=""`) @@ -729,7 +732,7 @@ func (w *dotWriter) dotPseudostateAttributes(node *Node) []string { if shown(node) != "" { return []string{shape, w.labels.dotLabel(node)} } - attrs := []string{shape, "fillcolor=black", `label=""`} + attrs := []string{shape, dotFillBlack, `label=""`} if _, ok := w.boxes[node.ID]; !ok { attrs = append(attrs, "width="+dotInches(dotPseudostateSize)) } @@ -1035,11 +1038,11 @@ func (w *dotWriter) dotEdgeAttributes(edge Edge) []string { } switch edge.Kind { case EdgeConnection: - attrs = append(attrs, "arrowhead=none", "penwidth=3") + attrs = append(attrs, dotArrowheadNone, "penwidth=3") case EdgeFlow: attrs = append(attrs, "style=dashed") case EdgeBinding: - attrs = append(attrs, "arrowhead=none") + attrs = append(attrs, dotArrowheadNone) } if len(edge.Route) > 1 { attrs = append(attrs, "pos="+dotQuote(w.dotSpline(edge.Route))) @@ -1060,7 +1063,7 @@ func (w *dotWriter) dotSpline(route []Point) string { // dotContainmentAttributes is a tree's containment edge, Mermaid's `---`. func dotContainmentAttributes() []string { - return []string{"arrowhead=none"} + return []string{dotArrowheadNone} } // dotKeywordPointSize is the font size of the guillemet keyword line, under the diff --git a/internal/translate/export/behavior.go b/internal/translate/export/behavior.go index e46f0e27d..372cc8d0c 100644 --- a/internal/translate/export/behavior.go +++ b/internal/translate/export/behavior.go @@ -1245,7 +1245,7 @@ func (d *decoder) sequencesFrom(el, from *element) bool { // it: the graph has to agree, or the order it states would not be written back. func (d *decoder) impliedSource(el, from *element) error { if _, states := d.sourceEnd(el); !states { - return d.missing(el, "sysml:"+pSourceFeature, "a succession written as `then` sequences from the member before it") + return d.missing(el, sysmlPrefix+pSourceFeature, "a succession written as `then` sequences from the member before it") } if !d.sequencesFrom(el, from) { return d.positionalError(el, "from", "before it") @@ -1451,7 +1451,7 @@ func (d *decoder) subactionText(el *element, depth int) (string, error) { case !ok && hasNormative: kind = normative case !ok: - return "", d.missing(el, "sysml:"+pKind+" or sysx:"+xSubactionKind, "a state subaction states whether it runs on entry, throughout or on exit") + return "", d.missing(el, sysmlPrefix+pKind+" or sysx:"+xSubactionKind, "a state subaction states whether it runs on entry, throughout or on exit") } keyword := d.keywordOr(el, kind) // A braced block is one anonymous action; a graph that wrote it as the @@ -1555,7 +1555,7 @@ func (d *decoder) transitionHead(el *element, syntax string) ([]string, error) { return nil, err } if source == "" { - return nil, d.missing(el, "sysml:"+pSourceFeature, "a transition written with `transition` names the state it leaves") + return nil, d.missing(el, sysmlPrefix+pSourceFeature, "a transition written with `transition` names the state it leaves") } keyword := "transition" if written, ok := d.stringOf(el, rdf.OpenSysML+xDeclaredKeyword); ok { diff --git a/internal/translate/export/rdf_in_normative.go b/internal/translate/export/rdf_in_normative.go index 136ed3ddf..1d153e865 100644 --- a/internal/translate/export/rdf_in_normative.go +++ b/internal/translate/export/rdf_in_normative.go @@ -93,7 +93,7 @@ var relationshipTargetEnds = []string{ // collapsed statement beside it: the target set its ends name is the set the // head's collapsed properties state, a conjugated definition is the `~` of its // owner, and a satisfy's subject parameter is the `by` the head states. -func (d *decoder) normativeImplied(el *element, parent *element) (bool, error) { +func (d *decoder) normativeImplied(el, parent *element) (bool, error) { ownedByParent := func() bool { return parent != nil } @@ -149,7 +149,7 @@ func (d *decoder) normativeImplied(el *element, parent *element) (bool, error) { // restates collapsed targets, it is implied, and any end disagreeing with the // owner or naming a target the collapsed form never states is refused: the // notation would otherwise pick one of the two statements. -func (d *decoder) impliedRelationship(el *element, parent *element) (bool, error) { +func (d *decoder) impliedRelationship(el, parent *element) (bool, error) { what := fmt.Sprintf("the %s <%s>", el.metaclass, el.iri) stated := map[string]bool{} literal := false @@ -378,7 +378,7 @@ func (d *decoder) parameterMember(el *element) bool { // verifyConjugated checks a conjugated port definition against its owner: it // is the `~` of the original's name, which the notation derives rather than // writing. -func (d *decoder) verifyConjugated(el *element, parent *element) error { +func (d *decoder) verifyConjugated(el, parent *element) error { name, _ := d.stringOf(el, rdf.SysML+pDeclaredName) original, ok := d.stringOf(parent, rdf.SysML+pDeclaredName) if !ok { @@ -732,7 +732,7 @@ func (d *decoder) headEnd(el, parent *element) bool { // payload parameter, the trigger AcceptActionUsage, and the SuccessionAsUsage // whose second end refers to the target are implied when they agree with the // head, refused when they contradict it, and ordinary members otherwise. -func (d *decoder) transitionImplied(el *element, parent *element) (bool, error) { +func (d *decoder) transitionImplied(el, parent *element) (bool, error) { subject := rdf.IRI(el.iri) what := fmt.Sprintf("the transition <%s>", parent.iri) m, owned := d.owningMembership[el.iri] diff --git a/internal/translate/export/rdf_in_toolkit.go b/internal/translate/export/rdf_in_toolkit.go index f43183804..fc990de53 100644 --- a/internal/translate/export/rdf_in_toolkit.go +++ b/internal/translate/export/rdf_in_toolkit.go @@ -306,13 +306,9 @@ func deriveNormativeGraph(graph *rdf.Graph, metaclasses map[rdf.Term]string) (*r memberOwner[member.Value] = owner memberMembership[member.Value] = subject ownerMembers[owner.Value] = append(ownerMembers[owner.Value], member) - case m == mFeatureValue: - nodeMember[member.Value] = true - nodeOwner[member.Value] = owner - case expressionMetaclasses[meta(member)]: - nodeMember[member.Value] = true - nodeOwner[member.Value] = owner - case (m == mParameterMembership || m == mReturnParameterMembership) && expressionMetaclasses[meta(owner)]: + case m == mFeatureValue, + expressionMetaclasses[meta(member)], + (m == mParameterMembership || m == mReturnParameterMembership) && expressionMetaclasses[meta(owner)]: nodeMember[member.Value] = true nodeOwner[member.Value] = owner default: diff --git a/internal/translate/migrate/documents.go b/internal/translate/migrate/documents.go index eaef88e57..aae46e209 100644 --- a/internal/translate/migrate/documents.go +++ b/internal/translate/migrate/documents.go @@ -89,6 +89,13 @@ type contentPlan struct { // docSuffix names a document's definition after its class. const docSuffix = " Document" +const ( + unmigrated = " is not migrated: " + leavesOut = "leaves out " + onlyElementCollected = "the only element collected, the " + titleRedefines = "attribute redefines title = " +) + // planDocuments plans every DocGen document once views and tables are, so the // names reserved account for each other and Diagram blocks find their views. func (m *migration) planDocuments() { @@ -207,7 +214,7 @@ func (m *migration) planMethod(dp *docPlan, sec *sectionPlan) { } steps, end := m.model.DocGenChain(v.Method) if end != "" { - sec.refused = "the method " + qualifiedName(v.Method) + " is not migrated: " + end + sec.refused = "the method " + qualifiedName(v.Method) + unmigrated + end m.report.Entries = append(m.report.Entries, *m.nodeEntry(v.Method, v.Method.DocGen(), Unmapped, sec.refused)) return } @@ -526,7 +533,7 @@ func (c *chain) fail(s *sysmlv1.DocGenStep, why string) { c.refuse(s, why) default: if c.broken == "" { - c.broken = "«" + c.kind(s) + "» " + qualifiedName(s.Node) + " is not migrated: " + why + c.broken = "«" + c.kind(s) + "» " + qualifiedName(s.Node) + unmigrated + why } c.ctx = qx{} c.m.report.Entries = append(c.m.report.Entries, *c.m.nodeEntry(s.Node, s.Application, Unmapped, why)) @@ -773,13 +780,13 @@ func (c *chain) collectShown(s *sysmlv1.DocGenStep) { } shown := " shown on the " + diagramKind(d) + " '" + d.Name + "' " if unknown > 0 { - c.note("leaves out " + plural(unknown, "element") + shown + "that the archive does not describe") + c.note(leavesOut + plural(unknown, "element") + shown + "that the archive does not describe") } if unwritten > 0 { - c.note("leaves out " + plural(unwritten, "element") + shown + "that the migration does not write") + c.note(leavesOut + plural(unwritten, "element") + shown + "that the migration does not write") } if folded > 0 { - c.note("leaves out " + plural(folded, "element") + shown + "written within the elements owning them, with no v2 element of their own") + c.note(leavesOut + plural(folded, "element") + shown + "written within the elements owning them, with no v2 element of their own") } } if len(names) > 0 { @@ -890,7 +897,7 @@ func (c *chain) noAssociated(holders []*sysmlv1.Element, kind string) string { case 0: return "no element is collected for it to follow" case 1: - return "the only element collected, the " + kindOf(holders[0]) + " " + qualifiedName(holders[0]) + ", has no typed attribute of " + kind + " aggregation" + return onlyElementCollected + kindOf(holders[0]) + " " + qualifiedName(holders[0]) + ", has no typed attribute of " + kind + " aggregation" } return "none of the " + strconv.Itoa(len(holders)) + " elements collected has a typed attribute of " + kind + " aggregation" } @@ -1133,7 +1140,7 @@ func (c *chain) keepHolders(s *sysmlv1.DocGenStep, keep func(*sysmlv1.Element) ( // noneOf describes collected elements none of which is a diagram. func noneOf(es []*sysmlv1.Element) string { if len(es) == 1 { - return "the only element collected, the " + kindOf(es[0]) + " " + qualifiedName(es[0]) + ", which is not a diagram" + return onlyElementCollected + kindOf(es[0]) + " " + qualifiedName(es[0]) + ", which is not a diagram" } return "all " + strconv.Itoa(len(es)) + " elements collected, none of them a diagram" } @@ -1443,7 +1450,7 @@ func (c *chain) group(s *sysmlv1.DocGenStep, flows bool) { } steps, end := c.m.model.DocGenChain(body) if end != "" { - c.abort(s, "its body "+qualifiedName(body)+" is not migrated: "+end) + c.abort(s, "its body "+qualifiedName(body)+unmigrated+end) return } if s.Application != nil && s.Application.Tag("loop") == "true" { @@ -1767,7 +1774,7 @@ func (c *chain) noDiagrams() string { case len(c.holders) == 0: why = "nothing is collected for it to draw" case len(c.holders) == 1: - why = "the only element collected, the " + kindOf(c.holders[0]) + " " + qualifiedName(c.holders[0]) + ", is not a diagram" + why = onlyElementCollected + kindOf(c.holders[0]) + " " + qualifiedName(c.holders[0]) + ", is not a diagram" default: why = "none of the " + strconv.Itoa(len(c.holders)) + " elements collected is a diagram" } @@ -1795,7 +1802,7 @@ func (c *chain) dynamicView(s *sysmlv1.DocGenStep) { var steps []*sysmlv1.DocGenStep steps, why = c.m.model.DocGenChain(body) if why != "" { - why = "its body " + qualifiedName(body) + " is not migrated: " + why + why = "its body " + qualifiedName(body) + unmigrated + why } else { sub := c.sub() sub.sec = sec @@ -1819,7 +1826,7 @@ func (m *migration) writeDocument(dp *docPlan) { target := m.qualified(append(m.segments(dp.host), dp.root.name)) m.inside(blockNames("Document", dp.root.names), func() { m.w.block("part def "+writeName(dp.root.name)+" :> "+m.queryPrefix(dp.host)+"Document", func() { - m.w.line("attribute redefines title = " + stringLiteral(dp.root.title) + ";") + m.w.line(titleRedefines + stringLiteral(dp.root.title) + ";") for _, a := range dp.anchors { m.w.line("ref " + writeName(a.name) + " : " + m.memberRef(a.def, dp.host) + ";") } @@ -1910,7 +1917,7 @@ func (m *migration) writeSectionBody(dp *docPlan, sec *sectionPlan, path string) } for _, child := range sec.children { m.blockPart(dp.host, child.name, "Section", child.names, func() { - m.w.line("attribute redefines title = " + stringLiteral(child.title) + ";") + m.w.line(titleRedefines + stringLiteral(child.title) + ";") notes = append(notes, m.writeSectionBody(dp, child, path+"::"+writeName(child.name))...) }) } @@ -1928,7 +1935,7 @@ func (m *migration) writeBlock(dp *docPlan, cp *contentPlan, path string) []stri case "Section": var notes []string m.blockPart(dp.host, cp.name, "Section", cp.section.names, func() { - m.w.line("attribute redefines title = " + stringLiteral(cp.section.title) + ";") + m.w.line(titleRedefines + stringLiteral(cp.section.title) + ";") notes = m.writeSectionBody(dp, cp.section, cp.target) }) return notes diff --git a/internal/translate/migrate/metadata.go b/internal/translate/migrate/metadata.go index 26006e82a..708d0f594 100644 --- a/internal/translate/migrate/metadata.go +++ b/internal/translate/migrate/metadata.go @@ -122,7 +122,7 @@ func (m *migration) metadataFeature(p *sysmlv1.Element) (kw, typ, note string) { } return "attribute", "", "" } - if sv := m.scalarValue(t); sv != "" { + if m.scalarValue(t) != "" { typ, note = m.typeRef(t, m.scope) return "attribute", typ, note } @@ -251,10 +251,10 @@ func (m *migration) tagLiteral(t *sysmlv1.Element, v string) (string, string) { return m.ref(lit, m.scope), "" } } - return "", "the value " + v + " is not a literal of " + qualifiedName(t) + return "", theValue + v + " is not a literal of " + qualifiedName(t) } if m.structuredValueType(t) { - return "", "the value " + v + " has no literal form: " + qualifiedName(t) + " is a structured value type" + return "", theValue + v + " has no literal form: " + qualifiedName(t) + " is a structured value type" } sv := m.scalarBase(t) text := strings.TrimSpace(v) @@ -266,19 +266,19 @@ func (m *migration) tagLiteral(t *sysmlv1.Element, v string) (string, string) { case "true", "false": return text, "" } - return "", "the value " + v + " is not a boolean" + return "", theValue + v + " is not a boolean" case "Integer", "Natural": n, ok := new(big.Int).SetString(text, 10) if !ok || (sv == "Natural" && n.Sign() < 0) { - return "", "the value " + v + " is not an integer" + return "", theValue + v + " is not an integer" } return n.String(), "" } if !decimal(text) { - return "", "the value " + v + " is not a number" + return "", theValue + v + " is not a number" } if _, ok := new(big.Rat).SetString(text); !ok { - return "", "the value " + v + " is not a number" + return "", theValue + v + " is not a number" } if !strings.ContainsAny(text, ".eE") { text += ".0" diff --git a/internal/translate/migrate/migrate.go b/internal/translate/migrate/migrate.go index 440098b86..c8477a04c 100644 --- a/internal/translate/migrate/migrate.go +++ b/internal/translate/migrate/migrate.go @@ -26,11 +26,15 @@ const ( classifierSubject = "the instance's classifier " individualSubject = "the individual " slotValueSubject = "the slot's value " + columnSubject = "the column " + sortBySubject = "the sort by " ) // scalarValuesPrefix qualifies a name from the standard ScalarValues package. const scalarValuesPrefix = "ScalarValues::" +const fromKeyword = " from " + // Result is a migration's output: the v2 notation, the report over it, and // the result snapshots of its run configurations. type Result struct { @@ -2645,7 +2649,7 @@ func (m *migration) itemFlow(f *sysmlv1.Element, ends []*sysmlv1.Element, paths } else { notes = append(notes, "no flow property typed by "+item.Name+" on both ends of the realizing connector") } - m.w.lines(commentLines("item flow of " + item.Name + " from " + from + " to " + to + " not migrated: " + notes[len(notes)-1])) + m.w.lines(commentLines("item flow of " + item.Name + fromKeyword + from + " to " + to + " not migrated: " + notes[len(notes)-1])) continue } m.w.line("flow " + from + "." + writeName(m.nameOf(sp)) + " to " + to + "." + writeName(m.nameOf(dp)) + ";") @@ -3020,7 +3024,7 @@ func (m *migration) dependencyPair(d *sysmlv1.Element, pl *placement, name strin } decl := "dependency " if name != "" { - decl += writeName(name) + " from " + decl += writeName(name) + fromKeyword m.madeUp(d, writeName(name)) } decl += from + " to " + to @@ -3418,7 +3422,7 @@ func (m *migration) stereotypeComments(e *sysmlv1.Element) { } var parts []string for _, ns := range outside { - parts = append(parts, strings.Join(byNamespace[ns], " ")+" from "+ns) + parts = append(parts, strings.Join(byNamespace[ns], " ")+fromKeyword+ns) } verdict := " is applied from a profile the document does not define; the application is kept as a comment" if len(outside) > 1 || len(byNamespace[outside[0]]) > 1 { diff --git a/internal/translate/migrate/montecarlo.go b/internal/translate/migrate/montecarlo.go index 399e603a3..181f13d63 100644 --- a/internal/translate/migrate/montecarlo.go +++ b/internal/translate/migrate/montecarlo.go @@ -16,6 +16,7 @@ const ( monteCarloRun = "run" monteCarloObserved = "observed" monteCarloRecorded = "Monte Carlo" + connectorSubject = "the connector binds the simulation tool's " ) // monteCarloStatistic maps a MonteCarloAnalysis statistic to the Simulation::MonteCarlo @@ -108,7 +109,7 @@ func (m *migration) monteCarloBound(owner, c *sysmlv1.Element) (stat string, f * if stat == "" { return "", nil, "" } - subject := "the connector binds the simulation tool's " + monteCarloAnalysisBlock + "::" + stat + subject := connectorSubject + monteCarloAnalysisBlock + "::" + stat switch { case len(c.Owned("end")) != 2: note = "a connector with " + strconv.Itoa(len(c.Owned("end"))) + " ends is not migrated" @@ -209,7 +210,7 @@ func (m *migration) settleMonteCarloBinding(cs *monteCarloCase, bound map[string return monteCarloBinding{}, false } b := monteCarloBinding{stat: stat} - subject := "the connector binds the simulation tool's " + monteCarloAnalysisBlock + "::" + stat + subject := connectorSubject + monteCarloAnalysisBlock + "::" + stat member, known := monteCarloMembers[stat] switch { case !known && len(c.Owned("end")) == 2: @@ -375,7 +376,7 @@ func (m *migration) monteCarloConnector(c *sysmlv1.Element) bool { } cs := m.monteCarloCaseOf(c.Parent) if cs == nil { - note := "the connector binds the simulation tool's " + monteCarloAnalysisBlock + "::" + stat + ", a statistic of an analysis its owner does not inherit" + note := connectorSubject + monteCarloAnalysisBlock + "::" + stat + ", a statistic of an analysis its owner does not inherit" if f != nil && !f.IsProxy() { note = "the connector binds " + f.Name + " to the simulation tool's " + monteCarloAnalysisBlock + "::" + stat + ", a statistic of an analysis its owner does not inherit" } @@ -423,7 +424,9 @@ func (m *migration) monteCarloSlots(e *sysmlv1.Element, slots []*sysmlv1.Element count[stat]++ } if len(stats) == 0 { - return others, func() {} + return others, func() { + // No held statistics means there is no recorded analysis to write. + } } cs := m.recordedCase(e) var lines []string @@ -455,7 +458,9 @@ func (m *migration) monteCarloSlots(e *sysmlv1.Element, slots []*sysmlv1.Element m.add(h.slot, verdictFor(note), m.v2Name(e)+"::"+writeName(monteCarloRecorded)+"::"+member.member, note) } if cs == nil { - return others, func() {} + return others, func() { + // Without a recorded case there is no analysis block to write. + } } return others, func() { m.w.block("analysis "+writeName(monteCarloRecorded)+" : "+m.refMember(cs.block.Parent, cs.name, namespaces(cs.segments), e, true), func() { diff --git a/internal/translate/migrate/tables.go b/internal/translate/migrate/tables.go index 2f82267a2..45e6bf270 100644 --- a/internal/translate/migrate/tables.go +++ b/internal/translate/migrate/tables.go @@ -434,7 +434,7 @@ func (m *migration) columnKey(c sysmlv1.Column, host *sysmlv1.Element) (key stri f := c.Feature.Element switch { case f == nil: - return "", nil, "the column " + c.ID + " names no property of the document" + return "", nil, columnSubject + c.ID + " names no property of the document" case monteCarloFeature(f) != "": return "", nil, monteCarloColumnNote(monteCarloFeature(f)) case !m.written(f): @@ -444,9 +444,9 @@ func (m *migration) columnKey(c sysmlv1.Column, host *sysmlv1.Element) (key stri } return m.nameOf(f), f, "" case sysmlv1.ColumnPropertyPair: - return "", nil, "the column " + c.ID + " reads a property of a property, which no Column expression reads" + return "", nil, columnSubject + c.ID + " reads a property of a property, which no Column expression reads" } - return "", nil, "the column " + c.ID + " is of a form the migrator does not read" + return "", nil, columnSubject + c.ID + " is of a form the migrator does not read" } // sorted orders rows by the table's sort keys, least significant first so the @@ -460,10 +460,10 @@ func (m *migration) sorted(rows qx, t *sysmlv1.Table, host *sysmlv1.Element, l * case s.Column == "-1" || s.Column == "" || strings.HasPrefix(s.Column, "_"): continue case s.Column == "ID" || strings.HasSuffix(s.Column, ":hierarchyId"): - l.note("the sort by " + s.Column + " orders rows by a tool id, which is dropped") + l.note(sortBySubject + s.Column + " orders rows by a tool id, which is dropped") continue } - l.note("the sort by " + s.Column + " names no column and is dropped") + l.note(sortBySubject + s.Column + " names no column and is dropped") continue } if col.Kind == sysmlv1.ColumnTool { @@ -471,7 +471,7 @@ func (m *migration) sorted(rows qx, t *sysmlv1.Table, host *sysmlv1.Element, l * } key, _, why := m.columnKey(col, host) if why != "" { - l.note("the sort by " + s.Column + " is dropped: " + why) + l.note(sortBySubject + s.Column + " is dropped: " + why) continue } dir := "ascending" @@ -511,12 +511,12 @@ func (m *migration) projected(rows qx, t *sysmlv1.Table, host *sysmlv1.Element, shown++ key, f, why := m.columnKey(c, host) if why != "" { - l.note("the column " + c.ID + " is omitted: " + why) + l.note(columnSubject + c.ID + " is omitted: " + why) continue } if f == nil { if !p.property(key) { - l.note("the column " + c.ID + " repeats the column " + key + " and is omitted") + l.note(columnSubject + c.ID + " repeats the column " + key + " and is omitted") } continue } @@ -603,7 +603,7 @@ func (p *projection) build(source qx) (project qx, notes []string) { } name := names.claim(e.name) if name != e.name { - notes = append(notes, "the column "+e.name+" is written as "+name+": column names are unique") + notes = append(notes, columnSubject+e.name+" is written as "+name+": column names are unique") } cols = append(cols, qcall("Column", qarg1("name", qstr(name)), qarg1("expression", e.expression))) } @@ -763,7 +763,7 @@ func (m *migration) lowerMatrix(t *sysmlv1.Table, host *sysmlv1.Element, l *lowe name += " (" + dir + ")" } if unique := names.claim(name); unique != name { - l.note("the column " + name + " is written as " + unique + ": column names are unique") + l.note(columnSubject + name + " is written as " + unique + ": column names are unique") name = unique } related = append(related, qcall("RelatedColumn", qarg1("name", qstr(name)), diff --git a/internal/translate/migrate/typefilter.go b/internal/translate/migrate/typefilter.go index 4673f2440..7470d466b 100644 --- a/internal/translate/migrate/typefilter.go +++ b/internal/translate/migrate/typefilter.go @@ -102,6 +102,7 @@ var packageableTypes = []string{typePackage, typeDefinition, typeViewUsage, type const ( noteDiagramViews = "the views diagrams became are listed too" noteClassifierExtra = "the views diagrams became and the action defs operations became are listed too" + elementTypeSubject = "the element type " ) // metaclassTypes maps a UML metaclass to the v2 metaclasses its elements @@ -212,7 +213,7 @@ func standardHref(href string) (doc, name string, ok bool) { func (m *migration) typeFilter(ref sysmlv1.ElementRef) typeFilter { e := ref.Element if e == nil { - return typeFilter{label: ref.ID, refused: "the element type " + ref.ID + " " + m.unresolvedRef(ref)} + return typeFilter{label: ref.ID, refused: elementTypeSubject + ref.ID + " " + m.unresolvedRef(ref)} } if doc, name, ok := standardHref(e.Href); ok { switch { @@ -232,16 +233,16 @@ func (m *migration) typeFilter(ref sysmlv1.ElementRef) typeFilter { return typeFilter{label: "«" + s.Name + "»", refused: "no v2 metaclass stands for the elements of «" + s.Name + "»"} } if e.Name == "" { - return typeFilter{label: e.Href, refused: "the element type " + e.Href + " is in a module the archive does not describe"} + return typeFilter{label: e.Href, refused: elementTypeSubject + e.Href + " is in a module the archive does not describe"} } if t, ok := stereotypeTypes[e.Name]; ok && isCustomizationHref(e.Href) { return fromTypes("«"+e.Name+"»", t) } if subs := m.specializers(e); len(subs) > 0 { return typeFilter{classifiers: subs, label: qualifiedName(e), - note: "the element type " + qualifiedName(e) + " is outside the document; rows are filtered by the document's classifiers specializing it"} + note: elementTypeSubject + qualifiedName(e) + " is outside the document; rows are filtered by the document's classifiers specializing it"} } - return typeFilter{label: qualifiedName(e), refused: "the element type " + qualifiedName(e) + " is outside the document, and not a UML metaclass or a SysML stereotype"} + return typeFilter{label: qualifiedName(e), refused: elementTypeSubject + qualifiedName(e) + " is outside the document, and not a UML metaclass or a SysML stereotype"} } if e.Type == "Stereotype" { if t, ok := stereotypeTypes[e.Name]; ok && m.isLibrary(e) && libraryRoots[pathRoot(qualifiedName(e))] { @@ -253,11 +254,11 @@ func (m *migration) typeFilter(ref sysmlv1.ElementRef) typeFilter { return typeFilter{label: "«" + e.Name + "»", refused: "«" + e.Name + "» is not written as a metadata def rows could be filtered by"} } if !m.written(e) { - return typeFilter{label: qualifiedName(e), refused: "the element type " + kindOf(e) + " " + qualifiedName(e) + " is not migrated"} + return typeFilter{label: qualifiedName(e), refused: elementTypeSubject + kindOf(e) + " " + qualifiedName(e) + " is not migrated"} } cat, _ := m.classify(e) if cat.keyword() == "" || cat == catPackage { - return typeFilter{label: qualifiedName(e), refused: "the element type " + kindOf(e) + " " + qualifiedName(e) + " is not a classifier rows can be typed by"} + return typeFilter{label: qualifiedName(e), refused: elementTypeSubject + kindOf(e) + " " + qualifiedName(e) + " is not a classifier rows can be typed by"} } return typeFilter{classifiers: []*sysmlv1.Element{e}, label: qualifiedName(e)} } diff --git a/internal/translate/migrate/typemodifier.go b/internal/translate/migrate/typemodifier.go index a2eb24229..5aa3e8a81 100644 --- a/internal/translate/migrate/typemodifier.go +++ b/internal/translate/migrate/typemodifier.go @@ -19,6 +19,8 @@ type typeModifier struct { refused string } +const typeModifierSubject = "the type modifier " + // isTypeModifier matches the tool's exact «typeModifier» application. func isTypeModifier(s *sysmlv1.Stereotype) bool { return s.Name == "typeModifier" && s.Namespace == sysmlv1.MagicDrawProfileNS @@ -59,9 +61,9 @@ func (m *migration) typeModifier(p *sysmlv1.Element) *typeModifier { case tm.text == "*", tm.text == "&": tm.reference(m, p) case !ok: - tm.refused = "the type modifier " + tm.text + " is not one the migrator reads" + tm.refused = typeModifierSubject + tm.text + " is not one the migrator reads" case strings.Count(tm.text, "[") > 1, strings.ContainsAny(shape, "*,"): - tm.refused = "the type modifier " + tm.text + " has no v2 form: a multiplicity has one dimension" + tm.refused = typeModifierSubject + tm.text + " has no v2 form: a multiplicity has one dimension" default: tm.collection(m, p, strings.TrimSuffix(shape, "]")) } @@ -71,16 +73,16 @@ func (m *migration) typeModifier(p *sysmlv1.Element) *typeModifier { // collection maps [] and [n] to a multiplicity, on a declaration of one value. func (tm *typeModifier) collection(m *migration, p *sysmlv1.Element, n string) { if !strings.HasSuffix(tm.text, "]") || (n != "" && !isNatural(n)) { - tm.refused = "the type modifier " + tm.text + " is not one the migrator reads" + tm.refused = typeModifierSubject + tm.text + " is not one the migrator reads" return } mult, note := m.declaredMultiplicity(p) if note != "" { - tm.refused = "the type modifier " + tm.text + " has no v2 form: the declared " + note + tm.refused = typeModifierSubject + tm.text + " has no v2 form: the declared " + note return } if mult != "" { - tm.refused = "the type modifier " + tm.text + " has no v2 form: the declared multiplicity " + mult + " is already a collection, and a collection of collections has no multiplicity" + tm.refused = typeModifierSubject + tm.text + " has no v2 form: the declared multiplicity " + mult + " is already a collection, and a collection of collections has no multiplicity" return } if n == "" { @@ -93,7 +95,7 @@ func (tm *typeModifier) collection(m *migration, p *sysmlv1.Element, n string) { // reference maps * and & to a reference usage, on a property typed by a block. func (tm *typeModifier) reference(m *migration, p *sysmlv1.Element) { if p.Type != "Property" || p.Parent == nil { - tm.refused = "the type modifier " + tm.text + " has no v2 form: a parameter is not held by reference" + tm.refused = typeModifierSubject + tm.text + " has no v2 form: a parameter is not held by reference" return } cat, _ := m.classify(p.Parent) @@ -101,7 +103,7 @@ func (tm *typeModifier) reference(m *migration, p *sysmlv1.Element) { case "part", "item": tm.ref = true default: - tm.refused = "the type modifier " + tm.text + " has no v2 form: only a part or item is held by reference, not " + kwArticle(kw) + tm.refused = typeModifierSubject + tm.text + " has no v2 form: only a part or item is held by reference, not " + kwArticle(kw) } } diff --git a/internal/translate/migrate/views.go b/internal/translate/migrate/views.go index fd3307b9a..d81fdd657 100644 --- a/internal/translate/migrate/views.go +++ b/internal/translate/migrate/views.go @@ -71,7 +71,7 @@ func (m *migration) viewpointNote(vp, view *sysmlv1.Element) string { case vp == nil: return "the viewpoint is not in the document" case vp.IsProxy(): - return "the viewpoint " + qualifiedName(vp) + " lives outside the document and is not written" + return "the viewpoint " + qualifiedName(vp) + livesOutsideDocument case !m.written(vp): _, why := m.classify(vp) return joinNotes("the viewpoint "+qualifiedName(vp)+" is not migrated", why) @@ -131,7 +131,7 @@ func (m *migration) taggedViewpoints(e *sysmlv1.Element) { for _, id := range v.IDs(tag) { vp := m.model.Lookup(id) if vp == nil { - m.downgrade(e, "the "+tag+" tag names "+id+", which is not in the document") + m.downgrade(e, "the "+tag+" tag names "+id+notInDocument) continue } if note := m.viewpointNote(vp, e); note != "" { @@ -143,6 +143,13 @@ func (m *migration) taggedViewpoints(e *sysmlv1.Element) { } } +const ( + livesOutsideDocument = " lives outside the document and is not written" + notInDocument = ", which is not in the document" + exposedSubject = "the exposed " + stakeholderSubject = "the stakeholder " +) + // viewpointTags are the «View» tags naming the viewpoint a view conforms to. var viewpointTags = []string{"viewpoint", "viewPoint"} @@ -242,10 +249,10 @@ func (m *migration) placeExpose(d *sysmlv1.Element) { func (m *migration) exposeNote(s *sysmlv1.Element) string { switch { case s.IsProxy(): - return "the exposed " + qualifiedName(s) + " lives outside the document and is not written" + return exposedSubject + qualifiedName(s) + livesOutsideDocument case !m.written(s): _, why := m.classify(s) - return joinNotes("the exposed "+kindOf(s)+" "+describe(s)+" is not migrated and is not written", why) + return joinNotes(exposedSubject+kindOf(s)+" "+describe(s)+" is not migrated and is not written", why) } return "" } @@ -273,7 +280,7 @@ func (m *migration) absentNote(id string) string { if name == "" { name = id } - return "the exposed " + strings.TrimPrefix(el.Type, "uml:") + " '" + name + "' is notation the tool keeps outside the model, which v2 does not carry" + return exposedSubject + strings.TrimPrefix(el.Type, "uml:") + " '" + name + "' is notation the tool keeps outside the model, which v2 does not carry" } } return "the exposed element " + id + " is not in the document" @@ -357,7 +364,7 @@ func (m *migration) framedComments(e *sysmlv1.Element) []*sysmlv1.Element { for _, id := range vp.IDs("concernList") { c := m.model.Lookup(id) if c == nil { - m.downgrade(e, "the concernList tag names "+id+", which is not in the document") + m.downgrade(e, "the concernList tag names "+id+notInDocument) continue } if c.Type != "Comment" { @@ -392,17 +399,17 @@ func (m *migration) stakeholder(vp *sysmlv1.Element, id string) { s := m.model.Lookup(id) switch { case s == nil: - m.downgrade(vp, "the stakeholder tag names "+id+", which is not in the document") + m.downgrade(vp, "the stakeholder tag names "+id+notInDocument) return case s.IsProxy(): - m.downgrade(vp, "the stakeholder "+qualifiedName(s)+" lives outside the document and is not written") + m.downgrade(vp, stakeholderSubject+qualifiedName(s)+livesOutsideDocument) return case !m.written(s): - m.downgrade(vp, "the stakeholder "+qualifiedName(s)+" is not migrated and is not written") + m.downgrade(vp, stakeholderSubject+qualifiedName(s)+" is not migrated and is not written") return } if cat, _ := m.classify(s); cat != catPartDef { - m.downgrade(vp, "the stakeholder "+qualifiedName(s)+" becomes a "+cat.keyword()+", which cannot type a stakeholder") + m.downgrade(vp, stakeholderSubject+qualifiedName(s)+" becomes a "+cat.keyword()+", which cannot type a stakeholder") return } name := m.freshName(vp, lowerFirst(m.nameFor(s))) diff --git a/internal/translate/xmi/sysmlv1/diagram_test.go b/internal/translate/xmi/sysmlv1/diagram_test.go index 3babea1b8..e77481ab2 100644 --- a/internal/translate/xmi/sysmlv1/diagram_test.go +++ b/internal/translate/xmi/sysmlv1/diagram_test.go @@ -90,11 +90,11 @@ func TestDiagramIsRead(t *testing.T) { // Each id once, in the order the tool listed them, resolved when defined; // hrefs and text spell the same element. An href into another document // resolves to the proxy the model already holds for it, or dangles. - const real = "http://www.omg.org/spec/UML/20131001/PrimitiveTypes.xmi#Real" - if got := ids(d.Shown); got != "_a _a_b _missing? _b "+real+" http://www.example.com/Unread.xmi#_elsewhere?" { + const realURI = "http://www.omg.org/spec/UML/20131001/PrimitiveTypes.xmi#Real" + if got := ids(d.Shown); got != "_a _a_b _missing? _b "+realURI+" http://www.example.com/Unread.xmi#_elsewhere?" { t.Errorf("shown = %q", got) } - if d.Shown[0].Element != m.Lookup("_a") || d.Shown[1].Element != m.Lookup("_a_b") || d.Shown[4].Element != m.Lookup(real) || !d.Shown[4].Element.IsProxy() { + if d.Shown[0].Element != m.Lookup("_a") || d.Shown[1].Element != m.Lookup("_a_b") || d.Shown[4].Element != m.Lookup(realURI) || !d.Shown[4].Element.IsProxy() { t.Errorf("shown elements = %+v", d.Shown) } // The diagram is no element of the model, and no skipped extension diff --git a/internal/translate/xmi/sysmlv1/docgen.go b/internal/translate/xmi/sysmlv1/docgen.go index 0fe0e3984..1c59a3da2 100644 --- a/internal/translate/xmi/sysmlv1/docgen.go +++ b/internal/translate/xmi/sysmlv1/docgen.go @@ -470,7 +470,7 @@ func (m *Model) flows(a *Element, from, to string) map[*Element][]*Element { // walk follows single control flows from cur until the chain ends or stop // is reached; it returns the steps and why the chain ended, "" when cleanly. -func (w *chainWalker) walk(cur *Element, stop *Element) ([]*DocGenStep, string) { +func (w *chainWalker) walk(cur, stop *Element) ([]*DocGenStep, string) { if w.seen == nil { w.seen = map[*Element]bool{} } diff --git a/internal/translate/xmi/sysmlv1/tables.go b/internal/translate/xmi/sysmlv1/tables.go index e4b4ef483..2cd1c68b5 100644 --- a/internal/translate/xmi/sysmlv1/tables.go +++ b/internal/translate/xmi/sysmlv1/tables.go @@ -241,7 +241,7 @@ func (m *Model) matrix(s, filter *Stereotype) *Table { t.ColumnTypes = m.TagRefs(filter, "columnElementType") t.IncludeColumnSubtypes = flag(filter, "includeSubtypesOfColumnTypes") for _, tag := range []string{"rowQuery", "columnQuery"} { - if v := filter.Tag(tag); v != "" { + if filter.Tag(tag) != "" { t.malformed(tag, "", "a structured query selects the elements") } } diff --git a/tools/referee/fuml/emit.go b/tools/referee/fuml/emit.go index b13ca68e9..d36115f6d 100644 --- a/tools/referee/fuml/emit.go +++ b/tools/referee/fuml/emit.go @@ -68,7 +68,7 @@ func Emit(a *Activity) (*Emitted, error) { } for _, o := range cl.objects { if names[o.name] { - return nil, &TranslateError{a.Name, "class " + o.name, "shares its name with an activity in the call closure"} + return nil, &TranslateError{a.Name, classKeyword + o.name, "shares its name with an activity in the call closure"} } names[o.name] = true } @@ -223,6 +223,13 @@ func (cl *closure) startsBehavior(o *objectDef) bool { // behavior; a start performs `object.classifierBehavior.start`. const startMember = "classifierBehavior" +const ( + classKeyword = "class " + noClassOfModel = ", which is no class of the model" + noSignalOfModel = ", which is no signal of the model" + scalarValuesPrefix = "ScalarValues::" +) + // closureOf collects everything the activity's translation declares. An // activity is an object's classifier when something creates an object of it or // types an object by it; one the translation also performs as an action, the @@ -412,7 +419,7 @@ func (cl *closure) classifiers() error { seen[c] = true for _, g := range c.Generals { if m.ClassOf(g) == nil { - return &TranslateError{root.Name, "class " + c.Name, "generalizes " + g.String() + ", which is no class of the model"} + return &TranslateError{root.Name, classKeyword + c.Name, "generalizes " + g.String() + noClassOfModel} } if err := visit(g); err != nil { return err @@ -466,19 +473,19 @@ func (cl *closure) addObject(o *objectDef) error { members := map[string]bool{startMember: o.classifier != nil} for _, p := range o.attributes { if members[p.Name] { - return &TranslateError{cl.root.Name, "class " + o.name, "has an attribute named " + p.Name + ", as the usage binding its classifier behavior is"} + return &TranslateError{cl.root.Name, classKeyword + o.name, "has an attribute named " + p.Name + ", as the usage binding its classifier behavior is"} } members[p.Name] = true } for _, b := range o.behaviors { name := o.behaviorName(b) if members[name] { - return &TranslateError{cl.root.Name, "class " + o.name, "has an attribute named " + name + ", as its owned behavior is"} + return &TranslateError{cl.root.Name, classKeyword + o.name, "has an attribute named " + name + ", as its owned behavior is"} } members[name] = true for _, p := range b.Parameters { if members[p.Name] { - return &TranslateError{cl.root.Name, "class " + o.name, "has a member named " + p.Name + ", as a parameter of its behavior " + name + " is"} + return &TranslateError{cl.root.Name, classKeyword + o.name, "has a member named " + p.Name + ", as a parameter of its behavior " + name + " is"} } } cl.owner[b] = o @@ -512,7 +519,7 @@ func (cl *closure) signalDefs() error { seen[sg] = true for _, g := range sg.Generals { if m.SignalOf(g) == nil { - return &TranslateError{root.Name, "signal " + sg.Name, "generalizes " + g.String() + ", which is no signal of the model"} + return &TranslateError{root.Name, "signal " + sg.Name, "generalizes " + g.String() + noSignalOfModel} } if err := visit(g); err != nil { return err @@ -758,17 +765,17 @@ func (m *Model) scalar(name string) string { } for _, c := range m.Classes { if c.Name == name { - return "ScalarValues::" + name + return scalarValuesPrefix + name } } for _, sg := range m.Signals { if sg.Name == name { - return "ScalarValues::" + name + return scalarValuesPrefix + name } } for _, a := range m.Activities { if a.Name == name { - return "ScalarValues::" + name + return scalarValuesPrefix + name } } return name @@ -1321,7 +1328,7 @@ func (s *scope) createNode(n *Node) error { } o := e.cl.objectOf(n.Classifier) if o == nil { - return e.fail(n.Label(), "creates a "+n.Classifier.String()+", which is no class of the model") + return e.fail(n.Label(), "creates a "+n.Classifier.String()+noClassOfModel) } name := s.names.name(nodeName(n)) s.pins[outs[0]] = "result" @@ -1379,7 +1386,7 @@ func (s *scope) startNode(n *Node) error { t := orType(object.Type, flowedType(object, map[*Node]bool{})) o := e.cl.objectOf(t) if o == nil { - return e.fail(n.Label(), "starts a "+t.String()+", which is no class of the model") + return e.fail(n.Label(), "starts a "+t.String()+noClassOfModel) } if !e.cl.startsBehavior(o) { return e.fail(n.Label(), "starts an object of "+o.name+", which has no classifier behavior") @@ -1404,7 +1411,7 @@ func (s *scope) featureNode(n *Node) error { return e.fail(n.Label(), untranslated("an association end")) } if e.cl.objectOf(f.Owner) == nil && !e.performance(f.Owner) { - return e.fail(n.Label(), "touches a feature of "+f.Owner.String()+", which is no class of the model") + return e.fail(n.Label(), "touches a feature of "+f.Owner.String()+noClassOfModel) } pins := map[string]*Node{} for _, p := range n.Pins { @@ -1481,7 +1488,7 @@ func (s *scope) sendNode(n *Node) error { e := s.e sg := e.a.Model.SignalOf(n.Signal) if sg == nil { - return e.fail(n.Label(), "sends "+n.Signal.String()+", which is no signal of the model") + return e.fail(n.Label(), "sends "+n.Signal.String()+noSignalOfModel) } if len(n.Outputs()) != 0 { return e.fail(n.Label(), "a send signal action has no result pin") @@ -1550,7 +1557,7 @@ func (s *scope) acceptNode(n *Node) error { } sg := e.a.Model.SignalOf(tr.Signal) if sg == nil { - return e.fail(n.Label(), "accepts "+tr.Signal.String()+", which is no signal of the model") + return e.fail(n.Label(), "accepts "+tr.Signal.String()+noSignalOfModel) } if len(n.Inputs()) != 0 { return e.fail(n.Label(), "an accept event action has no input pin")