diff options
author | Bjørn Erik Pedersen <[email protected]> | 2022-05-02 16:07:52 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-05-06 19:43:22 +0200 |
commit | f2946da9e806c2bafbdd26707fe339db79bd980b (patch) | |
tree | b5609317a861ea5f399e094e1b9287ca71dc22d1 /common | |
parent | 6eea32bd6bc8e7a7dd07a8cb6a8343ae2c74aba0 (diff) | |
download | hugo-f2946da9e806c2bafbdd26707fe339db79bd980b.tar.gz hugo-f2946da9e806c2bafbdd26707fe339db79bd980b.zip |
Improve error messages, esp. when the server is running
* Add file context to minifier errors when publishing
* Misc fixes (see issues)
* Allow custom server error template in layouts/server/error.html
To get to this, this commit also cleans up and simplifies the code surrounding errors and files. This also removes the usage of `github.com/pkg/errors`, mostly because of https://github.com/pkg/errors/issues/223 -- but also because most of this is now built-in to Go.
Fixes #9852
Fixes #9857
Fixes #9863
Diffstat (limited to 'common')
-rw-r--r-- | common/herrors/error_locator.go | 132 | ||||
-rw-r--r-- | common/herrors/error_locator_test.go | 38 | ||||
-rw-r--r-- | common/herrors/errors.go | 26 | ||||
-rw-r--r-- | common/herrors/file_error.go | 253 | ||||
-rw-r--r-- | common/herrors/file_error_test.go | 46 | ||||
-rw-r--r-- | common/herrors/line_number_extractors.go | 24 | ||||
-rw-r--r-- | common/hugio/copy.go | 5 |
7 files changed, 272 insertions, 252 deletions
diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go index 2c0d215b1..5d2c10c04 100644 --- a/common/herrors/error_locator.go +++ b/common/herrors/error_locator.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2022 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,8 +21,6 @@ import ( "strings" "github.com/gohugoio/hugo/common/text" - - "github.com/spf13/afero" ) // LineMatcher contains the elements used to match an error to a line @@ -43,8 +41,6 @@ var SimpleLineMatcher = func(m LineMatcher) bool { return m.Position.LineNumber == m.LineNumber } -var _ text.Positioner = ErrorContext{} - // ErrorContext contains contextual information about an error. This will // typically be the lines surrounding some problem in a file. type ErrorContext struct { @@ -56,125 +52,11 @@ type ErrorContext struct { // The position of the error in the Lines above. 0 based. LinesPos int - position text.Position - // The lexer to use for syntax highlighting. // https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages ChromaLexer string } -// Position returns the text position of this error. -func (e ErrorContext) Position() text.Position { - return e.position -} - -var _ causer = (*ErrorWithFileContext)(nil) - -// ErrorWithFileContext is an error with some additional file context related -// to that error. -type ErrorWithFileContext struct { - cause error - ErrorContext -} - -func (e *ErrorWithFileContext) Error() string { - pos := e.Position() - if pos.IsValid() { - return pos.String() + ": " + e.cause.Error() - } - return e.cause.Error() -} - -func (e *ErrorWithFileContext) Cause() error { - return e.cause -} - -// WithFileContextForFile will try to add a file context with lines matching the given matcher. -// If no match could be found, the original error is returned with false as the second return value. -func WithFileContextForFile(e error, realFilename, filename string, fs afero.Fs, matcher LineMatcherFn) (error, bool) { - f, err := fs.Open(filename) - if err != nil { - return e, false - } - defer f.Close() - return WithFileContext(e, realFilename, f, matcher) -} - -// WithFileContextForFileDefault tries to add file context using the default line matcher. -func WithFileContextForFileDefault(err error, filename string, fs afero.Fs) error { - err, _ = WithFileContextForFile( - err, - filename, - filename, - fs, - SimpleLineMatcher) - return err -} - -// WithFileContextForFile will try to add a file context with lines matching the given matcher. -// If no match could be found, the original error is returned with false as the second return value. -func WithFileContext(e error, realFilename string, r io.Reader, matcher LineMatcherFn) (error, bool) { - if e == nil { - panic("error missing") - } - le := UnwrapFileError(e) - - if le == nil { - var ok bool - if le, ok = ToFileError("", e).(FileError); !ok { - return e, false - } - } - - var errCtx ErrorContext - - posle := le.Position() - - if posle.Offset != -1 { - errCtx = locateError(r, le, func(m LineMatcher) bool { - if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) { - lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber - m.Position = text.Position{LineNumber: lno} - } - return matcher(m) - }) - } else { - errCtx = locateError(r, le, matcher) - } - - pos := &errCtx.position - - if pos.LineNumber == -1 { - return e, false - } - - pos.Filename = realFilename - - if le.Type() != "" { - errCtx.ChromaLexer = chromaLexerFromType(le.Type()) - } else { - errCtx.ChromaLexer = chromaLexerFromFilename(realFilename) - } - - return &ErrorWithFileContext{cause: e, ErrorContext: errCtx}, true -} - -// UnwrapErrorWithFileContext tries to unwrap an ErrorWithFileContext from err. -// It returns nil if this is not possible. -func UnwrapErrorWithFileContext(err error) *ErrorWithFileContext { - for err != nil { - switch v := err.(type) { - case *ErrorWithFileContext: - return v - case causer: - err = v.Cause() - default: - return nil - } - } - return nil -} - func chromaLexerFromType(fileType string) string { switch fileType { case "html", "htm": @@ -196,23 +78,23 @@ func chromaLexerFromFilename(filename string) string { return chromaLexerFromType(ext) } -func locateErrorInString(src string, matcher LineMatcherFn) ErrorContext { +func locateErrorInString(src string, matcher LineMatcherFn) (*ErrorContext, text.Position) { return locateError(strings.NewReader(src), &fileError{}, matcher) } -func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext { +func locateError(r io.Reader, le FileError, matches LineMatcherFn) (*ErrorContext, text.Position) { if le == nil { panic("must provide an error") } - errCtx := ErrorContext{position: text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}, LinesPos: -1} + errCtx := &ErrorContext{LinesPos: -1} + pos := text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1} b, err := ioutil.ReadAll(r) if err != nil { - return errCtx + return errCtx, pos } - pos := &errCtx.position lepos := le.Position() lines := strings.Split(string(b), "\n") @@ -262,5 +144,5 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext } - return errCtx + return errCtx, pos } diff --git a/common/herrors/error_locator_test.go b/common/herrors/error_locator_test.go index 0cd5fb2d7..10b016fa8 100644 --- a/common/herrors/error_locator_test.go +++ b/common/herrors/error_locator_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2022 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,44 +38,48 @@ LINE 7 LINE 8 ` - location := locateErrorInString(lines, lineMatcher) + location, pos := locateErrorInString(lines, lineMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"}) - pos := location.Position() c.Assert(pos.LineNumber, qt.Equals, 5) c.Assert(location.LinesPos, qt.Equals, 2) - c.Assert(locateErrorInString(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"}) + locate := func(s string, m LineMatcherFn) *ErrorContext { + ctx, _ := locateErrorInString(s, m) + return ctx + } + + c.Assert(locate(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"}) - location = locateErrorInString(`L1 + location, pos = locateErrorInString(`L1 This is THEONE L2 `, lineMatcher) - c.Assert(location.Position().LineNumber, qt.Equals, 2) + c.Assert(pos.LineNumber, qt.Equals, 2) c.Assert(location.LinesPos, qt.Equals, 1) c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""}) - location = locateErrorInString(`This is THEONE + location = locate(`This is THEONE L2 `, lineMatcher) c.Assert(location.LinesPos, qt.Equals, 0) c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""}) - location = locateErrorInString(`L1 + location = locate(`L1 This THEONE `, lineMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""}) c.Assert(location.LinesPos, qt.Equals, 1) - location = locateErrorInString(`L1 + location = locate(`L1 L2 This THEONE `, lineMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "L2", "This THEONE", ""}) c.Assert(location.LinesPos, qt.Equals, 2) - location = locateErrorInString("NO MATCH", lineMatcher) - c.Assert(location.Position().LineNumber, qt.Equals, -1) + location, pos = locateErrorInString("NO MATCH", lineMatcher) + c.Assert(pos.LineNumber, qt.Equals, -1) c.Assert(location.LinesPos, qt.Equals, -1) c.Assert(len(location.Lines), qt.Equals, 0) @@ -83,7 +87,7 @@ This THEONE return m.LineNumber == 6 } - location = locateErrorInString(`A + location, pos = locateErrorInString(`A B C D @@ -95,7 +99,7 @@ I J`, lineMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"}) - c.Assert(location.Position().LineNumber, qt.Equals, 6) + c.Assert(pos.LineNumber, qt.Equals, 6) c.Assert(location.LinesPos, qt.Equals, 2) // Test match EOF @@ -103,26 +107,26 @@ J`, lineMatcher) return m.LineNumber == 4 } - location = locateErrorInString(`A + location, pos = locateErrorInString(`A B C `, lineMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""}) - c.Assert(location.Position().LineNumber, qt.Equals, 4) + c.Assert(pos.LineNumber, qt.Equals, 4) c.Assert(location.LinesPos, qt.Equals, 2) offsetMatcher := func(m LineMatcher) bool { return m.Offset == 1 } - location = locateErrorInString(`A + location, pos = locateErrorInString(`A B C D E`, offsetMatcher) c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"}) - c.Assert(location.Position().LineNumber, qt.Equals, 2) + c.Assert(pos.LineNumber, qt.Equals, 2) c.Assert(location.LinesPos, qt.Equals, 1) } diff --git a/common/herrors/errors.go b/common/herrors/errors.go index 27cfd2693..6ce908853 100644 --- a/common/herrors/errors.go +++ b/common/herrors/errors.go @@ -19,37 +19,11 @@ import ( "errors" "fmt" "io" - "os" "runtime" "runtime/debug" "strconv" - - _errors "github.com/pkg/errors" ) -// As defined in https://godoc.org/github.com/pkg/errors -type causer interface { - Cause() error -} - -type stackTracer interface { - StackTrace() _errors.StackTrace -} - -// PrintStackTraceFromErr prints the error's stack trace to stdoud. -func PrintStackTraceFromErr(err error) { - FprintStackTraceFromErr(os.Stdout, err) -} - -// FprintStackTraceFromErr prints the error's stack trace to w. -func FprintStackTraceFromErr(w io.Writer, err error) { - if err, ok := err.(stackTracer); ok { - for _, f := range err.StackTrace() { - fmt.Fprintf(w, "%+s:%d\n", f, f) - } - } -} - // PrintStackTrace prints the current stacktrace to w. func PrintStackTrace(w io.Writer) { buf := make([]byte, 1<<16) diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 1cb31ff9f..abd36cfbc 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -1,11 +1,11 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2022 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software +// Unless required by applicable lfmtaw or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and @@ -15,59 +15,217 @@ package herrors import ( "encoding/json" + "fmt" + "io" + "path/filepath" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/text" + "github.com/pelletier/go-toml/v2" + "github.com/spf13/afero" + "github.com/tdewolff/parse/v2" - "github.com/pkg/errors" + "errors" ) -var _ causer = (*fileError)(nil) - // FileError represents an error when handling a file: Parsing a config file, // execute a template etc. type FileError interface { error + // ErroContext holds some context information about the error. + ErrorContext() *ErrorContext + text.Positioner - // A string identifying the type of file, e.g. JSON, TOML, markdown etc. - Type() string + // UpdatePosition updates the position of the error. + UpdatePosition(pos text.Position) FileError + + // UpdateContent updates the error with a new ErrorContext from the content of the file. + UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError +} + +// Unwrapper can unwrap errors created with fmt.Errorf. +type Unwrapper interface { + Unwrap() error } -var _ FileError = (*fileError)(nil) +var ( + _ FileError = (*fileError)(nil) + _ Unwrapper = (*fileError)(nil) +) + +func (fe *fileError) UpdatePosition(pos text.Position) FileError { + oldFilename := fe.Position().Filename + if pos.Filename != "" && fe.fileType == "" { + _, fe.fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename)) + } + if pos.Filename == "" { + pos.Filename = oldFilename + } + fe.position = pos + return fe +} + +func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError { + if linematcher == nil { + linematcher = SimpleLineMatcher + } + + var ( + contentPos text.Position + posle = fe.position + errorContext *ErrorContext + ) + + if posle.LineNumber <= 1 && posle.Offset > 0 { + // Try to locate the line number from the content if offset is set. + errorContext, contentPos = locateError(r, fe, func(m LineMatcher) bool { + if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) { + lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber + m.Position = text.Position{LineNumber: lno} + return linematcher(m) + } + return false + }) + } else { + errorContext, contentPos = locateError(r, fe, linematcher) + } + + if errorContext.ChromaLexer == "" { + if fe.fileType != "" { + errorContext.ChromaLexer = chromaLexerFromType(fe.fileType) + } else { + errorContext.ChromaLexer = chromaLexerFromFilename(fe.Position().Filename) + } + } + + fe.errorContext = errorContext + + if contentPos.LineNumber > 0 { + fe.position.LineNumber = contentPos.LineNumber + } + + return fe + +} type fileError struct { - position text.Position + position text.Position + errorContext *ErrorContext fileType string cause error } +type fileErrorWithErrorContext struct { + *fileError +} + +func (e *fileError) ErrorContext() *ErrorContext { + return e.errorContext +} + // Position returns the text position of this error. func (e fileError) Position() text.Position { return e.position } -func (e *fileError) Type() string { - return e.fileType +func (e *fileError) Error() string { + return fmt.Sprintf("%s: %s", e.position, e.cause) } -func (e *fileError) Error() string { - if e.cause == nil { - return "" +func (e *fileError) Unwrap() error { + return e.cause +} + +// NewFileError creates a new FileError that wraps err. +// The value for name should identify the file, the best +// being the full filename to the file on disk. +func NewFileError(name string, err error) FileError { + if err == nil { + panic("err is nil") + } + + // Filetype is used to determine the Chroma lexer to use. + fileType, pos := extractFileTypePos(err) + pos.Filename = name + if fileType == "" { + _, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name)) } - return e.cause.Error() + + if pos.LineNumber < 0 { + panic(fmt.Sprintf("invalid line number: %d", pos.LineNumber)) + } + + return &fileError{cause: err, fileType: fileType, position: pos} + } -func (f *fileError) Cause() error { - return f.cause +// NewFileErrorFromFile is a convenience method to create a new FileError from a file. +func NewFileErrorFromFile(err error, filename, realFilename string, fs afero.Fs, linematcher LineMatcherFn) FileError { + if err == nil { + panic("err is nil") + } + if linematcher == nil { + linematcher = SimpleLineMatcher + } + f, err2 := fs.Open(filename) + if err2 != nil { + return NewFileError(realFilename, err) + } + defer f.Close() + return NewFileError(realFilename, err).UpdateContent(f, linematcher) } -// NewFileError creates a new FileError. -func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError { - pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber} - return &fileError{cause: err, fileType: fileType, position: pos} +// Cause returns the underlying error or itself if it does not implement Unwrap. +func Cause(err error) error { + if u := errors.Unwrap(err); u != nil { + return u + } + return err +} + +func extractFileTypePos(err error) (string, text.Position) { + err = Cause(err) + var fileType string + + // Fall back to line/col 1:1 if we cannot find any better information. + pos := text.Position{ + Offset: -1, + LineNumber: 1, + ColumnNumber: 1, + } + + // JSON errors. + offset, typ := extractOffsetAndType(err) + if fileType == "" { + fileType = typ + } + + if offset >= 0 { + pos.Offset = offset + } + + // The error type from the minifier contains line number and column number. + if line, col := exctractLineNumberAndColumnNumber(err); line >= 0 { + pos.LineNumber = line + pos.ColumnNumber = col + return fileType, pos + } + + // Look in the error message for the line number. + for _, handle := range lineNumberExtractors { + lno, col := handle(err) + if lno > 0 { + pos.ColumnNumber = col + pos.LineNumber = lno + break + } + } + + return fileType, pos } // UnwrapFileError tries to unwrap a FileError from err. @@ -77,49 +235,26 @@ func UnwrapFileError(err error) FileError { switch v := err.(type) { case FileError: return v - case causer: - err = v.Cause() default: - return nil + err = errors.Unwrap(err) } } return nil } -// ToFileErrorWithOffset will return a new FileError with a line number -// with the given offset from the original. -func ToFileErrorWithOffset(fe FileError, offset int) FileError { - pos := fe.Position() - return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset) -} - -// ToFileErrorWithOffset will return a new FileError with the given line number. -func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError { - pos := fe.Position() - pos.LineNumber = lineNumber - return &fileError{cause: fe, fileType: fe.Type(), position: pos} -} - -// ToFileError will convert the given error to an error supporting -// the FileError interface. -func ToFileError(fileType string, err error) FileError { - for _, handle := range lineNumberExtractors { - lno, col := handle(err) - offset, typ := extractOffsetAndType(err) - if fileType == "" { - fileType = typ - } - - if lno > 0 || offset != -1 { - return NewFileError(fileType, offset, lno, col, err) +// UnwrapFileErrorsWithErrorContext tries to unwrap all FileError in err that has an ErrorContext. +func UnwrapFileErrorsWithErrorContext(err error) []FileError { + var errs []FileError + for err != nil { + if v, ok := err.(FileError); ok && v.ErrorContext() != nil { + errs = append(errs, v) } + err = errors.Unwrap(err) } - // Fall back to the pointing to line number 1. - return NewFileError(fileType, -1, 1, 1, err) + return errs } func extractOffsetAndType(e error) (int, string) { - e = errors.Cause(e) switch v := e.(type) { case *json.UnmarshalTypeError: return int(v.Offset), "json" @@ -129,3 +264,15 @@ func extractOffsetAndType(e error) (int, string) { return -1, "" } } + +func exctractLineNumberAndColumnNumber(e error) (int, int) { + switch v := e.(type) { + case *parse.Error: + return v.Line, v.Column + case *toml.DecodeError: + return v.Position() + + } + + return -1, -1 +} diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go index 675be94e8..e6595aa28 100644 --- a/common/herrors/file_error_test.go +++ b/common/herrors/file_error_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2022 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,14 +14,44 @@ package herrors import ( + "fmt" + "strings" "testing" - "github.com/pkg/errors" + "errors" + + "github.com/gohugoio/hugo/common/text" qt "github.com/frankban/quicktest" ) -func TestToLineNumberError(t *testing.T) { +func TestNewFileError(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + fe := NewFileError("foo.html", errors.New("bar")) + c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`) + + lines := "" + for i := 1; i <= 100; i++ { + lines += fmt.Sprintf("line %d\n", i) + } + + fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2}) + c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`) + fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212}) + fe.UpdateContent(strings.NewReader(lines), SimpleLineMatcher) + c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`) + errorContext := fe.ErrorContext() + c.Assert(errorContext, qt.IsNotNil) + c.Assert(errorContext.Lines, qt.DeepEquals, []string{"line 30", "line 31", "line 32", "line 33", "line 34"}) + c.Assert(errorContext.LinesPos, qt.Equals, 2) + c.Assert(errorContext.ChromaLexer, qt.Equals, "go-html-template") + +} + +func TestNewFileErrorExtractFromMessage(t *testing.T) { t.Parallel() c := qt.New(t) @@ -37,18 +67,16 @@ func TestToLineNumberError(t *testing.T) { {errors.New("parse failed: template: _default/bundle-resource-meta.html:11: unexpected in operand"), 0, 11, 1}, {errors.New(`failed:: template: _default/bundle-resource-meta.html:2:7: executing "main" at <.Titles>`), 0, 2, 7}, {errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7}, + {errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at <partial "foo.html" .>: error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5}, } { - got := ToFileError("template", test.in) + got := NewFileError("test.txt", test.in) errMsg := qt.Commentf("[%d][%T]", i, got) - le, ok := got.(FileError) - c.Assert(ok, qt.Equals, true) - c.Assert(ok, qt.Equals, true, errMsg) - pos := le.Position() + pos := got.Position() c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg) c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg) - c.Assert(errors.Cause(got), qt.Not(qt.IsNil)) + c.Assert(errors.Unwrap(got), qt.Not(qt.IsNil)) } } diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go index 3df62bdfe..f70a2691f 100644 --- a/common/herrors/line_number_extractors.go +++ b/common/herrors/line_number_extractors.go @@ -16,36 +16,22 @@ package herrors import ( "regexp" "strconv" - - "github.com/pkg/errors" - - "github.com/pelletier/go-toml/v2" ) var lineNumberExtractors = []lineNumberExtractor{ // Template/shortcode parse errors - newLineNumberErrHandlerFromRegexp(".*:(\\d+):(\\d*):"), - newLineNumberErrHandlerFromRegexp(".*:(\\d+):"), + newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`), + newLineNumberErrHandlerFromRegexp(`:(\d+):`), - // TOML parse errors - tomlLineNumberExtractor, // YAML parse errors - newLineNumberErrHandlerFromRegexp("line (\\d+):"), + newLineNumberErrHandlerFromRegexp(`line (\d+):`), // i18n bundle errors - newLineNumberErrHandlerFromRegexp("\\((\\d+),\\s(\\d*)"), + newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`), } type lineNumberExtractor func(e error) (int, int) -var tomlLineNumberExtractor = func(e error) (int, int) { - e = errors.Cause(e) - if terr, ok := e.(*toml.DecodeError); ok { - return terr.Position() - } - return -1, -1 -} - func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor { re := regexp.MustCompile(expression) return extractLineNo(re) @@ -72,6 +58,6 @@ func extractLineNo(re *regexp.Regexp) lineNumberExtractor { return lno, col } - return -1, col + return 0, col } } diff --git a/common/hugio/copy.go b/common/hugio/copy.go index be4506f4c..7c52f8723 100644 --- a/common/hugio/copy.go +++ b/common/hugio/copy.go @@ -14,13 +14,12 @@ package hugio import ( + "fmt" "io" "io/ioutil" "os" "path/filepath" - "github.com/pkg/errors" - "github.com/spf13/afero" ) @@ -60,7 +59,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool } if !fi.IsDir() { - return errors.Errorf("%q is not a directory", from) + return fmt.Errorf("%q is not a directory", from) } err = fs.MkdirAll(to, 0777) // before umask |