Improve error reporting

Report module name and variant and property names in errors.

Change-Id: I747f840402497b2c92bf8c565d7c50af33e6ab0e
This commit is contained in:
Colin Cross 2016-10-07 17:13:10 -07:00
parent ad914cea1a
commit 2c62844274
5 changed files with 85 additions and 52 deletions

View file

@ -174,7 +174,9 @@ func fatalErrors(errs []error) {
for _, err := range errs { for _, err := range errs {
switch err := err.(type) { switch err := err.(type) {
case *blueprint.Error: case *blueprint.BlueprintError,
*blueprint.ModuleError,
*blueprint.PropertyError:
fmt.Printf("%serror:%s %s\n", red, unred, err.Error()) fmt.Printf("%serror:%s %s\n", red, unred, err.Error())
default: default:
fmt.Printf("%sinternal error:%s %s\n", red, unred, err) fmt.Printf("%sinternal error:%s %s\n", red, unred, err)

View file

@ -105,11 +105,37 @@ type Context struct {
// An Error describes a problem that was encountered that is related to a // An Error describes a problem that was encountered that is related to a
// particular location in a Blueprints file. // particular location in a Blueprints file.
type Error struct { type BlueprintError struct {
Err error // the error that occurred Err error // the error that occurred
Pos scanner.Position // the relevant Blueprints file location Pos scanner.Position // the relevant Blueprints file location
} }
// A ModuleError describes a problem that was encountered that is related to a
// particular module in a Blueprints file
type ModuleError struct {
BlueprintError
module *moduleInfo
}
// A PropertyError describes a problem that was encountered that is related to a
// particular property in a Blueprints file
type PropertyError struct {
ModuleError
property string
}
func (e *BlueprintError) Error() string {
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}
func (e *ModuleError) Error() string {
return fmt.Sprintf("%s: %s: %s", e.Pos, e.module, e.Err)
}
func (e *PropertyError) Error() string {
return fmt.Sprintf("%s: %s: %s: %s", e.Pos, e.module, e.property, e.Err)
}
type localBuildActions struct { type localBuildActions struct {
variables []*localVariable variables []*localVariable
rules []*localRule rules []*localRule
@ -229,11 +255,6 @@ type mutatorInfo struct {
parallel bool parallel bool
} }
func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}
// NewContext creates a new Context object. The created context initially has // NewContext creates a new Context object. The created context initially has
// no module or singleton factories registered, so the RegisterModuleFactory and // no module or singleton factories registered, so the RegisterModuleFactory and
// RegisterSingletonFactory methods must be called before it can do anything // RegisterSingletonFactory methods must be called before it can do anything
@ -514,7 +535,7 @@ func (c *Context) parse(rootDir, filename string, r io.Reader,
if len(errs) > 0 { if len(errs) > 0 {
for i, err := range errs { for i, err := range errs {
if parseErr, ok := err.(*parser.ParseError); ok { if parseErr, ok := err.(*parser.ParseError); ok {
err = &Error{ err = &BlueprintError{
Err: parseErr.Err, Err: parseErr.Err,
Pos: parseErr.Pos, Pos: parseErr.Pos,
} }
@ -787,7 +808,7 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
globPattern := filepath.Join(dir, file) globPattern := filepath.Join(dir, file)
matches, matchedDirs, err := pathtools.Glob(globPattern) matches, matchedDirs, err := pathtools.Glob(globPattern)
if err != nil { if err != nil {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: %s", globPattern, err.Error()), Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
Pos: buildPos, Pos: buildPos,
}) })
@ -795,7 +816,7 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
} }
if len(matches) == 0 { if len(matches) == 0 {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: not found", globPattern), Err: fmt.Errorf("%q: not found", globPattern),
Pos: buildPos, Pos: buildPos,
}) })
@ -809,12 +830,12 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)
} else if !exists { } else if !exists {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q not found", foundBlueprints), Err: fmt.Errorf("%q not found", foundBlueprints),
}) })
continue continue
} else if dir { } else if dir {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q is a directory", foundBlueprints), Err: fmt.Errorf("%q is a directory", foundBlueprints),
}) })
continue continue
@ -835,7 +856,7 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
globPattern := filepath.Join(dir, subdir) globPattern := filepath.Join(dir, subdir)
matches, matchedDirs, err := pathtools.Glob(globPattern) matches, matchedDirs, err := pathtools.Glob(globPattern)
if err != nil { if err != nil {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: %s", globPattern, err.Error()), Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
Pos: subdirsPos, Pos: subdirsPos,
}) })
@ -843,7 +864,7 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
} }
if len(matches) == 0 && !optional { if len(matches) == 0 && !optional {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: not found", globPattern), Err: fmt.Errorf("%q: not found", globPattern),
Pos: subdirsPos, Pos: subdirsPos,
}) })
@ -916,7 +937,7 @@ func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scann
return ret, assignment.EqualsPos, nil return ret, assignment.EqualsPos, nil
case *parser.Bool, *parser.String: case *parser.Bool, *parser.String:
return nil, scanner.Position{}, &Error{ return nil, scanner.Position{}, &BlueprintError{
Err: fmt.Errorf("%q must be a list of strings", v), Err: fmt.Errorf("%q must be a list of strings", v),
Pos: assignment.EqualsPos, Pos: assignment.EqualsPos,
} }
@ -934,7 +955,7 @@ func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position
case *parser.String: case *parser.String:
return value.Value, assignment.EqualsPos, nil return value.Value, assignment.EqualsPos, nil
case *parser.Bool, *parser.List: case *parser.Bool, *parser.List:
return "", scanner.Position{}, &Error{ return "", scanner.Position{}, &BlueprintError{
Err: fmt.Errorf("%q must be a string", v), Err: fmt.Errorf("%q must be a string", v),
Pos: assignment.EqualsPos, Pos: assignment.EqualsPos,
} }
@ -1050,7 +1071,7 @@ func (c *Context) convertDepsToVariation(module *moduleInfo,
} }
} }
if newDep == nil { if newDep == nil {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("failed to find variation %q for module %q needed by %q", Err: fmt.Errorf("failed to find variation %q for module %q needed by %q",
variationName, dep.module.properties.Name, module.properties.Name), variationName, dep.module.properties.Name, module.properties.Name),
Pos: module.pos, Pos: module.pos,
@ -1085,7 +1106,7 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
} }
return nil, []error{ return nil, []error{
&Error{ &BlueprintError{
Err: fmt.Errorf("unrecognized module type %q", moduleDef.Type), Err: fmt.Errorf("unrecognized module type %q", moduleDef.Type),
Pos: moduleDef.TypePos, Pos: moduleDef.TypePos,
}, },
@ -1126,11 +1147,11 @@ func (c *Context) addModule(module *moduleInfo) []error {
if group, present := c.moduleGroups[name]; present { if group, present := c.moduleGroups[name]; present {
return []error{ return []error{
&Error{ &BlueprintError{
Err: fmt.Errorf("module %q already defined", name), Err: fmt.Errorf("module %q already defined", name),
Pos: module.pos, Pos: module.pos,
}, },
&Error{ &BlueprintError{
Err: fmt.Errorf("<-- previous definition here"), Err: fmt.Errorf("<-- previous definition here"),
Pos: group.modules[0].pos, Pos: group.modules[0].pos,
}, },
@ -1223,7 +1244,7 @@ func (c *Context) findMatchingVariant(module *moduleInfo, group *moduleGroup) *m
func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName string) []error { func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName string) []error {
if depName == module.properties.Name { if depName == module.properties.Name {
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on itself", depName), Err: fmt.Errorf("%q depends on itself", depName),
Pos: module.pos, Pos: module.pos,
}} }}
@ -1235,7 +1256,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s
module.missingDeps = append(module.missingDeps, depName) module.missingDeps = append(module.missingDeps, depName)
return nil return nil
} }
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q", Err: fmt.Errorf("%q depends on undefined module %q",
module.properties.Name, depName), module.properties.Name, depName),
Pos: module.pos, Pos: module.pos,
@ -1254,7 +1275,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s
return nil return nil
} }
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("dependency %q of %q missing variant %q", Err: fmt.Errorf("dependency %q of %q missing variant %q",
depGroup.modules[0].properties.Name, module.properties.Name, depGroup.modules[0].properties.Name, module.properties.Name,
c.prettyPrintVariant(module.dependencyVariant)), c.prettyPrintVariant(module.dependencyVariant)),
@ -1264,7 +1285,7 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s
func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*moduleInfo, []error) { func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*moduleInfo, []error) {
if destName == module.properties.Name { if destName == module.properties.Name {
return nil, []error{&Error{ return nil, []error{&BlueprintError{
Err: fmt.Errorf("%q depends on itself", destName), Err: fmt.Errorf("%q depends on itself", destName),
Pos: module.pos, Pos: module.pos,
}} }}
@ -1272,7 +1293,7 @@ func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*m
destInfo, ok := c.moduleGroups[destName] destInfo, ok := c.moduleGroups[destName]
if !ok { if !ok {
return nil, []error{&Error{ return nil, []error{&BlueprintError{
Err: fmt.Errorf("%q has a reverse dependency on undefined module %q", Err: fmt.Errorf("%q has a reverse dependency on undefined module %q",
module.properties.Name, destName), module.properties.Name, destName),
Pos: module.pos, Pos: module.pos,
@ -1283,7 +1304,7 @@ func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*m
return m, nil return m, nil
} }
return nil, []error{&Error{ return nil, []error{&BlueprintError{
Err: fmt.Errorf("reverse dependency %q of %q missing variant %q", Err: fmt.Errorf("reverse dependency %q of %q missing variant %q",
destName, module.properties.Name, destName, module.properties.Name,
c.prettyPrintVariant(module.dependencyVariant)), c.prettyPrintVariant(module.dependencyVariant)),
@ -1300,7 +1321,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
module.missingDeps = append(module.missingDeps, depName) module.missingDeps = append(module.missingDeps, depName)
return nil return nil
} }
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q", Err: fmt.Errorf("%q depends on undefined module %q",
module.properties.Name, depName), module.properties.Name, depName),
Pos: module.pos, Pos: module.pos,
@ -1329,7 +1350,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
} }
if found { if found {
if module == m { if module == m {
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on itself", depName), Err: fmt.Errorf("%q depends on itself", depName),
Pos: module.pos, Pos: module.pos,
}} }}
@ -1338,7 +1359,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
// that module is earlier in the module list than this one, since we always // that module is earlier in the module list than this one, since we always
// run GenerateBuildActions in order for the variants of a module // run GenerateBuildActions in order for the variants of a module
if depGroup == module.group && beforeInModuleList(module, m, module.group.modules) { if depGroup == module.group && beforeInModuleList(module, m, module.group.modules) {
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on later version of itself", depName), Err: fmt.Errorf("%q depends on later version of itself", depName),
Pos: module.pos, Pos: module.pos,
}} }}
@ -1349,7 +1370,7 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
} }
} }
return []error{&Error{ return []error{&BlueprintError{
Err: fmt.Errorf("dependency %q of %q missing variant %q", Err: fmt.Errorf("dependency %q of %q missing variant %q",
depGroup.modules[0].properties.Name, module.properties.Name, depGroup.modules[0].properties.Name, module.properties.Name,
c.prettyPrintVariant(newVariant)), c.prettyPrintVariant(newVariant)),
@ -1498,7 +1519,7 @@ func (c *Context) updateDependencies() (errs []error) {
// for generating the errors. The cycle list is in // for generating the errors. The cycle list is in
// reverse order because all the 'check' calls append // reverse order because all the 'check' calls append
// their own module to the list. // their own module to the list.
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("encountered dependency cycle:"), Err: fmt.Errorf("encountered dependency cycle:"),
Pos: cycle[len(cycle)-1].pos, Pos: cycle[len(cycle)-1].pos,
}) })
@ -1507,7 +1528,7 @@ func (c *Context) updateDependencies() (errs []error) {
curModule := cycle[0] curModule := cycle[0]
for i := len(cycle) - 1; i >= 0; i-- { for i := len(cycle) - 1; i >= 0; i-- {
nextModule := cycle[i] nextModule := cycle[i]
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf(" %q depends on %q", Err: fmt.Errorf(" %q depends on %q",
curModule.properties.Name, curModule.properties.Name,
nextModule.properties.Name), nextModule.properties.Name),
@ -1975,7 +1996,7 @@ func (c *Context) generateModuleBuildActions(config interface{},
if module.missingDeps != nil && !mctx.handledMissingDeps { if module.missingDeps != nil && !mctx.handledMissingDeps {
var errs []error var errs []error
for _, depName := range module.missingDeps { for _, depName := range module.missingDeps {
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q", Err: fmt.Errorf("%q depends on undefined module %q",
module.properties.Name, depName), module.properties.Name, depName),
Pos: module.pos, Pos: module.pos,
@ -2446,7 +2467,7 @@ func (c *Context) ModuleErrorf(logicModule Module, format string,
args ...interface{}) error { args ...interface{}) error {
module := c.moduleInfo[logicModule] module := c.moduleInfo[logicModule]
return &Error{ return &BlueprintError{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: module.pos, Pos: module.pos,
} }

View file

@ -193,7 +193,7 @@ func (d *baseModuleContext) error(err error) {
func (d *baseModuleContext) Errorf(pos scanner.Position, func (d *baseModuleContext) Errorf(pos scanner.Position,
format string, args ...interface{}) { format string, args ...interface{}) {
d.error(&Error{ d.error(&BlueprintError{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: pos, Pos: pos,
}) })
@ -202,9 +202,12 @@ func (d *baseModuleContext) Errorf(pos scanner.Position,
func (d *baseModuleContext) ModuleErrorf(format string, func (d *baseModuleContext) ModuleErrorf(format string,
args ...interface{}) { args ...interface{}) {
d.error(&Error{ d.error(&ModuleError{
Err: fmt.Errorf(format, args...), BlueprintError: BlueprintError{
Pos: d.module.pos, Err: fmt.Errorf(format, args...),
Pos: d.module.pos,
},
module: d.module,
}) })
} }
@ -217,11 +220,15 @@ func (d *baseModuleContext) PropertyErrorf(property, format string,
pos = d.module.pos pos = d.module.pos
} }
format = property + ": " + format d.error(&PropertyError{
ModuleError: ModuleError{
d.error(&Error{ BlueprintError: BlueprintError{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: pos, Pos: pos,
},
module: d.module,
},
property: property,
}) })
} }
@ -248,9 +255,12 @@ func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string,
args ...interface{}) { args ...interface{}) {
module := m.context.moduleInfo[logicModule] module := m.context.moduleInfo[logicModule]
m.errs = append(m.errs, &Error{ m.errs = append(m.errs, &ModuleError{
Err: fmt.Errorf(format, args...), BlueprintError: BlueprintError{
Pos: module.pos, Err: fmt.Errorf(format, args...),
Pos: module.pos,
},
module: module,
}) })
} }

View file

@ -63,7 +63,7 @@ func unpackProperties(propertyDefs []*parser.Property,
for name, packedProperty := range propertyMap { for name, packedProperty := range propertyMap {
result[name] = packedProperty.property result[name] = packedProperty.property
if !packedProperty.unpacked { if !packedProperty.unpacked {
err := &Error{ err := &BlueprintError{
Err: fmt.Errorf("unrecognized property %q", name), Err: fmt.Errorf("unrecognized property %q", name),
Pos: packedProperty.property.ColonPos, Pos: packedProperty.property.ColonPos,
} }
@ -88,11 +88,11 @@ func buildPropertyMap(namePrefix string, propertyDefs []*parser.Property,
// We've already added this property. // We've already added this property.
continue continue
} }
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("property %q already defined", name), Err: fmt.Errorf("property %q already defined", name),
Pos: propertyDef.ColonPos, Pos: propertyDef.ColonPos,
}) })
errs = append(errs, &Error{ errs = append(errs, &BlueprintError{
Err: fmt.Errorf("<-- previous definition here"), Err: fmt.Errorf("<-- previous definition here"),
Pos: first.property.ColonPos, Pos: first.property.ColonPos,
}) })
@ -216,7 +216,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
if proptools.HasTag(field, "blueprint", "mutated") { if proptools.HasTag(field, "blueprint", "mutated") {
errs = append(errs, errs = append(errs,
&Error{ &BlueprintError{
Err: fmt.Errorf("mutated field %s cannot be set in a Blueprint file", propertyName), Err: fmt.Errorf("mutated field %s cannot be set in a Blueprint file", propertyName),
Pos: packedProperty.property.ColonPos, Pos: packedProperty.property.ColonPos,
}) })
@ -228,7 +228,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
if filterKey != "" && !proptools.HasTag(field, filterKey, filterValue) { if filterKey != "" && !proptools.HasTag(field, filterKey, filterValue) {
errs = append(errs, errs = append(errs,
&Error{ &BlueprintError{
Err: fmt.Errorf("filtered field %s cannot be set in a Blueprint file", propertyName), Err: fmt.Errorf("filtered field %s cannot be set in a Blueprint file", propertyName),
Pos: packedProperty.property.ColonPos, Pos: packedProperty.property.ColonPos,
}) })

View file

@ -244,7 +244,7 @@ var validUnpackTestCases = []struct {
}, },
}, },
errs: []error{ errs: []error{
&Error{ &BlueprintError{
Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"), Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
Pos: mkpos(30, 4, 9), Pos: mkpos(30, 4, 9),
}, },