func (c Config) QuickFixOnce() (bool, error) { fset := c.Fset files := c.Files errs := []error{} config := &types.Config{ Error: func(err error) { errs = append(errs, err) }, } _, err := config.Check("_quickfix", fset, files, c.TypeInfo) if err == nil { return false, nil } // apply fixes on AST later so that we won't break funcs that inspect AST by positions fixes := map[error]func() bool{} unhandled := ErrorList{} foundError := len(errs) > 0 for _, err := range errs { err, ok := err.(types.Error) if !ok { unhandled = append(unhandled, err) continue } f := findFile(c.Files, err.Pos) if f == nil { e := ErrCouldNotLocate{ Err: err, Fset: fset, } unhandled = append(unhandled, e) continue } nodepath, _ := astutil.PathEnclosingInterval(f, err.Pos, err.Pos) var fix func() bool // - "%s declared but not used" // - "%q imported but not used" (+ " as %s") // - "label %s declared but not used" TODO // - "no new variables on left side of :=" if m := declaredNotUsed.FindStringSubmatch(err.Msg); m != nil { identName := m[1] fix = func() bool { return fixDeclaredNotUsed(nodepath, identName) } } else if m := importedNotUsed.FindStringSubmatch(err.Msg); m != nil { pkgPath := m[1] // quoted string, but it's okay because this will be compared to ast.BasicLit.Value. fix = func() bool { return fixImportedNotUsed(nodepath, pkgPath) } } else if err.Msg == noNewVariablesOnDefine { fix = func() bool { return fixNoNewVariables(nodepath) } } else { unhandled = append(unhandled, err) } if fix != nil { fixes[err] = fix } } for err, fix := range fixes { if fix() == false { unhandled = append(unhandled, err) } } return foundError, unhandled.any() }
// doQuickFix tries to fix the source AST so that it compiles well. func (s *Session) doQuickFix() error { const maxAttempts = 10 s.reset() quickFixAttempt: for i := 0; i < maxAttempts; i++ { s.TypeInfo = types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), } files := s.ExtraFiles files = append(files, s.File) config := quickfix.Config{ Fset: s.Fset, Files: files, TypeInfo: &s.TypeInfo, } _, err := config.QuickFixOnce() if err == nil { break } debugf("quickFix :: err = %#v", err) errList, ok := err.(quickfix.ErrorList) if !ok { continue } // (try to) fix gore-specific remaining errors for _, err := range errList { err, ok := err.(types.Error) if !ok { continue } // "... used as value": // // convert // __gore_pp(funcWithSideEffectReturningNoValue()) // to // funcWithSideEffectReturningNoValue() if strings.HasSuffix(err.Msg, " used as value") { nodepath, _ := astutil.PathEnclosingInterval(s.File, err.Pos, err.Pos) for _, node := range nodepath { stmt, ok := node.(ast.Stmt) if !ok { continue } for i := range s.mainBody.List { if s.mainBody.List[i] != stmt { continue } exprs := printedExprs(stmt) stmts := s.mainBody.List[0:i] for _, expr := range exprs { stmts = append(stmts, &ast.ExprStmt{expr}) } s.mainBody.List = append(stmts, s.mainBody.List[i+1:]...) continue quickFixAttempt } } } } debugf("quickFix :: give up: %#v", err) } return nil }