locerr

package module
v0.0.0-...-9e34f7a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2017 License: MIT Imports: 9 Imported by: 28

README

❌ locerr

Build Status Windows Build status Coverage Status GoDoc

locerr is a small library to make a nice-looking locational error in a source code. It provides a struct to represent a source file, a specific position in code and an error related to specific range or position in source.

This library is useful to provide a unified look for error messages raised by compilers, interpreters or translators.

By using locerr.Source and locerr.Pos types as position information, this library can provide an error type which shows nice look error message.

  • It shows the code snippet which caused an error
  • Enable to add notes to error by nesting an error instance like pkg/errors
  • Proper location is automatically added to error messages and notes
  • Colorized label like 'Error:' or 'Note:'
  • Windows is supported

It's important to make a good error when compilation or execution errors found. locerr helps it. This library is actually used in some my compiler implementation.

Installation

Please use go get.

$ go get -u github.com/rhysd/locerr

Usage

As example, let's say to make a locational error for following pseudo code. In this code, function foo is defined with 1 parameter but called with 3 parameters.

function foo(x: bool): int {
  return (if x then 42 else 21)
}

function main() {
  foo(true,
      42,
      "test")
}

We can make a locational error with some notes using locerr as following.

package main

import (
	"fmt"
	"os"

	"github.com/rhysd/locerr"
)

func main() {
	// At first you should gain entire source as *locerr.Source instance.

	code :=
`function foo(x: bool): int {
  return (if x then 42 else 21)
}

function main() {
  foo(true,
      42,
      "test")
}`
	src := locerr.NewDummySource(code)

	// You can get *locerr.Source instance from file (NewSourceFromFile) or stdin (NewSourceFromStdin) also.

	// Let's say to find an error at some range in the source. 'start' indicates the head of the first argument.
    // 'end' indicates the end of the last argument.

	start := locerr.Pos{
		Offset: 88,
		Line:   6,
		Column: 7,
		File:   src,
	}
	end := locerr.Pos{
		Offset: 116,
		Line:   9,
		Column: 12,
		File:   src,
	}

	// NewError or other factory functions make a new error instance with the range. locerr.Error instance
	// implements error interface so it can be handled like other error types.

	err := locerr.ErrorIn(start, end, "Calling 'foo' with wrong number of argument")

	// Assume that you find additional information (location of variable and its type). Then you can add some
	// notes to the error. Notes can be added by wrapping errors like pkg/errors library.

	prev := locerr.Pos{
		Offset: 9,
		Line:   1,
		Column: 10,
		File:   src,
	}

	err = err.NoteAt(prev, "Defined with 1 parameter")
	err = err.NoteAt(prev, "'foo' was defined as 'bool -> int'")

	// Finally you can see the result!

	// Get the error message as string. Note that this is only for non-Windows OS.
	fmt.Println(err)

	// Directly writes the error message into given file.
	// This supports Windows. Useful to output from stdout or stderr.
	err.PrintToFile(os.Stdout)
}

Above code should show the following output:

Error: Calling 'foo' with wrong number of argument (at <dummy>:6:7)
  Note: Defined with 1 parameter (at <dummy>:1:10)
  Note: 'foo' was defined as 'bool -> int' (at <dummy>:1:10)

>   foo(true,
>       42,
>       "test")

output screenshot

Labels such as 'Error:' or 'Notes:' are colorized. Main error message is emphasized with bold font. And source code location information (file name, line and column) is added with gray text. If the error has range information, the error shows code snippet which caused the error at the end of error message.

If you have only one position information rather than two, 'start' position and 'end' position, ErrorAt is available instead of ErrorIn. ErrorAt takes one Pos instance.

err := locerr.ErrorAt(start, "Calling 'foo' with wrong number of argument")

In this case, line snippet is shown in error message. pos.Line is used to get line from source text. fmt.Println(err) will show the following.

Error: Calling 'foo' with wrong number of argument (at <dummy>:6:7)

>   foo(true,

Development

How to run tests
$ go test ./

Note that go test -v may fail because color sequences are not assumed in tests.

How to run fuzzing test

Fuzzing test using go-fuzz.

$ cd ./fuzz
$ go-fuzz-build github.com/rhysd/locerr/fuzz
$ go-fuzz -bin=./locerr_fuzz-fuzz.zip -workdir=fuzz

Last command starts fuzzing tests until stopped with ^C. Every 3 seconds it reports the current result. It makes 3 directories in fuzz directory as the result, corpus, crashers and suppressions. crashers contains the information about the crash caused by fuzzing.

Documentation

Overview

Package locerr is a small library to make an error with source code location information. It provides a struct to represent a source file, a specific position in code and an error related to specific range or position in source.

It's important to make a good error when compilation or execution errors found. locerr helps it. This library is actually used in some my compiler implementation.

Repository: https://github.com/rhysd/locerr

At first you should gain entire source as *Source instance.

code :=
	`package main

func main() {
	foo := 42

	foo := true
}
`
src := locerr.NewDummySource(code)

You can get *Source instance from file (NewSourceFromFile) or stdin (NewSourceFromStdin) also.

Let's say to find an error at some range in the source.

start := locerr.Pos{
	Offset: 41,
	Line:   6,
	Column: 2,
	File:   src,
}
end := locerr.Pos{
	Offset: 52,
	Line:   6,
	Column: 12,
	File:   src,
}

ErrorIn or other factory functions make a new error instance with the range. Error instance implements error interface so it can be handled like other error types.

err := locerr.ErrorIn(start, end, "Found duplicate symbol 'foo'")

Assume that you find additional information (location of variable and its type). Then you can add some notes to the error. Notes can be added by wrapping errors like pkg/errors library.

prev := locerr.Pos{
	Offset: 26,
	Line:   4,
	Column: 1,
	File:   src,
}

err = err.NoteAt(prev, "Defined here at first")
err = err.NoteAt(prev, "Previously defined as int")

Finally you can see the result! err.Error() gets the error message as string. Note that this is only for non-Windows OS.

fmt.Println(err)

It should output following:

Error: Found duplicate symbol 'foo' (at <dummy>:6:1)
  Note: Defined here at first (at <dummy>:4:1)
  Note: Previously defined as int (at <dummy>:4:1)

>       foo := true

To support Windows, please use PrintToFile() method. It directly writes the error message into given file. This supports Windows and is useful to output from stdout or stderr.

err.PrintToFile(os.Stderr)

Labels such as 'Error:' or 'Notes:' are colorized. Main error message is emphasized with bold font. And source code location information (file name, line and column) is added with gray text. If the error has range information, the error shows code snippet which caused the error at the end of error message

Colorized output can be seen at https://github.com/rhysd/ss/blob/master/locerr/output.png?raw=true

If you have only one position information rather than two, 'start' position and 'end' position, ErrorAt() is available instead of ErrorIn() ErrorAt() takes one Pos instance.

err = ErrorAt(start, "Calling 'foo' with wrong number of argument")

In this case, line snippet is shown in error message. `pos.Line` is used to get line from source text.

fmt.Println(err)

It should output following:

Output:
Error: Calling 'foo' with wrong number of argument (at <dummy>:6:7)

>   foo(true,

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetColor

func SetColor(enabled bool)

SetColor controls font should be colorful or not.

Types

type Error

type Error struct {
	Start    Pos
	End      Pos
	Messages []string
}

Error represents a compilation error with positional information and stacked messages.

func ErrorAt

func ErrorAt(pos Pos, msg string) *Error

ErrorAt makes a new compilation error with the position.

func ErrorIn

func ErrorIn(start, end Pos, msg string) *Error

ErrorIn makes a new compilation error with the range.

func Errorf

func Errorf(format string, args ...interface{}) *Error

Errorf makes locerr.Error instance without source location information following given format.

func ErrorfAt

func ErrorfAt(pos Pos, format string, args ...interface{}) *Error

ErrorfAt makes a new compilation error with the position and formatted message.

func ErrorfIn

func ErrorfIn(start, end Pos, format string, args ...interface{}) *Error

ErrorfIn makes a new compilation error with the range and formatted message.

func NewError

func NewError(msg string) *Error

NewError makes locerr.Error instance without source location information.

func Note

func Note(err error, msg string) *Error

Note adds note to the given error. If given error is not locerr.Error, it's converted into locerr.Error.

func NoteAt

func NoteAt(pos Pos, err error, msg string) *Error

NoteAt adds positional information and stack additional message to the original error. If given error is not locerr.Error, it's converted into locerr.Error.

func NoteIn

func NoteIn(start, end Pos, err error, msg string) *Error

NoteIn adds range information and stack additional message to the original error. If given error is not locerr.Error, it's converted into locerr.Error.

func Notef

func Notef(err error, format string, args ...interface{}) *Error

Notef adds note to the given error. Description will be created following given format and arguments. If given error is not locerr.Error, it's converted into locerr.Error.

func NotefAt

func NotefAt(pos Pos, err error, format string, args ...interface{}) *Error

NotefAt adds positional information and stack additional formatted message to the original error If given error is not locerr.Error, it's converted into locerr.Error.

func NotefIn

func NotefIn(start, end Pos, err error, format string, args ...interface{}) *Error

NotefIn adds range information and stack additional formatted message to the original error. If given error is not locerr.Error, it's converted into locerr.Error.

func WithPos

func WithPos(pos Pos, err error) *Error

WithPos adds positional information to the passed error.

func WithRange

func WithRange(start, end Pos, err error) *Error

WithRange adds range information to the passed error.

func (*Error) At

func (err *Error) At(pos Pos) *Error

At sets a position where error occurred.

func (*Error) Error

func (err *Error) Error() string

Error builds error message for the error.

func (*Error) In

func (err *Error) In(start, end Pos) *Error

In sets start and end positions of the error.

func (*Error) Note

func (err *Error) Note(msg string) *Error

Note stacks the additional message upon current error.

func (*Error) NoteAt

func (err *Error) NoteAt(pos Pos, msg string) *Error

NoteAt stacks the additional message upon current error with position.

func (*Error) Notef

func (err *Error) Notef(format string, args ...interface{}) *Error

Notef stacks the additional formatted message upon current error.

func (*Error) NotefAt

func (err *Error) NotefAt(pos Pos, format string, args ...interface{}) *Error

NotefAt stacks the additional formatted message upon current error with poisition.

func (*Error) PrintToFile

func (err *Error) PrintToFile(f *os.File)

PrintToFile prints error message to the given file. This is useful on Windows because Error() does not support colorful string on Windows.

func (*Error) WriteMessage

func (err *Error) WriteMessage(w io.Writer)

WriteMessage writes error message to the given writer

type Pos

type Pos struct {
	// Offset from the beginning of code.
	Offset int
	// Line number.
	Line int
	// Column number.
	Column int
	// File of this position.
	File *Source
}

Pos represents some point in a source code.

func (Pos) String

func (p Pos) String() string

String makes a string representation of the position. Format is 'file:line:column'.

type Source

type Source struct {
	// Path of the file. <stdin> if it is stdin. <dummy> if it is a dummy source.
	Path string
	// Code contained in this source.
	Code []byte
	// Exists indicates this source exists in filesystem or not.
	Exists bool
}

Source represents Dachs source code file. It may be a file on filesystem, stdin or dummy file.

func NewDummySource

func NewDummySource(code string) *Source

NewDummySource make *Source with passed code. The source is actually does not exist in filesystem (so dummy). This is used for tests.

func NewSourceFromFile

func NewSourceFromFile(file string) (*Source, error)

NewSourceFromFile make *Source object from file path.

func NewSourceFromStdin

func NewSourceFromStdin() (*Source, error)

NewSourceFromStdin make *Source object from stdin. User will need to input source code into stdin.

func (*Source) BaseName

func (src *Source) BaseName() string

BaseName makes a base name from the name of source. If the source does not exist in filesystem, its base name will be 'out'.

func (*Source) String

func (src *Source) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL