This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git

commit 0d173ab42bef38bea76d269c35d6c79b87fd8684
Author: Szymon Janc <[email protected]>
AuthorDate: Mon Oct 19 17:54:35 2026 +0200

    newt/ycfg: Warn about ignored non-map values
    
    Values of keys that must be maps (e.g., syscfg.defs, syscfg.vals,
    pkg.init) were silently ignored if they were not maps. Common
    mistakes are "syscfg.vals: [FOO]" lists and flattened keys like
    "syscfg.vals.FOO: 1", where FOO is a condition, not a setting name.
    Warn about such values, including values under conditions which are
    false.
    
    Assisted-by: Claude:claude-opus-5-5
---
 newt/error_reporting_test.go | 47 ++++++++++++++++++++++++++++++++++++++++++++
 newt/ycfg/ycfg.go            | 37 ++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+)

diff --git a/newt/error_reporting_test.go b/newt/error_reporting_test.go
index 299f1fba..9d7ab1c1 100644
--- a/newt/error_reporting_test.go
+++ b/newt/error_reporting_test.go
@@ -171,3 +171,50 @@ func TestErrorReportingInfoUnknownRepo(t *testing.T) {
                }
        }
 }
+
+// Values that must be maps but are not (e.g., a setting value written as a
+// flattened "syscfg.vals.FOO" key) are ignored; newt must warn about them.
+func TestErrorReportingNonMapSyscfg(t *testing.T) {
+       tests := []struct {
+               name   string
+               syscfg string
+               msg    string
+       }{
+               {
+                       name:   "flattened-val",
+                       syscfg: "syscfg.vals.FOO: 1\n",
+                       msg: `ignoring "syscfg.vals.FOO"; "FOO" is a condition 
` +
+                               "and value must be a map",
+               },
+               {
+                       name:   "vals-list",
+                       syscfg: "syscfg.vals:\n    - FOO\n",
+                       msg:    `ignoring "syscfg.vals"; value must be a map`,
+               },
+               {
+                       name:   "defs-list",
+                       syscfg: "syscfg.defs:\n    - BAR\n",
+                       msg:    `ignoring "syscfg.defs"; value must be a map`,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       dir := copyToTemp(t, errorReportingProjDir)
+                       errorReportingWriteFiles(t, dir, map[string]string{
+                               "targets/app/syscfg.yml": tt.syscfg,
+                       })
+
+                       out, err := newtCombinedOutput(dir, nil, "target", 
"config",
+                               "show", "app")
+                       if err != nil {
+                               t.Fatalf("newt target config show failed: 
%s\n%s", err, out)
+                       }
+                       if !strings.Contains(out, "WARNING: ") ||
+                               !strings.Contains(out, tt.msg) {
+
+                               t.Errorf("missing warning %q\n%s", tt.msg, out)
+                       }
+               })
+       }
+}
diff --git a/newt/ycfg/ycfg.go b/newt/ycfg/ycfg.go
index 53571175..b588b235 100644
--- a/newt/ycfg/ycfg.go
+++ b/newt/ycfg/ycfg.go
@@ -453,9 +453,46 @@ func (yc *YCfg) GetValStringSliceNonempty(
 // values to type map[string]interface{}.  The returned map[string]YCfgEntry is
 // formed from the union of all these maps.  The returned error is a set of
 // warnings just as in `Get`.
+// warnNonMapValues warns about values of the specified key, and of its
+// conditional variants, that are not maps. Such values are ignored when a map
+// is expected. For example, "syscfg.vals.FOO: 1" is "syscfg.vals" conditional
+// on FOO, not a value of setting FOO, and "syscfg.vals: [FOO]" is a list.
+func (yc *YCfg) warnNonMapValues(key string) {
+       node := yc.find(key)
+       if node == nil {
+               return
+       }
+
+       isMapOrEmpty := func(val interface{}) bool {
+               if val == nil {
+                       return true
+               }
+               if s, ok := val.(string); ok && s == "" {
+                       return true
+               }
+               _, err := cast.ToStringMapE(val)
+               return err == nil
+       }
+
+       if !isMapOrEmpty(node.Value) {
+               util.OneTimeWarning("%s: ignoring \"%s\"; value must be a map, 
"+
+                       "not %T", yc.name, key, node.Value)
+       }
+
+       for _, child := range node.Children {
+               if !isMapOrEmpty(child.Value) {
+                       util.OneTimeWarning("%s: ignoring \"%s\"; \"%s\" is a 
condition "+
+                               "and value must be a map, not %T", yc.name,
+                               child.FullName(), child.Name, child.Value)
+               }
+       }
+}
+
 func (yc *YCfg) GetStringMap(
        key string, settings *cfgv.Settings) (map[string]YCfgEntry, error) {
 
+       yc.warnNonMapValues(key)
+
        mapEntries, getErr := yc.Get(key, settings)
        if len(mapEntries) == 0 {
                return nil, getErr

Reply via email to