refactor/rename: make -from syntax support string literals for complex import paths.

(They may contain any character, after all.)

Also, allow but don't require parens and stars.
e.g. (*"encoding/json".Decoder).Decode or "encoding/json".Decoder.Decode
     but not encoding/json.Decoder.Decode.

Since -from queries are now Go expressions, we use the Go parser.
(Thanks to Rog Peppe for the suggestion.)

LGTM=sameer
R=sameer
CC=golang-codereviews, gri, rogpeppe
https://golang.org/cl/154610043
diff --git a/cmd/gorename/main.go b/cmd/gorename/main.go
index 255eb49..8ba61f8 100644
--- a/cmd/gorename/main.go
+++ b/cmd/gorename/main.go
@@ -86,7 +86,7 @@
 
   Rename the object whose identifier is at byte offset 123 within file file.go.
 
-% gorename -from '(bytes.Buffer).Len' -to Size
+% gorename -from '"bytes".Buffer.Len' -to Size
 
   Rename the "Len" method of the *bytes.Buffer type to "Size".
 
@@ -98,6 +98,7 @@
 - sketch a proof of exhaustiveness.
 
 Features:
+- support running on packages specified as *.go files on the command line
 - support running on programs containing errors (loader.Config.AllowErrors)
 - allow users to specify a scope other than "global" (to avoid being
   stuck by neglected packages in $GOPATH that don't build).
@@ -114,7 +115,6 @@
   all local variables of a given type,
   all PkgNames for a given package.
 - emit JSON output for other editors and tools.
-- integration with editors other than Emacs.
 `
 
 func main() {
diff --git a/refactor/rename/rename_test.go b/refactor/rename/rename_test.go
index 2e46569..808e949 100644
--- a/refactor/rename/rename_test.go
+++ b/refactor/rename/rename_test.go
@@ -305,7 +305,7 @@
 				`would conflict with this method`,
 		},
 		{
-			from: "(main.I).f", to: "h",
+			from: `("main".I).f`, to: "h", // NB: exercises quoted import paths too
 			want: `renaming this interface method "f" to "h".*` +
 				`would conflict with this method.*` +
 				`in named interface type "J"`,
diff --git a/refactor/rename/spec.go b/refactor/rename/spec.go
index d8d320a..dc390f0 100644
--- a/refactor/rename/spec.go
+++ b/refactor/rename/spec.go
@@ -68,16 +68,19 @@
 const FromFlagUsage = `
 A legal -from query has one of the following forms:
 
-  (encoding/json.Decoder).Decode      method of package-level named type
-  (encoding/json.Decoder).buf         field of package-level named struct type
-  encoding/json.HTMLEscape            package member (const, func, var, type)
-  (encoding/json.Decoder).Decode::x   local object x within a method
-  encoding/json.HTMLEscape::x         local object x within a function
-  encoding/json::x                    object x anywhere within a package
-  json.go::x                          object x within file json.go
+  "encoding/json".Decoder.Decode	method of package-level named type
+  (*"encoding/json".Decoder).Decode	ditto, alternative syntax
+  "encoding/json".Decoder.buf           field of package-level named struct type
+  "encoding/json".HTMLEscape            package member (const, func, var, type)
+  "encoding/json".Decoder.Decode::x     local object x within a method
+  "encoding/json".HTMLEscape::x         local object x within a function
+  "encoding/json"::x                    object x anywhere within a package
+  json.go::x                            object x within file json.go
 
-  For methods attached to a pointer type, the '*' must not be specified.
-  [TODO(adonovan): fix that.]
+  For methods, the parens and '*' on the receiver type are both optional.
+
+  Double-quotes may be omitted for single-segment import paths such as
+  fmt.  They may need to be escaped when writing a shell command.
 
   It is an error if one of the ::x queries matches multiple objects.
 `
@@ -100,14 +103,8 @@
 		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
+		// main is "filename.go"
 		if spec.searchFor == "" {
 			return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
 		}
@@ -122,30 +119,14 @@
 		}
 		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
+		// 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 != "" {
@@ -171,14 +152,70 @@
 	return &spec, nil
 }
 
-// "encoding/json.HTMLEscape" -> ("encoding/json", "HTMLEscape")
-// "encoding/json"            -> ("encoding/json", "")
-func splitAtLastDot(s string) (string, string) {
-	i := strings.LastIndex(s, ".")
-	if i == -1 {
-		return s, ""
+// parseObjectSpec parses main as one of the non-filename forms of
+// object specification.
+func parseObjectSpec(spec *spec, main string) error {
+	// Parse main as a Go expression, albeit a strange one.
+	e, _ := parser.ParseExpr(main)
+
+	if pkg := parseImportPath(e); pkg != "" {
+		// e.g. bytes or "encoding/json": a package
+		spec.pkg = pkg
+		if spec.searchFor == "" {
+			return fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
+				main, main)
+		}
+		return nil
 	}
-	return s[:i], s[i+1:]
+
+	if e, ok := e.(*ast.SelectorExpr); ok {
+		x := unparen(e.X)
+
+		// Strip off star constructor, if any.
+		if star, ok := x.(*ast.StarExpr); ok {
+			x = star.X
+		}
+
+		if pkg := parseImportPath(x); pkg != "" {
+			// package member e.g. "encoding/json".HTMLEscape
+			spec.pkg = pkg              // e.g. "encoding/json"
+			spec.pkgMember = e.Sel.Name // e.g. "HTMLEscape"
+			spec.fromName = e.Sel.Name
+			return nil
+		}
+
+		if x, ok := x.(*ast.SelectorExpr); ok {
+			// field/method of type e.g. ("encoding/json".Decoder).Decode
+			y := unparen(x.X)
+			if pkg := parseImportPath(y); pkg != "" {
+				spec.pkg = pkg               // e.g. "encoding/json"
+				spec.pkgMember = x.Sel.Name  // e.g. "Decoder"
+				spec.typeMember = e.Sel.Name // e.g. "Decode"
+				spec.fromName = e.Sel.Name
+				return nil
+			}
+		}
+	}
+
+	return fmt.Errorf("-from %q: invalid expression")
+}
+
+// parseImportPath returns the import path of the package denoted by e.
+// Any import path may be represented as a string literal;
+// single-segment import paths (e.g. "bytes") may also be represented as
+// ast.Ident.  parseImportPath returns "" for all other expressions.
+func parseImportPath(e ast.Expr) string {
+	switch e := e.(type) {
+	case *ast.Ident:
+		return e.Name // e.g. bytes
+
+	case *ast.BasicLit:
+		if e.Kind == token.STRING {
+			pkgname, _ := strconv.Unquote(e.Value)
+			return pkgname // e.g. "encoding/json"
+		}
+	}
+	return ""
 }
 
 // parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
diff --git a/refactor/rename/util.go b/refactor/rename/util.go
index f9354d8..67f48e8 100644
--- a/refactor/rename/util.go
+++ b/refactor/rename/util.go
@@ -5,6 +5,7 @@
 package rename
 
 import (
+	"go/ast"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -98,3 +99,15 @@
 	}
 	return false
 }
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+	for {
+		p, ok := e.(*ast.ParenExpr)
+		if !ok {
+			break
+		}
+		e = p.X
+	}
+	return e
+}