Repository: goccy/go-yaml
Pull request #474 (2024-09-27 20:00:24)

Fix "null" string encoded as nil
This PR fixing this open issue: [#413 ](https://github.com/goccy/go-yaml/issues/413)

**Solution**: incorporate each keyword from reservedNullKeywords into reservedEncKeywordMap to indicate that null keywords should be enclosed in quotes. This ensures that only Golang's nil is represented as YAML null.

Currently, any string from reservedNullKeywords is being encoded as YAML null, which is not the intended behavior and may cause confusion. Because users use Golang's nil if they want YAML null and for some reason "null" string if they want string in yaml document

This fix reverts the some behavior that was erroneously changed in this [this commit](https://github.com/goccy/go-yaml/commit/f3319d6c809402b2e4a64de82097d6e7faacfb0c). Prior to this commit, only Golang's nil was represented as YAML null.

codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/goccy/go-yaml/pull/474?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 78.79%. Comparing base [(`3a90d71`)](https://app.codecov.io/gh/goccy/go-yaml/commit/3a90d7186f4be93a1ad3575c36d5eebcc4a50181?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) to head [(`fa39d09`)](https://app.codecov.io/gh/goccy/go-yaml/commit/fa39d09da8aefce293847bc0c924f024ba23f561?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima).

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #474      +/-   ##
==========================================
+ Coverage   78.78%   78.79%   +0.01%     
==========================================
  Files          13       13              
  Lines        4007     4009       +2     
==========================================
+ Hits         3157     3159       +2     
  Misses        591      591              
  Partials      259      259              
```

</details>
Nikscorp: @goccy can you review this pr please?

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 74140f3..27337dd 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -5,12 +5,26 @@ on:
       - master
   pull_request:
 jobs:
+  lint:
+    name: lint
+    runs-on: ubuntu-latest
+    steps:
+      - name: checkout
+        uses: actions/checkout@v4
+      - name: setup Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: "1.23"
+      - name: run linters
+        run: |
+          make lint
+
   race-test:
     name: Test with -race
     strategy:
       matrix:
         os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
-        go-version: [ "1.19", "1.20", "1.21" ]
+        go-version: [ "1.21", "1.22", "1.23" ]
     runs-on: ${{ matrix.os }}
     steps:
     - name: checkout
@@ -28,7 +42,7 @@ jobs:
     strategy:
       matrix:
         os: [ "ubuntu-latest", "windows-latest" ]
-        go-version: [ "1.19", "1.20", "1.21" ]
+        go-version: [ "1.21", "1.22", "1.23" ]
     runs-on: ${{ matrix.os }}
     env:
       GOARCH: "386"
@@ -52,7 +66,7 @@ jobs:
       - name: setup Go
         uses: actions/setup-go@v4
         with:
-          go-version: "1.21"
+          go-version: "1.23"
       - name: build
         run: |
           make ycat/build
@@ -69,7 +83,7 @@ jobs:
       - name: setup Go
         uses: actions/setup-go@v4
         with:
-          go-version: "1.21"
+          go-version: "1.23"
       - name: measure coverage
         run: |
           make cover
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e660fd9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,1 @@
+bin/
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..ec8d731
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,42 @@
+run:
+  timeout: 5m
+
+linters-settings:
+  errcheck:
+    check-type-assertions: true
+  gci:
+    sections:
+      - "standard"
+      - "default"
+      - "prefix(github.com/goccy/go-yaml)"
+      - "blank"
+      - "dot"
+  gofmt:
+    simplify: true
+  govet:
+    disable:
+      - tests
+  misspell:
+    locale: US
+  staticcheck:
+    checks: ["all", "-ST1000", "-ST1005"]
+
+linters:
+  disable-all: true
+  enable:
+    - errcheck
+    - gci
+    - gofmt
+    - gosimple
+    - govet
+    - ineffassign
+    - misspell
+    - staticcheck
+    - typecheck
+    - unused
+
+issues:
+  exclude-rules:
+    - path: _test\.go
+      linters:
+        - staticcheck
diff --git a/Makefile b/Makefile
index 1b1d923..02a38dd 100644
--- a/Makefile
+++ b/Makefile
@@ -17,3 +17,31 @@ cover-html: cover
 .PHONY: ycat/build
 ycat/build:
 	go build -o ycat ./cmd/ycat
+
+.PHONY: lint
+lint: golangci-lint ## Run golangci-lint
+	@$(GOLANGCI_LINT) run
+
+.PHONY: fmt
+fmt: golangci-lint ## Ensure consistent code style
+	@go mod tidy
+	@go fmt ./...
+	@$(GOLANGCI_LINT) run --fix
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+	mkdir -p $(LOCALBIN)
+
+## Tool Binaries
+GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
+
+## Tool Versions
+GOLANGCI_VERSION := 1.61.0
+
+.PHONY: golangci-lint
+.PHONY: $(GOLANGCI_LINT)
+golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
+$(GOLANGCI_LINT): $(LOCALBIN)
+	@test -s $(LOCALBIN)/golangci-lint && $(LOCALBIN)/golangci-lint version --format short | grep -q $(GOLANGCI_VERSION) || \
+	curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(LOCALBIN) v$(GOLANGCI_VERSION)
diff --git a/ast/ast.go b/ast/ast.go
index b4d5ec4..66ddfd9 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -7,8 +7,9 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/goccy/go-yaml/token"
 	"golang.org/x/xerrors"
+
+	"github.com/goccy/go-yaml/token"
 )
 
 var (
@@ -2092,10 +2093,10 @@ func Merge(dst Node, src Node) error {
 	err := &ErrInvalidMergeType{dst: dst, src: src}
 	switch dst.Type() {
 	case DocumentType:
-		node := dst.(*DocumentNode)
+		node, _ := dst.(*DocumentNode)
 		return Merge(node.Body, src)
 	case MappingType:
-		node := dst.(*MappingNode)
+		node, _ := dst.(*MappingNode)
 		target, ok := src.(*MappingNode)
 		if !ok {
 			return err
@@ -2103,7 +2104,7 @@ func Merge(dst Node, src Node) error {
 		node.Merge(target)
 		return nil
 	case SequenceType:
-		node := dst.(*SequenceNode)
+		node, _ := dst.(*SequenceNode)
 		target, ok := src.(*SequenceNode)
 		if !ok {
 			return err
diff --git a/cmd/ycat/ycat.go b/cmd/ycat/ycat.go
index dbac4df..b810b6f 100644
--- a/cmd/ycat/ycat.go
+++ b/cmd/ycat/ycat.go
@@ -6,10 +6,11 @@ import (
 	"os"
 
 	"github.com/fatih/color"
+	"github.com/mattn/go-colorable"
+
 	"github.com/goccy/go-yaml"
 	"github.com/goccy/go-yaml/lexer"
 	"github.com/goccy/go-yaml/printer"
-	"github.com/mattn/go-colorable"
 )
 
 const escape = "\x1b"
@@ -71,7 +72,7 @@ func _main(args []string) error {
 		}
 	}
 	writer := colorable.NewColorableStdout()
-	writer.Write([]byte(p.PrintTokens(tokens) + "\n"))
+	_, _ = writer.Write([]byte(p.PrintTokens(tokens) + "\n"))
 	return nil
 }
 
diff --git a/decode.go b/decode.go
index 04b94bc..ce2c751 100644
--- a/decode.go
+++ b/decode.go
@@ -100,7 +100,7 @@ func (d *Decoder) castToFloat(v interface{}) interface{} {
 
 func (d *Decoder) mergeValueNode(value ast.Node) ast.Node {
 	if value.Type() == ast.AliasType {
-		aliasNode := value.(*ast.AliasNode)
+		aliasNode, _ := value.(*ast.AliasNode)
 		aliasName := aliasNode.Value.GetToken().Value
 		return d.anchorNodeMap[aliasName]
 	}
@@ -360,7 +360,7 @@ func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
 			if err != nil {
 				return nil, err
 			}
-			n.Values[idx] = value.(*ast.MappingValueNode)
+			n.Values[idx], _ = value.(*ast.MappingValueNode)
 		}
 	case *ast.TagNode:
 		value, err := d.resolveAlias(n.Value)
@@ -389,7 +389,7 @@ func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
 			if err != nil {
 				return nil, err
 			}
-			n.Key = key.(ast.MapKeyNode)
+			n.Key, _ = key.(ast.MapKeyNode)
 			value, err := d.resolveAlias(n.Value)
 			if err != nil {
 				return nil, err
@@ -476,15 +476,6 @@ func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) {
 	return arrayNode, nil
 }
 
-func (d *Decoder) fileToNode(f *ast.File) ast.Node {
-	for _, doc := range f.Docs {
-		if v := d.nodeToValue(doc.Body); v != nil {
-			return doc.Body
-		}
-	}
-	return nil
-}
-
 func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) {
 	if typ.Kind() != reflect.String {
 		if !v.Type().ConvertibleTo(typ) {
@@ -590,7 +581,7 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
 		}
 
 		if structField.IsInline {
-			d.deleteStructKeys(field.Type, unknownFields)
+			_ = d.deleteStructKeys(field.Type, unknownFields)
 		} else {
 			delete(unknownFields, structField.RenderName)
 		}
@@ -1275,7 +1266,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
 				}
 				continue
 			}
-			d.setDefaultValueIfConflicted(newFieldValue, structFieldMap)
+			_ = d.setDefaultValueIfConflicted(newFieldValue, structFieldMap)
 			fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type()))
 			continue
 		}
@@ -1623,13 +1614,13 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) {
 
 func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) {
 	readers := []io.Reader{}
-	if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+	if err := filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error {
 		if !d.isYAMLFile(path) {
 			return nil
 		}
-		reader, err := d.fileToReader(path)
-		if err != nil {
-			return errors.Wrapf(err, "failed to get reader")
+		reader, readerErr := d.fileToReader(path)
+		if readerErr != nil {
+			return errors.Wrapf(readerErr, "failed to get reader")
 		}
 		readers = append(readers, reader)
 		return nil
diff --git a/encode.go b/encode.go
index 3b9b298..f54040f 100644
--- a/encode.go
+++ b/encode.go
@@ -12,12 +12,13 @@ import (
 	"strings"
 	"time"
 
+	"golang.org/x/xerrors"
+
 	"github.com/goccy/go-yaml/ast"
 	"github.com/goccy/go-yaml/internal/errors"
 	"github.com/goccy/go-yaml/parser"
 	"github.com/goccy/go-yaml/printer"
 	"github.com/goccy/go-yaml/token"
-	"golang.org/x/xerrors"
 )
 
 const (
@@ -93,10 +94,10 @@ func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error {
 		e.written = true
 	} else {
 		// write document separator
-		e.writer.Write([]byte("---\n"))
+		_, _ = e.writer.Write([]byte("---\n"))
 	}
 	var p printer.Printer
-	e.writer.Write(p.PrintNode(node))
+	_, _ = e.writer.Write(p.PrintNode(node))
 	return nil
 }
 
diff --git a/error.go b/error.go
index 163dcc5..26935eb 100644
--- a/error.go
+++ b/error.go
@@ -1,8 +1,9 @@
 package yaml
 
 import (
-	"github.com/goccy/go-yaml/ast"
 	"golang.org/x/xerrors"
+
+	"github.com/goccy/go-yaml/ast"
 )
 
 var (
diff --git a/internal/errors/error.go b/internal/errors/error.go
index 7f1ea9a..1a137c8 100644
--- a/internal/errors/error.go
+++ b/internal/errors/error.go
@@ -5,9 +5,10 @@ import (
 	"fmt"
 	"reflect"
 
+	"golang.org/x/xerrors"
+
 	"github.com/goccy/go-yaml/printer"
 	"github.com/goccy/go-yaml/token"
-	"golang.org/x/xerrors"
 )
 
 const (
@@ -119,7 +120,7 @@ func (e *wrapError) FormatError(p xerrors.Printer) error {
 	}
 	e.chainStateAndVerb(err)
 	if fmtErr, ok := err.(xerrors.Formatter); ok {
-		fmtErr.FormatError(p)
+		_ = fmtErr.FormatError(p)
 	} else {
 		p.Print(err)
 	}
@@ -143,6 +144,7 @@ func (s *wrapState) Precision() (prec int, ok bool) {
 }
 
 func (s *wrapState) Flag(c int) bool {
+	//nolint:gosimple
 	// set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text
 	if c == '#' {
 		// ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail.
diff --git a/parser/context.go b/parser/context.go
index 42cc4f8..c4752f9 100644
--- a/parser/context.go
+++ b/parser/context.go
@@ -110,13 +110,6 @@ func (c *context) nextToken() *token.Token {
 	return c.tokens[c.idx+1]
 }
 
-func (c *context) afterNextToken() *token.Token {
-	if c.idx+2 >= c.size {
-		return nil
-	}
-	return c.tokens[c.idx+2]
-}
-
 func (c *context) nextNotCommentToken() *token.Token {
 	for i := c.idx + 1; i < c.size; i++ {
 		tk := c.tokens[i]
diff --git a/parser/parser.go b/parser/parser.go
index 2bec5fe..09b3885 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -5,11 +5,12 @@ import (
 	"os"
 	"strings"
 
+	"golang.org/x/xerrors"
+
 	"github.com/goccy/go-yaml/ast"
 	"github.com/goccy/go-yaml/internal/errors"
 	"github.com/goccy/go-yaml/lexer"
 	"github.com/goccy/go-yaml/token"
-	"golang.org/x/xerrors"
 )
 
 type parser struct{}
@@ -175,7 +176,7 @@ func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken
 		nullNode := ast.Null(nullToken)
 
 		if comment != nil {
-			nullNode.SetComment(comment)
+			_ = nullNode.SetComment(comment)
 		} else {
 			// If there is a comment, it is already bound to the key node,
 			// so remove the comment from the key to bind it to the null value.
@@ -184,7 +185,7 @@ func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken
 				if err := key.SetComment(nil); err != nil {
 					return nil, err
 				}
-				nullNode.SetComment(keyComment)
+				_ = nullNode.SetComment(keyComment)
 			}
 		}
 		return nullNode, nil
@@ -199,7 +200,7 @@ func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken
 		ctx.insertToken(ctx.idx, nullToken)
 		nullNode := ast.Null(nullToken)
 		if comment != nil {
-			nullNode.SetComment(comment)
+			_ = nullNode.SetComment(comment)
 		}
 		return nullNode, nil
 	}
@@ -209,7 +210,7 @@ func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken
 		return nil, errors.Wrapf(err, "failed to parse mapping 'value' node")
 	}
 	if comment != nil {
-		value.SetComment(comment)
+		_ = value.SetComment(comment)
 	}
 	return value, nil
 }
@@ -279,7 +280,7 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
 		}
 		switch value.Type() {
 		case ast.MappingType:
-			c := value.(*ast.MappingNode)
+			c, _ := value.(*ast.MappingNode)
 			comment := c.GetComment()
 			for idx, v := range c.Values {
 				if idx == 0 && comment != nil {
diff --git a/path.go b/path.go
index b79c666..010cc12 100644
--- a/path.go
+++ b/path.go
@@ -534,7 +534,7 @@ func (n *selectorNode) filter(node ast.Node) (ast.Node, error) {
 			}
 		}
 	case ast.MappingValueType:
-		value := node.(*ast.MappingValueNode)
+		value, _ := node.(*ast.MappingValueNode)
 		key := value.Key.GetToken().Value
 		if key == selector {
 			if n.child == nil {
@@ -578,7 +578,7 @@ func (n *selectorNode) replace(node ast.Node, target ast.Node) error {
 			}
 		}
 	case ast.MappingValueType:
-		value := node.(*ast.MappingValueNode)
+		value, _ := node.(*ast.MappingValueNode)
 		if err := n.replaceMapValue(value, target); err != nil {
 			return errors.Wrapf(err, "failed to replace map value")
 		}
@@ -614,7 +614,7 @@ func (n *indexNode) filter(node ast.Node) (ast.Node, error) {
 	if node.Type() != ast.SequenceType {
 		return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
 	}
-	sequence := node.(*ast.SequenceNode)
+	sequence, _ := node.(*ast.SequenceNode)
 	if n.selector >= uint(len(sequence.Values)) {
 		return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values)
 	}
@@ -633,7 +633,7 @@ func (n *indexNode) replace(node ast.Node, target ast.Node) error {
 	if node.Type() != ast.SequenceType {
 		return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
 	}
-	sequence := node.(*ast.SequenceNode)
+	sequence, _ := node.(*ast.SequenceNode)
 	if n.selector >= uint(len(sequence.Values)) {
 		return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values)
 	}
@@ -679,7 +679,7 @@ func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) {
 	if node.Type() != ast.SequenceType {
 		return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
 	}
-	sequence := node.(*ast.SequenceNode)
+	sequence, _ := node.(*ast.SequenceNode)
 	if n.child == nil {
 		return sequence, nil
 	}
@@ -699,7 +699,7 @@ func (n *indexAllNode) replace(node ast.Node, target ast.Node) error {
 	if node.Type() != ast.SequenceType {
 		return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type())
 	}
-	sequence := node.(*ast.SequenceNode)
+	sequence, _ := node.(*ast.SequenceNode)
 	if n.child == nil {
 		for idx := range sequence.Values {
 			if err := sequence.Replace(idx, target); err != nil {
diff --git a/printer/printer.go b/printer/printer.go
index d5e25dc..a039322 100644
--- a/printer/printer.go
+++ b/printer/printer.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"github.com/fatih/color"
+
 	"github.com/goccy/go-yaml/ast"
 	"github.com/goccy/go-yaml/token"
 )
diff --git a/scanner/context.go b/scanner/context.go
index 3aaec56..5f92a6e 100644
--- a/scanner/context.go
+++ b/scanner/context.go
@@ -42,7 +42,7 @@ func createContext() *Context {
 }
 
 func newContext(src []rune) *Context {
-	ctx := ctxPool.Get().(*Context)
+	ctx, _ := ctxPool.Get().(*Context)
 	ctx.reset(src)
 	return ctx
 }
diff --git a/scanner/scanner.go b/scanner/scanner.go
index 77acb41..0c7596a 100644
--- a/scanner/scanner.go
+++ b/scanner/scanner.go
@@ -142,7 +142,7 @@ func (s *Scanner) newLineCount(src []rune) int {
 }
 
 func (s *Scanner) updateIndentState(ctx *Context) {
-	indentNumBasedIndentState := s.indentState
+	var indentNumBasedIndentState IndentState
 	if s.prevIndentNum < s.indentNum {
 		s.indentLevel = s.prevIndentLevel + 1
 		indentNumBasedIndentState = IndentStateUp
@@ -164,7 +164,7 @@ func (s *Scanner) updateIndentState(ctx *Context) {
 			// - prevIndentColumn: 1 of 'a'
 			// - indentNumBasedIndentState: IndentStateDown because d's indentNum(1) is less than c's indentNum(3).
 			// Therefore, s.prevIndentColumn(1) == s.column(1) is true, but we want to treat this as IndentStateDown.
-			// So, we look also current indentState value by the above prevIndentNum based logic, and determins finally indentState.
+			// So, we look also current indentState value by the above prevIndentNum based logic, and determines finally indentState.
 			// ---
 			// a:
 			//   b
@@ -470,7 +470,6 @@ func (s *Scanner) scanComment(ctx *Context) (tk *token.Token, pos int) {
 	ctx.addOriginBuf('#')
 	ctx.progress(1) // skip '#' character
 	for idx, c := range ctx.src[ctx.idx:] {
-		pos = idx + 1
 		ctx.addOriginBuf(c)
 		switch c {
 		case '\n', '\r':
@@ -588,7 +587,6 @@ func (s *Scanner) scanLiteralHeader(ctx *Context) (pos int, err error) {
 				ctx.literalOpt = opt
 				return
 			}
-			break
 		}
 	}
 	err = xerrors.New("invalid literal header")
diff --git a/struct.go b/struct.go
index a3da8dd..9d251f3 100644
--- a/struct.go
+++ b/struct.go
@@ -84,11 +84,7 @@ func isIgnoredStructField(field reflect.StructField) bool {
 		// private field
 		return true
 	}
-	tag := getTag(field)
-	if tag == "-" {
-		return true
-	}
-	return false
+	return getTag(field) == "-"
 }
 
 type StructFieldMap map[string]*StructField
diff --git a/token/token.go b/token/token.go
index 14d7622..bc8b531 100644
--- a/token/token.go
+++ b/token/token.go
@@ -339,9 +339,12 @@ func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token {
 
 func init() {
 	for _, keyword := range reservedNullKeywords {
-		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
+		f := func(value, org string, pos *Position) *Token {
 			return reservedKeywordToken(NullType, value, org, pos)
 		}
+
+		reservedKeywordMap[keyword] = f
+		reservedEncKeywordMap[keyword] = f
 	}
 	for _, keyword := range reservedBoolKeywords {
 		f := func(value, org string, pos *Position) *Token {
diff --git a/yaml.go b/yaml.go
index 25b1056..38214a8 100644
--- a/yaml.go
+++ b/yaml.go
@@ -7,9 +7,10 @@ import (
 	"reflect"
 	"sync"
 
+	"golang.org/x/xerrors"
+
 	"github.com/goccy/go-yaml/ast"
 	"github.com/goccy/go-yaml/internal/errors"
-	"golang.org/x/xerrors"
 )
 
 // BytesMarshaler interface may be implemented by types to customize their
@@ -80,11 +81,11 @@ func (s MapSlice) ToMap() map[interface{}]interface{} {
 // of the generated document will reflect the structure of the value itself.
 // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
 //
-// Struct fields are only marshalled if they are exported (have an upper case
-// first letter), and are marshalled using the field name lowercased as the
+// Struct fields are only marshaled if they are exported (have an upper case
+// first letter), and are marshaled using the field name lowercased as the
 // default key. Custom keys may be defined via the "yaml" name in the field
 // tag: the content preceding the first comma is used as the key, and the
-// following comma-separated options are used to tweak the marshalling process.
+// following comma-separated options are used to tweak the marshaling process.
 // Conflicting names result in a runtime error.
 //
 // The field tag format accepted is:
@@ -161,7 +162,7 @@ func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) {
 // lowercased as the default key. Custom keys may be defined via the
 // "yaml" name in the field tag: the content preceding the first comma
 // is used as the key, and the following comma-separated options are
-// used to tweak the marshalling process (see Marshal).
+// used to tweak the marshaling process (see Marshal).
 // Conflicting names result in a runtime error.
 //
 // For example:
@@ -216,7 +217,7 @@ func FormatError(e error, colored, inclSource bool) string {
 	var pp errors.PrettyPrinter
 	if xerrors.As(e, &pp) {
 		var buf bytes.Buffer
-		pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource)
+		pp.PrettyPrint(&errors.Sink{Buffer: &buf}, colored, inclSource)
 		return buf.String()
 	}
 


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 0d47a29..cff8640 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -592,11 +592,11 @@ func TestDecoder(t *testing.T) {
 		},
 		{
 			"v: [A,B,C,]",
-			map[string][]string{"v": []string{"A", "B", "C"}},
+			map[string][]string{"v": {"A", "B", "C"}},
 		},
 		{
 			"v: [A,1,C]",
-			map[string][]string{"v": []string{"A", "1", "C"}},
+			map[string][]string{"v": {"A", "1", "C"}},
 		},
 		{
 			"v: [A,1,C]",
@@ -610,11 +610,11 @@ func TestDecoder(t *testing.T) {
 		},
 		{
 			"v:\n - A\n - B\n - C",
-			map[string][]string{"v": []string{"A", "B", "C"}},
+			map[string][]string{"v": {"A", "B", "C"}},
 		},
 		{
 			"v:\n - A\n - 1\n - C",
-			map[string][]string{"v": []string{"A", "1", "C"}},
+			map[string][]string{"v": {"A", "1", "C"}},
 		},
 		{
 			"v:\n - A\n - 1\n - C",
@@ -1470,7 +1470,7 @@ items:
 		if err := dec.Decode(&v); err != nil {
 			t.Fatalf("%+v", err)
 		}
-		items := v.(map[string]interface{})["items"].([]interface{})
+		items, _ := v.(map[string]interface{})["items"].([]interface{})
 		if len(items) != 2 {
 			t.Fatal("failed to decode with merge key")
 		}
@@ -2152,16 +2152,16 @@ func Example_DisallowUnknownField() {
 
 	const src = `---
 simple: string
-complecated: string
+unknown: string
 `
 	err := yaml.NewDecoder(strings.NewReader(src), yaml.DisallowUnknownField()).Decode(&v)
 	fmt.Printf("%v\n", err)
 
 	// OUTPUT:
-	// [3:1] unknown field "complecated"
+	// [3:1] unknown field "unknown"
 	//        1 | ---
 	//        2 | simple: string
-	//     >  3 | complecated: string
+	//     >  3 | unknown: string
 	//            ^
 }
 
@@ -2712,22 +2712,22 @@ func TestDecoder_LiteralWithNewLine(t *testing.T) {
 		LastNode string `yaml:"last"`
 	}
 	tests := []A{
-		A{
+		{
 			Node: "hello\nworld",
 		},
-		A{
+		{
 			Node: "hello\nworld\n",
 		},
-		A{
+		{
 			Node: "hello\nworld\n\n",
 		},
-		A{
+		{
 			LastNode: "hello\nworld",
 		},
-		A{
+		{
 			LastNode: "hello\nworld\n",
 		},
-		A{
+		{
 			LastNode: "hello\nworld\n\n",
 		},
 	}
diff --git a/encode_test.go b/encode_test.go
index 74388b9..7c55269 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -10,10 +10,9 @@ import (
 	"testing"
 	"time"
 
-	"github.com/goccy/go-yaml/parser"
-
 	"github.com/goccy/go-yaml"
 	"github.com/goccy/go-yaml/ast"
+	"github.com/goccy/go-yaml/parser"
 )
 
 var zero = 0
@@ -683,12 +682,12 @@ func TestEncoder(t *testing.T) {
 		// time value
 		{
 			"v: 0001-01-01T00:00:00Z\n",
-			map[string]time.Time{"v": time.Time{}},
+			map[string]time.Time{"v": {}},
 			nil,
 		},
 		{
 			"v: 0001-01-01T00:00:00Z\n",
-			map[string]*time.Time{"v": &time.Time{}},
+			map[string]*time.Time{"v": {}},
 			nil,
 		},
 		{
@@ -1078,7 +1077,7 @@ func TestEncoder_FlowRecursive(t *testing.T) {
 		M map[string][]int `yaml:",flow"`
 	}
 	v.M = map[string][]int{
-		"test": []int{1, 2, 3},
+		"test": {1, 2, 3},
 	}
 	var buf bytes.Buffer
 	if err := yaml.NewEncoder(&buf).Encode(v); err != nil {
@@ -1188,7 +1187,7 @@ func TestEncoder_MarshalAnchor(t *testing.T) {
 	hostIdx := 1
 	opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error {
 		if _, ok := value.(*Host); ok {
-			nameNode := anchor.Name.(*ast.StringNode)
+			nameNode, _ := anchor.Name.(*ast.StringNode)
 			nameNode.Value = fmt.Sprintf("host%d", hostIdx)
 			hostIdx++
 		}
diff --git a/token/token_test.go b/token/token_test.go
index dc1b4df..27565fb 100644
--- a/token/token_test.go
+++ b/token/token_test.go
@@ -121,6 +121,10 @@ func TestIsNeedQuoted(t *testing.T) {
 		" a",
 		" a ",
 		"a ",
+		"null",
+		"Null",
+		"NULL",
+		"~",
 	}
 	for i, test := range needQuotedTests {
 		if !token.IsNeedQuoted(test) {
diff --git a/validate_test.go b/validate_test.go
index 265deb8..d73ee3a 100644
--- a/validate_test.go
+++ b/validate_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/go-playground/validator/v10"
+
 	"github.com/goccy/go-yaml"
 )
 
diff --git a/yaml_test.go b/yaml_test.go
index 4446d31..61f5327 100644
--- a/yaml_test.go
+++ b/yaml_test.go
@@ -325,7 +325,7 @@ b: *a
 	if len(anchors) != 1 {
 		t.Fatal("failed to filter node")
 	}
-	anchor := anchors[0].(*ast.AnchorNode)
+	anchor, _ := anchors[0].(*ast.AnchorNode)
 	if err := anchor.SetName("b"); err != nil {
 		t.Fatal(err)
 	}
@@ -333,7 +333,7 @@ b: *a
 	if len(anchors) != 1 {
 		t.Fatal("failed to filter node")
 	}
-	alias := aliases[0].(*ast.AliasNode)
+	alias, _ := aliases[0].(*ast.AliasNode)
 	if err := alias.SetName("b"); err != nil {
 		t.Fatal(err)
 	}