Skip to content

Commit

Permalink
Merge pull request #4 from moznion/support_caused_at
Browse files Browse the repository at this point in the history
Support `caused at` on error message
  • Loading branch information
moznion authored Jan 21, 2019
2 parents 2225a55 + bbb6ce9 commit f275574
Show file tree
Hide file tree
Showing 39 changed files with 408 additions and 153 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ bootstrap: installdeps
github.com/moznion/go-errgen/cmd/errgen

errgen:
./author/errgen.sh
go generate ./...

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ Please refer to the godoc: [![GoDoc](https://godoc.org/github.com/moznion/gowrtr

Methods of this library act as immutable. It means it doesn't change any internal state implicitly, so you can take a snapshot of the code generator. That is useful to reuse and derive the code generator instance.

### Debug friendly

This library shows "where is a cause of the error" when code generator raises an error. This means each error message contains a pointer for the error source (i.e. file name and the line number). This should be helpful for debugging.

Error messages example:

```
[GOWRTR-14] condition of case must not be empty, but it gets empty (caused at /tmp/main.go:22)
```

### Supported syntax

- [x] `package`
Expand Down
Empty file removed author/bin/.gitkeep
Empty file.
7 changes: 0 additions & 7 deletions author/errgen.sh

This file was deleted.

11 changes: 9 additions & 2 deletions generator/anonymous_func.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package generator

import "github.com/moznion/gowrtr/internal/errmsg"
import (
"github.com/moznion/gowrtr/internal/errmsg"
)

// AnonymousFunc represents a code generator for anonymous func.
type AnonymousFunc struct {
goFunc bool
anonymousFuncSignature *AnonymousFuncSignature
statements []Statement
funcInvocation *FuncInvocation
caller string
}

// NewAnonymousFunc returns a new `AnonymousFunc`.
Expand All @@ -17,6 +20,7 @@ func NewAnonymousFunc(goFunc bool, signature *AnonymousFuncSignature, statements
goFunc: goFunc,
anonymousFuncSignature: signature,
statements: statements,
caller: fetchClientCallerLine(),
}
}

Expand All @@ -28,6 +32,7 @@ func (ifg *AnonymousFunc) AddStatements(statements ...Statement) *AnonymousFunc
anonymousFuncSignature: ifg.anonymousFuncSignature,
statements: append(ifg.statements, statements...),
funcInvocation: ifg.funcInvocation,
caller: ifg.caller,
}
}

Expand All @@ -39,6 +44,7 @@ func (ifg *AnonymousFunc) Statements(statements ...Statement) *AnonymousFunc {
anonymousFuncSignature: ifg.anonymousFuncSignature,
statements: statements,
funcInvocation: ifg.funcInvocation,
caller: ifg.caller,
}
}

Expand All @@ -50,6 +56,7 @@ func (ifg *AnonymousFunc) Invocation(funcInvocation *FuncInvocation) *AnonymousF
anonymousFuncSignature: ifg.anonymousFuncSignature,
statements: ifg.statements,
funcInvocation: funcInvocation,
caller: ifg.caller,
}
}

Expand All @@ -64,7 +71,7 @@ func (ifg *AnonymousFunc) Generate(indentLevel int) (string, error) {
stmt += "func"

if ifg.anonymousFuncSignature == nil {
return "", errmsg.AnonymousFuncSignatureIsNilError()
return "", errmsg.AnonymousFuncSignatureIsNilError(ifg.caller)
}

sig, err := ifg.anonymousFuncSignature.Generate(0)
Expand Down
13 changes: 11 additions & 2 deletions generator/anonymous_func_signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
type AnonymousFuncSignature struct {
funcParameters []*FuncParameter
returnTypes []string
callers []string
}

// NewAnonymousFuncSignature returns a new `AnonymousFuncSignature`.
Expand All @@ -23,6 +24,7 @@ func (f *AnonymousFuncSignature) AddParameters(funcParameters ...*FuncParameter)
return &AnonymousFuncSignature{
funcParameters: append(f.funcParameters, funcParameters...),
returnTypes: f.returnTypes,
callers: append(f.callers, fetchClientCallerLineAsSlice(len(funcParameters))...),
}
}

Expand All @@ -32,6 +34,7 @@ func (f *AnonymousFuncSignature) Parameters(funcParameters ...*FuncParameter) *A
return &AnonymousFuncSignature{
funcParameters: funcParameters,
returnTypes: f.returnTypes,
callers: fetchClientCallerLineAsSlice(len(funcParameters)),
}
}

Expand All @@ -41,6 +44,7 @@ func (f *AnonymousFuncSignature) AddReturnTypes(returnTypes ...string) *Anonymou
return &AnonymousFuncSignature{
funcParameters: f.funcParameters,
returnTypes: append(f.returnTypes, returnTypes...),
callers: f.callers,
}
}

Expand All @@ -50,6 +54,7 @@ func (f *AnonymousFuncSignature) ReturnTypes(returnTypes ...string) *AnonymousFu
return &AnonymousFuncSignature{
funcParameters: f.funcParameters,
returnTypes: returnTypes,
callers: f.callers,
}
}

Expand All @@ -58,22 +63,26 @@ func (f *AnonymousFuncSignature) Generate(indentLevel int) (string, error) {
stmt := "("

typeExisted := true
typeMissingCaller := ""
params := make([]string, len(f.funcParameters))
for i, param := range f.funcParameters {
if param.name == "" {
return "", errmsg.FuncParameterNameIsEmptyErr()
return "", errmsg.FuncParameterNameIsEmptyErr(f.callers[i])
}

paramSet := param.name
typeExisted = param.typ != ""
if typeExisted {
paramSet += " " + param.typ
}
if !typeExisted {
typeMissingCaller = f.callers[i]
}
params[i] = paramSet
}

if !typeExisted {
return "", errmsg.LastFuncParameterTypeIsEmptyErr()
return "", errmsg.LastFuncParameterTypeIsEmptyErr(typeMissingCaller)
}

stmt += strings.Join(params, ", ") + ")"
Expand Down
10 changes: 8 additions & 2 deletions generator/anonymous_func_signature_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package generator

import (
"regexp"
"strings"
"testing"

"github.com/moznion/gowrtr/internal/errmsg"
Expand Down Expand Up @@ -41,13 +43,17 @@ func TestShouldGenerateAnonymousFuncSignatureRaisesErrorWhenParamNameIsEmpty(t *
NewFuncParameter("", "string"),
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncParameterNameIsEmptyErr().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncParameterNameIsEmptyErr("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateAnonymousFuncSignatureRaisesErrorWhenParamTypeIsEmpty(t *testing.T) {
generator := NewAnonymousFuncSignature().AddParameters(
NewFuncParameter("foo", ""),
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.LastFuncParameterTypeIsEmptyErr().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.LastFuncParameterTypeIsEmptyErr("").Error(), " ")[0],
), err.Error())
}
18 changes: 14 additions & 4 deletions generator/anonymous_func_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package generator

import (
"regexp"
"strings"
"testing"

"github.com/moznion/gowrtr/internal/errmsg"
Expand Down Expand Up @@ -117,7 +119,9 @@ func TestShouldGenerateAnonymousFuncRaisesErrorWhenAnonymousFuncSignatureIsNil(t
nil,
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.AnonymousFuncSignatureIsNilError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.AnonymousFuncSignatureIsNilError("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateAnonymousFuncRaisesErrorWhenAnonymousFuncSignatureRaisesError(t *testing.T) {
Expand All @@ -128,7 +132,9 @@ func TestShouldGenerateAnonymousFuncRaisesErrorWhenAnonymousFuncSignatureRaisesE
),
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncParameterNameIsEmptyErr().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncParameterNameIsEmptyErr("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateAnonymousFuncRaisesErrorWhenStatementRaisesError(t *testing.T) {
Expand All @@ -139,7 +145,9 @@ func TestShouldGenerateAnonymousFuncRaisesErrorWhenStatementRaisesError(t *testi
)

_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncNameIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateAnonymousFuncRaisesErrorWhenFuncInvocationRaisesError(t *testing.T) {
Expand All @@ -148,5 +156,7 @@ func TestShouldGenerateAnonymousFuncRaisesErrorWhenFuncInvocationRaisesError(t *
NewAnonymousFuncSignature(),
).Invocation(NewFuncInvocation(""))
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncInvocationParameterIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncInvocationParameterIsEmptyError("").Error(), " ")[0],
), err.Error())
}
4 changes: 3 additions & 1 deletion generator/case.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import (
type Case struct {
condition string
statements []Statement
caller string
}

// NewCase creates a new `Case`.
func NewCase(condition string, statements ...Statement) *Case {
return &Case{
condition: condition,
statements: statements,
caller: fetchClientCallerLine(),
}
}

Expand All @@ -43,7 +45,7 @@ func (c *Case) Statements(statements ...Statement) *Case {
func (c *Case) Generate(indentLevel int) (string, error) {
condition := c.condition
if condition == "" {
return "", errmsg.CaseConditionIsEmptyError()
return "", errmsg.CaseConditionIsEmptyError(c.caller)
}

indent := buildIndent(indentLevel)
Expand Down
10 changes: 8 additions & 2 deletions generator/case_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package generator

import (
"regexp"
"strings"
"testing"

"github.com/moznion/gowrtr/internal/errmsg"
Expand Down Expand Up @@ -51,7 +53,9 @@ func TestShouldGenerateCase(t *testing.T) {
func TestShouldGenerateCaseRaisesErrorWhenConditionIsEmpty(t *testing.T) {
generator := NewCase("")
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.CaseConditionIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.CaseConditionIsEmptyError("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateCaseRaisesErrorWhenStatementsRaisesError(t *testing.T) {
Expand All @@ -60,5 +64,7 @@ func TestShouldGenerateCaseRaisesErrorWhenStatementsRaisesError(t *testing.T) {
NewFunc(nil, NewFuncSignature("")),
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncNameIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0],
), err.Error())
}
6 changes: 5 additions & 1 deletion generator/code_block_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package generator

import (
"regexp"
"strings"
"testing"

"github.com/moznion/gowrtr/internal/errmsg"
Expand Down Expand Up @@ -94,5 +96,7 @@ func TestShouldGenerateCodeBlockGiveUpWhenStatementRaisesError(t *testing.T) {
NewFunc(nil, NewFuncSignature("")),
)
_, err := generator.Generate(0)
assert.EqualError(t, err, errmsg.FuncNameIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0],
), err.Error())
}
12 changes: 8 additions & 4 deletions generator/composite_literal.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type compositeLiteralField struct {
// CompositeLiteral represents a code generator for composite literal.
// Please see also: https://golang.org/doc/effective_go.html#composite_literals
type CompositeLiteral struct {
typ string
fields []*compositeLiteralField
typ string
fields []*compositeLiteralField
callers []string
}

// NewCompositeLiteral returns a new `CompositeLiteral`.
Expand All @@ -35,6 +36,7 @@ func (c *CompositeLiteral) AddField(key string, value Statement) *CompositeLiter
key: key,
value: value,
}),
callers: append(c.callers, fetchClientCallerLine()),
}
}

Expand All @@ -47,6 +49,7 @@ func (c *CompositeLiteral) AddFieldStr(key string, value string) *CompositeLiter
key: key,
value: NewRawStatement(fmt.Sprintf(`"%s"`, value)),
}),
callers: append(c.callers, fetchClientCallerLine()),
}
}

Expand All @@ -59,6 +62,7 @@ func (c *CompositeLiteral) AddFieldRaw(key string, value interface{}) *Composite
key: key,
value: NewRawStatement(fmt.Sprintf("%v", value)),
}),
callers: append(c.callers, fetchClientCallerLine()),
}
}

Expand All @@ -68,7 +72,7 @@ func (c *CompositeLiteral) Generate(indentLevel int) (string, error) {
nextLevelIndent := buildIndent(indentLevel + 1)

stmt := fmt.Sprintf("%s%s{\n", indent, c.typ)
for _, field := range c.fields {
for i, field := range c.fields {
genValue, err := field.value.Generate(indentLevel + 1)
if err != nil {
return "", err
Expand All @@ -82,7 +86,7 @@ func (c *CompositeLiteral) Generate(indentLevel int) (string, error) {
stmt += key + ": "
}
if genValue == "" {
return "", errmsg.ValueOfCompositeLiteralIsEmptyError()
return "", errmsg.ValueOfCompositeLiteralIsEmptyError(c.callers[i])
}
stmt += fmt.Sprintf("%s,\n", genValue)
}
Expand Down
10 changes: 8 additions & 2 deletions generator/composite_literal_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package generator

import (
"regexp"
"strings"
"testing"

"github.com/moznion/gowrtr/internal/errmsg"
Expand Down Expand Up @@ -73,10 +75,14 @@ func TestShouldGenerateCompositeLiteralWithEmptyKey(t *testing.T) {

func TestShouldGenerateCompositeLiteralRaiseError(t *testing.T) {
_, err := NewCompositeLiteral("").AddField("foo", NewIf("")).Generate(0)
assert.EqualError(t, err, errmsg.IfConditionIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.IfConditionIsEmptyError("").Error(), " ")[0],
), err.Error())
}

func TestShouldGenerateCompositeLiteralRaiseErrorWhenValueIsEmpty(t *testing.T) {
_, err := NewCompositeLiteral("[]string").AddField("foo", NewRawStatement("")).Generate(0)
assert.EqualError(t, err, errmsg.ValueOfCompositeLiteralIsEmptyError().Error())
assert.Regexp(t, regexp.MustCompile(
`^\`+strings.Split(errmsg.ValueOfCompositeLiteralIsEmptyError("").Error(), " ")[0],
), err.Error())
}
Loading

0 comments on commit f275574

Please sign in to comment.