Repository: mitchellh/mapstructure (go)

Issue:
Squash decoder option squashes everything
The squash decoder option should squash only embedded structs as documented, but it squashes all struct fields regardless of being embedded or not.

The proposed fix is trivial and was implemented in one of the similar PRs (https://github.com/mitchellh/mapstructure/pull/134): test for `fieldType.Anonymous` flag when this option is enabled.

However, this would be a breaking change since some users might rely on the "squash everything" behavior of the option already. I will create a PR with a straightforward fix but not sure if such a breaking change is acceptable in this repo.  

Alternatively, the `config.Squash` option could be complemented with something along the lines of `config.SquashEmbedded`.

Test case to demonstrate the issue:
```go
package mapstructure_test

import (
	"testing"

	"github.com/mitchellh/mapstructure"
)

type (
	Config struct {
		Nested        // expected to be squashed
		Named  Nested // expected to not be squashed
	}
	Nested struct {
		Value string
	}
)

func TestDecode_SquashOption(t *testing.T) {
	input := map[string]interface{}{
		"Value": "foo",
		"Named": map[string]interface{}{
			"Value": "bar",
		},
	}

	result := Config{}
	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Result: &result,
		Squash: true,
	})
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	err = decoder.Decode(input)
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	if result.Nested.Value != "foo" {
		t.Errorf("Nested.Value value should be 'foo': %#v", result.Nested.Value)
	}
	if result.Named.Value != "bar" {
		t.Errorf("Named.Value value should be 'bar': %#v", result.Named.Value)
	}
}

func TestDecodeFrom_SquashOption(t *testing.T) {
	input := Config{
		Nested: Nested{Value: "foo"},
		Named:  Nested{Value: "bar"},
	}

	result := map[string]interface{}{}
	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Result: &result,
		Squash: true,
	})
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	err = decoder.Decode(input)
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	if v, ok := result["Value"]; !ok {
		t.Error("Value (from an embedded field) should be present in map")
	} else if v != "foo" {
		t.Errorf("Value (from an embedded field) should be 'foo': %#v", v)
	}

	if v, ok := result["Named"]; !ok {
		t.Error("Named field should be present in map")
	} else {
		named := v.(map[string]interface{})
		if v, ok := named["Value"]; !ok {
			t.Error("Named: Value should be present in map")
		} else if v != "bar" {
			t.Errorf("Named: Value should be 'bar': %#v", v)
		}
	}
}
```

Pull request:
Fix squash decoder option to squash only embedded fields
Fixes #193

Patch:
diff --git a/mapstructure.go b/mapstructure.go
index 5e9408d..bae0c39 100644
--- a/mapstructure.go
+++ b/mapstructure.go
@@ -843,7 +843,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
 		keyName := f.Name
 
 		// If Squash is set in the config, we squash the field down.
-		squash := d.config.Squash && v.Kind() == reflect.Struct
+		squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
 		// Determine the name of the key in the map
 		if index := strings.Index(tagValue, ","); index != -1 {
 			if tagValue[:index] == "-" {
@@ -1187,7 +1187,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
 			fieldKind := fieldType.Type.Kind()
 
 			// If "squash" is specified in the tag, we squash the field down.
-			squash := d.config.Squash && fieldKind == reflect.Struct
+			squash := d.config.Squash && fieldKind == reflect.Struct && fieldType.Anonymous
 			remain := false
 
 			// We always parse the tags cause we're looking for other tags too


Test patch:
diff --git a/mapstructure_test.go b/mapstructure_test.go
index 97c8e90..7274f14 100644
--- a/mapstructure_test.go
+++ b/mapstructure_test.go
@@ -61,6 +61,12 @@ type EmbeddedSquash struct {
 	Vunique string
 }
 
+type EmbeddedAndNamed struct {
+	Basic
+	Named   Basic
+	Vunique string
+}
+
 type SliceAlias []string
 
 type EmbeddedSlice struct {
@@ -612,9 +618,12 @@ func TestDecode_EmbeddedSquashConfig(t *testing.T) {
 	input := map[string]interface{}{
 		"vstring": "foo",
 		"vunique": "bar",
+		"Named": map[string]interface{}{
+			"vstring": "baz",
+		},
 	}
 
-	var result Embedded
+	var result EmbeddedAndNamed
 	config := &DecoderConfig{
 		Squash: true,
 		Result: &result,
@@ -637,6 +646,66 @@ func TestDecode_EmbeddedSquashConfig(t *testing.T) {
 	if result.Vunique != "bar" {
 		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
 	}
+
+	if result.Named.Vstring != "baz" {
+		t.Errorf("Named.vstring value should be 'baz': %#v", result.Named.Vstring)
+	}
+}
+
+func TestDecodeFrom_EmbeddedSquashConfig(t *testing.T) {
+	t.Parallel()
+
+	input := EmbeddedAndNamed{
+		Basic:   Basic{Vstring: "foo"},
+		Named:   Basic{Vstring: "baz"},
+		Vunique: "bar",
+	}
+
+	result := map[string]interface{}{}
+	config := &DecoderConfig{
+		Squash: true,
+		Result: &result,
+	}
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if _, ok := result["Basic"]; ok {
+		t.Error("basic should not be present in map")
+	}
+
+	v, ok := result["Vstring"]
+	if !ok {
+		t.Error("vstring should be present in map")
+	} else if !reflect.DeepEqual(v, "foo") {
+		t.Errorf("vstring value should be 'foo': %#v", v)
+	}
+
+	v, ok = result["Vunique"]
+	if !ok {
+		t.Error("vunique should be present in map")
+	} else if !reflect.DeepEqual(v, "bar") {
+		t.Errorf("vunique value should be 'bar': %#v", v)
+	}
+
+	v, ok = result["Named"]
+	if !ok {
+		t.Error("Named should be present in map")
+	} else {
+		named := v.(map[string]interface{})
+		v, ok := named["Vstring"]
+		if !ok {
+			t.Error("Named: vstring should be present in map")
+		} else if !reflect.DeepEqual(v, "baz") {
+			t.Errorf("Named: vstring should be 'baz': %#v", v)
+		}
+	}
 }
 
 func TestDecode_SquashOnNonStructType(t *testing.T) {