// parseOffsetFlag interprets the "-offset" flag value as a renaming specification. func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) { var spec spec // Validate -offset, e.g. file.go:#123 parts := strings.Split(offsetFlag, ":#") if len(parts) != 2 { return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag) } spec.filename = parts[0] if !buildutil.FileExists(ctxt, spec.filename) { return nil, fmt.Errorf("no such file: %s", spec.filename) } bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename) if err != nil { return nil, err } spec.pkg = bp.ImportPath for _, r := range parts[1] { if !isDigit(r) { return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag) } } spec.offset, err = strconv.Atoi(parts[1]) if err != nil { return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag) } // Parse the file and check there's an identifier at that offset. fset := token.NewFileSet() f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments) if err != nil { return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err) } id := identAtOffset(fset, f, spec.offset) if id == nil { return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag) } spec.fromName = id.Name return &spec, nil }
// parseFromFlag interprets the "-from" flag value as a renaming specification. // See FromFlagUsage for valid formats. func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) { var spec spec var main string // sans "::x" suffix switch parts := strings.Split(fromFlag, "::"); len(parts) { case 1: main = parts[0] case 2: main = parts[0] spec.searchFor = parts[1] if parts[1] == "" { // error } default: return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag) } // main is one of: // filename.go // importpath // importpath.member // (importpath.type).fieldormethod if strings.HasSuffix(main, ".go") { // filename.go if spec.searchFor == "" { return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main) } spec.filename = main if !buildutil.FileExists(ctxt, spec.filename) { return nil, fmt.Errorf("no such file: %s", spec.filename) } bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename) if err != nil { return nil, err } spec.pkg = bp.ImportPath } else if a, b := splitAtLastDot(main); b == "" { // importpath e.g. "encoding/json" if spec.searchFor == "" { return nil, fmt.Errorf("-from %q: package import path %q must have a ::name suffix", main, a) } spec.pkg = a } else if strings.HasPrefix(a, "(") && strings.HasSuffix(a, ")") { // field/method of type e.g. (encoding/json.Decoder).Decode c, d := splitAtLastDot(a[1 : len(a)-1]) if d == "" { return nil, fmt.Errorf("-from %q: not a package-level named type: %q", a) } spec.pkg = c // e.g. "encoding/json" spec.pkgMember = d // e.g. "Decoder" spec.typeMember = b // e.g. "Decode" spec.fromName = b } else { // package member e.g. "encoding/json.HTMLEscape" spec.pkg = a // e.g. "encoding/json" spec.pkgMember = b // e.g. "HTMLEscape" spec.fromName = b } if spec.searchFor != "" { spec.fromName = spec.searchFor } // Sanitize the package. // TODO(adonovan): test with relative packages. May need loader changes. bp, err := ctxt.Import(spec.pkg, ".", build.FindOnly) if err != nil { return nil, fmt.Errorf("can't find package %q", spec.pkg) } spec.pkg = bp.ImportPath if !isValidIdentifier(spec.fromName) { return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName) } if Verbose { fmt.Fprintf(os.Stderr, "-from spec: %+v\n", spec) } return &spec, nil }
// parseFromFlag interprets the "-from" flag value as a renaming specification. // See FromFlagUsage for valid formats. func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) { var spec spec var main string // sans "::x" suffix switch parts := strings.Split(fromFlag, "::"); len(parts) { case 1: main = parts[0] case 2: main = parts[0] spec.searchFor = parts[1] if parts[1] == "" { // error } default: return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag) } if strings.HasSuffix(main, ".go") { // main is "filename.go" if spec.searchFor == "" { return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main) } spec.filename = main if !buildutil.FileExists(ctxt, spec.filename) { return nil, fmt.Errorf("no such file: %s", spec.filename) } bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename) if err != nil { return nil, err } spec.pkg = bp.ImportPath } else { // main is one of: // "importpath" // "importpath".member // (*"importpath".type).fieldormethod (parens and star optional) if err := parseObjectSpec(&spec, main); err != nil { return nil, err } } if spec.searchFor != "" { spec.fromName = spec.searchFor } // Sanitize the package. // TODO(adonovan): test with relative packages. May need loader changes. bp, err := ctxt.Import(spec.pkg, ".", build.FindOnly) if err != nil { return nil, fmt.Errorf("can't find package %q", spec.pkg) } spec.pkg = bp.ImportPath if !isValidIdentifier(spec.fromName) { return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName) } if Verbose { fmt.Fprintf(os.Stderr, "-from spec: %+v\n", spec) } return &spec, nil }