From d9f94846124da038b1884c193510860f21a2f2f4 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 13 Oct 2016 11:53:44 -0400 Subject: [PATCH] cmd/oracle: oracle is dead, long live guru Change-Id: I0f729b1477d8b14f255538414087d25f99b20c1e Reviewed-on: https://go-review.googlesource.com/30982 Reviewed-by: Robert Griesemer --- cmd/oracle/emacs-test.bash | 50 -- cmd/oracle/main.go | 208 ----- cmd/oracle/oracle.el | 230 ----- cmd/oracle/oracle.vim | 107 --- oracle/TODO | 83 -- oracle/callees.go | 260 ------ oracle/callers.go | 115 --- oracle/callstack.go | 126 --- oracle/definition.go | 78 -- oracle/describe.go | 775 ----------------- oracle/describe15.go | 786 ------------------ oracle/freevars.go | 224 ----- oracle/implements.go | 354 -------- oracle/oracle.go | 368 -------- oracle/oracle_test.go | 278 ------- oracle/peers.go | 254 ------ oracle/pointsto.go | 293 ------- oracle/pos.go | 143 ---- oracle/referrers.go | 243 ------ oracle/serial/serial.go | 258 ------ oracle/testdata/src/calls-json/main.go | 16 - oracle/testdata/src/calls-json/main.golden | 34 - oracle/testdata/src/calls/main.go | 129 --- oracle/testdata/src/calls/main.golden | 125 --- oracle/testdata/src/describe-json/main.go | 29 - oracle/testdata/src/describe-json/main.golden | 111 --- oracle/testdata/src/describe/main.go | 96 --- oracle/testdata/src/describe/main.golden | 184 ---- oracle/testdata/src/freevars/main.go | 41 - oracle/testdata/src/freevars/main.golden | 18 - oracle/testdata/src/implements-json/main.go | 27 - .../testdata/src/implements-json/main.golden | 159 ---- .../src/implements-methods-json/main.go | 37 - .../src/implements-methods-json/main.golden | 312 ------- .../testdata/src/implements-methods/main.go | 37 - .../src/implements-methods/main.golden | 39 - oracle/testdata/src/implements/main.go | 39 - oracle/testdata/src/implements/main.golden | 46 - oracle/testdata/src/imports/main.go | 29 - oracle/testdata/src/imports/main.golden | 57 -- oracle/testdata/src/lib/lib.go | 20 - oracle/testdata/src/main/multi.go | 13 - oracle/testdata/src/peers-json/main.go | 13 - oracle/testdata/src/peers-json/main.golden | 15 - oracle/testdata/src/peers/main.go | 52 -- oracle/testdata/src/peers/main.golden | 100 --- oracle/testdata/src/pointsto-json/main.go | 27 - oracle/testdata/src/pointsto-json/main.golden | 35 - oracle/testdata/src/pointsto/main.go | 75 -- oracle/testdata/src/pointsto/main.golden | 96 --- oracle/testdata/src/referrers-json/main.go | 24 - .../testdata/src/referrers-json/main.golden | 59 -- oracle/testdata/src/referrers/ext_test.go | 12 - oracle/testdata/src/referrers/int_test.go | 8 - oracle/testdata/src/referrers/main.go | 34 - oracle/testdata/src/referrers/main.golden | 42 - oracle/testdata/src/reflection/main.go | 30 - oracle/testdata/src/reflection/main.golden | 34 - oracle/testdata/src/what-json/main.go | 9 - oracle/testdata/src/what-json/main.golden | 51 -- oracle/testdata/src/what/main.go | 11 - oracle/testdata/src/what/main.golden | 39 - oracle/testdata/src/whicherrs/main.go | 27 - oracle/testdata/src/whicherrs/main.golden | 8 - oracle/what.go | 210 ----- oracle/whicherrs.go | 328 -------- 66 files changed, 8170 deletions(-) delete mode 100755 cmd/oracle/emacs-test.bash delete mode 100644 cmd/oracle/main.go delete mode 100644 cmd/oracle/oracle.el delete mode 100644 cmd/oracle/oracle.vim delete mode 100644 oracle/TODO delete mode 100644 oracle/callees.go delete mode 100644 oracle/callers.go delete mode 100644 oracle/callstack.go delete mode 100644 oracle/definition.go delete mode 100644 oracle/describe.go delete mode 100644 oracle/describe15.go delete mode 100644 oracle/freevars.go delete mode 100644 oracle/implements.go delete mode 100644 oracle/oracle.go delete mode 100644 oracle/oracle_test.go delete mode 100644 oracle/peers.go delete mode 100644 oracle/pointsto.go delete mode 100644 oracle/pos.go delete mode 100644 oracle/referrers.go delete mode 100644 oracle/serial/serial.go delete mode 100644 oracle/testdata/src/calls-json/main.go delete mode 100644 oracle/testdata/src/calls-json/main.golden delete mode 100644 oracle/testdata/src/calls/main.go delete mode 100644 oracle/testdata/src/calls/main.golden delete mode 100644 oracle/testdata/src/describe-json/main.go delete mode 100644 oracle/testdata/src/describe-json/main.golden delete mode 100644 oracle/testdata/src/describe/main.go delete mode 100644 oracle/testdata/src/describe/main.golden delete mode 100644 oracle/testdata/src/freevars/main.go delete mode 100644 oracle/testdata/src/freevars/main.golden delete mode 100644 oracle/testdata/src/implements-json/main.go delete mode 100644 oracle/testdata/src/implements-json/main.golden delete mode 100644 oracle/testdata/src/implements-methods-json/main.go delete mode 100644 oracle/testdata/src/implements-methods-json/main.golden delete mode 100644 oracle/testdata/src/implements-methods/main.go delete mode 100644 oracle/testdata/src/implements-methods/main.golden delete mode 100644 oracle/testdata/src/implements/main.go delete mode 100644 oracle/testdata/src/implements/main.golden delete mode 100644 oracle/testdata/src/imports/main.go delete mode 100644 oracle/testdata/src/imports/main.golden delete mode 100644 oracle/testdata/src/lib/lib.go delete mode 100644 oracle/testdata/src/main/multi.go delete mode 100644 oracle/testdata/src/peers-json/main.go delete mode 100644 oracle/testdata/src/peers-json/main.golden delete mode 100644 oracle/testdata/src/peers/main.go delete mode 100644 oracle/testdata/src/peers/main.golden delete mode 100644 oracle/testdata/src/pointsto-json/main.go delete mode 100644 oracle/testdata/src/pointsto-json/main.golden delete mode 100644 oracle/testdata/src/pointsto/main.go delete mode 100644 oracle/testdata/src/pointsto/main.golden delete mode 100644 oracle/testdata/src/referrers-json/main.go delete mode 100644 oracle/testdata/src/referrers-json/main.golden delete mode 100644 oracle/testdata/src/referrers/ext_test.go delete mode 100644 oracle/testdata/src/referrers/int_test.go delete mode 100644 oracle/testdata/src/referrers/main.go delete mode 100644 oracle/testdata/src/referrers/main.golden delete mode 100644 oracle/testdata/src/reflection/main.go delete mode 100644 oracle/testdata/src/reflection/main.golden delete mode 100644 oracle/testdata/src/what-json/main.go delete mode 100644 oracle/testdata/src/what-json/main.golden delete mode 100644 oracle/testdata/src/what/main.go delete mode 100644 oracle/testdata/src/what/main.golden delete mode 100644 oracle/testdata/src/whicherrs/main.go delete mode 100644 oracle/testdata/src/whicherrs/main.golden delete mode 100644 oracle/what.go delete mode 100644 oracle/whicherrs.go diff --git a/cmd/oracle/emacs-test.bash b/cmd/oracle/emacs-test.bash deleted file mode 100755 index 8c390916..00000000 --- a/cmd/oracle/emacs-test.bash +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# -# Simple test of Go oracle/Emacs integration. -# Requires that GOROOT and GOPATH are set. -# Side effect: builds and installs oracle in $GOROOT. - -set -eu - -[ -z "$GOROOT" ] && { echo "Error: GOROOT is unset." >&2; exit 1; } -[ -z "$GOPATH" ] && { echo "Error: GOPATH is unset." >&2; exit 1; } - -log=/tmp/$(basename $0)-$$.log -thisdir=$(dirname $0) - -function die() { - echo "Error: $@." - cat $log - exit 1 -} >&2 - -trap "rm -f $log" EXIT - -# Build and install oracle. -go get golang.org/x/tools/cmd/oracle || die "'go get' failed" -mv -f $GOPATH/bin/oracle $GOROOT/bin/ -$GOROOT/bin/oracle >$log 2>&1 || true # (prints usage and exits 1) -grep -q "Run.*help" $log || die "$GOROOT/bin/oracle not installed" - - -# Run Emacs, set the scope to the oracle tool itself, -# load ./main.go, and describe the "fmt" import. -emacs --batch --no-splash --no-window-system --no-init \ - --load $GOROOT/misc/emacs/go-mode.el \ - --load $thisdir/oracle.el \ - --eval ' -(progn - (setq go-oracle-scope "golang.org/x/tools/cmd/oracle") - (find-file "'$thisdir'/main.go") - (search-forward "\"fmt\"") - (backward-char) - (go-oracle-describe) - (princ (with-current-buffer "*go-oracle*" - (buffer-substring-no-properties (point-min) (point-max)))) - (kill-emacs 0)) -' main.go >$log 2>&1 || die "emacs command failed" - -# Check that Println is mentioned. -grep -q "fmt/print.go.*func Println" $log || die "didn't find expected lines in log; got:" - -echo "PASS" diff --git a/cmd/oracle/main.go b/cmd/oracle/main.go deleted file mode 100644 index cbdc3dd0..00000000 --- a/cmd/oracle/main.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// oracle: a tool for answering questions about Go source code. -// http://golang.org/s/oracle-design -// http://golang.org/s/oracle-user-manual -// -// DEPRECATED: oracle has been superseded by guru; -// see https://golang.org/s/using-guru for details. -// This package will be deleted on October 1, 2016. -// -// Run with -help flag or help subcommand for usage information. -// -package main // import "golang.org/x/tools/cmd/oracle" - -import ( - "bufio" - "encoding/json" - "encoding/xml" - "flag" - "fmt" - "go/build" - "io" - "log" - "os" - "runtime" - "runtime/pprof" - - "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/oracle" -) - -var posFlag = flag.String("pos", "", - "Filename and byte offset or extent of a syntax element about which to query, "+ - "e.g. foo.go:#123,#456, bar.go:#123.") - -var ptalogFlag = flag.String("ptalog", "", - "Location of the points-to analysis log file, or empty to disable logging.") - -var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.") - -var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).") - -func init() { - flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) -} - -const useHelp = "Run 'oracle -help' for more information.\n" - -const helpMessage = `Go source code oracle. -Usage: oracle [ ...] ... - -The -format flag controls the output format: - plain an editor-friendly format in which every line of output - is of the form "pos: text", where pos is "-" if unknown. - json structured data in JSON syntax. - xml structured data in XML syntax. - -The -pos flag is required in all modes. - -The mode argument determines the query to perform: - - callees show possible targets of selected function call - callers show possible callers of selected function - callstack show path from callgraph root to selected function - definition show declaration of selected identifier - describe describe selected syntax: definition, methods, etc - freevars show free variables of selection - implements show 'implements' relation for selected type or method - peers show send/receive corresponding to selected channel op - pointsto show variables to which the selected pointer may point - referrers show all refs to entity denoted by selected identifier - what show basic information about the selected syntax node - whicherrs show possible values of the selected error variable - -The user manual is available here: http://golang.org/s/oracle-user-manual - -Examples: - -Describe the syntax at offset 530 in this file (an import spec): -% oracle -pos=src/golang.org/x/tools/cmd/oracle/main.go:#530 describe \ - golang.org/x/tools/cmd/oracle - -` + loader.FromArgsUsage - -var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") - -func init() { - // If $GOMAXPROCS isn't set, use the full capacity of the machine. - // For small machines, use at least 4 threads. - if os.Getenv("GOMAXPROCS") == "" { - n := runtime.NumCPU() - if n < 4 { - n = 4 - } - runtime.GOMAXPROCS(n) - } -} - -func printHelp() { - fmt.Fprintln(os.Stderr, helpMessage) - fmt.Fprintln(os.Stderr, "Flags:") - flag.PrintDefaults() -} - -func main() { - // Don't print full help unless -help was requested. - // Just gently remind users that it's there. - flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) } - flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack - if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { - // (err has already been printed) - if err == flag.ErrHelp { - printHelp() - } - os.Exit(2) - } - - args := flag.Args() - if len(args) == 0 || args[0] == "" { - fmt.Fprint(os.Stderr, "oracle: a mode argument is required.\n"+useHelp) - os.Exit(2) - } - - mode := args[0] - args = args[1:] - if mode == "help" { - printHelp() - os.Exit(2) - } - - // Set up points-to analysis log file. - var ptalog io.Writer - if *ptalogFlag != "" { - if f, err := os.Create(*ptalogFlag); err != nil { - log.Fatalf("Failed to create PTA log file: %s", err) - } else { - buf := bufio.NewWriter(f) - ptalog = buf - defer func() { - if err := buf.Flush(); err != nil { - log.Printf("flush: %s", err) - } - if err := f.Close(); err != nil { - log.Printf("close: %s", err) - } - }() - } - } - - // Profiling support. - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - // -format flag - switch *formatFlag { - case "json", "plain", "xml": - // ok - default: - fmt.Fprintf(os.Stderr, "oracle: illegal -format value: %q.\n"+useHelp, *formatFlag) - os.Exit(2) - } - - // Ask the oracle. - query := oracle.Query{ - Mode: mode, - Pos: *posFlag, - Build: &build.Default, - Scope: args, - PTALog: ptalog, - Reflection: *reflectFlag, - } - - if err := oracle.Run(&query); err != nil { - fmt.Fprintf(os.Stderr, "oracle: %s\n", err) - os.Exit(1) - } - - // Print the result. - switch *formatFlag { - case "json": - b, err := json.MarshalIndent(query.Serial(), "", "\t") - if err != nil { - fmt.Fprintf(os.Stderr, "oracle: JSON error: %s\n", err) - os.Exit(1) - } - os.Stdout.Write(b) - - case "xml": - b, err := xml.MarshalIndent(query.Serial(), "", "\t") - if err != nil { - fmt.Fprintf(os.Stderr, "oracle: XML error: %s\n", err) - os.Exit(1) - } - os.Stdout.Write(b) - - case "plain": - query.WriteTo(os.Stdout) - } -} diff --git a/cmd/oracle/oracle.el b/cmd/oracle/oracle.el deleted file mode 100644 index 33c7409a..00000000 --- a/cmd/oracle/oracle.el +++ /dev/null @@ -1,230 +0,0 @@ -;;; -;;; Integration of the Go 'oracle' analysis tool into Emacs. -;;; -;;; To install the Go oracle, run: -;;; % export GOROOT=... GOPATH=... -;;; % go get golang.org/x/tools/cmd/oracle -;;; % mv $GOPATH/bin/oracle $GOROOT/bin/ -;;; -;;; Load this file into Emacs and set go-oracle-scope to your -;;; configuration. Then, find a file of Go source code, enable -;;; go-oracle-mode, select an expression of interest, and press `C-c C-o d' -;;; (for "describe") or run one of the other go-oracle-xxx commands. -;;; -;;; TODO(adonovan): simplify installation and configuration by making -;;; oracle a subcommand of 'go tool'. - -(require 'compile) -(require 'go-mode) -(require 'cl) - -(defgroup go-oracle nil - "Options specific to the Go oracle." - :group 'go) - -(defcustom go-oracle-command "oracle" - "The Go oracle command." - :type 'string - :group 'go-oracle) - -(defcustom go-oracle-scope "" - "The scope of the analysis. See `go-oracle-set-scope'." - :type 'string - :group 'go-oracle) - -(defvar go-oracle--scope-history - nil - "History of values supplied to `go-oracle-set-scope'.") - -;; Extend go-mode-map. -(let ((m go-mode-map)) - (define-key m (kbd "C-c C-o t") #'go-oracle-describe) ; t for type - (define-key m (kbd "C-c C-o f") #'go-oracle-freevars) - (define-key m (kbd "C-c C-o g") #'go-oracle-callgraph) - (define-key m (kbd "C-c C-o i") #'go-oracle-implements) - (define-key m (kbd "C-c C-o c") #'go-oracle-peers) ; c for channel - (define-key m (kbd "C-c C-o r") #'go-oracle-referrers) - (define-key m (kbd "C-c C-o d") #'go-oracle-definition) - (define-key m (kbd "C-c C-o p") #'go-oracle-pointsto) - (define-key m (kbd "C-c C-o s") #'go-oracle-callstack) - (define-key m (kbd "C-c C-o <") #'go-oracle-callers) - (define-key m (kbd "C-c C-o >") #'go-oracle-callees) - (define-key m (kbd "") #'go-oracle-describe) - (define-key m (kbd "") #'go-oracle-referrers)) - -;; TODO(dominikh): Rethink set-scope some. Setting it to a file is -;; painful because it doesn't use find-file, and variables/~ aren't -;; expanded. Setting it to an import path is somewhat painful because -;; it doesn't make use of go-mode's import path completion. One option -;; would be having two different functions, but then we can't -;; automatically call it when no scope has been set. Also it wouldn't -;; easily allow specifying more than one file/package. -(defun go-oracle-set-scope () - "Set the scope for the Go oracle, prompting the user to edit the -previous scope. - -The scope specifies a set of arguments, separated by spaces. -It may be: -1) a set of packages whose main() functions will be analyzed. -2) a list of *.go filenames; they will treated like as a single - package (see #3). -3) a single package whose main() function and/or Test* functions - will be analyzed. - -In the common case, this is similar to the argument(s) you would -specify to 'go build'." - (interactive) - (let ((scope (read-from-minibuffer "Go oracle scope: " - go-oracle-scope - nil - nil - 'go-oracle--scope-history))) - (if (string-equal "" scope) - (error "You must specify a non-empty scope for the Go oracle")) - (setq go-oracle-scope scope))) - -(defun go-oracle--run (mode &optional need-scope) - "Run the Go oracle in the specified MODE, passing it the -selected region of the current buffer. If NEED-SCOPE, prompt for -a scope if not already set. Process the output to replace each -file name with a small hyperlink. Display the result." - (if (not buffer-file-name) - (error "Cannot use oracle on a buffer without a file name")) - ;; It's not sufficient to save a modified buffer since if - ;; gofmt-before-save is on the before-save-hook, saving will - ;; disturb the selected region. - (if (buffer-modified-p) - (error "Please save the buffer before invoking go-oracle")) - (and need-scope - (string-equal "" go-oracle-scope) - (go-oracle-set-scope)) - (let* ((filename (file-truename buffer-file-name)) - (posflag (if (use-region-p) - (format "-pos=%s:#%d,#%d" - filename - (1- (go--position-bytes (region-beginning))) - (1- (go--position-bytes (region-end)))) - (format "-pos=%s:#%d" - filename - (1- (position-bytes (point)))))) - (env-vars (go-root-and-paths)) - (goroot-env (concat "GOROOT=" (car env-vars))) - (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))) - (with-current-buffer (get-buffer-create "*go-oracle*") - (setq buffer-read-only nil) - (erase-buffer) - (insert "Go Oracle\n") - (let ((args (append (list go-oracle-command nil t nil posflag mode) - (split-string go-oracle-scope " " t)))) - ;; Log the command to *Messages*, for debugging. - (message "Command: %s:" args) - (message nil) ; clears/shrinks minibuffer - - (message "Running oracle...") - ;; Use dynamic binding to modify/restore the environment - (let ((process-environment (list* goroot-env gopath-env process-environment))) - (apply #'call-process args))) - (insert "\n") - (compilation-mode) - (setq compilation-error-screen-columns nil) - - ;; Hide the file/line info to save space. - ;; Replace each with a little widget. - ;; compilation-mode + this loop = slooow. - ;; TODO(adonovan): have oracle give us JSON - ;; and we'll do the markup directly. - (let ((buffer-read-only nil) - (p 1)) - (while (not (null p)) - (let ((np (compilation-next-single-property-change p 'compilation-message))) - (if np - (when (equal (line-number-at-pos p) (line-number-at-pos np)) - ;; Using a fixed width greatly improves readability, so - ;; if the filename is longer than 20, show ".../last/17chars.go". - ;; This usually includes the last segment of the package name. - ;; Don't show the line or column number. - (let* ((loc (buffer-substring p np)) ; "/home/foo/go/pkg/file.go:1:2-3:4" - (i (search ":" loc))) - (setq loc (cond - ((null i) "...") - ((>= i 17) (concat "..." (substring loc (- i 17) i))) - (t (substring loc 0 i)))) - ;; np is (typically) the space following ":"; consume it too. - (put-text-property p np 'display (concat loc ":"))) - (goto-char np) - (insert " ") - (incf np))) ; so we don't get stuck (e.g. on a panic stack dump) - (setq p np))) - (message nil)) - - (let ((w (display-buffer (current-buffer)))) - (balance-windows) - (shrink-window-if-larger-than-buffer w) - (set-window-point w (point-min)))))) - -(defun go-oracle-callees () - "Show possible callees of the function call at the current point." - (interactive) - (go-oracle--run "callees" t)) - -(defun go-oracle-callers () - "Show the set of callers of the function containing the current point." - (interactive) - (go-oracle--run "callers" t)) - -(defun go-oracle-callgraph () - "Show the callgraph of the current program." - (interactive) - (go-oracle--run "callgraph" t)) - -(defun go-oracle-callstack () - "Show an arbitrary path from a root of the call graph to the -function containing the current point." - (interactive) - (go-oracle--run "callstack" t)) - -(defun go-oracle-definition () - "Show the definition of the selected identifier." - (interactive) - (go-oracle--run "definition")) - -(defun go-oracle-describe () - "Describe the selected syntax, its kind, type and methods." - (interactive) - (go-oracle--run "describe")) - -(defun go-oracle-pointsto () - "Show what the selected expression points to." - (interactive) - (go-oracle--run "pointsto" t)) - -(defun go-oracle-implements () - "Describe the 'implements' relation for types in the package -containing the current point." - (interactive) - (go-oracle--run "implements")) - -(defun go-oracle-freevars () - "Enumerate the free variables of the current selection." - (interactive) - (go-oracle--run "freevars")) - -(defun go-oracle-peers () - "Enumerate the set of possible corresponding sends/receives for -this channel receive/send operation." - (interactive) - (go-oracle--run "peers" t)) - -(defun go-oracle-referrers () - "Enumerate all references to the object denoted by the selected -identifier." - (interactive) - (go-oracle--run "referrers")) - -(defun go-oracle-whicherrs () - "Show globals, constants and types to which the selected -expression (of type 'error') may refer." - (interactive) - (go-oracle--run "whicherrs" t)) - -(provide 'go-oracle) diff --git a/cmd/oracle/oracle.vim b/cmd/oracle/oracle.vim deleted file mode 100644 index cadda781..00000000 --- a/cmd/oracle/oracle.vim +++ /dev/null @@ -1,107 +0,0 @@ -" -*- text -*- -" oracle.vim -- Vim integration for the Go oracle. -" -" Load with (e.g.) :source oracle.vim -" Call with (e.g.) :GoOracleDescribe -" while cursor or selection is over syntax of interest. -" Run :copen to show the quick-fix file. -" -" This is an absolutely rudimentary integration of the Go Oracle into -" Vim's quickfix mechanism and it needs a number of usability -" improvements before it can be practically useful to Vim users. -" Voluntary contributions welcomed! -" -" TODO(adonovan): -" - reject buffers with no filename. -" - hide all filenames in quickfix buffer. - -" Get the path to the Go oracle executable. -func! s:go_oracle_bin() - let [ext, sep] = (has('win32') || has('win64') ? ['.exe', ';'] : ['', ':']) - let go_oracle = globpath(join(split($GOPATH, sep), ','), '/bin/oracle' . ext) - if go_oracle == '' - let go_oracle = globpath($GOROOT, '/bin/oracle' . ext) - endif - return go_oracle -endfunction - -let s:go_oracle = s:go_oracle_bin() - -func! s:qflist(output) - let qflist = [] - " Parse GNU-style 'file:line.col-line.col: message' format. - let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$' - for line in split(a:output, "\n") - let ml = matchlist(line, mx) - " Ignore non-match lines or warnings - if ml == [] || ml[4] =~ '^ warning:' - continue - endif - let item = { - \ 'filename': ml[1], - \ 'text': ml[4], - \ 'lnum': ml[2], - \ 'col': ml[3], - \} - let bnr = bufnr(fnameescape(ml[1])) - if bnr != -1 - let item['bufnr'] = bnr - endif - call add(qflist, item) - endfor - call setqflist(qflist) - cwindow -endfun - -func! s:getpos(l, c) - if &encoding != 'utf-8' - let buf = a:l == 1 ? '' : (join(getline(1, a:l-1), "\n") . "\n") - let buf .= a:c == 1 ? '' : getline('.')[:a:c-2] - return len(iconv(buf, &encoding, 'utf-8')) - endif - return line2byte(a:l) + (a:c-2) -endfun - -func! s:RunOracle(mode, selected) range abort - let fname = expand('%:p') - let sname = get(g:, 'go_oracle_scope_file', fname) - if a:selected != -1 - let pos1 = s:getpos(line("'<"), col("'<")) - let pos2 = s:getpos(line("'>"), col("'>")) - let cmd = printf('%s -pos=%s:#%d,#%d %s %s', - \ s:go_oracle, - \ shellescape(fname), pos1, pos2, a:mode, shellescape(sname)) - else - let pos = s:getpos(line('.'), col('.')) - let cmd = printf('%s -pos=%s:#%d %s %s', - \ s:go_oracle, - \ shellescape(fname), pos, a:mode, shellescape(sname)) - endif - call s:qflist(system(cmd)) -endfun - -" Describe the expression at the current point. -command! -range=% GoOracleDescribe - \ call s:RunOracle('describe', ) - -" Show possible callees of the function call at the current point. -command! -range=% GoOracleCallees - \ call s:RunOracle('callees', ) - -" Show the set of callers of the function containing the current point. -command! -range=% GoOracleCallers - \ call s:RunOracle('callers', ) - -" Show the callgraph of the current program. -command! -range=% GoOracleCallgraph - \ call s:RunOracle('callgraph', ) - -" Describe the 'implements' relation for types in the -" package containing the current point. -command! -range=% GoOracleImplements - \ call s:RunOracle('implements', ) - -" Enumerate the set of possible corresponding sends/receives for -" this channel receive/send operation. -command! -range=% GoOracleChannelPeers - \ call s:RunOracle('peers', ) diff --git a/oracle/TODO b/oracle/TODO deleted file mode 100644 index 8fbf5e86..00000000 --- a/oracle/TODO +++ /dev/null @@ -1,83 +0,0 @@ - - -ORACLE TODO -=========== - -General -======= - -Save unsaved editor buffers into an archive and provide that to the -tools, which should act as if they were saved. - -Include complete pos/end information Serial output. - But beware that sometimes a single token (e.g. +) is more helpful - than the pos/end of the containing expression (e.g. x \n + \n y). - -Specific queries -================ - -callers, callees - - Use a type-based (e.g. RTA) callgraph when a callers/callees query is - outside the analysis scope. - -implements - - Make it require that the selection is a type, and show only the - implements relation as it applies to that type. - -definition, referrers - - definition: Make it work with qualified identifiers (SelectorExpr) too. - - references: Make it work on things that are implicit idents, like - import specs, perhaps? - -what - - Report def/ref info if available. - Editors could use it to highlight all idents of the same local var. - - More tests. - -pointsto - - When invoked on a function Ident, we get an error. - - When invoked on a named return parameter, we get an error. - -describe - - When invoked on a var, we want to see the type and its methods. - - Split "show type" and "describe syntax" into separate commands? - -peers - - Permit querying from a makechan, for...range, or reflective op. - - Report aliasing reflect.{Send,Recv,Close} and close() operations. - -New queries - -"updaters": show all statements that may update the selected lvalue - (local, global, field, etc). - -"creators": show all places where an object of type T is created - (&T{}, var t T, new(T), new(struct{array [3]T}), etc. - (Useful for datatypes whose zero value is not safe) - - -Editor-specific -=============== - -Add support for "what" to .el; clean up. - -Emacs: use JSON to get the raw information from the oracle. Don't - open an editor buffer for simpler queries, just jump to the result - and/or display it in the modeline. - -Emacs: go-root-and-paths depends on the current buffer, so be sure to - call it from within the source file, not the *go-oracle* buffer: - the user may have switched workspaces and the oracle should run in - the new one. diff --git a/oracle/callees.go b/oracle/callees.go deleted file mode 100644 index 06c2c15e..00000000 --- a/oracle/callees.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "sort" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/pointer" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -// Callees reports the possible callees of the function call site -// identified by the specified source location. -func callees(q *Query) error { - lconf := loader.Config{Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos - if err != nil { - return err - } - - // Determine the enclosing call for the specified position. - var e *ast.CallExpr - for _, n := range qpos.path { - if e, _ = n.(*ast.CallExpr); e != nil { - break - } - } - if e == nil { - return fmt.Errorf("there is no function call here") - } - // TODO(adonovan): issue an error if the call is "too far - // away" from the current selection, as this most likely is - // not what the user intended. - - // Reject type conversions. - if qpos.info.Types[e.Fun].IsType() { - return fmt.Errorf("this is a type conversion, not a function call") - } - - // Deal with obviously static calls before constructing SSA form. - // Some static calls may yet require SSA construction, - // e.g. f := func(){}; f(). - switch funexpr := unparen(e.Fun).(type) { - case *ast.Ident: - switch obj := qpos.info.Uses[funexpr].(type) { - case *types.Builtin: - // Reject calls to built-ins. - return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name()) - case *types.Func: - // This is a static function call - q.result = &calleesTypesResult{ - site: e, - callee: obj, - } - return nil - } - case *ast.SelectorExpr: - sel := qpos.info.Selections[funexpr] - if sel == nil { - // qualified identifier. - // May refer to top level function variable - // or to top level function. - callee := qpos.info.Uses[funexpr.Sel] - if obj, ok := callee.(*types.Func); ok { - q.result = &calleesTypesResult{ - site: e, - callee: obj, - } - return nil - } - } else if sel.Kind() == types.MethodVal { - // Inspect the receiver type of the selected method. - // If it is concrete, the call is statically dispatched. - // (Due to implicit field selections, it is not enough to look - // at sel.Recv(), the type of the actual receiver expression.) - method := sel.Obj().(*types.Func) - recvtype := method.Type().(*types.Signature).Recv().Type() - if !types.IsInterface(recvtype) { - // static method call - q.result = &calleesTypesResult{ - site: e, - callee: method, - } - return nil - } - } - } - - prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - pkg := prog.Package(qpos.info.Pkg) - if pkg == nil { - return fmt.Errorf("no SSA package") - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - // Ascertain calling function and call site. - callerFn := ssa.EnclosingFunction(pkg, qpos.path) - if callerFn == nil { - return fmt.Errorf("no SSA function built for this location (dead code?)") - } - - // Find the call site. - site, err := findCallSite(callerFn, e) - if err != nil { - return err - } - - funcs, err := findCallees(ptaConfig, site) - if err != nil { - return err - } - - q.result = &calleesSSAResult{ - site: site, - funcs: funcs, - } - return nil -} - -func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) { - instr, _ := fn.ValueForExpr(call) - callInstr, _ := instr.(ssa.CallInstruction) - if instr == nil { - return nil, fmt.Errorf("this call site is unreachable in this analysis") - } - return callInstr, nil -} - -func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) { - // Avoid running the pointer analysis for static calls. - if callee := site.Common().StaticCallee(); callee != nil { - switch callee.String() { - case "runtime.SetFinalizer", "(reflect.Value).Call": - // The PTA treats calls to these intrinsics as dynamic. - // TODO(adonovan): avoid reliance on PTA internals. - - default: - return []*ssa.Function{callee}, nil // singleton - } - } - - // Dynamic call: use pointer analysis. - conf.BuildCallGraph = true - cg := ptrAnalysis(conf).CallGraph - cg.DeleteSyntheticNodes() - - // Find all call edges from the site. - n := cg.Nodes[site.Parent()] - if n == nil { - return nil, fmt.Errorf("this call site is unreachable in this analysis") - } - calleesMap := make(map[*ssa.Function]bool) - for _, edge := range n.Out { - if edge.Site == site { - calleesMap[edge.Callee.Func] = true - } - } - - // De-duplicate and sort. - funcs := make([]*ssa.Function, 0, len(calleesMap)) - for f := range calleesMap { - funcs = append(funcs, f) - } - sort.Sort(byFuncPos(funcs)) - return funcs, nil -} - -type calleesSSAResult struct { - site ssa.CallInstruction - funcs []*ssa.Function -} - -type calleesTypesResult struct { - site *ast.CallExpr - callee *types.Func -} - -func (r *calleesSSAResult) display(printf printfFunc) { - if len(r.funcs) == 0 { - // dynamic call on a provably nil func/interface - printf(r.site, "%s on nil value", r.site.Common().Description()) - } else { - printf(r.site, "this %s dispatches to:", r.site.Common().Description()) - for _, callee := range r.funcs { - printf(callee, "\t%s", callee) - } - } -} - -func (r *calleesSSAResult) toSerial(res *serial.Result, fset *token.FileSet) { - j := &serial.Callees{ - Pos: fset.Position(r.site.Pos()).String(), - Desc: r.site.Common().Description(), - } - for _, callee := range r.funcs { - j.Callees = append(j.Callees, &serial.CalleesItem{ - Name: callee.String(), - Pos: fset.Position(callee.Pos()).String(), - }) - } - res.Callees = j -} - -func (r *calleesTypesResult) display(printf printfFunc) { - printf(r.site, "this static function call dispatches to:") - printf(r.callee, "\t%s", r.callee.FullName()) -} - -func (r *calleesTypesResult) toSerial(res *serial.Result, fset *token.FileSet) { - j := &serial.Callees{ - Pos: fset.Position(r.site.Pos()).String(), - Desc: "static function call", - } - j.Callees = []*serial.CalleesItem{ - &serial.CalleesItem{ - Name: r.callee.FullName(), - Pos: fset.Position(r.callee.Pos()).String(), - }, - } - res.Callees = j -} - -// NB: byFuncPos is not deterministic across packages since it depends on load order. -// Use lessPos if the tests need it. -type byFuncPos []*ssa.Function - -func (a byFuncPos) Len() int { return len(a) } -func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } -func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/oracle/callers.go b/oracle/callers.go deleted file mode 100644 index e7f3c8f7..00000000 --- a/oracle/callers.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package oracle - -import ( - "fmt" - "go/token" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -// Callers reports the possible callers of the function -// immediately enclosing the specified source location. -// -func callers(q *Query) error { - lconf := loader.Config{Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - prog := ssautil.CreateProgram(lprog, 0) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - pkg := prog.Package(qpos.info.Pkg) - if pkg == nil { - return fmt.Errorf("no SSA package") - } - if !ssa.HasEnclosingFunction(pkg, qpos.path) { - return fmt.Errorf("this position is not inside a function") - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - target := ssa.EnclosingFunction(pkg, qpos.path) - if target == nil { - return fmt.Errorf("no SSA function built for this location (dead code?)") - } - - // TODO(adonovan): opt: if function is never address-taken, skip - // the pointer analysis. Just look for direct calls. This can - // be done in a single pass over the SSA. - - // Run the pointer analysis, recording each - // call found to originate from target. - ptaConfig.BuildCallGraph = true - cg := ptrAnalysis(ptaConfig).CallGraph - cg.DeleteSyntheticNodes() - edges := cg.CreateNode(target).In - // TODO(adonovan): sort + dedup calls to ensure test determinism. - - q.result = &callersResult{ - target: target, - callgraph: cg, - edges: edges, - } - return nil -} - -type callersResult struct { - target *ssa.Function - callgraph *callgraph.Graph - edges []*callgraph.Edge -} - -func (r *callersResult) display(printf printfFunc) { - root := r.callgraph.Root - if r.edges == nil { - printf(r.target, "%s is not reachable in this program.", r.target) - } else { - printf(r.target, "%s is called from these %d sites:", r.target, len(r.edges)) - for _, edge := range r.edges { - if edge.Caller == root { - printf(r.target, "the root of the call graph") - } else { - printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func) - } - } - } -} - -func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) { - var callers []serial.Caller - for _, edge := range r.edges { - callers = append(callers, serial.Caller{ - Caller: edge.Caller.Func.String(), - Pos: fset.Position(edge.Pos()).String(), - Desc: edge.Description(), - }) - } - res.Callers = callers -} diff --git a/oracle/callstack.go b/oracle/callstack.go deleted file mode 100644 index ba88e994..00000000 --- a/oracle/callstack.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package oracle - -import ( - "fmt" - "go/token" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -// Callstack displays an arbitrary path from a root of the callgraph -// to the function at the current position. -// -// The information may be misleading in a context-insensitive -// analysis. e.g. the call path X->Y->Z might be infeasible if Y never -// calls Z when it is called from X. TODO(adonovan): think about UI. -// -// TODO(adonovan): permit user to specify a starting point other than -// the analysis root. -// -func callstack(q *Query) error { - fset := token.NewFileSet() - lconf := loader.Config{Fset: fset, Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - prog := ssautil.CreateProgram(lprog, 0) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - pkg := prog.Package(qpos.info.Pkg) - if pkg == nil { - return fmt.Errorf("no SSA package") - } - - if !ssa.HasEnclosingFunction(pkg, qpos.path) { - return fmt.Errorf("this position is not inside a function") - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - target := ssa.EnclosingFunction(pkg, qpos.path) - if target == nil { - return fmt.Errorf("no SSA function built for this location (dead code?)") - } - - // Run the pointer analysis and build the complete call graph. - ptaConfig.BuildCallGraph = true - cg := ptrAnalysis(ptaConfig).CallGraph - cg.DeleteSyntheticNodes() - - // Search for an arbitrary path from a root to the target function. - isEnd := func(n *callgraph.Node) bool { return n.Func == target } - callpath := callgraph.PathSearch(cg.Root, isEnd) - if callpath != nil { - callpath = callpath[1:] // remove synthetic edge from - } - - q.Fset = fset - q.result = &callstackResult{ - qpos: qpos, - target: target, - callpath: callpath, - } - return nil -} - -type callstackResult struct { - qpos *queryPos - target *ssa.Function - callpath []*callgraph.Edge -} - -func (r *callstackResult) display(printf printfFunc) { - if r.callpath != nil { - printf(r.qpos, "Found a call path from root to %s", r.target) - printf(r.target, "%s", r.target) - for i := len(r.callpath) - 1; i >= 0; i-- { - edge := r.callpath[i] - printf(edge, "%s from %s", edge.Description(), edge.Caller.Func) - } - } else { - printf(r.target, "%s is unreachable in this analysis scope", r.target) - } -} - -func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) { - var callers []serial.Caller - for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first) - edge := r.callpath[i] - callers = append(callers, serial.Caller{ - Pos: fset.Position(edge.Pos()).String(), - Caller: edge.Caller.Func.String(), - Desc: edge.Description(), - }) - } - res.Callstack = &serial.CallStack{ - Pos: fset.Position(r.target.Pos()).String(), - Target: r.target.String(), - Callers: callers, - } -} diff --git a/oracle/definition.go b/oracle/definition.go deleted file mode 100644 index 67f65f97..00000000 --- a/oracle/definition.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/oracle/serial" -) - -// definition reports the location of the definition of an identifier. -// -// TODO(adonovan): opt: for intra-file references, the parser's -// resolution might be enough; we should start with that. -// -func definition(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - if _, err := importQueryPackage(q.Pos, &lconf); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - id, _ := qpos.path[0].(*ast.Ident) - if id == nil { - return fmt.Errorf("no identifier here") - } - - obj := qpos.info.ObjectOf(id) - if obj == nil { - // Happens for y in "switch y := x.(type)", - // and the package declaration, - // but I think that's all. - return fmt.Errorf("no object for identifier") - } - - q.result = &definitionResult{qpos, obj} - return nil -} - -type definitionResult struct { - qpos *queryPos - obj types.Object // object it denotes -} - -func (r *definitionResult) display(printf printfFunc) { - printf(r.obj, "defined here as %s", r.qpos.objectString(r.obj)) -} - -func (r *definitionResult) toSerial(res *serial.Result, fset *token.FileSet) { - definition := &serial.Definition{ - Desc: r.obj.String(), - } - if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos() - definition.ObjPos = fset.Position(pos).String() - } - res.Definition = definition -} diff --git a/oracle/describe.go b/oracle/describe.go deleted file mode 100644 index 3be32df4..00000000 --- a/oracle/describe.go +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.6 - -package oracle - -import ( - "bytes" - "fmt" - "go/ast" - exact "go/constant" - "go/token" - "go/types" - "log" - "os" - "strings" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types/typeutil" - "golang.org/x/tools/oracle/serial" -) - -// describe describes the syntax node denoted by the query position, -// including: -// - its syntactic category -// - the definition of its referent (for identifiers) [now redundant] -// - its type and method set (for an expression or type expression) -// -func describe(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - if _, err := importQueryPackage(q.Pos, &lconf); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos) - if err != nil { - return err - } - - if false { // debugging - fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s", - astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path)) - } - - path, action := findInterestingNode(qpos.info, qpos.path) - switch action { - case actionExpr: - q.result, err = describeValue(qpos, path) - - case actionType: - q.result, err = describeType(qpos, path) - - case actionPackage: - q.result, err = describePackage(qpos, path) - - case actionStmt: - q.result, err = describeStmt(qpos, path) - - case actionUnknown: - q.result = &describeUnknownResult{path[0]} - - default: - panic(action) // unreachable - } - return err -} - -type describeUnknownResult struct { - node ast.Node -} - -func (r *describeUnknownResult) display(printf printfFunc) { - // Nothing much to say about misc syntax. - printf(r.node, "%s", astutil.NodeDescription(r.node)) -} - -func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { - res.Describe = &serial.Describe{ - Desc: astutil.NodeDescription(r.node), - Pos: fset.Position(r.node.Pos()).String(), - } -} - -type action int - -const ( - actionUnknown action = iota // None of the below - actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var}) - actionType // type Expr or Ident(types.TypeName). - actionStmt // Stmt or Ident(types.Label) - actionPackage // Ident(types.Package) or ImportSpec -) - -// findInterestingNode classifies the syntax node denoted by path as one of: -// - an expression, part of an expression or a reference to a constant -// or variable; -// - a type, part of a type, or a reference to a named type; -// - a statement, part of a statement, or a label referring to a statement; -// - part of a package declaration or import spec. -// - none of the above. -// and returns the most "interesting" associated node, which may be -// the same node, an ancestor or a descendent. -// -func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) { - // TODO(adonovan): integrate with go/types/stdlib_test.go and - // apply this to every AST node we can find to make sure it - // doesn't crash. - - // TODO(adonovan): audit for ParenExpr safety, esp. since we - // traverse up and down. - - // TODO(adonovan): if the users selects the "." in - // "fmt.Fprintf()", they'll get an ambiguous selection error; - // we won't even reach here. Can we do better? - - // TODO(adonovan): describing a field within 'type T struct {...}' - // describes the (anonymous) struct type and concludes "no methods". - // We should ascend to the enclosing type decl, if any. - - for len(path) > 0 { - switch n := path[0].(type) { - case *ast.GenDecl: - if len(n.Specs) == 1 { - // Descend to sole {Import,Type,Value}Spec child. - path = append([]ast.Node{n.Specs[0]}, path...) - continue - } - return path, actionUnknown // uninteresting - - case *ast.FuncDecl: - // Descend to function name. - path = append([]ast.Node{n.Name}, path...) - continue - - case *ast.ImportSpec: - return path, actionPackage - - case *ast.ValueSpec: - if len(n.Names) == 1 { - // Descend to sole Ident child. - path = append([]ast.Node{n.Names[0]}, path...) - continue - } - return path, actionUnknown // uninteresting - - case *ast.TypeSpec: - // Descend to type name. - path = append([]ast.Node{n.Name}, path...) - continue - - case ast.Stmt: - return path, actionStmt - - case *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - return path, actionType - - case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause: - return path, actionUnknown // uninteresting - - case *ast.Ellipsis: - // Continue to enclosing node. - // e.g. [...]T in ArrayType - // f(x...) in CallExpr - // f(x...T) in FuncType - - case *ast.Field: - // TODO(adonovan): this needs more thought, - // since fields can be so many things. - if len(n.Names) == 1 { - // Descend to sole Ident child. - path = append([]ast.Node{n.Names[0]}, path...) - continue - } - // Zero names (e.g. anon field in struct) - // or multiple field or param names: - // continue to enclosing field list. - - case *ast.FieldList: - // Continue to enclosing node: - // {Struct,Func,Interface}Type or FuncDecl. - - case *ast.BasicLit: - if _, ok := path[1].(*ast.ImportSpec); ok { - return path[1:], actionPackage - } - return path, actionExpr - - case *ast.SelectorExpr: - // TODO(adonovan): use Selections info directly. - if pkginfo.Uses[n.Sel] == nil { - // TODO(adonovan): is this reachable? - return path, actionUnknown - } - // Descend to .Sel child. - path = append([]ast.Node{n.Sel}, path...) - continue - - case *ast.Ident: - switch pkginfo.ObjectOf(n).(type) { - case *types.PkgName: - return path, actionPackage - - case *types.Const: - return path, actionExpr - - case *types.Label: - return path, actionStmt - - case *types.TypeName: - return path, actionType - - case *types.Var: - // For x in 'struct {x T}', return struct type, for now. - if _, ok := path[1].(*ast.Field); ok { - _ = path[2].(*ast.FieldList) // assertion - if _, ok := path[3].(*ast.StructType); ok { - return path[3:], actionType - } - } - return path, actionExpr - - case *types.Func: - return path, actionExpr - - case *types.Builtin: - // For reference to built-in function, return enclosing call. - path = path[1:] // ascend to enclosing function call - continue - - case *types.Nil: - return path, actionExpr - } - - // No object. - switch path[1].(type) { - case *ast.SelectorExpr: - // Return enclosing selector expression. - return path[1:], actionExpr - - case *ast.Field: - // TODO(adonovan): test this. - // e.g. all f in: - // struct { f, g int } - // interface { f() } - // func (f T) method(f, g int) (f, g bool) - // - // switch path[3].(type) { - // case *ast.FuncDecl: - // case *ast.StructType: - // case *ast.InterfaceType: - // } - // - // return path[1:], actionExpr - // - // Unclear what to do with these. - // Struct.Fields -- field - // Interface.Methods -- field - // FuncType.{Params.Results} -- actionExpr - // FuncDecl.Recv -- actionExpr - - case *ast.File: - // 'package foo' - return path, actionPackage - - case *ast.ImportSpec: - // TODO(adonovan): fix: why no package object? go/types bug? - return path[1:], actionPackage - - default: - // e.g. blank identifier - // or y in "switch y := x.(type)" - // or code in a _test.go file that's not part of the package. - log.Printf("unknown reference %s in %T\n", n, path[1]) - return path, actionUnknown - } - - case *ast.StarExpr: - if pkginfo.Types[n].IsType() { - return path, actionType - } - return path, actionExpr - - case ast.Expr: - // All Expr but {BasicLit,Ident,StarExpr} are - // "true" expressions that evaluate to a value. - return path, actionExpr - } - - // Ascend to parent. - path = path[1:] - } - - return nil, actionUnknown // unreachable -} - -func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) { - var expr ast.Expr - var obj types.Object - switch n := path[0].(type) { - case *ast.ValueSpec: - // ambiguous ValueSpec containing multiple names - return nil, fmt.Errorf("multiple value specification") - case *ast.Ident: - obj = qpos.info.ObjectOf(n) - expr = n - case ast.Expr: - expr = n - default: - // TODO(adonovan): is this reachable? - return nil, fmt.Errorf("unexpected AST for expr: %T", n) - } - - typ := qpos.info.TypeOf(expr) - constVal := qpos.info.Types[expr].Value - - return &describeValueResult{ - qpos: qpos, - expr: expr, - typ: typ, - constVal: constVal, - obj: obj, - }, nil -} - -type describeValueResult struct { - qpos *queryPos - expr ast.Expr // query node - typ types.Type // type of expression - constVal exact.Value // value of expression, if constant - obj types.Object // var/func/const object, if expr was Ident -} - -func (r *describeValueResult) display(printf printfFunc) { - var prefix, suffix string - if r.constVal != nil { - suffix = fmt.Sprintf(" of constant value %s", r.constVal) - } - switch obj := r.obj.(type) { - case *types.Func: - if recv := obj.Type().(*types.Signature).Recv(); recv != nil { - if _, ok := recv.Type().Underlying().(*types.Interface); ok { - prefix = "interface method " - } else { - prefix = "method " - } - } - } - - // Describe the expression. - if r.obj != nil { - if r.obj.Pos() == r.expr.Pos() { - // defining ident - printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) - } else { - // referring ident - printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) - if def := r.obj.Pos(); def != token.NoPos { - printf(def, "defined here") - } - } - } else { - desc := astutil.NodeDescription(r.expr) - if suffix != "" { - // constant expression - printf(r.expr, "%s%s", desc, suffix) - } else { - // non-constant expression - printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ)) - } - } -} - -func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { - var value, objpos string - if r.constVal != nil { - value = r.constVal.String() - } - if r.obj != nil { - objpos = fset.Position(r.obj.Pos()).String() - } - - res.Describe = &serial.Describe{ - Desc: astutil.NodeDescription(r.expr), - Pos: fset.Position(r.expr.Pos()).String(), - Detail: "value", - Value: &serial.DescribeValue{ - Type: r.qpos.typeString(r.typ), - Value: value, - ObjPos: objpos, - }, - } -} - -// ---- TYPE ------------------------------------------------------------ - -func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { - var description string - var t types.Type - switch n := path[0].(type) { - case *ast.Ident: - t = qpos.info.TypeOf(n) - switch t := t.(type) { - case *types.Basic: - description = "reference to built-in " - - case *types.Named: - isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above - if isDef { - description = "definition of " - } else { - description = "reference to " - } - } - - case ast.Expr: - t = qpos.info.TypeOf(n) - - default: - // Unreachable? - return nil, fmt.Errorf("unexpected AST for type: %T", n) - } - - description = description + "type " + qpos.typeString(t) - - // Show sizes for structs and named types (it's fairly obvious for others). - switch t.(type) { - case *types.Named, *types.Struct: - szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64 - description = fmt.Sprintf("%s (size %d, align %d)", description, - szs.Sizeof(t), szs.Alignof(t)) - } - - return &describeTypeResult{ - qpos: qpos, - node: path[0], - description: description, - typ: t, - methods: accessibleMethods(t, qpos.info.Pkg), - }, nil -} - -type describeTypeResult struct { - qpos *queryPos - node ast.Node - description string - typ types.Type - methods []*types.Selection -} - -func (r *describeTypeResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) - - // Show the underlying type for a reference to a named type. - if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() { - printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying())) - } - - // Print the method set, if the type kind is capable of bearing methods. - switch r.typ.(type) { - case *types.Interface, *types.Struct, *types.Named: - if len(r.methods) > 0 { - printf(r.node, "Method set:") - for _, meth := range r.methods { - // TODO(adonovan): print these relative - // to the owning package, not the - // query package. - printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth)) - } - } else { - printf(r.node, "No methods.") - } - } -} - -func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) { - var namePos, nameDef string - if nt, ok := r.typ.(*types.Named); ok { - namePos = fset.Position(nt.Obj().Pos()).String() - nameDef = nt.Underlying().String() - } - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "type", - Type: &serial.DescribeType{ - Type: r.qpos.typeString(r.typ), - NamePos: namePos, - NameDef: nameDef, - Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset), - }, - } -} - -// ---- PACKAGE ------------------------------------------------------------ - -func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) { - var description string - var pkg *types.Package - switch n := path[0].(type) { - case *ast.ImportSpec: - var obj types.Object - if n.Name != nil { - obj = qpos.info.Defs[n.Name] - } else { - obj = qpos.info.Implicits[n] - } - pkgname, _ := obj.(*types.PkgName) - if pkgname == nil { - return nil, fmt.Errorf("can't import package %s", n.Path.Value) - } - pkg = pkgname.Imported() - description = fmt.Sprintf("import of package %q", pkg.Path()) - - case *ast.Ident: - if _, isDef := path[1].(*ast.File); isDef { - // e.g. package id - pkg = qpos.info.Pkg - description = fmt.Sprintf("definition of package %q", pkg.Path()) - } else { - // e.g. import id "..." - // or id.F() - pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported() - description = fmt.Sprintf("reference to package %q", pkg.Path()) - } - - default: - // Unreachable? - return nil, fmt.Errorf("unexpected AST for package: %T", n) - } - - var members []*describeMember - // NB: "unsafe" has no types.Package - if pkg != nil { - // Enumerate the accessible package members - // in lexicographic order. - for _, name := range pkg.Scope().Names() { - if pkg == qpos.info.Pkg || ast.IsExported(name) { - mem := pkg.Scope().Lookup(name) - var methods []*types.Selection - if mem, ok := mem.(*types.TypeName); ok { - methods = accessibleMethods(mem.Type(), qpos.info.Pkg) - } - members = append(members, &describeMember{ - mem, - methods, - }) - - } - } - } - - return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil -} - -type describePackageResult struct { - fset *token.FileSet - node ast.Node - description string - pkg *types.Package - members []*describeMember // in lexicographic name order -} - -type describeMember struct { - obj types.Object - methods []*types.Selection // in types.MethodSet order -} - -func (r *describePackageResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) - - // Compute max width of name "column". - maxname := 0 - for _, mem := range r.members { - if l := len(mem.obj.Name()); l > maxname { - maxname = l - } - } - - for _, mem := range r.members { - printf(mem.obj, "\t%s", formatMember(mem.obj, maxname)) - for _, meth := range mem.methods { - printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg))) - } - } -} - -func formatMember(obj types.Object, maxname int) string { - qualifier := types.RelativeTo(obj.Pkg()) - var buf bytes.Buffer - fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) - switch obj := obj.(type) { - case *types.Const: - fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val().String()) - - case *types.Func: - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) - - case *types.TypeName: - // Abbreviate long aggregate type names. - var abbrev string - switch t := obj.Type().Underlying().(type) { - case *types.Interface: - if t.NumMethods() > 1 { - abbrev = "interface{...}" - } - case *types.Struct: - if t.NumFields() > 1 { - abbrev = "struct{...}" - } - } - if abbrev == "" { - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) - } else { - fmt.Fprintf(&buf, " %s", abbrev) - } - - case *types.Var: - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) - } - return buf.String() -} - -func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) { - var members []*serial.DescribeMember - for _, mem := range r.members { - typ := mem.obj.Type() - var val string - switch mem := mem.obj.(type) { - case *types.Const: - val = mem.Val().String() - case *types.TypeName: - typ = typ.Underlying() - } - members = append(members, &serial.DescribeMember{ - Name: mem.obj.Name(), - Type: typ.String(), - Value: val, - Pos: fset.Position(mem.obj.Pos()).String(), - Kind: tokenOf(mem.obj), - Methods: methodsToSerial(r.pkg, mem.methods, fset), - }) - } - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "package", - Package: &serial.DescribePackage{ - Path: r.pkg.Path(), - Members: members, - }, - } -} - -func tokenOf(o types.Object) string { - switch o.(type) { - case *types.Func: - return "func" - case *types.Var: - return "var" - case *types.TypeName: - return "type" - case *types.Const: - return "const" - case *types.PkgName: - return "package" - case *types.Builtin: - return "builtin" // e.g. when describing package "unsafe" - case *types.Nil: - return "nil" - case *types.Label: - return "label" - } - panic(o) -} - -// ---- STATEMENT ------------------------------------------------------------ - -func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) { - var description string - switch n := path[0].(type) { - case *ast.Ident: - if qpos.info.Defs[n] != nil { - description = "labelled statement" - } else { - description = "reference to labelled statement" - } - - default: - // Nothing much to say about statements. - description = astutil.NodeDescription(n) - } - return &describeStmtResult{qpos.fset, path[0], description}, nil -} - -type describeStmtResult struct { - fset *token.FileSet - node ast.Node - description string -} - -func (r *describeStmtResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) -} - -func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) { - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "unknown", - } -} - -// ------------------- Utilities ------------------- - -// pathToString returns a string containing the concrete types of the -// nodes in path. -func pathToString(path []ast.Node) string { - var buf bytes.Buffer - fmt.Fprint(&buf, "[") - for i, n := range path { - if i > 0 { - fmt.Fprint(&buf, " ") - } - fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.")) - } - fmt.Fprint(&buf, "]") - return buf.String() -} - -func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { - var methods []*types.Selection - for _, meth := range typeutil.IntuitiveMethodSet(t, nil) { - if isAccessibleFrom(meth.Obj(), from) { - methods = append(methods, meth) - } - } - return methods -} - -func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { - return ast.IsExported(obj.Name()) || obj.Pkg() == pkg -} - -func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod { - qualifier := types.RelativeTo(this) - var jmethods []serial.DescribeMethod - for _, meth := range methods { - var ser serial.DescribeMethod - if meth != nil { // may contain nils when called by implements (on a method) - ser = serial.DescribeMethod{ - Name: types.SelectionString(meth, qualifier), - Pos: fset.Position(meth.Obj().Pos()).String(), - } - } - jmethods = append(jmethods, ser) - } - return jmethods -} diff --git a/oracle/describe15.go b/oracle/describe15.go deleted file mode 100644 index 1276f9a1..00000000 --- a/oracle/describe15.go +++ /dev/null @@ -1,786 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5,!go1.6 - -package oracle - -import ( - "bytes" - "fmt" - "go/ast" - exact "go/constant" - "go/token" - "go/types" - "log" - "os" - "strings" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types/typeutil" - "golang.org/x/tools/oracle/serial" -) - -// describe describes the syntax node denoted by the query position, -// including: -// - its syntactic category -// - the definition of its referent (for identifiers) [now redundant] -// - its type and method set (for an expression or type expression) -// -func describe(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - if _, err := importQueryPackage(q.Pos, &lconf); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos) - if err != nil { - return err - } - - if false { // debugging - fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s", - astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path)) - } - - path, action := findInterestingNode(qpos.info, qpos.path) - switch action { - case actionExpr: - q.result, err = describeValue(qpos, path) - - case actionType: - q.result, err = describeType(qpos, path) - - case actionPackage: - q.result, err = describePackage(qpos, path) - - case actionStmt: - q.result, err = describeStmt(qpos, path) - - case actionUnknown: - q.result = &describeUnknownResult{path[0]} - - default: - panic(action) // unreachable - } - return err -} - -type describeUnknownResult struct { - node ast.Node -} - -func (r *describeUnknownResult) display(printf printfFunc) { - // Nothing much to say about misc syntax. - printf(r.node, "%s", astutil.NodeDescription(r.node)) -} - -func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { - res.Describe = &serial.Describe{ - Desc: astutil.NodeDescription(r.node), - Pos: fset.Position(r.node.Pos()).String(), - } -} - -type action int - -const ( - actionUnknown action = iota // None of the below - actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var}) - actionType // type Expr or Ident(types.TypeName). - actionStmt // Stmt or Ident(types.Label) - actionPackage // Ident(types.Package) or ImportSpec -) - -// findInterestingNode classifies the syntax node denoted by path as one of: -// - an expression, part of an expression or a reference to a constant -// or variable; -// - a type, part of a type, or a reference to a named type; -// - a statement, part of a statement, or a label referring to a statement; -// - part of a package declaration or import spec. -// - none of the above. -// and returns the most "interesting" associated node, which may be -// the same node, an ancestor or a descendent. -// -func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) { - // TODO(adonovan): integrate with go/types/stdlib_test.go and - // apply this to every AST node we can find to make sure it - // doesn't crash. - - // TODO(adonovan): audit for ParenExpr safety, esp. since we - // traverse up and down. - - // TODO(adonovan): if the users selects the "." in - // "fmt.Fprintf()", they'll get an ambiguous selection error; - // we won't even reach here. Can we do better? - - // TODO(adonovan): describing a field within 'type T struct {...}' - // describes the (anonymous) struct type and concludes "no methods". - // We should ascend to the enclosing type decl, if any. - - for len(path) > 0 { - switch n := path[0].(type) { - case *ast.GenDecl: - if len(n.Specs) == 1 { - // Descend to sole {Import,Type,Value}Spec child. - path = append([]ast.Node{n.Specs[0]}, path...) - continue - } - return path, actionUnknown // uninteresting - - case *ast.FuncDecl: - // Descend to function name. - path = append([]ast.Node{n.Name}, path...) - continue - - case *ast.ImportSpec: - return path, actionPackage - - case *ast.ValueSpec: - if len(n.Names) == 1 { - // Descend to sole Ident child. - path = append([]ast.Node{n.Names[0]}, path...) - continue - } - return path, actionUnknown // uninteresting - - case *ast.TypeSpec: - // Descend to type name. - path = append([]ast.Node{n.Name}, path...) - continue - - case ast.Stmt: - return path, actionStmt - - case *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - return path, actionType - - case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause: - return path, actionUnknown // uninteresting - - case *ast.Ellipsis: - // Continue to enclosing node. - // e.g. [...]T in ArrayType - // f(x...) in CallExpr - // f(x...T) in FuncType - - case *ast.Field: - // TODO(adonovan): this needs more thought, - // since fields can be so many things. - if len(n.Names) == 1 { - // Descend to sole Ident child. - path = append([]ast.Node{n.Names[0]}, path...) - continue - } - // Zero names (e.g. anon field in struct) - // or multiple field or param names: - // continue to enclosing field list. - - case *ast.FieldList: - // Continue to enclosing node: - // {Struct,Func,Interface}Type or FuncDecl. - - case *ast.BasicLit: - if _, ok := path[1].(*ast.ImportSpec); ok { - return path[1:], actionPackage - } - return path, actionExpr - - case *ast.SelectorExpr: - // TODO(adonovan): use Selections info directly. - if pkginfo.Uses[n.Sel] == nil { - // TODO(adonovan): is this reachable? - return path, actionUnknown - } - // Descend to .Sel child. - path = append([]ast.Node{n.Sel}, path...) - continue - - case *ast.Ident: - switch pkginfo.ObjectOf(n).(type) { - case *types.PkgName: - return path, actionPackage - - case *types.Const: - return path, actionExpr - - case *types.Label: - return path, actionStmt - - case *types.TypeName: - return path, actionType - - case *types.Var: - // For x in 'struct {x T}', return struct type, for now. - if _, ok := path[1].(*ast.Field); ok { - _ = path[2].(*ast.FieldList) // assertion - if _, ok := path[3].(*ast.StructType); ok { - return path[3:], actionType - } - } - return path, actionExpr - - case *types.Func: - return path, actionExpr - - case *types.Builtin: - // For reference to built-in function, return enclosing call. - path = path[1:] // ascend to enclosing function call - continue - - case *types.Nil: - return path, actionExpr - } - - // No object. - switch path[1].(type) { - case *ast.SelectorExpr: - // Return enclosing selector expression. - return path[1:], actionExpr - - case *ast.Field: - // TODO(adonovan): test this. - // e.g. all f in: - // struct { f, g int } - // interface { f() } - // func (f T) method(f, g int) (f, g bool) - // - // switch path[3].(type) { - // case *ast.FuncDecl: - // case *ast.StructType: - // case *ast.InterfaceType: - // } - // - // return path[1:], actionExpr - // - // Unclear what to do with these. - // Struct.Fields -- field - // Interface.Methods -- field - // FuncType.{Params.Results} -- actionExpr - // FuncDecl.Recv -- actionExpr - - case *ast.File: - // 'package foo' - return path, actionPackage - - case *ast.ImportSpec: - // TODO(adonovan): fix: why no package object? go/types bug? - return path[1:], actionPackage - - default: - // e.g. blank identifier - // or y in "switch y := x.(type)" - // or code in a _test.go file that's not part of the package. - log.Printf("unknown reference %s in %T\n", n, path[1]) - return path, actionUnknown - } - - case *ast.StarExpr: - if pkginfo.Types[n].IsType() { - return path, actionType - } - return path, actionExpr - - case ast.Expr: - // All Expr but {BasicLit,Ident,StarExpr} are - // "true" expressions that evaluate to a value. - return path, actionExpr - } - - // Ascend to parent. - path = path[1:] - } - - return nil, actionUnknown // unreachable -} - -func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) { - var expr ast.Expr - var obj types.Object - switch n := path[0].(type) { - case *ast.ValueSpec: - // ambiguous ValueSpec containing multiple names - return nil, fmt.Errorf("multiple value specification") - case *ast.Ident: - obj = qpos.info.ObjectOf(n) - expr = n - case ast.Expr: - expr = n - default: - // TODO(adonovan): is this reachable? - return nil, fmt.Errorf("unexpected AST for expr: %T", n) - } - - typ := qpos.info.TypeOf(expr) - constVal := qpos.info.Types[expr].Value - - return &describeValueResult{ - qpos: qpos, - expr: expr, - typ: typ, - constVal: constVal, - obj: obj, - }, nil -} - -type describeValueResult struct { - qpos *queryPos - expr ast.Expr // query node - typ types.Type // type of expression - constVal exact.Value // value of expression, if constant - obj types.Object // var/func/const object, if expr was Ident -} - -func (r *describeValueResult) display(printf printfFunc) { - var prefix, suffix string - if r.constVal != nil { - suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal)) - } - switch obj := r.obj.(type) { - case *types.Func: - if recv := obj.Type().(*types.Signature).Recv(); recv != nil { - if _, ok := recv.Type().Underlying().(*types.Interface); ok { - prefix = "interface method " - } else { - prefix = "method " - } - } - } - - // Describe the expression. - if r.obj != nil { - if r.obj.Pos() == r.expr.Pos() { - // defining ident - printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) - } else { - // referring ident - printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) - if def := r.obj.Pos(); def != token.NoPos { - printf(def, "defined here") - } - } - } else { - desc := astutil.NodeDescription(r.expr) - if suffix != "" { - // constant expression - printf(r.expr, "%s%s", desc, suffix) - } else { - // non-constant expression - printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ)) - } - } -} - -func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { - var value, objpos string - if r.constVal != nil { - value = r.constVal.String() - } - if r.obj != nil { - objpos = fset.Position(r.obj.Pos()).String() - } - - res.Describe = &serial.Describe{ - Desc: astutil.NodeDescription(r.expr), - Pos: fset.Position(r.expr.Pos()).String(), - Detail: "value", - Value: &serial.DescribeValue{ - Type: r.qpos.typeString(r.typ), - Value: value, - ObjPos: objpos, - }, - } -} - -// ---- TYPE ------------------------------------------------------------ - -func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { - var description string - var t types.Type - switch n := path[0].(type) { - case *ast.Ident: - t = qpos.info.TypeOf(n) - switch t := t.(type) { - case *types.Basic: - description = "reference to built-in " - - case *types.Named: - isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above - if isDef { - description = "definition of " - } else { - description = "reference to " - } - } - - case ast.Expr: - t = qpos.info.TypeOf(n) - - default: - // Unreachable? - return nil, fmt.Errorf("unexpected AST for type: %T", n) - } - - description = description + "type " + qpos.typeString(t) - - // Show sizes for structs and named types (it's fairly obvious for others). - switch t.(type) { - case *types.Named, *types.Struct: - szs := types.StdSizes{8, 8} // assume amd64 - description = fmt.Sprintf("%s (size %d, align %d)", description, - szs.Sizeof(t), szs.Alignof(t)) - } - - return &describeTypeResult{ - qpos: qpos, - node: path[0], - description: description, - typ: t, - methods: accessibleMethods(t, qpos.info.Pkg), - }, nil -} - -type describeTypeResult struct { - qpos *queryPos - node ast.Node - description string - typ types.Type - methods []*types.Selection -} - -func (r *describeTypeResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) - - // Show the underlying type for a reference to a named type. - if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() { - printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying())) - } - - // Print the method set, if the type kind is capable of bearing methods. - switch r.typ.(type) { - case *types.Interface, *types.Struct, *types.Named: - if len(r.methods) > 0 { - printf(r.node, "Method set:") - for _, meth := range r.methods { - // TODO(adonovan): print these relative - // to the owning package, not the - // query package. - printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth)) - } - } else { - printf(r.node, "No methods.") - } - } -} - -func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) { - var namePos, nameDef string - if nt, ok := r.typ.(*types.Named); ok { - namePos = fset.Position(nt.Obj().Pos()).String() - nameDef = nt.Underlying().String() - } - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "type", - Type: &serial.DescribeType{ - Type: r.qpos.typeString(r.typ), - NamePos: namePos, - NameDef: nameDef, - Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset), - }, - } -} - -// ---- PACKAGE ------------------------------------------------------------ - -func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) { - var description string - var pkg *types.Package - switch n := path[0].(type) { - case *ast.ImportSpec: - var obj types.Object - if n.Name != nil { - obj = qpos.info.Defs[n.Name] - } else { - obj = qpos.info.Implicits[n] - } - pkgname, _ := obj.(*types.PkgName) - if pkgname == nil { - return nil, fmt.Errorf("can't import package %s", n.Path.Value) - } - pkg = pkgname.Imported() - description = fmt.Sprintf("import of package %q", pkg.Path()) - - case *ast.Ident: - if _, isDef := path[1].(*ast.File); isDef { - // e.g. package id - pkg = qpos.info.Pkg - description = fmt.Sprintf("definition of package %q", pkg.Path()) - } else { - // e.g. import id "..." - // or id.F() - pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported() - description = fmt.Sprintf("reference to package %q", pkg.Path()) - } - - default: - // Unreachable? - return nil, fmt.Errorf("unexpected AST for package: %T", n) - } - - var members []*describeMember - // NB: "unsafe" has no types.Package - if pkg != nil { - // Enumerate the accessible package members - // in lexicographic order. - for _, name := range pkg.Scope().Names() { - if pkg == qpos.info.Pkg || ast.IsExported(name) { - mem := pkg.Scope().Lookup(name) - var methods []*types.Selection - if mem, ok := mem.(*types.TypeName); ok { - methods = accessibleMethods(mem.Type(), qpos.info.Pkg) - } - members = append(members, &describeMember{ - mem, - methods, - }) - - } - } - } - - return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil -} - -type describePackageResult struct { - fset *token.FileSet - node ast.Node - description string - pkg *types.Package - members []*describeMember // in lexicographic name order -} - -type describeMember struct { - obj types.Object - methods []*types.Selection // in types.MethodSet order -} - -func (r *describePackageResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) - - // Compute max width of name "column". - maxname := 0 - for _, mem := range r.members { - if l := len(mem.obj.Name()); l > maxname { - maxname = l - } - } - - for _, mem := range r.members { - printf(mem.obj, "\t%s", formatMember(mem.obj, maxname)) - for _, meth := range mem.methods { - printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg))) - } - } -} - -func formatMember(obj types.Object, maxname int) string { - qualifier := types.RelativeTo(obj.Pkg()) - var buf bytes.Buffer - fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) - switch obj := obj.(type) { - case *types.Const: - fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val())) - - case *types.Func: - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) - - case *types.TypeName: - // Abbreviate long aggregate type names. - var abbrev string - switch t := obj.Type().Underlying().(type) { - case *types.Interface: - if t.NumMethods() > 1 { - abbrev = "interface{...}" - } - case *types.Struct: - if t.NumFields() > 1 { - abbrev = "struct{...}" - } - } - if abbrev == "" { - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) - } else { - fmt.Fprintf(&buf, " %s", abbrev) - } - - case *types.Var: - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) - } - return buf.String() -} - -func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) { - var members []*serial.DescribeMember - for _, mem := range r.members { - typ := mem.obj.Type() - var val string - switch mem := mem.obj.(type) { - case *types.Const: - val = constValString(mem.Val()) - case *types.TypeName: - typ = typ.Underlying() - } - members = append(members, &serial.DescribeMember{ - Name: mem.obj.Name(), - Type: typ.String(), - Value: val, - Pos: fset.Position(mem.obj.Pos()).String(), - Kind: tokenOf(mem.obj), - Methods: methodsToSerial(r.pkg, mem.methods, fset), - }) - } - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "package", - Package: &serial.DescribePackage{ - Path: r.pkg.Path(), - Members: members, - }, - } -} - -func tokenOf(o types.Object) string { - switch o.(type) { - case *types.Func: - return "func" - case *types.Var: - return "var" - case *types.TypeName: - return "type" - case *types.Const: - return "const" - case *types.PkgName: - return "package" - case *types.Builtin: - return "builtin" // e.g. when describing package "unsafe" - case *types.Nil: - return "nil" - case *types.Label: - return "label" - } - panic(o) -} - -// ---- STATEMENT ------------------------------------------------------------ - -func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) { - var description string - switch n := path[0].(type) { - case *ast.Ident: - if qpos.info.Defs[n] != nil { - description = "labelled statement" - } else { - description = "reference to labelled statement" - } - - default: - // Nothing much to say about statements. - description = astutil.NodeDescription(n) - } - return &describeStmtResult{qpos.fset, path[0], description}, nil -} - -type describeStmtResult struct { - fset *token.FileSet - node ast.Node - description string -} - -func (r *describeStmtResult) display(printf printfFunc) { - printf(r.node, "%s", r.description) -} - -func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) { - res.Describe = &serial.Describe{ - Desc: r.description, - Pos: fset.Position(r.node.Pos()).String(), - Detail: "unknown", - } -} - -// ------------------- Utilities ------------------- - -// pathToString returns a string containing the concrete types of the -// nodes in path. -func pathToString(path []ast.Node) string { - var buf bytes.Buffer - fmt.Fprint(&buf, "[") - for i, n := range path { - if i > 0 { - fmt.Fprint(&buf, " ") - } - fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.")) - } - fmt.Fprint(&buf, "]") - return buf.String() -} - -func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { - var methods []*types.Selection - for _, meth := range typeutil.IntuitiveMethodSet(t, nil) { - if isAccessibleFrom(meth.Obj(), from) { - methods = append(methods, meth) - } - } - return methods -} - -func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { - return ast.IsExported(obj.Name()) || obj.Pkg() == pkg -} - -func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod { - qualifier := types.RelativeTo(this) - var jmethods []serial.DescribeMethod - for _, meth := range methods { - var ser serial.DescribeMethod - if meth != nil { // may contain nils when called by implements (on a method) - ser = serial.DescribeMethod{ - Name: types.SelectionString(meth, qualifier), - Pos: fset.Position(meth.Obj().Pos()).String(), - } - } - jmethods = append(jmethods, ser) - } - return jmethods -} - -// constValString emulates Go 1.6's go/constant.ExactString well enough -// to make the tests pass. This is just a stopgap until we throw away -// all the *15.go files. -func constValString(v exact.Value) string { - if v.Kind() == exact.Float { - f, _ := exact.Float64Val(v) - return fmt.Sprintf("%g", f) - } - return v.String() -} diff --git a/oracle/freevars.go b/oracle/freevars.go deleted file mode 100644 index 6b89cb39..00000000 --- a/oracle/freevars.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "bytes" - "go/ast" - "go/printer" - "go/token" - "go/types" - "sort" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/oracle/serial" -) - -// freevars displays the lexical (not package-level) free variables of -// the selection. -// -// It treats A.B.C as a separate variable from A to reveal the parts -// of an aggregate type that are actually needed. -// This aids refactoring. -// -// TODO(adonovan): optionally display the free references to -// file/package scope objects, and to objects from other packages. -// Depending on where the resulting function abstraction will go, -// these might be interesting. Perhaps group the results into three -// bands. -// -func freevars(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - if _, err := importQueryPackage(q.Pos, &lconf); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - file := qpos.path[len(qpos.path)-1] // the enclosing file - fileScope := qpos.info.Scopes[file] - pkgScope := fileScope.Parent() - - // The id and sel functions return non-nil if they denote an - // object o or selection o.x.y that is referenced by the - // selection but defined neither within the selection nor at - // file scope, i.e. it is in the lexical environment. - var id func(n *ast.Ident) types.Object - var sel func(n *ast.SelectorExpr) types.Object - - sel = func(n *ast.SelectorExpr) types.Object { - switch x := unparen(n.X).(type) { - case *ast.SelectorExpr: - return sel(x) - case *ast.Ident: - return id(x) - } - return nil - } - - id = func(n *ast.Ident) types.Object { - obj := qpos.info.Uses[n] - if obj == nil { - return nil // not a reference - } - if _, ok := obj.(*types.PkgName); ok { - return nil // imported package - } - if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) { - return nil // not defined in this file - } - scope := obj.Parent() - if scope == nil { - return nil // e.g. interface method, struct field - } - if scope == fileScope || scope == pkgScope { - return nil // defined at file or package scope - } - if qpos.start <= obj.Pos() && obj.Pos() <= qpos.end { - return nil // defined within selection => not free - } - return obj - } - - // Maps each reference that is free in the selection - // to the object it refers to. - // The map de-duplicates repeated references. - refsMap := make(map[string]freevarsRef) - - // Visit all the identifiers in the selected ASTs. - ast.Inspect(qpos.path[0], func(n ast.Node) bool { - if n == nil { - return true // popping DFS stack - } - - // Is this node contained within the selection? - // (freevars permits inexact selections, - // like two stmts in a block.) - if qpos.start <= n.Pos() && n.End() <= qpos.end { - var obj types.Object - var prune bool - switch n := n.(type) { - case *ast.Ident: - obj = id(n) - - case *ast.SelectorExpr: - obj = sel(n) - prune = true - } - - if obj != nil { - var kind string - switch obj.(type) { - case *types.Var: - kind = "var" - case *types.Func: - kind = "func" - case *types.TypeName: - kind = "type" - case *types.Const: - kind = "const" - case *types.Label: - kind = "label" - default: - panic(obj) - } - - typ := qpos.info.TypeOf(n.(ast.Expr)) - ref := freevarsRef{kind, printNode(lprog.Fset, n), typ, obj} - refsMap[ref.ref] = ref - - if prune { - return false // don't descend - } - } - } - - return true // descend - }) - - refs := make([]freevarsRef, 0, len(refsMap)) - for _, ref := range refsMap { - refs = append(refs, ref) - } - sort.Sort(byRef(refs)) - - q.result = &freevarsResult{ - qpos: qpos, - refs: refs, - } - return nil -} - -type freevarsResult struct { - qpos *queryPos - refs []freevarsRef -} - -type freevarsRef struct { - kind string - ref string - typ types.Type - obj types.Object -} - -func (r *freevarsResult) display(printf printfFunc) { - if len(r.refs) == 0 { - printf(r.qpos, "No free identifiers.") - } else { - printf(r.qpos, "Free identifiers:") - qualifier := types.RelativeTo(r.qpos.info.Pkg) - for _, ref := range r.refs { - // Avoid printing "type T T". - var typstr string - if ref.kind != "type" { - typstr = " " + types.TypeString(ref.typ, qualifier) - } - printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr) - } - } -} - -func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) { - var refs []*serial.FreeVar - for _, ref := range r.refs { - refs = append(refs, - &serial.FreeVar{ - Pos: fset.Position(ref.obj.Pos()).String(), - Kind: ref.kind, - Ref: ref.ref, - Type: ref.typ.String(), - }) - } - res.Freevars = refs -} - -// -------- utils -------- - -type byRef []freevarsRef - -func (p byRef) Len() int { return len(p) } -func (p byRef) Less(i, j int) bool { return p[i].ref < p[j].ref } -func (p byRef) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// printNode returns the pretty-printed syntax of n. -func printNode(fset *token.FileSet, n ast.Node) string { - var buf bytes.Buffer - printer.Fprint(&buf, fset, n) - return buf.String() -} diff --git a/oracle/implements.go b/oracle/implements.go deleted file mode 100644 index 7c9da1dc..00000000 --- a/oracle/implements.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "reflect" - "sort" - "strings" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types/typeutil" - "golang.org/x/tools/oracle/serial" - "golang.org/x/tools/refactor/importgraph" -) - -// Implements displays the "implements" relation as it pertains to the -// selected type. -// If the selection is a method, 'implements' displays -// the corresponding methods of the types that would have been reported -// by an implements query on the receiver type. -// -func implements(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - qpkg, err := importQueryPackage(q.Pos, &lconf) - if err != nil { - return err - } - - // Set the packages to search. - if len(q.Scope) > 0 { - // Inspect all packages in the analysis scope, if specified. - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - } else { - // Otherwise inspect the forward and reverse - // transitive closure of the selected package. - // (In theory even this is incomplete.) - _, rev, _ := importgraph.Build(q.Build) - for path := range rev.Search(qpkg) { - lconf.ImportWithTests(path) - } - - // TODO(adonovan): for completeness, we should also - // type-check and inspect function bodies in all - // imported packages. This would be expensive, but we - // could optimize by skipping functions that do not - // contain type declarations. This would require - // changing the loader's TypeCheckFuncBodies hook to - // provide the []*ast.File. - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - // Find the selected type. - path, action := findInterestingNode(qpos.info, qpos.path) - - var method *types.Func - var T types.Type // selected type (receiver if method != nil) - - switch action { - case actionExpr: - // method? - if id, ok := path[0].(*ast.Ident); ok { - if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { - recv := obj.Type().(*types.Signature).Recv() - if recv == nil { - return fmt.Errorf("this function is not a method") - } - method = obj - T = recv.Type() - } - } - case actionType: - T = qpos.info.TypeOf(path[0].(ast.Expr)) - } - if T == nil { - return fmt.Errorf("no type or method here") - } - - // Find all named types, even local types (which can have - // methods via promotion) and the built-in "error". - var allNamed []types.Type - for _, info := range lprog.AllPackages { - for _, obj := range info.Defs { - if obj, ok := obj.(*types.TypeName); ok { - allNamed = append(allNamed, obj.Type()) - } - } - } - allNamed = append(allNamed, types.Universe.Lookup("error").Type()) - - var msets typeutil.MethodSetCache - - // Test each named type. - var to, from, fromPtr []types.Type - for _, U := range allNamed { - if isInterface(T) { - if msets.MethodSet(T).Len() == 0 { - continue // empty interface - } - if isInterface(U) { - if msets.MethodSet(U).Len() == 0 { - continue // empty interface - } - - // T interface, U interface - if !types.Identical(T, U) { - if types.AssignableTo(U, T) { - to = append(to, U) - } - if types.AssignableTo(T, U) { - from = append(from, U) - } - } - } else { - // T interface, U concrete - if types.AssignableTo(U, T) { - to = append(to, U) - } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { - to = append(to, pU) - } - } - } else if isInterface(U) { - if msets.MethodSet(U).Len() == 0 { - continue // empty interface - } - - // T concrete, U interface - if types.AssignableTo(T, U) { - from = append(from, U) - } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { - fromPtr = append(fromPtr, U) - } - } - } - - var pos interface{} = qpos - if nt, ok := deref(T).(*types.Named); ok { - pos = nt.Obj() - } - - // Sort types (arbitrarily) to ensure test determinism. - sort.Sort(typesByString(to)) - sort.Sort(typesByString(from)) - sort.Sort(typesByString(fromPtr)) - - var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils - if method != nil { - for _, t := range to { - toMethod = append(toMethod, - types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) - } - for _, t := range from { - fromMethod = append(fromMethod, - types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) - } - for _, t := range fromPtr { - fromPtrMethod = append(fromPtrMethod, - types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) - } - } - - q.result = &implementsResult{ - qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod, - } - return nil -} - -type implementsResult struct { - qpos *queryPos - - t types.Type // queried type (not necessarily named) - pos interface{} // pos of t (*types.Name or *QueryPos) - to []types.Type // named or ptr-to-named types assignable to interface T - from []types.Type // named interfaces assignable from T - fromPtr []types.Type // named interfaces assignable only from *T - - // if a method was queried: - method *types.Func // queried method - toMethod []*types.Selection // method of type to[i], if any - fromMethod []*types.Selection // method of type from[i], if any - fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any -} - -func (r *implementsResult) display(printf printfFunc) { - relation := "is implemented by" - - meth := func(sel *types.Selection) { - if sel != nil { - printf(sel.Obj(), "\t%s method (%s).%s", - relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name()) - } - } - - if isInterface(r.t) { - if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset - printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t)) - return - } - - if r.method == nil { - printf(r.pos, "interface type %s", r.qpos.typeString(r.t)) - } else { - printf(r.method, "abstract method %s", r.qpos.objectString(r.method)) - } - - // Show concrete types (or methods) first; use two passes. - for i, sub := range r.to { - if !isInterface(sub) { - if r.method == nil { - printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", - relation, typeKind(sub), r.qpos.typeString(sub)) - } else { - meth(r.toMethod[i]) - } - } - } - for i, sub := range r.to { - if isInterface(sub) { - if r.method == nil { - printf(sub.(*types.Named).Obj(), "\t%s %s type %s", - relation, typeKind(sub), r.qpos.typeString(sub)) - } else { - meth(r.toMethod[i]) - } - } - } - - relation = "implements" - for i, super := range r.from { - if r.method == nil { - printf(super.(*types.Named).Obj(), "\t%s %s", - relation, r.qpos.typeString(super)) - } else { - meth(r.fromMethod[i]) - } - } - } else { - relation = "implements" - - if r.from != nil { - if r.method == nil { - printf(r.pos, "%s type %s", - typeKind(r.t), r.qpos.typeString(r.t)) - } else { - printf(r.method, "concrete method %s", - r.qpos.objectString(r.method)) - } - for i, super := range r.from { - if r.method == nil { - printf(super.(*types.Named).Obj(), "\t%s %s", - relation, r.qpos.typeString(super)) - } else { - meth(r.fromMethod[i]) - } - } - } - if r.fromPtr != nil { - if r.method == nil { - printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t)) - } else { - // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f. - printf(r.method, "concrete method %s", - r.qpos.objectString(r.method)) - } - - for i, psuper := range r.fromPtr { - if r.method == nil { - printf(psuper.(*types.Named).Obj(), "\t%s %s", - relation, r.qpos.typeString(psuper)) - } else { - meth(r.fromPtrMethod[i]) - } - } - } else if r.from == nil { - printf(r.pos, "%s type %s implements only interface{}", - typeKind(r.t), r.qpos.typeString(r.t)) - } - } -} - -func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) { - res.Implements = &serial.Implements{ - T: makeImplementsType(r.t, fset), - AssignableTo: makeImplementsTypes(r.to, fset), - AssignableFrom: makeImplementsTypes(r.from, fset), - AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset), - AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset), - AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset), - AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset), - } - if r.method != nil { - res.Implements.Method = &serial.DescribeMethod{ - Name: r.qpos.objectString(r.method), - Pos: fset.Position(r.method.Pos()).String(), - } - } -} - -func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType { - var r []serial.ImplementsType - for _, t := range tt { - r = append(r, makeImplementsType(t, fset)) - } - return r -} - -func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType { - var pos token.Pos - if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named - pos = nt.Obj().Pos() - } - return serial.ImplementsType{ - Name: T.String(), - Pos: fset.Position(pos).String(), - Kind: typeKind(T), - } -} - -// typeKind returns a string describing the underlying kind of type, -// e.g. "slice", "array", "struct". -func typeKind(T types.Type) string { - s := reflect.TypeOf(T.Underlying()).String() - return strings.ToLower(strings.TrimPrefix(s, "*types.")) -} - -func isInterface(T types.Type) bool { return types.IsInterface(T) } - -type typesByString []types.Type - -func (p typesByString) Len() int { return len(p) } -func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() } -func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/oracle/oracle.go b/oracle/oracle.go deleted file mode 100644 index c84ebd35..00000000 --- a/oracle/oracle.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -// Package oracle contains the implementation of the oracle tool whose -// command-line is provided by golang.org/x/tools/cmd/oracle. -// -// DEPRECATED: oracle has been superseded by guru; -// see https://golang.org/s/using-guru for details. -// This package will be deleted on October 1, 2016. -// -package oracle // import "golang.org/x/tools/oracle" - -// This file defines oracle.Query, the entry point for the oracle tool. -// The actual executable is defined in cmd/oracle. - -// TODO(adonovan): new queries -// - show all statements that may update the selected lvalue -// (local, global, field, etc). -// - show all places where an object of type T is created -// (&T{}, var t T, new(T), new(struct{array [3]T}), etc. - -import ( - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "go/types" - "io" - "path/filepath" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/pointer" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/oracle/serial" -) - -type printfFunc func(pos interface{}, format string, args ...interface{}) - -// queryResult is the interface of each query-specific result type. -type queryResult interface { - toSerial(res *serial.Result, fset *token.FileSet) - display(printf printfFunc) -} - -// A QueryPos represents the position provided as input to a query: -// a textual extent in the program's source code, the AST node it -// corresponds to, and the package to which it belongs. -// Instances are created by parseQueryPos. -type queryPos struct { - fset *token.FileSet - start, end token.Pos // source extent of query - path []ast.Node // AST path from query node to root of ast.File - exact bool // 2nd result of PathEnclosingInterval - info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos) -} - -// TypeString prints type T relative to the query position. -func (qpos *queryPos) typeString(T types.Type) string { - return types.TypeString(T, types.RelativeTo(qpos.info.Pkg)) -} - -// ObjectString prints object obj relative to the query position. -func (qpos *queryPos) objectString(obj types.Object) string { - return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg)) -} - -// SelectionString prints selection sel relative to the query position. -func (qpos *queryPos) selectionString(sel *types.Selection) string { - return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg)) -} - -// A Query specifies a single oracle query. -type Query struct { - Mode string // query mode ("callers", etc) - Pos string // query position - Build *build.Context // package loading configuration - - // pointer analysis options - Scope []string // main packages in (*loader.Config).FromArgs syntax - PTALog io.Writer // (optional) pointer-analysis log file - Reflection bool // model reflection soundly (currently slow). - - // Populated during Run() - Fset *token.FileSet - result queryResult -} - -// Serial returns an instance of serial.Result, which implements the -// {xml,json}.Marshaler interfaces so that query results can be -// serialized as JSON or XML. -// -func (q *Query) Serial() *serial.Result { - resj := &serial.Result{Mode: q.Mode} - q.result.toSerial(resj, q.Fset) - return resj -} - -// WriteTo writes the oracle query result res to out in a compiler diagnostic format. -func (q *Query) WriteTo(out io.Writer) { - printf := func(pos interface{}, format string, args ...interface{}) { - fprintf(out, q.Fset, pos, format, args...) - } - q.result.display(printf) -} - -// Run runs an oracle query and populates its Fset and Result. -func Run(q *Query) error { - switch q.Mode { - case "callees": - return callees(q) - case "callers": - return callers(q) - case "callstack": - return callstack(q) - case "peers": - return peers(q) - case "pointsto": - return pointsto(q) - case "whicherrs": - return whicherrs(q) - case "definition": - return definition(q) - case "describe": - return describe(q) - case "freevars": - return freevars(q) - case "implements": - return implements(q) - case "referrers": - return referrers(q) - case "what": - return what(q) - default: - return fmt.Errorf("invalid mode: %q", q.Mode) - } -} - -func setPTAScope(lconf *loader.Config, scope []string) error { - if len(scope) == 0 { - return fmt.Errorf("no packages specified for pointer analysis scope") - } - - // Determine initial packages for PTA. - args, err := lconf.FromArgs(scope, true) - if err != nil { - return err - } - if len(args) > 0 { - return fmt.Errorf("surplus arguments: %q", args) - } - return nil -} - -// Create a pointer.Config whose scope is the initial packages of lprog -// and their dependencies. -func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) { - // TODO(adonovan): the body of this function is essentially - // duplicated in all go/pointer clients. Refactor. - - // For each initial package (specified on the command line), - // if it has a main function, analyze that, - // otherwise analyze its tests, if any. - var testPkgs, mains []*ssa.Package - for _, info := range lprog.InitialPackages() { - initialPkg := prog.Package(info.Pkg) - - // Add package to the pointer analysis scope. - if initialPkg.Func("main") != nil { - mains = append(mains, initialPkg) - } else { - testPkgs = append(testPkgs, initialPkg) - } - } - if testPkgs != nil { - if p := prog.CreateTestMainPackage(testPkgs...); p != nil { - mains = append(mains, p) - } - } - if mains == nil { - return nil, fmt.Errorf("analysis scope has no main and no tests") - } - return &pointer.Config{ - Log: ptaLog, - Reflection: reflection, - Mains: mains, - }, nil -} - -// importQueryPackage finds the package P containing the -// query position and tells conf to import it. -// It returns the package's path. -func importQueryPackage(pos string, conf *loader.Config) (string, error) { - fqpos, err := fastQueryPos(pos) - if err != nil { - return "", err // bad query - } - filename := fqpos.fset.File(fqpos.start).Name() - - // This will not work for ad-hoc packages - // such as $GOROOT/src/net/http/triv.go. - // TODO(adonovan): ensure we report a clear error. - _, importPath, err := guessImportPath(filename, conf.Build) - if err != nil { - return "", err // can't find GOPATH dir - } - if importPath == "" { - return "", fmt.Errorf("can't guess import path from %s", filename) - } - - // Check that it's possible to load the queried package. - // (e.g. oracle tests contain different 'package' decls in same dir.) - // Keep consistent with logic in loader/util.go! - cfg2 := *conf.Build - cfg2.CgoEnabled = false - bp, err := cfg2.Import(importPath, "", 0) - if err != nil { - return "", err // no files for package - } - - switch pkgContainsFile(bp, filename) { - case 'T': - conf.ImportWithTests(importPath) - case 'X': - conf.ImportWithTests(importPath) - importPath += "_test" // for TypeCheckFuncBodies - case 'G': - conf.Import(importPath) - default: - return "", fmt.Errorf("package %q doesn't contain file %s", - importPath, filename) - } - - conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath } - - return importPath, nil -} - -// pkgContainsFile reports whether file was among the packages Go -// files, Test files, eXternal test files, or not found. -func pkgContainsFile(bp *build.Package, filename string) byte { - for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} { - for _, file := range files { - if sameFile(filepath.Join(bp.Dir, file), filename) { - return "GTX"[i] - } - } - } - return 0 // not found -} - -// ParseQueryPos parses the source query position pos and returns the -// AST node of the loaded program lprog that it identifies. -// If needExact, it must identify a single AST subtree; -// this is appropriate for queries that allow fairly arbitrary syntax, -// e.g. "describe". -// -func parseQueryPos(lprog *loader.Program, posFlag string, needExact bool) (*queryPos, error) { - filename, startOffset, endOffset, err := parsePosFlag(posFlag) - if err != nil { - return nil, err - } - start, end, err := findQueryPos(lprog.Fset, filename, startOffset, endOffset) - if err != nil { - return nil, err - } - info, path, exact := lprog.PathEnclosingInterval(start, end) - if path == nil { - return nil, fmt.Errorf("no syntax here") - } - if needExact && !exact { - return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0])) - } - return &queryPos{lprog.Fset, start, end, path, exact, info}, nil -} - -// ---------- Utilities ---------- - -// allowErrors causes type errors to be silently ignored. -// (Not suitable if SSA construction follows.) -func allowErrors(lconf *loader.Config) { - ctxt := *lconf.Build // copy - ctxt.CgoEnabled = false - lconf.Build = &ctxt - lconf.AllowErrors = true - // AllErrors makes the parser always return an AST instead of - // bailing out after 10 errors and returning an empty ast.File. - lconf.ParserMode = parser.AllErrors - lconf.TypeChecker.Error = func(err error) {} -} - -// ptrAnalysis runs the pointer analysis and returns its result. -func ptrAnalysis(conf *pointer.Config) *pointer.Result { - result, err := pointer.Analyze(conf) - if err != nil { - panic(err) // pointer analysis internal error - } - return result -} - -func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) } - -// deref returns a pointer's element type; otherwise it returns typ. -func deref(typ types.Type) types.Type { - if p, ok := typ.Underlying().(*types.Pointer); ok { - return p.Elem() - } - return typ -} - -// fprintf prints to w a message of the form "location: message\n" -// where location is derived from pos. -// -// pos must be one of: -// - a token.Pos, denoting a position -// - an ast.Node, denoting an interval -// - anything with a Pos() method: -// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc. -// - a QueryPos, denoting the extent of the user's query. -// - nil, meaning no position at all. -// -// The output format is is compatible with the 'gnu' -// compilation-error-regexp in Emacs' compilation mode. -// TODO(adonovan): support other editors. -// -func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) { - var start, end token.Pos - switch pos := pos.(type) { - case ast.Node: - start = pos.Pos() - end = pos.End() - case token.Pos: - start = pos - end = start - case interface { - Pos() token.Pos - }: - start = pos.Pos() - end = start - case *queryPos: - start = pos.start - end = pos.end - case nil: - // no-op - default: - panic(fmt.Sprintf("invalid pos: %T", pos)) - } - - if sp := fset.Position(start); start == end { - // (prints "-: " for token.NoPos) - fmt.Fprintf(w, "%s: ", sp) - } else { - ep := fset.Position(end) - // The -1 below is a concession to Emacs's broken use of - // inclusive (not half-open) intervals. - // Other editors may not want it. - // TODO(adonovan): add an -editor=vim|emacs|acme|auto - // flag; auto uses EMACS=t / VIM=... / etc env vars. - fmt.Fprintf(w, "%s:%d.%d-%d.%d: ", - sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1) - } - fmt.Fprintf(w, format, args...) - io.WriteString(w, "\n") -} diff --git a/oracle/oracle_test.go b/oracle/oracle_test.go deleted file mode 100644 index 4aec489c..00000000 --- a/oracle/oracle_test.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package oracle_test - -// This file defines a test framework for oracle queries. -// -// The files beneath testdata/src/main contain Go programs containing -// query annotations of the form: -// -// @verb id "select" -// -// where verb is the query mode (e.g. "callers"), id is a unique name -// for this query, and "select" is a regular expression matching the -// substring of the current line that is the query's input selection. -// -// The expected output for each query is provided in the accompanying -// .golden file. -// -// (Location information is not included because it's too fragile to -// display as text. TODO(adonovan): think about how we can test its -// correctness, since it is critical information.) -// -// Run this test with: -// % go test golang.org/x/tools/oracle -update -// to update the golden files. - -import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "go/build" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "os/exec" - "regexp" - "runtime" - "strconv" - "strings" - "testing" - - "golang.org/x/tools/oracle" -) - -var updateFlag = flag.Bool("update", false, "Update the golden files.") - -type query struct { - id string // unique id - verb string // query mode, e.g. "callees" - posn token.Position // position of of query - filename string - queryPos string // value of -pos flag -} - -func parseRegexp(text string) (*regexp.Regexp, error) { - pattern, err := strconv.Unquote(text) - if err != nil { - return nil, fmt.Errorf("can't unquote %s", text) - } - return regexp.Compile(pattern) -} - -// parseQueries parses and returns the queries in the named file. -func parseQueries(t *testing.T, filename string) []*query { - filedata, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatal(err) - } - - // Parse the file once to discover the test queries. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, filename, filedata, parser.ParseComments) - if err != nil { - t.Fatal(err) - } - - lines := bytes.Split(filedata, []byte("\n")) - - var queries []*query - queriesById := make(map[string]*query) - - // Find all annotations of these forms: - expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp" - for _, c := range f.Comments { - text := strings.TrimSpace(c.Text()) - if text == "" || text[0] != '@' { - continue - } - posn := fset.Position(c.Pos()) - - // @verb id "regexp" - match := expectRe.FindStringSubmatch(text) - if match == nil { - t.Errorf("%s: ill-formed query: %s", posn, text) - continue - } - - id := match[2] - if prev, ok := queriesById[id]; ok { - t.Errorf("%s: duplicate id %s", posn, id) - t.Errorf("%s: previously used here", prev.posn) - continue - } - - q := &query{ - id: id, - verb: match[1], - filename: filename, - posn: posn, - } - - if match[3] != `"nopos"` { - selectRe, err := parseRegexp(match[3]) - if err != nil { - t.Errorf("%s: %s", posn, err) - continue - } - - // Find text of the current line, sans query. - // (Queries must be // not /**/ comments.) - line := lines[posn.Line-1][:posn.Column-1] - - // Apply regexp to current line to find input selection. - loc := selectRe.FindIndex(line) - if loc == nil { - t.Errorf("%s: selection pattern %s doesn't match line %q", - posn, match[3], string(line)) - continue - } - - // Assumes ASCII. TODO(adonovan): test on UTF-8. - linestart := posn.Offset - (posn.Column - 1) - - // Compute the file offsets. - q.queryPos = fmt.Sprintf("%s:#%d,#%d", - filename, linestart+loc[0], linestart+loc[1]) - } - - queries = append(queries, q) - queriesById[id] = q - } - - // Return the slice, not map, for deterministic iteration. - return queries -} - -// WriteResult writes res (-format=plain) to w, stripping file locations. -func WriteResult(w io.Writer, q *oracle.Query) { - capture := new(bytes.Buffer) // capture standard output - q.WriteTo(capture) - for _, line := range strings.Split(capture.String(), "\n") { - // Remove a "file:line: " prefix. - if i := strings.Index(line, ": "); i >= 0 { - line = line[i+2:] - } - fmt.Fprintf(w, "%s\n", line) - } -} - -// doQuery poses query q to the oracle and writes its response and -// error (if any) to out. -func doQuery(out io.Writer, q *query, useJson bool) { - fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id) - - var buildContext = build.Default - buildContext.GOPATH = "testdata" - query := oracle.Query{ - Mode: q.verb, - Pos: q.queryPos, - Build: &buildContext, - Scope: []string{q.filename}, - Reflection: true, - } - if err := oracle.Run(&query); err != nil { - fmt.Fprintf(out, "\nError: %s\n", err) - return - } - - if useJson { - // JSON output - b, err := json.MarshalIndent(query.Serial(), "", "\t") - if err != nil { - fmt.Fprintf(out, "JSON error: %s\n", err.Error()) - return - } - out.Write(b) - fmt.Fprintln(out) - } else { - // "plain" (compiler diagnostic format) output - WriteResult(out, &query) - } -} - -func TestOracle(t *testing.T) { - switch runtime.GOOS { - case "android": - t.Skipf("skipping test on %q (no testdata dir)", runtime.GOOS) - case "windows": - t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS) - } - - for _, filename := range []string{ - "testdata/src/calls/main.go", - "testdata/src/describe/main.go", - "testdata/src/freevars/main.go", - "testdata/src/implements/main.go", - "testdata/src/implements-methods/main.go", - "testdata/src/imports/main.go", - "testdata/src/peers/main.go", - "testdata/src/pointsto/main.go", - "testdata/src/referrers/main.go", - "testdata/src/reflection/main.go", - "testdata/src/what/main.go", - "testdata/src/whicherrs/main.go", - // JSON: - // TODO(adonovan): most of these are very similar; combine them. - "testdata/src/calls-json/main.go", - "testdata/src/peers-json/main.go", - "testdata/src/describe-json/main.go", - "testdata/src/implements-json/main.go", - "testdata/src/implements-methods-json/main.go", - "testdata/src/pointsto-json/main.go", - "testdata/src/referrers-json/main.go", - "testdata/src/what-json/main.go", - } { - if filename == "testdata/src/referrers/main.go" && runtime.GOOS == "plan9" { - // Disable this test on plan9 since it expects a particular - // wording for a "no such file or directory" error. - continue - } - - useJson := strings.Contains(filename, "-json/") - queries := parseQueries(t, filename) - golden := filename + "lden" - got := filename + "t" - gotfh, err := os.Create(got) - if err != nil { - t.Errorf("Create(%s) failed: %s", got, err) - continue - } - defer gotfh.Close() - defer os.Remove(got) - - // Run the oracle on each query, redirecting its output - // and error (if any) to the foo.got file. - for _, q := range queries { - doQuery(gotfh, q, useJson) - } - - // Compare foo.got with foo.golden. - var cmd *exec.Cmd - switch runtime.GOOS { - case "plan9": - cmd = exec.Command("/bin/diff", "-c", golden, got) - default: - cmd = exec.Command("/usr/bin/diff", "-u", golden, got) - } - buf := new(bytes.Buffer) - cmd.Stdout = buf - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - t.Errorf("Oracle tests for %s failed: %s.\n%s\n", - filename, err, buf) - - if *updateFlag { - t.Logf("Updating %s...", golden) - if err := exec.Command("/bin/cp", got, golden).Run(); err != nil { - t.Errorf("Update failed: %s", err) - } - } - } - } -} diff --git a/oracle/peers.go b/oracle/peers.go deleted file mode 100644 index 0564516d..00000000 --- a/oracle/peers.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "sort" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -// peers enumerates, for a given channel send (or receive) operation, -// the set of possible receives (or sends) that correspond to it. -// -// TODO(adonovan): support reflect.{Select,Recv,Send,Close}. -// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv), -// or the implicit receive in "for v := range ch". -func peers(q *Query) error { - lconf := loader.Config{Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - opPos := findOp(qpos) - if opPos == token.NoPos { - return fmt.Errorf("there is no channel operation here") - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - var queryOp chanOp // the originating send or receive operation - var ops []chanOp // all sends/receives of opposite direction - - // Look at all channel operations in the whole ssa.Program. - // Build a list of those of same type as the query. - allFuncs := ssautil.AllFunctions(prog) - for fn := range allFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - for _, op := range chanOps(instr) { - ops = append(ops, op) - if op.pos == opPos { - queryOp = op // we found the query op - } - } - } - } - } - if queryOp.ch == nil { - return fmt.Errorf("ssa.Instruction for send/receive not found") - } - - // Discard operations of wrong channel element type. - // Build set of channel ssa.Values as query to pointer analysis. - // We compare channels by element types, not channel types, to - // ignore both directionality and type names. - queryType := queryOp.ch.Type() - queryElemType := queryType.Underlying().(*types.Chan).Elem() - ptaConfig.AddQuery(queryOp.ch) - i := 0 - for _, op := range ops { - if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) { - ptaConfig.AddQuery(op.ch) - ops[i] = op - i++ - } - } - ops = ops[:i] - - // Run the pointer analysis. - ptares := ptrAnalysis(ptaConfig) - - // Find the points-to set. - queryChanPtr := ptares.Queries[queryOp.ch] - - // Ascertain which make(chan) labels the query's channel can alias. - var makes []token.Pos - for _, label := range queryChanPtr.PointsTo().Labels() { - makes = append(makes, label.Pos()) - } - sort.Sort(byPos(makes)) - - // Ascertain which channel operations can alias the same make(chan) labels. - var sends, receives, closes []token.Pos - for _, op := range ops { - if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) { - switch op.dir { - case types.SendOnly: - sends = append(sends, op.pos) - case types.RecvOnly: - receives = append(receives, op.pos) - case types.SendRecv: - closes = append(closes, op.pos) - } - } - } - sort.Sort(byPos(sends)) - sort.Sort(byPos(receives)) - sort.Sort(byPos(closes)) - - q.result = &peersResult{ - queryPos: opPos, - queryType: queryType, - makes: makes, - sends: sends, - receives: receives, - closes: closes, - } - return nil -} - -// findOp returns the position of the enclosing send/receive/close op. -// For send and receive operations, this is the position of the <- token; -// for close operations, it's the Lparen of the function call. -// -// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements. -func findOp(qpos *queryPos) token.Pos { - for _, n := range qpos.path { - switch n := n.(type) { - case *ast.UnaryExpr: - if n.Op == token.ARROW { - return n.OpPos - } - case *ast.SendStmt: - return n.Arrow - case *ast.CallExpr: - // close function call can only exist as a direct identifier - if close, ok := unparen(n.Fun).(*ast.Ident); ok { - if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" { - return n.Lparen - } - } - } - } - return token.NoPos -} - -// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState. -type chanOp struct { - ch ssa.Value - dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close - pos token.Pos -} - -// chanOps returns a slice of all the channel operations in the instruction. -func chanOps(instr ssa.Instruction) []chanOp { - // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too. - var ops []chanOp - switch instr := instr.(type) { - case *ssa.UnOp: - if instr.Op == token.ARROW { - ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()}) - } - case *ssa.Send: - ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()}) - case *ssa.Select: - for _, st := range instr.States { - ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos}) - } - case ssa.CallInstruction: - cc := instr.Common() - if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" { - ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()}) - } - } - return ops -} - -type peersResult struct { - queryPos token.Pos // of queried channel op - queryType types.Type // type of queried channel - makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs -} - -func (r *peersResult) display(printf printfFunc) { - if len(r.makes) == 0 { - printf(r.queryPos, "This channel can't point to anything.") - return - } - printf(r.queryPos, "This channel of type %s may be:", r.queryType) - for _, alloc := range r.makes { - printf(alloc, "\tallocated here") - } - for _, send := range r.sends { - printf(send, "\tsent to, here") - } - for _, receive := range r.receives { - printf(receive, "\treceived from, here") - } - for _, clos := range r.closes { - printf(clos, "\tclosed, here") - } -} - -func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) { - peers := &serial.Peers{ - Pos: fset.Position(r.queryPos).String(), - Type: r.queryType.String(), - } - for _, alloc := range r.makes { - peers.Allocs = append(peers.Allocs, fset.Position(alloc).String()) - } - for _, send := range r.sends { - peers.Sends = append(peers.Sends, fset.Position(send).String()) - } - for _, receive := range r.receives { - peers.Receives = append(peers.Receives, fset.Position(receive).String()) - } - for _, clos := range r.closes { - peers.Closes = append(peers.Closes, fset.Position(clos).String()) - } - res.Peers = peers -} - -// -------- utils -------- - -// NB: byPos is not deterministic across packages since it depends on load order. -// Use lessPos if the tests need it. -type byPos []token.Pos - -func (p byPos) Len() int { return len(p) } -func (p byPos) Less(i, j int) bool { return p[i] < p[j] } -func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/oracle/pointsto.go b/oracle/pointsto.go deleted file mode 100644 index 9b862f5e..00000000 --- a/oracle/pointsto.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "sort" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/pointer" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -// pointsto runs the pointer analysis on the selected expression, -// and reports its points-to set (for a pointer-like expression) -// or its dynamic types (for an interface, reflect.Value, or -// reflect.Type expression) and their points-to sets. -// -// All printed sets are sorted to ensure determinism. -// -func pointsto(q *Query) error { - lconf := loader.Config{Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos - if err != nil { - return err - } - - prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - path, action := findInterestingNode(qpos.info, qpos.path) - if action != actionExpr { - return fmt.Errorf("pointer analysis wants an expression; got %s", - astutil.NodeDescription(qpos.path[0])) - } - - var expr ast.Expr - var obj types.Object - switch n := path[0].(type) { - case *ast.ValueSpec: - // ambiguous ValueSpec containing multiple names - return fmt.Errorf("multiple value specification") - case *ast.Ident: - obj = qpos.info.ObjectOf(n) - expr = n - case ast.Expr: - expr = n - default: - // TODO(adonovan): is this reachable? - return fmt.Errorf("unexpected AST for expr: %T", n) - } - - // Reject non-pointerlike types (includes all constants---except nil). - // TODO(adonovan): reject nil too. - typ := qpos.info.TypeOf(expr) - if !pointer.CanPoint(typ) { - return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ) - } - - // Determine the ssa.Value for the expression. - var value ssa.Value - var isAddr bool - if obj != nil { - // def/ref of func/var object - value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path) - } else { - value, isAddr, err = ssaValueForExpr(prog, qpos.info, path) - } - if err != nil { - return err // e.g. trivially dead code - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - // Run the pointer analysis. - ptrs, err := runPTA(ptaConfig, value, isAddr) - if err != nil { - return err // e.g. analytically unreachable - } - - q.result = &pointstoResult{ - qpos: qpos, - typ: typ, - ptrs: ptrs, - } - return nil -} - -// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path -// to the root of the AST is path. isAddr reports whether the -// ssa.Value is the address denoted by the ast.Ident, not its value. -// -func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { - switch obj := obj.(type) { - case *types.Var: - pkg := prog.Package(qinfo.Pkg) - pkg.Build() - if v, addr := prog.VarValue(obj, pkg, path); v != nil { - return v, addr, nil - } - return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) - - case *types.Func: - fn := prog.FuncValue(obj) - if fn == nil { - return nil, false, fmt.Errorf("%s is an interface method", obj) - } - // TODO(adonovan): there's no point running PTA on a *Func ident. - // Eliminate this feature. - return fn, false, nil - } - panic(obj) -} - -// ssaValueForExpr returns the ssa.Value of the non-ast.Ident -// expression whose path to the root of the AST is path. -// -func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) { - pkg := prog.Package(qinfo.Pkg) - pkg.SetDebugMode(true) - pkg.Build() - - fn := ssa.EnclosingFunction(pkg, path) - if fn == nil { - return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)") - } - - if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil { - return v, addr, nil - } - - return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn) -} - -// runPTA runs the pointer analysis of the selected SSA value or address. -func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) { - T := v.Type() - if isAddr { - conf.AddIndirectQuery(v) - T = deref(T) - } else { - conf.AddQuery(v) - } - ptares := ptrAnalysis(conf) - - var ptr pointer.Pointer - if isAddr { - ptr = ptares.IndirectQueries[v] - } else { - ptr = ptares.Queries[v] - } - if ptr == (pointer.Pointer{}) { - return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)") - } - pts := ptr.PointsTo() - - if pointer.CanHaveDynamicTypes(T) { - // Show concrete types for interface/reflect.Value expression. - if concs := pts.DynamicTypes(); concs.Len() > 0 { - concs.Iterate(func(conc types.Type, pta interface{}) { - labels := pta.(pointer.PointsToSet).Labels() - sort.Sort(byPosAndString(labels)) // to ensure determinism - ptrs = append(ptrs, pointerResult{conc, labels}) - }) - } - } else { - // Show labels for other expressions. - labels := pts.Labels() - sort.Sort(byPosAndString(labels)) // to ensure determinism - ptrs = append(ptrs, pointerResult{T, labels}) - } - sort.Sort(byTypeString(ptrs)) // to ensure determinism - return ptrs, nil -} - -type pointerResult struct { - typ types.Type // type of the pointer (always concrete) - labels []*pointer.Label // set of labels -} - -type pointstoResult struct { - qpos *queryPos - typ types.Type // type of expression - ptrs []pointerResult // pointer info (typ is concrete => len==1) -} - -func (r *pointstoResult) display(printf printfFunc) { - if pointer.CanHaveDynamicTypes(r.typ) { - // Show concrete types for interface, reflect.Type or - // reflect.Value expression. - - if len(r.ptrs) > 0 { - printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ)) - for _, ptr := range r.ptrs { - var obj types.Object - if nt, ok := deref(ptr.typ).(*types.Named); ok { - obj = nt.Obj() - } - if len(ptr.labels) > 0 { - printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ)) - printLabels(printf, ptr.labels, "\t\t") - } else { - printf(obj, "\t%s", r.qpos.typeString(ptr.typ)) - } - } - } else { - printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ) - } - } else { - // Show labels for other expressions. - if ptr := r.ptrs[0]; len(ptr.labels) > 0 { - printf(r.qpos, "this %s may point to these objects:", - r.qpos.typeString(r.typ)) - printLabels(printf, ptr.labels, "\t") - } else { - printf(r.qpos, "this %s may not point to anything.", - r.qpos.typeString(r.typ)) - } - } -} - -func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) { - var pts []serial.PointsTo - for _, ptr := range r.ptrs { - var namePos string - if nt, ok := deref(ptr.typ).(*types.Named); ok { - namePos = fset.Position(nt.Obj().Pos()).String() - } - var labels []serial.PointsToLabel - for _, l := range ptr.labels { - labels = append(labels, serial.PointsToLabel{ - Pos: fset.Position(l.Pos()).String(), - Desc: l.String(), - }) - } - pts = append(pts, serial.PointsTo{ - Type: r.qpos.typeString(ptr.typ), - NamePos: namePos, - Labels: labels, - }) - } - res.PointsTo = pts -} - -type byTypeString []pointerResult - -func (a byTypeString) Len() int { return len(a) } -func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() } -func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type byPosAndString []*pointer.Label - -func (a byPosAndString) Len() int { return len(a) } -func (a byPosAndString) Less(i, j int) bool { - cmp := a[i].Pos() - a[j].Pos() - return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String()) -} -func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) { - // TODO(adonovan): due to context-sensitivity, many of these - // labels may differ only by context, which isn't apparent. - for _, label := range labels { - printf(label, "%s%s", prefix, label) - } -} diff --git a/oracle/pos.go b/oracle/pos.go deleted file mode 100644 index 3c706f35..00000000 --- a/oracle/pos.go +++ /dev/null @@ -1,143 +0,0 @@ -package oracle - -// This file defines utilities for working with file positions. - -import ( - "fmt" - "go/parser" - "go/token" - "os" - "path/filepath" - "strconv" - "strings" - - "golang.org/x/tools/go/ast/astutil" -) - -// parseOctothorpDecimal returns the numeric value if s matches "#%d", -// otherwise -1. -func parseOctothorpDecimal(s string) int { - if s != "" && s[0] == '#' { - if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil { - return int(s) - } - } - return -1 -} - -// parsePosFlag parses a string of the form "file:pos" or -// file:start,end" where pos, start, end match #%d and represent byte -// offsets, and returns its components. -// -// (Numbers without a '#' prefix are reserved for future use, -// e.g. to indicate line/column positions.) -// -func parsePosFlag(posFlag string) (filename string, startOffset, endOffset int, err error) { - if posFlag == "" { - err = fmt.Errorf("no source position specified (-pos flag)") - return - } - - colon := strings.LastIndex(posFlag, ":") - if colon < 0 { - err = fmt.Errorf("invalid source position -pos=%q", posFlag) - return - } - filename, offset := posFlag[:colon], posFlag[colon+1:] - startOffset = -1 - endOffset = -1 - if hyphen := strings.Index(offset, ","); hyphen < 0 { - // e.g. "foo.go:#123" - startOffset = parseOctothorpDecimal(offset) - endOffset = startOffset - } else { - // e.g. "foo.go:#123,#456" - startOffset = parseOctothorpDecimal(offset[:hyphen]) - endOffset = parseOctothorpDecimal(offset[hyphen+1:]) - } - if startOffset < 0 || endOffset < 0 { - err = fmt.Errorf("invalid -pos offset %q", offset) - return - } - return -} - -// findQueryPos searches fset for filename and translates the -// specified file-relative byte offsets into token.Pos form. It -// returns an error if the file was not found or the offsets were out -// of bounds. -// -func findQueryPos(fset *token.FileSet, filename string, startOffset, endOffset int) (start, end token.Pos, err error) { - var file *token.File - fset.Iterate(func(f *token.File) bool { - if sameFile(filename, f.Name()) { - // (f.Name() is absolute) - file = f - return false // done - } - return true // continue - }) - if file == nil { - err = fmt.Errorf("couldn't find file containing position") - return - } - - // Range check [start..end], inclusive of both end-points. - - if 0 <= startOffset && startOffset <= file.Size() { - start = file.Pos(int(startOffset)) - } else { - err = fmt.Errorf("start position is beyond end of file") - return - } - - if 0 <= endOffset && endOffset <= file.Size() { - end = file.Pos(int(endOffset)) - } else { - err = fmt.Errorf("end position is beyond end of file") - return - } - - return -} - -// sameFile returns true if x and y have the same basename and denote -// the same file. -// -func sameFile(x, y string) bool { - if filepath.Base(x) == filepath.Base(y) { // (optimisation) - if xi, err := os.Stat(x); err == nil { - if yi, err := os.Stat(y); err == nil { - return os.SameFile(xi, yi) - } - } - } - return false -} - -// fastQueryPos parses the -pos flag and returns a QueryPos. -// It parses only a single file, and does not run the type checker. -func fastQueryPos(posFlag string) (*queryPos, error) { - filename, startOffset, endOffset, err := parsePosFlag(posFlag) - if err != nil { - return nil, err - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, filename, nil, 0) - if err != nil { - return nil, err - } - - start, end, err := findQueryPos(fset, filename, startOffset, endOffset) - if err != nil { - return nil, err - } - - path, exact := astutil.PathEnclosingInterval(f, start, end) - if path == nil { - return nil, fmt.Errorf("no syntax here") - } - - return &queryPos{fset, start, end, path, exact, nil}, nil -} diff --git a/oracle/referrers.go b/oracle/referrers.go deleted file mode 100644 index 35454065..00000000 --- a/oracle/referrers.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - "go/types" - "io/ioutil" - "sort" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/oracle/serial" - "golang.org/x/tools/refactor/importgraph" -) - -// Referrers reports all identifiers that resolve to the same object -// as the queried identifier, within any package in the analysis scope. -func referrers(q *Query) error { - lconf := loader.Config{Build: q.Build} - allowErrors(&lconf) - - if _, err := importQueryPackage(q.Pos, &lconf); err != nil { - return err - } - - var id *ast.Ident - var obj types.Object - var lprog *loader.Program - var pass2 bool - var qpos *queryPos - for { - // Load/parse/type-check the program. - var err error - lprog, err = lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err = parseQueryPos(lprog, q.Pos, false) - if err != nil { - return err - } - - id, _ = qpos.path[0].(*ast.Ident) - if id == nil { - return fmt.Errorf("no identifier here") - } - - obj = qpos.info.ObjectOf(id) - if obj == nil { - // Happens for y in "switch y := x.(type)", - // the package declaration, - // and unresolved identifiers. - if _, ok := qpos.path[1].(*ast.File); ok { // package decl? - pkg := qpos.info.Pkg - obj = types.NewPkgName(id.Pos(), pkg, pkg.Name(), pkg) - } else { - return fmt.Errorf("no object for identifier: %T", qpos.path[1]) - } - } - - if pass2 { - break - } - - // If the identifier is exported, we must load all packages that - // depend transitively upon the package that defines it. - // Treat PkgNames as exported, even though they're lowercase. - if _, isPkg := obj.(*types.PkgName); !(isPkg || obj.Exported()) { - break // not exported - } - - // Scan the workspace and build the import graph. - // Ignore broken packages. - _, rev, _ := importgraph.Build(q.Build) - - // Re-load the larger program. - // Create a new file set so that ... - // External test packages are never imported, - // so they will never appear in the graph. - // (We must reset the Config here, not just reset the Fset field.) - lconf = loader.Config{ - Fset: token.NewFileSet(), - Build: q.Build, - } - allowErrors(&lconf) - for path := range rev.Search(obj.Pkg().Path()) { - lconf.ImportWithTests(path) - } - pass2 = true - } - - // Iterate over all go/types' Uses facts for the entire program. - var refs []*ast.Ident - for _, info := range lprog.AllPackages { - for id2, obj2 := range info.Uses { - if sameObj(obj, obj2) { - refs = append(refs, id2) - } - } - } - sort.Sort(byNamePos{q.Fset, refs}) - - q.result = &referrersResult{ - qpos: qpos, - query: id, - obj: obj, - refs: refs, - } - return nil -} - -// same reports whether x and y are identical, or both are PkgNames -// that import the same Package. -// -func sameObj(x, y types.Object) bool { - if x == y { - return true - } - if x, ok := x.(*types.PkgName); ok { - if y, ok := y.(*types.PkgName); ok { - return x.Imported() == y.Imported() - } - } - return false -} - -// -------- utils -------- - -// An deterministic ordering for token.Pos that doesn't -// depend on the order in which packages were loaded. -func lessPos(fset *token.FileSet, x, y token.Pos) bool { - fx := fset.File(x) - fy := fset.File(y) - if fx != fy { - return fx.Name() < fy.Name() - } - return x < y -} - -type byNamePos struct { - fset *token.FileSet - ids []*ast.Ident -} - -func (p byNamePos) Len() int { return len(p.ids) } -func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] } -func (p byNamePos) Less(i, j int) bool { - return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos) -} - -type referrersResult struct { - qpos *queryPos - query *ast.Ident // identifier of query - obj types.Object // object it denotes - refs []*ast.Ident // set of all other references to it -} - -func (r *referrersResult) display(printf printfFunc) { - printf(r.obj, "%d references to %s", len(r.refs), r.qpos.objectString(r.obj)) - - // Show referring lines, like grep. - type fileinfo struct { - refs []*ast.Ident - linenums []int // line number of refs[i] - data chan interface{} // file contents or error - } - var fileinfos []*fileinfo - fileinfosByName := make(map[string]*fileinfo) - - // First pass: start the file reads concurrently. - sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency - for _, ref := range r.refs { - posn := r.qpos.fset.Position(ref.Pos()) - fi := fileinfosByName[posn.Filename] - if fi == nil { - fi = &fileinfo{data: make(chan interface{})} - fileinfosByName[posn.Filename] = fi - fileinfos = append(fileinfos, fi) - - // First request for this file: - // start asynchronous read. - go func() { - sema <- struct{}{} // acquire token - content, err := ioutil.ReadFile(posn.Filename) - <-sema // release token - if err != nil { - fi.data <- err - } else { - fi.data <- content - } - }() - } - fi.refs = append(fi.refs, ref) - fi.linenums = append(fi.linenums, posn.Line) - } - - // Second pass: print refs in original order. - // One line may have several refs at different columns. - for _, fi := range fileinfos { - v := <-fi.data // wait for I/O completion - - // Print one item for all refs in a file that could not - // be loaded (perhaps due to //line directives). - if err, ok := v.(error); ok { - var suffix string - if more := len(fi.refs) - 1; more > 0 { - suffix = fmt.Sprintf(" (+ %d more refs in this file)", more) - } - printf(fi.refs[0], "%v%s", err, suffix) - continue - } - - lines := bytes.Split(v.([]byte), []byte("\n")) - for i, ref := range fi.refs { - printf(ref, "%s", lines[fi.linenums[i]-1]) - } - } -} - -// TODO(adonovan): encode extent, not just Pos info, in Serial form. - -func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) { - referrers := &serial.Referrers{ - Pos: fset.Position(r.query.Pos()).String(), - Desc: r.obj.String(), - } - if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos() - referrers.ObjPos = fset.Position(pos).String() - } - for _, ref := range r.refs { - referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String()) - } - res.Referrers = referrers -} diff --git a/oracle/serial/serial.go b/oracle/serial/serial.go deleted file mode 100644 index 65f0822a..00000000 --- a/oracle/serial/serial.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package serial defines the oracle's schema for structured data -// serialization using JSON, XML, etc. -package serial - -// All 'pos' strings are of the form "file:line:col". -// TODO(adonovan): improve performance by sharing filename strings. -// TODO(adonovan): improve precision by providing the start/end -// interval when available. -// -// TODO(adonovan): consider richer encodings of types, functions, -// methods, etc. - -// A Peers is the result of a 'peers' query. -// If Allocs is empty, the selected channel can't point to anything. -type Peers struct { - Pos string `json:"pos"` // location of the selected channel op (<-) - Type string `json:"type"` // type of the selected channel - Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops - Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops - Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops - Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops -} - -// A Referrers is the result of a 'referrers' query. -type Referrers struct { - Pos string `json:"pos"` // location of the query reference - ObjPos string `json:"objpos,omitempty"` // location of the definition - Desc string `json:"desc"` // description of the denoted object - Refs []string `json:"refs,omitempty"` // locations of all references -} - -// A Definition is the result of a 'definition' query. -type Definition struct { - ObjPos string `json:"objpos,omitempty"` // location of the definition - Desc string `json:"desc"` // description of the denoted object -} - -type CalleesItem struct { - Name string `json:"name"` // full name of called function - Pos string `json:"pos"` // location of called function -} - -// A Callees is the result of a 'callees' query. -// -// Callees is nonempty unless the call was a dynamic call on a -// provably nil func or interface value. -type Callees struct { - Pos string `json:"pos"` // location of selected call site - Desc string `json:"desc"` // description of call site - Callees []*CalleesItem `json:"callees,omitempty"` // set of possible call targets -} - -// A Caller is one element of the slice returned by a 'callers' query. -// (Callstack also contains a similar slice.) -// -// The root of the callgraph has an unspecified "Caller" string. -type Caller struct { - Pos string `json:"pos,omitempty"` // location of the calling function - Desc string `json:"desc"` // description of call site - Caller string `json:"caller"` // full name of calling function -} - -// A CallStack is the result of a 'callstack' query. -// It indicates an arbitrary path from the root of the callgraph to -// the query function. -// -// If the Callers slice is empty, the function was unreachable in this -// analysis scope. -type CallStack struct { - Pos string `json:"pos"` // location of the selected function - Target string `json:"target"` // the selected function - Callers []Caller `json:"callers"` // enclosing calls, innermost first. -} - -// A FreeVar is one element of the slice returned by a 'freevars' -// query. Each one identifies an expression referencing a local -// identifier defined outside the selected region. -type FreeVar struct { - Pos string `json:"pos"` // location of the identifier's definition - Kind string `json:"kind"` // one of {var,func,type,const,label} - Ref string `json:"ref"` // referring expression (e.g. "x" or "x.y.z") - Type string `json:"type"` // type of the expression -} - -// An Implements contains the result of an 'implements' query. -// It describes the queried type, the set of named non-empty interface -// types to which it is assignable, and the set of named/*named types -// (concrete or non-empty interface) which may be assigned to it. -// -type Implements struct { - T ImplementsType `json:"type,omitempty"` // the queried type - AssignableTo []ImplementsType `json:"to,omitempty"` // types assignable to T - AssignableFrom []ImplementsType `json:"from,omitempty"` // interface types assignable from T - AssignableFromPtr []ImplementsType `json:"fromptr,omitempty"` // interface types assignable only from *T - - // The following fields are set only if the query was a method. - // Assignable{To,From,FromPtr}Method[i] is the corresponding - // method of type Assignable{To,From,FromPtr}[i], or blank - // {"",""} if that type lacks the method. - Method *DescribeMethod `json:"method,omitempty"` // the queried method - AssignableToMethod []DescribeMethod `json:"to_method,omitempty"` - AssignableFromMethod []DescribeMethod `json:"from_method,omitempty"` - AssignableFromPtrMethod []DescribeMethod `json:"fromptr_method,omitempty"` -} - -// An ImplementsType describes a single type as part of an 'implements' query. -type ImplementsType struct { - Name string `json:"name"` // full name of the type - Pos string `json:"pos"` // location of its definition - Kind string `json:"kind"` // "basic", "array", etc -} - -// A SyntaxNode is one element of a stack of enclosing syntax nodes in -// a "what" query. -type SyntaxNode struct { - Description string `json:"desc"` // description of syntax tree - Start int `json:"start"` // start byte offset, 0-based - End int `json:"end"` // end byte offset -} - -// A What is the result of the "what" query, which quickly identifies -// the selection, parsing only a single file. It is intended for use -// in low-latency GUIs. -type What struct { - Enclosing []SyntaxNode `json:"enclosing"` // enclosing nodes of syntax tree - Modes []string `json:"modes"` // query modes enabled for this selection. - SrcDir string `json:"srcdir,omitempty"` // $GOROOT src directory containing queried package - ImportPath string `json:"importpath,omitempty"` // import path of queried package -} - -// A PointsToLabel describes a pointer analysis label. -// -// A "label" is an object that may be pointed to by a pointer, map, -// channel, 'func', slice or interface. Labels include: -// - functions -// - globals -// - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s)) -// - stack- and heap-allocated variables (including composite literals) -// - arrays allocated by append() -// - channels, maps and arrays created by make() -// - and their subelements, e.g. "alloc.y[*].z" -// -type PointsToLabel struct { - Pos string `json:"pos"` // location of syntax that allocated the object - Desc string `json:"desc"` // description of the label -} - -// A PointsTo is one element of the result of a 'pointsto' query on an -// expression. It describes a single pointer: its type and the set of -// "labels" it points to. -// -// If the pointer is of interface type, it will have one PTS entry -// describing each concrete type that it may contain. For each -// concrete type that is a pointer, the PTS entry describes the labels -// it may point to. The same is true for reflect.Values, except the -// dynamic types needn't be concrete. -// -type PointsTo struct { - Type string `json:"type"` // (concrete) type of the pointer - NamePos string `json:"namepos,omitempty"` // location of type defn, if Named - Labels []PointsToLabel `json:"labels,omitempty"` // pointed-to objects -} - -// A DescribeValue is the additional result of a 'describe' query -// if the selection indicates a value or expression. -type DescribeValue struct { - Type string `json:"type"` // type of the expression - Value string `json:"value,omitempty"` // value of the expression, if constant - ObjPos string `json:"objpos,omitempty"` // location of the definition, if an Ident -} - -type DescribeMethod struct { - Name string `json:"name"` // method name, as defined by types.Selection.String() - Pos string `json:"pos"` // location of the method's definition -} - -// A DescribeType is the additional result of a 'describe' query -// if the selection indicates a type. -type DescribeType struct { - Type string `json:"type"` // the string form of the type - NamePos string `json:"namepos,omitempty"` // location of definition of type, if named - NameDef string `json:"namedef,omitempty"` // underlying definition of type, if named - Methods []DescribeMethod `json:"methods,omitempty"` // methods of the type -} - -type DescribeMember struct { - Name string `json:"name"` // name of member - Type string `json:"type,omitempty"` // type of member (underlying, if 'type') - Value string `json:"value,omitempty"` // value of member (if 'const') - Pos string `json:"pos"` // location of definition of member - Kind string `json:"kind"` // one of {var,const,func,type} - Methods []DescribeMethod `json:"methods,omitempty"` // methods (if member is a type) -} - -// A DescribePackage is the additional result of a 'describe' if -// the selection indicates a package. -type DescribePackage struct { - Path string `json:"path"` // import path of the package - Members []*DescribeMember `json:"members,omitempty"` // accessible members of the package -} - -// A Describe is the result of a 'describe' query. -// It may contain an element describing the selected semantic entity -// in detail. -type Describe struct { - Desc string `json:"desc"` // description of the selected syntax node - Pos string `json:"pos"` // location of the selected syntax node - Detail string `json:"detail,omitempty"` // one of {package, type, value}, or "". - - // At most one of the following fields is populated: - // the one specified by 'detail'. - Package *DescribePackage `json:"package,omitempty"` - Type *DescribeType `json:"type,omitempty"` - Value *DescribeValue `json:"value,omitempty"` -} - -// A WhichErrs is the result of a 'whicherrs' query. -// It contains the position of the queried error and the possible globals, -// constants, and types it may point to. -type WhichErrs struct { - ErrPos string `json:"errpos,omitempty"` // location of queried error - Globals []string `json:"globals,omitempty"` // locations of globals - Constants []string `json:"constants,omitempty"` // locations of constants - Types []WhichErrsType `json:"types,omitempty"` // Types -} - -type WhichErrsType struct { - Type string `json:"type,omitempty"` - Position string `json:"position,omitempty"` -} - -// A Result is the common result of any oracle query. -// It contains a query-specific result element. -// -// TODO(adonovan): perhaps include other info such as: analysis scope, -// raw query position, stack of ast nodes, query package, etc. -type Result struct { - Mode string `json:"mode"` // mode of the query - - // Exactly one of the following fields is populated: - // the one specified by 'mode'. - Callees *Callees `json:"callees,omitempty"` - Callers []Caller `json:"callers,omitempty"` - Callstack *CallStack `json:"callstack,omitempty"` - Definition *Definition `json:"definition,omitempty"` - Describe *Describe `json:"describe,omitempty"` - Freevars []*FreeVar `json:"freevars,omitempty"` - Implements *Implements `json:"implements,omitempty"` - Peers *Peers `json:"peers,omitempty"` - PointsTo []PointsTo `json:"pointsto,omitempty"` - Referrers *Referrers `json:"referrers,omitempty"` - What *What `json:"what,omitempty"` - WhichErrs *WhichErrs `json:"whicherrs,omitempty"` -} diff --git a/oracle/testdata/src/calls-json/main.go b/oracle/testdata/src/calls-json/main.go deleted file mode 100644 index 1c7a6c99..00000000 --- a/oracle/testdata/src/calls-json/main.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -// Tests of call-graph queries, -format=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See calls-json.golden for expected query results. - -func call(f func()) { - f() // @callees @callees-f "f" -} - -func main() { - call(func() { - // @callers callers-main.anon "^" - // @callstack callstack-main.anon "^" - }) -} diff --git a/oracle/testdata/src/calls-json/main.golden b/oracle/testdata/src/calls-json/main.golden deleted file mode 100644 index f5eced6b..00000000 --- a/oracle/testdata/src/calls-json/main.golden +++ /dev/null @@ -1,34 +0,0 @@ --------- @callees @callees-f -------- -{ - "mode": "callees", - "callees": { - "pos": "testdata/src/calls-json/main.go:8:3", - "desc": "dynamic function call", - "callees": [ - { - "name": "main.main$1", - "pos": "testdata/src/calls-json/main.go:12:7" - } - ] - } -} --------- @callstack callstack-main.anon -------- -{ - "mode": "callstack", - "callstack": { - "pos": "testdata/src/calls-json/main.go:12:7", - "target": "main.main$1", - "callers": [ - { - "pos": "testdata/src/calls-json/main.go:8:3", - "desc": "dynamic function call", - "caller": "main.call" - }, - { - "pos": "testdata/src/calls-json/main.go:12:6", - "desc": "static function call", - "caller": "main.main" - } - ] - } -} diff --git a/oracle/testdata/src/calls/main.go b/oracle/testdata/src/calls/main.go deleted file mode 100644 index 849fb6ed..00000000 --- a/oracle/testdata/src/calls/main.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "fmt" -) - -// Tests of call-graph queries. -// See go.tools/oracle/oracle_test.go for explanation. -// See calls.golden for expected query results. - -func A(x *int) { // @pointsto pointsto-A-x "x" - // @callers callers-A "^" - // @callstack callstack-A "^" -} - -func B(x *int) { // @pointsto pointsto-B-x "x" - // @callers callers-B "^" -} - -func foo() { -} - -// apply is not (yet) treated context-sensitively. -func apply(f func(x *int), x *int) { - f(x) // @callees callees-apply "f" - // @callers callers-apply "^" -} - -// store *is* treated context-sensitively, -// so the points-to sets for pc, pd are precise. -func store(ptr **int, value *int) { - *ptr = value - // @callers callers-store "^" -} - -func call(f func() *int) { - // Result points to anon function. - f() // @pointsto pointsto-result-f "f" - - // Target of call is anon function. - f() // @callees callees-main.call-f "f" - - // @callers callers-main.call "^" -} - -func main() { - var a, b int - go apply(A, &a) // @callees callees-main-apply1 "app" - defer apply(B, &b) - - var c, d int - var pc, pd *int // @pointsto pointsto-pc "pc" - store(&pc, &c) - store(&pd, &d) - _ = pd // @pointsto pointsto-pd "pd" - - call(func() *int { - // We are called twice from main.call - // @callers callers-main.anon "^" - return &a - }) - - // Errors - _ = "no function call here" // @callees callees-err-no-call "no" - print("builtin") // @callees callees-err-builtin "builtin" - _ = string("type conversion") // @callees callees-err-conversion "str" - call(nil) // @callees callees-err-bad-selection "call\\(nil" - if false { - main() // @callees callees-err-deadcode1 "main" - } - var nilFunc func() - nilFunc() // @callees callees-err-nil-func "nilFunc" - var i interface { - f() - } - i.f() // @callees callees-err-nil-interface "i.f" - - i = new(myint) - i.f() // @callees callees-not-a-wrapper "f" - - // statically dispatched calls. Handled specially by callees, so test that they work. - foo() // @callees callees-static-call "foo" - fmt.Println() // @callees callees-qualified-call "Println" - m := new(method) - m.f() // @callees callees-static-method-call "f" - g := new(embeddedIface) - g.iface = m - g.f() // @callees callees-implicit-selection-method-call "f" -} - -type myint int - -func (myint) f() { - // @callers callers-not-a-wrapper "^" -} - -type method int - -func (method) f() { -} - -type embeddedIface struct { - iface -} - -type iface interface { - f() -} - -var dynamic = func() {} - -func deadcode() { - main() // @callees callees-err-deadcode2 "main" - // @callers callers-err-deadcode "^" - // @callstack callstack-err-deadcode "^" - - // Within dead code, dynamic calls have no callees. - dynamic() // @callees callees-err-deadcode3 "dynamic" -} - -// This code belongs to init. -var global = 123 // @callers callers-global "global" - -// The package initializer may be called by other packages' inits, or -// in this case, the root of the callgraph. The source-level init functions -// are in turn called by it. -func init() { - // @callstack callstack-init "^" -} diff --git a/oracle/testdata/src/calls/main.golden b/oracle/testdata/src/calls/main.golden deleted file mode 100644 index 9159cd60..00000000 --- a/oracle/testdata/src/calls/main.golden +++ /dev/null @@ -1,125 +0,0 @@ --------- @pointsto pointsto-A-x -------- -this *int may point to these objects: - a - b - --------- @callstack callstack-A -------- -Found a call path from root to main.A -main.A -dynamic function call from main.apply -concurrent static function call from main.main - --------- @pointsto pointsto-B-x -------- -this *int may point to these objects: - a - b - --------- @callers callers-B -------- -main.B is called from these 1 sites: - dynamic function call from main.apply - --------- @callees callees-apply -------- -this dynamic function call dispatches to: - main.A - main.B - --------- @callers callers-apply -------- -main.apply is called from these 2 sites: - concurrent static function call from main.main - deferred static function call from main.main - --------- @callers callers-store -------- -main.store is called from these 2 sites: - static function call from main.main - static function call from main.main - --------- @pointsto pointsto-result-f -------- -this func() *int may point to these objects: - main.main$1 - --------- @callees callees-main.call-f -------- -this dynamic function call dispatches to: - main.main$1 - --------- @callers callers-main.call -------- -main.call is called from these 2 sites: - static function call from main.main - static function call from main.main - --------- @callees callees-main-apply1 -------- -this static function call dispatches to: - main.apply - --------- @pointsto pointsto-pc -------- -this *int may point to these objects: - c - --------- @pointsto pointsto-pd -------- -this *int may point to these objects: - d - --------- @callees callees-err-no-call -------- - -Error: there is no function call here --------- @callees callees-err-builtin -------- - -Error: this is a call to the built-in 'print' operator --------- @callees callees-err-conversion -------- - -Error: this is a type conversion, not a function call --------- @callees callees-err-bad-selection -------- - -Error: ambiguous selection within function call (or conversion) --------- @callees callees-err-deadcode1 -------- -this static function call dispatches to: - main.main - --------- @callees callees-err-nil-func -------- -dynamic function call on nil value - --------- @callees callees-err-nil-interface -------- -dynamic method call on nil value - --------- @callees callees-not-a-wrapper -------- -this dynamic method call dispatches to: - (main.myint).f - --------- @callees callees-static-call -------- -this static function call dispatches to: - main.foo - --------- @callees callees-qualified-call -------- -this static function call dispatches to: - fmt.Println - --------- @callees callees-static-method-call -------- -this static function call dispatches to: - (main.method).f - --------- @callees callees-implicit-selection-method-call -------- -this dynamic method call dispatches to: - (main.method).f - --------- @callers callers-not-a-wrapper -------- -(main.myint).f is called from these 1 sites: - dynamic method call from main.main - --------- @callees callees-err-deadcode2 -------- -this static function call dispatches to: - main.main - --------- @callstack callstack-err-deadcode -------- -main.deadcode is unreachable in this analysis scope - --------- @callees callees-err-deadcode3 -------- - -Error: this call site is unreachable in this analysis --------- @callers callers-global -------- -main.init is called from these 1 sites: -the root of the call graph - --------- @callstack callstack-init -------- -Found a call path from root to main.init#1 -main.init#1 -static function call from main.init - diff --git a/oracle/testdata/src/describe-json/main.go b/oracle/testdata/src/describe-json/main.go deleted file mode 100644 index 359c7310..00000000 --- a/oracle/testdata/src/describe-json/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package describe // @describe pkgdecl "describe" - -// Tests of 'describe' query, -format=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See describe-json.golden for expected query results. - -func main() { - var s struct{ x [3]int } - p := &s.x[0] // @describe desc-val-p "p" - _ = p - - var i I = C(0) - if i == nil { - i = new(D) - } - print(i) // @describe desc-val-i "\\bi\\b" - - go main() // @describe desc-stmt "go" -} - -type I interface { - f() -} - -type C int // @describe desc-type-C "C" -type D struct{} - -func (c C) f() {} -func (d *D) f() {} diff --git a/oracle/testdata/src/describe-json/main.golden b/oracle/testdata/src/describe-json/main.golden deleted file mode 100644 index 9d03661a..00000000 --- a/oracle/testdata/src/describe-json/main.golden +++ /dev/null @@ -1,111 +0,0 @@ --------- @describe pkgdecl -------- -{ - "mode": "describe", - "describe": { - "desc": "definition of package \"describe-json\"", - "pos": "testdata/src/describe-json/main.go:1:9", - "detail": "package", - "package": { - "path": "describe-json", - "members": [ - { - "name": "C", - "type": "int", - "pos": "testdata/src/describe-json/main.go:25:6", - "kind": "type", - "methods": [ - { - "name": "method (C) f()", - "pos": "testdata/src/describe-json/main.go:28:12" - } - ] - }, - { - "name": "D", - "type": "struct{}", - "pos": "testdata/src/describe-json/main.go:26:6", - "kind": "type", - "methods": [ - { - "name": "method (*D) f()", - "pos": "testdata/src/describe-json/main.go:29:13" - } - ] - }, - { - "name": "I", - "type": "interface{f()}", - "pos": "testdata/src/describe-json/main.go:21:6", - "kind": "type", - "methods": [ - { - "name": "method (I) f()", - "pos": "testdata/src/describe-json/main.go:22:2" - } - ] - }, - { - "name": "main", - "type": "func()", - "pos": "testdata/src/describe-json/main.go:7:6", - "kind": "func" - } - ] - } - } -} --------- @describe desc-val-p -------- -{ - "mode": "describe", - "describe": { - "desc": "identifier", - "pos": "testdata/src/describe-json/main.go:9:2", - "detail": "value", - "value": { - "type": "*int", - "objpos": "testdata/src/describe-json/main.go:9:2" - } - } -} --------- @describe desc-val-i -------- -{ - "mode": "describe", - "describe": { - "desc": "identifier", - "pos": "testdata/src/describe-json/main.go:16:8", - "detail": "value", - "value": { - "type": "I", - "objpos": "testdata/src/describe-json/main.go:12:6" - } - } -} --------- @describe desc-stmt -------- -{ - "mode": "describe", - "describe": { - "desc": "go statement", - "pos": "testdata/src/describe-json/main.go:18:2", - "detail": "unknown" - } -} --------- @describe desc-type-C -------- -{ - "mode": "describe", - "describe": { - "desc": "definition of type C (size 8, align 8)", - "pos": "testdata/src/describe-json/main.go:25:6", - "detail": "type", - "type": { - "type": "C", - "namepos": "testdata/src/describe-json/main.go:25:6", - "namedef": "int", - "methods": [ - { - "name": "method (C) f()", - "pos": "testdata/src/describe-json/main.go:28:12" - } - ] - } - } -} diff --git a/oracle/testdata/src/describe/main.go b/oracle/testdata/src/describe/main.go deleted file mode 100644 index f48d6d0d..00000000 --- a/oracle/testdata/src/describe/main.go +++ /dev/null @@ -1,96 +0,0 @@ -package describe // @describe pkgdecl "describe" - -// Tests of 'describe' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See describe.golden for expected query results. - -// TODO(adonovan): more coverage of the (extensive) logic. - -import ( - "nosuchpkg" // @describe badimport1 "nosuchpkg" - nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2" - _ "unsafe" // @describe unsafe "unsafe" -) - -var _ nosuchpkg.T -var _ nosuchpkg2.T - -type cake float64 // @describe type-ref-builtin "float64" - -const c = iota // @describe const-ref-iota "iota" - -const pi = 3.141 // @describe const-def-pi "pi" -const pie = cake(pi) // @describe const-def-pie "pie" -const _ = pi // @describe const-ref-pi "pi" - -var global = new(string) // NB: ssa.Global is indirect, i.e. **string - -func main() { // @describe func-def-main "main" - // func objects - _ = main // @describe func-ref-main "main" - _ = (*C).f // @describe func-ref-*C.f "..C..f" - _ = D.f // @describe func-ref-D.f "D.f" - _ = I.f // @describe func-ref-I.f "I.f" - var d D // @describe type-D "D" - var i I // @describe type-I "I" - _ = d.f // @describe func-ref-d.f "d.f" - _ = i.f // @describe func-ref-i.f "i.f" - - // var objects - anon := func() { - _ = d // @describe ref-lexical-d "d" - } - _ = anon // @describe ref-anon "anon" - _ = global // @describe ref-global "global" - - // SSA affords some local flow sensitivity. - var a, b int - var x = &a // @describe var-def-x-1 "x" - _ = x // @describe var-ref-x-1 "x" - x = &b // @describe var-def-x-2 "x" - _ = x // @describe var-ref-x-2 "x" - - i = new(C) // @describe var-ref-i-C "i" - if i != nil { - i = D{} // @describe var-ref-i-D "i" - } - print(i) // @describe var-ref-i "\\bi\\b" - - // const objects - const localpi = 3.141 // @describe const-local-pi "localpi" - const localpie = cake(pi) // @describe const-local-pie "localpie" - const _ = localpi // @describe const-ref-localpi "localpi" - - // type objects - type T int // @describe type-def-T "T" - var three T = 3 // @describe type-ref-T "T" - _ = three - - print(1 + 2*3) // @describe const-expr " 2.3" - print(real(1+2i) - 3) // @describe const-expr2 "real.*3" - - m := map[string]*int{"a": &a} - mapval, _ := m["a"] // @describe map-lookup,ok "m..a.." - _ = mapval // @describe mapval "mapval" - _ = m // @describe m "m" - - defer main() // @describe defer-stmt "defer" - go main() // @describe go-stmt "go" - - panic(3) // @describe builtin-ref-panic "panic" - - var a2 int // @describe var-decl-stmt "var a2 int" - _ = a2 - var _ int // @describe var-decl-stmt2 "var _ int" - var _ int // @describe var-def-blank "_" -} - -type I interface { // @describe def-iface-I "I" - f() // @describe def-imethod-I.f "f" -} - -type C int -type D struct{} - -func (c *C) f() {} -func (d D) f() {} diff --git a/oracle/testdata/src/describe/main.golden b/oracle/testdata/src/describe/main.golden deleted file mode 100644 index bc6d9f70..00000000 --- a/oracle/testdata/src/describe/main.golden +++ /dev/null @@ -1,184 +0,0 @@ --------- @describe pkgdecl -------- -definition of package "describe" - type C int - method (*C) f() - type D struct{} - method (D) f() - type I interface{f()} - method (I) f() - const c untyped int = 0 - type cake float64 - var global *string - func main func() - const pi untyped float = 3.141 - const pie cake = 3.141 - --------- @describe badimport1 -------- - -Error: can't import package "nosuchpkg" --------- @describe badimport2 -------- - -Error: can't import package "nosuchpkg" --------- @describe unsafe -------- -import of package "unsafe" - builtin Alignof - builtin Offsetof - type Pointer unsafe.Pointer - builtin Sizeof - --------- @describe type-ref-builtin -------- -reference to built-in type float64 - --------- @describe const-ref-iota -------- -reference to const iota untyped int of constant value 0 - --------- @describe const-def-pi -------- -definition of const pi untyped float - --------- @describe const-def-pie -------- -definition of const pie cake - --------- @describe const-ref-pi -------- -reference to const pi untyped float of constant value 3.141 -defined here - --------- @describe func-def-main -------- -definition of func main() - --------- @describe func-ref-main -------- -reference to func main() -defined here - --------- @describe func-ref-*C.f -------- -reference to method func (*C).f() -defined here - --------- @describe func-ref-D.f -------- -reference to method func (D).f() -defined here - --------- @describe func-ref-I.f -------- -reference to interface method func (I).f() -defined here - --------- @describe type-D -------- -reference to type D (size 0, align 1) -defined as struct{} -Method set: - method (D) f() - --------- @describe type-I -------- -reference to type I (size 16, align 8) -defined as interface{f()} -Method set: - method (I) f() - --------- @describe func-ref-d.f -------- -reference to method func (D).f() -defined here - --------- @describe func-ref-i.f -------- -reference to interface method func (I).f() -defined here - --------- @describe ref-lexical-d -------- -reference to var d D -defined here - --------- @describe ref-anon -------- -reference to var anon func() -defined here - --------- @describe ref-global -------- -reference to var global *string -defined here - --------- @describe var-def-x-1 -------- -definition of var x *int - --------- @describe var-ref-x-1 -------- -reference to var x *int -defined here - --------- @describe var-def-x-2 -------- -reference to var x *int -defined here - --------- @describe var-ref-x-2 -------- -reference to var x *int -defined here - --------- @describe var-ref-i-C -------- -reference to var i I -defined here - --------- @describe var-ref-i-D -------- -reference to var i I -defined here - --------- @describe var-ref-i -------- -reference to var i I -defined here - --------- @describe const-local-pi -------- -definition of const localpi untyped float - --------- @describe const-local-pie -------- -definition of const localpie cake - --------- @describe const-ref-localpi -------- -reference to const localpi untyped float of constant value 3.141 -defined here - --------- @describe type-def-T -------- -definition of type T (size 8, align 8) -No methods. - --------- @describe type-ref-T -------- -reference to type T (size 8, align 8) -defined as int -No methods. - --------- @describe const-expr -------- -binary * operation of constant value 6 - --------- @describe const-expr2 -------- -binary - operation of constant value -2 - --------- @describe map-lookup,ok -------- -index expression of type (*int, bool) - --------- @describe mapval -------- -reference to var mapval *int -defined here - --------- @describe m -------- -reference to var m map[string]*int -defined here - --------- @describe defer-stmt -------- -defer statement - --------- @describe go-stmt -------- -go statement - --------- @describe builtin-ref-panic -------- -function call (or conversion) of type () - --------- @describe var-decl-stmt -------- -definition of var a2 int - --------- @describe var-decl-stmt2 -------- -definition of var _ int - --------- @describe var-def-blank -------- -definition of var _ int - --------- @describe def-iface-I -------- -definition of type I (size 16, align 8) -Method set: - method (I) f() - --------- @describe def-imethod-I.f -------- -definition of interface method func (I).f() - diff --git a/oracle/testdata/src/freevars/main.go b/oracle/testdata/src/freevars/main.go deleted file mode 100644 index 1ce0ae6b..00000000 --- a/oracle/testdata/src/freevars/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -// Tests of 'freevars' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See freevars.golden for expected query results. - -// TODO(adonovan): it's hard to test this query in a single line of gofmt'd code. - -type T struct { - a, b int -} - -type S struct { - x int - t T -} - -func f(int) {} - -func main() { - type C int - x := 1 - const exp = 6 - if y := 2; x+y+int(C(3)) != exp { // @freevars fv1 "if.*{" - panic("expected 6") - } - - var s S - - for x, y := range "foo" { - println(s.x + s.t.a + s.t.b + x + int(y)) // @freevars fv2 "print.*y." - } - - f(x) // @freevars fv3 "f.x." - - // TODO(adonovan): enable when go/types supports labels. -loop: // #@freevars fv-def-label "loop:" - for { - break loop // #@freevars fv-ref-label "break loop" - } -} diff --git a/oracle/testdata/src/freevars/main.golden b/oracle/testdata/src/freevars/main.golden deleted file mode 100644 index b9eeab21..00000000 --- a/oracle/testdata/src/freevars/main.golden +++ /dev/null @@ -1,18 +0,0 @@ --------- @freevars fv1 -------- -Free identifiers: -type C -const exp int -var x int - --------- @freevars fv2 -------- -Free identifiers: -var s.t.a int -var s.t.b int -var s.x int -var x int -var y rune - --------- @freevars fv3 -------- -Free identifiers: -var x int - diff --git a/oracle/testdata/src/implements-json/main.go b/oracle/testdata/src/implements-json/main.go deleted file mode 100644 index d5f8102b..00000000 --- a/oracle/testdata/src/implements-json/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -// Tests of 'implements' query, -output=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See implements.golden for expected query results. - -func main() { -} - -type E interface{} // @implements E "E" - -type F interface { // @implements F "F" - f() -} - -type FG interface { // @implements FG "FG" - f() - g() []int // @implements slice "..int" -} - -type C int // @implements C "C" -type D struct{} - -func (c *C) f() {} // @implements starC ".C" -func (d D) f() {} // @implements D "D" - -func (d *D) g() []int { return nil } // @implements starD ".D" diff --git a/oracle/testdata/src/implements-json/main.golden b/oracle/testdata/src/implements-json/main.golden deleted file mode 100644 index 7e37f9e0..00000000 --- a/oracle/testdata/src/implements-json/main.golden +++ /dev/null @@ -1,159 +0,0 @@ --------- @implements E -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-json.E", - "pos": "testdata/src/implements-json/main.go:10:6", - "kind": "interface" - } - } -} --------- @implements F -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - }, - "to": [ - { - "name": "*implements-json.C", - "pos": "testdata/src/implements-json/main.go:21:6", - "kind": "pointer" - }, - { - "name": "implements-json.D", - "pos": "testdata/src/implements-json/main.go:22:6", - "kind": "struct" - }, - { - "name": "implements-json.FG", - "pos": "testdata/src/implements-json/main.go:16:6", - "kind": "interface" - } - ] - } -} --------- @implements FG -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-json.FG", - "pos": "testdata/src/implements-json/main.go:16:6", - "kind": "interface" - }, - "to": [ - { - "name": "*implements-json.D", - "pos": "testdata/src/implements-json/main.go:22:6", - "kind": "pointer" - } - ], - "from": [ - { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - } - ] - } -} --------- @implements slice -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "[]int", - "pos": "-", - "kind": "slice" - } - } -} --------- @implements C -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-json.C", - "pos": "testdata/src/implements-json/main.go:21:6", - "kind": "basic" - }, - "fromptr": [ - { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - } - ] - } -} --------- @implements starC -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "*implements-json.C", - "pos": "testdata/src/implements-json/main.go:21:6", - "kind": "pointer" - }, - "from": [ - { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - } - ] - } -} --------- @implements D -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-json.D", - "pos": "testdata/src/implements-json/main.go:22:6", - "kind": "struct" - }, - "from": [ - { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - } - ], - "fromptr": [ - { - "name": "implements-json.FG", - "pos": "testdata/src/implements-json/main.go:16:6", - "kind": "interface" - } - ] - } -} --------- @implements starD -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "*implements-json.D", - "pos": "testdata/src/implements-json/main.go:22:6", - "kind": "pointer" - }, - "from": [ - { - "name": "implements-json.F", - "pos": "testdata/src/implements-json/main.go:12:6", - "kind": "interface" - }, - { - "name": "implements-json.FG", - "pos": "testdata/src/implements-json/main.go:16:6", - "kind": "interface" - } - ] - } -} diff --git a/oracle/testdata/src/implements-methods-json/main.go b/oracle/testdata/src/implements-methods-json/main.go deleted file mode 100644 index 6c5bbf45..00000000 --- a/oracle/testdata/src/implements-methods-json/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -// Tests of 'implements' query applied to methods, -output=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See implements-methods.golden for expected query results. - -import _ "lib" - -func main() { -} - -type F interface { - f() // @implements F.f "f" -} - -type FG interface { - f() // @implements FG.f "f" - g() []int // @implements FG.g "g" -} - -type C int -type D struct{} - -func (c *C) f() {} // @implements *C.f "f" -func (d D) f() {} // @implements D.f "f" - -func (d *D) g() []int { return nil } // @implements *D.g "g" - -type sorter []int - -func (sorter) Len() int { return 0 } // @implements Len "Len" -func (sorter) Less(i, j int) bool { return false } -func (sorter) Swap(i, j int) {} - -type I interface { - Method(*int) *int // @implements I.Method "Method" -} diff --git a/oracle/testdata/src/implements-methods-json/main.golden b/oracle/testdata/src/implements-methods-json/main.golden deleted file mode 100644 index 831900a5..00000000 --- a/oracle/testdata/src/implements-methods-json/main.golden +++ /dev/null @@ -1,312 +0,0 @@ --------- @implements F.f -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - }, - "to": [ - { - "name": "*implements-methods-json.C", - "pos": "testdata/src/implements-methods-json/main.go:21:6", - "kind": "pointer" - }, - { - "name": "implements-methods-json.D", - "pos": "testdata/src/implements-methods-json/main.go:22:6", - "kind": "struct" - }, - { - "name": "implements-methods-json.FG", - "pos": "testdata/src/implements-methods-json/main.go:16:6", - "kind": "interface" - } - ], - "method": { - "name": "func (F).f()", - "pos": "testdata/src/implements-methods-json/main.go:13:2" - }, - "to_method": [ - { - "name": "method (*C) f()", - "pos": "testdata/src/implements-methods-json/main.go:24:13" - }, - { - "name": "method (D) f()", - "pos": "testdata/src/implements-methods-json/main.go:25:12" - }, - { - "name": "method (FG) f()", - "pos": "testdata/src/implements-methods-json/main.go:17:2" - } - ] - } -} --------- @implements FG.f -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.FG", - "pos": "testdata/src/implements-methods-json/main.go:16:6", - "kind": "interface" - }, - "to": [ - { - "name": "*implements-methods-json.D", - "pos": "testdata/src/implements-methods-json/main.go:22:6", - "kind": "pointer" - } - ], - "from": [ - { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - } - ], - "method": { - "name": "func (FG).f()", - "pos": "testdata/src/implements-methods-json/main.go:17:2" - }, - "to_method": [ - { - "name": "method (*D) f()", - "pos": "testdata/src/implements-methods-json/main.go:25:12" - } - ], - "from_method": [ - { - "name": "method (F) f()", - "pos": "testdata/src/implements-methods-json/main.go:13:2" - } - ] - } -} --------- @implements FG.g -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.FG", - "pos": "testdata/src/implements-methods-json/main.go:16:6", - "kind": "interface" - }, - "to": [ - { - "name": "*implements-methods-json.D", - "pos": "testdata/src/implements-methods-json/main.go:22:6", - "kind": "pointer" - } - ], - "from": [ - { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - } - ], - "method": { - "name": "func (FG).g() []int", - "pos": "testdata/src/implements-methods-json/main.go:18:2" - }, - "to_method": [ - { - "name": "method (*D) g() []int", - "pos": "testdata/src/implements-methods-json/main.go:27:13" - } - ], - "from_method": [ - { - "name": "", - "pos": "" - } - ] - } -} --------- @implements *C.f -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "*implements-methods-json.C", - "pos": "testdata/src/implements-methods-json/main.go:21:6", - "kind": "pointer" - }, - "from": [ - { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - } - ], - "method": { - "name": "func (*C).f()", - "pos": "testdata/src/implements-methods-json/main.go:24:13" - }, - "from_method": [ - { - "name": "method (F) f()", - "pos": "testdata/src/implements-methods-json/main.go:13:2" - } - ] - } -} --------- @implements D.f -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.D", - "pos": "testdata/src/implements-methods-json/main.go:22:6", - "kind": "struct" - }, - "from": [ - { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - } - ], - "fromptr": [ - { - "name": "implements-methods-json.FG", - "pos": "testdata/src/implements-methods-json/main.go:16:6", - "kind": "interface" - } - ], - "method": { - "name": "func (D).f()", - "pos": "testdata/src/implements-methods-json/main.go:25:12" - }, - "from_method": [ - { - "name": "method (F) f()", - "pos": "testdata/src/implements-methods-json/main.go:13:2" - } - ], - "fromptr_method": [ - { - "name": "method (FG) f()", - "pos": "testdata/src/implements-methods-json/main.go:17:2" - } - ] - } -} --------- @implements *D.g -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "*implements-methods-json.D", - "pos": "testdata/src/implements-methods-json/main.go:22:6", - "kind": "pointer" - }, - "from": [ - { - "name": "implements-methods-json.F", - "pos": "testdata/src/implements-methods-json/main.go:12:6", - "kind": "interface" - }, - { - "name": "implements-methods-json.FG", - "pos": "testdata/src/implements-methods-json/main.go:16:6", - "kind": "interface" - } - ], - "method": { - "name": "func (*D).g() []int", - "pos": "testdata/src/implements-methods-json/main.go:27:13" - }, - "from_method": [ - { - "name": "", - "pos": "" - }, - { - "name": "method (FG) g() []int", - "pos": "testdata/src/implements-methods-json/main.go:18:2" - } - ] - } -} --------- @implements Len -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.sorter", - "pos": "testdata/src/implements-methods-json/main.go:29:6", - "kind": "slice" - }, - "from": [ - { - "name": "lib.Sorter", - "pos": "testdata/src/lib/lib.go:16:6", - "kind": "interface" - } - ], - "method": { - "name": "func (sorter).Len() int", - "pos": "testdata/src/implements-methods-json/main.go:31:15" - }, - "from_method": [ - { - "name": "method (lib.Sorter) Len() int", - "pos": "testdata/src/lib/lib.go:17:2" - } - ] - } -} --------- @implements I.Method -------- -{ - "mode": "implements", - "implements": { - "type": { - "name": "implements-methods-json.I", - "pos": "testdata/src/implements-methods-json/main.go:35:6", - "kind": "interface" - }, - "to": [ - { - "name": "lib.Type", - "pos": "testdata/src/lib/lib.go:3:6", - "kind": "basic" - }, - { - "name": "main.I", - "pos": "testdata/src/implements-methods-json/main.go:35:6", - "kind": "interface" - } - ], - "from": [ - { - "name": "main.I", - "pos": "testdata/src/implements-methods-json/main.go:35:6", - "kind": "interface" - } - ], - "method": { - "name": "func (I).Method(*int) *int", - "pos": "testdata/src/implements-methods-json/main.go:36:2" - }, - "to_method": [ - { - "name": "method (lib.Type) Method(x *int) *int", - "pos": "testdata/src/lib/lib.go:5:13" - }, - { - "name": "method (main.I) Method(*int) *int", - "pos": "testdata/src/implements-methods-json/main.go:36:2" - } - ], - "from_method": [ - { - "name": "method (main.I) Method(*int) *int", - "pos": "testdata/src/implements-methods-json/main.go:36:2" - } - ] - } -} diff --git a/oracle/testdata/src/implements-methods/main.go b/oracle/testdata/src/implements-methods/main.go deleted file mode 100644 index a24854a6..00000000 --- a/oracle/testdata/src/implements-methods/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -// Tests of 'implements' query applied to methods. -// See go.tools/oracle/oracle_test.go for explanation. -// See implements-methods.golden for expected query results. - -import _ "lib" - -func main() { -} - -type F interface { - f() // @implements F.f "f" -} - -type FG interface { - f() // @implements FG.f "f" - g() []int // @implements FG.g "g" -} - -type C int -type D struct{} - -func (c *C) f() {} // @implements *C.f "f" -func (d D) f() {} // @implements D.f "f" - -func (d *D) g() []int { return nil } // @implements *D.g "g" - -type sorter []int - -func (sorter) Len() int { return 0 } // @implements Len "Len" -func (sorter) Less(i, j int) bool { return false } -func (sorter) Swap(i, j int) {} - -type I interface { - Method(*int) *int // @implements I.Method "Method" -} diff --git a/oracle/testdata/src/implements-methods/main.golden b/oracle/testdata/src/implements-methods/main.golden deleted file mode 100644 index 227d305c..00000000 --- a/oracle/testdata/src/implements-methods/main.golden +++ /dev/null @@ -1,39 +0,0 @@ --------- @implements F.f -------- -abstract method func (F).f() - is implemented by method (*C).f - is implemented by method (D).f - is implemented by method (FG).f - --------- @implements FG.f -------- -abstract method func (FG).f() - is implemented by method (*D).f - implements method (F).f - --------- @implements FG.g -------- -abstract method func (FG).g() []int - is implemented by method (*D).g - --------- @implements *C.f -------- -concrete method func (*C).f() - implements method (F).f - --------- @implements D.f -------- -concrete method func (D).f() - implements method (F).f -concrete method func (D).f() - implements method (FG).f - --------- @implements *D.g -------- -concrete method func (*D).g() []int - implements method (FG).g - --------- @implements Len -------- -concrete method func (sorter).Len() int - implements method (lib.Sorter).Len - --------- @implements I.Method -------- -abstract method func (I).Method(*int) *int - is implemented by method (lib.Type).Method - is implemented by method (main.I).Method - implements method (main.I).Method - diff --git a/oracle/testdata/src/implements/main.go b/oracle/testdata/src/implements/main.go deleted file mode 100644 index 14a2f16f..00000000 --- a/oracle/testdata/src/implements/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -// Tests of 'implements' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See implements.golden for expected query results. - -import _ "lib" - -func main() { -} - -type E interface{} // @implements E "E" - -type F interface { // @implements F "F" - f() -} - -type FG interface { // @implements FG "FG" - f() - g() []int // @implements slice "..int" -} - -type C int // @implements C "C" -type D struct{} - -func (c *C) f() {} // @implements starC ".C" -func (d D) f() {} // @implements D "D" - -func (d *D) g() []int { return nil } // @implements starD ".D" - -type sorter []int // @implements sorter "sorter" - -func (sorter) Len() int { return 0 } -func (sorter) Less(i, j int) bool { return false } -func (sorter) Swap(i, j int) {} - -type I interface { // @implements I "I" - Method(*int) *int -} diff --git a/oracle/testdata/src/implements/main.golden b/oracle/testdata/src/implements/main.golden deleted file mode 100644 index cb2f2ac5..00000000 --- a/oracle/testdata/src/implements/main.golden +++ /dev/null @@ -1,46 +0,0 @@ --------- @implements E -------- -empty interface type E - --------- @implements F -------- -interface type F - is implemented by pointer type *C - is implemented by struct type D - is implemented by interface type FG - --------- @implements FG -------- -interface type FG - is implemented by pointer type *D - implements F - --------- @implements slice -------- -slice type []int implements only interface{} - --------- @implements C -------- -pointer type *C - implements F - --------- @implements starC -------- -pointer type *C - implements F - --------- @implements D -------- -struct type D - implements F -pointer type *D - implements FG - --------- @implements starD -------- -pointer type *D - implements F - implements FG - --------- @implements sorter -------- -slice type sorter - implements lib.Sorter - --------- @implements I -------- -interface type I - is implemented by basic type lib.Type - is implemented by interface type main.I - implements main.I - diff --git a/oracle/testdata/src/imports/main.go b/oracle/testdata/src/imports/main.go deleted file mode 100644 index 0f616abe..00000000 --- a/oracle/testdata/src/imports/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "hash/fnv" // @describe ref-pkg-import2 "fnv" - "lib" // @describe ref-pkg-import "lib" -) - -// Tests that import another package. (To make the tests run quickly, -// we avoid using imports in all the other tests. Remember, each -// query causes parsing and typechecking of the whole program.) -// -// See go.tools/oracle/oracle_test.go for explanation. -// See imports.golden for expected query results. - -var a int - -func main() { - const c = lib.Const // @describe ref-const "Const" - lib.Func() // @describe ref-func "Func" - lib.Var++ // @describe ref-var "Var" - var t lib.Type // @describe ref-type "Type" - p := t.Method(&a) // @describe ref-method "Method" - - print(*p + 1) // @pointsto p "p " - - var _ lib.Type // @describe ref-pkg "lib" - - fnv.New32() -} diff --git a/oracle/testdata/src/imports/main.golden b/oracle/testdata/src/imports/main.golden deleted file mode 100644 index 91442106..00000000 --- a/oracle/testdata/src/imports/main.golden +++ /dev/null @@ -1,57 +0,0 @@ --------- @describe ref-pkg-import2 -------- -import of package "hash/fnv" - func New32 func() hash.Hash32 - func New32a func() hash.Hash32 - func New64 func() hash.Hash64 - func New64a func() hash.Hash64 - --------- @describe ref-pkg-import -------- -import of package "lib" - const Const untyped int = 3 - func Func func() - type Sorter interface{...} - method (Sorter) Len() int - method (Sorter) Less(i int, j int) bool - method (Sorter) Swap(i int, j int) - type Type int - method (Type) Method(x *int) *int - var Var int - --------- @describe ref-const -------- -reference to const lib.Const untyped int -defined here - --------- @describe ref-func -------- -reference to func lib.Func() -defined here - --------- @describe ref-var -------- -reference to var lib.Var int -defined here - --------- @describe ref-type -------- -reference to type lib.Type (size 8, align 8) -defined as int -Method set: - method (lib.Type) Method(x *int) *int - --------- @describe ref-method -------- -reference to method func (lib.Type).Method(x *int) *int -defined here - --------- @pointsto p -------- -this *int may point to these objects: - main.a - --------- @describe ref-pkg -------- -reference to package "lib" - const Const untyped int = 3 - func Func func() - type Sorter interface{...} - method (Sorter) Len() int - method (Sorter) Less(i int, j int) bool - method (Sorter) Swap(i int, j int) - type Type int - method (Type) Method(x *int) *int - var Var int - diff --git a/oracle/testdata/src/lib/lib.go b/oracle/testdata/src/lib/lib.go deleted file mode 100644 index 9131c274..00000000 --- a/oracle/testdata/src/lib/lib.go +++ /dev/null @@ -1,20 +0,0 @@ -package lib - -type Type int - -func (Type) Method(x *int) *int { - return x -} - -func Func() { -} - -const Const = 3 - -var Var = 0 - -type Sorter interface { - Len() int - Less(i, j int) bool - Swap(i, j int) -} diff --git a/oracle/testdata/src/main/multi.go b/oracle/testdata/src/main/multi.go deleted file mode 100644 index 8c650cd2..00000000 --- a/oracle/testdata/src/main/multi.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -func g(x int) { -} - -func f() { - x := 1 - g(x) // "g(x)" is the selection for multiple queries -} - -func main() { - f() -} diff --git a/oracle/testdata/src/peers-json/main.go b/oracle/testdata/src/peers-json/main.go deleted file mode 100644 index 1df550be..00000000 --- a/oracle/testdata/src/peers-json/main.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -// Tests of channel 'peers' query, -format=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See peers-json.golden for expected query results. - -func main() { - chA := make(chan *int) - <-chA - select { - case <-chA: // @peers peer-recv-chA "<-" - } -} diff --git a/oracle/testdata/src/peers-json/main.golden b/oracle/testdata/src/peers-json/main.golden deleted file mode 100644 index 8c2d06c7..00000000 --- a/oracle/testdata/src/peers-json/main.golden +++ /dev/null @@ -1,15 +0,0 @@ --------- @peers peer-recv-chA -------- -{ - "mode": "peers", - "peers": { - "pos": "testdata/src/peers-json/main.go:11:7", - "type": "chan *int", - "allocs": [ - "testdata/src/peers-json/main.go:8:13" - ], - "receives": [ - "testdata/src/peers-json/main.go:9:2", - "testdata/src/peers-json/main.go:11:7" - ] - } -} diff --git a/oracle/testdata/src/peers/main.go b/oracle/testdata/src/peers/main.go deleted file mode 100644 index a4cf91b8..00000000 --- a/oracle/testdata/src/peers/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -// Tests of channel 'peers' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See peers.golden for expected query results. - -var a2 int - -func main() { - chA := make(chan *int) - a1 := 1 - chA <- &a1 - - chA2 := make(chan *int, 2) - if a2 == 0 { - chA = chA2 - } - - chB := make(chan *int) - b := 3 - chB <- &b - - <-chA // @pointsto pointsto-chA "chA" - <-chA2 // @pointsto pointsto-chA2 "chA2" - <-chB // @pointsto pointsto-chB "chB" - - select { - case rA := <-chA: // @peers peer-recv-chA "<-" - _ = rA // @pointsto pointsto-rA "rA" - case rB := <-chB: // @peers peer-recv-chB "<-" - _ = rB // @pointsto pointsto-rB "rB" - - case <-chA: // @peers peer-recv-chA' "<-" - - case chA2 <- &a2: // @peers peer-send-chA' "<-" - } - - for _ = range chA { - } - - close(chA) // @peers peer-close-chA "chA" - - chC := make(chan *int) - (close)(chC) // @peers peer-close-chC "chC" - - close := func(ch chan *int) chan *int { - return ch - } - - close(chC) <- &b // @peers peer-send-chC "chC" - <-close(chC) // @peers peer-recv-chC "chC" -} diff --git a/oracle/testdata/src/peers/main.golden b/oracle/testdata/src/peers/main.golden deleted file mode 100644 index 597a3c6c..00000000 --- a/oracle/testdata/src/peers/main.golden +++ /dev/null @@ -1,100 +0,0 @@ --------- @pointsto pointsto-chA -------- -this chan *int may point to these objects: - makechan - makechan - --------- @pointsto pointsto-chA2 -------- -this chan *int may point to these objects: - makechan - --------- @pointsto pointsto-chB -------- -this chan *int may point to these objects: - makechan - --------- @peers peer-recv-chA -------- -This channel of type chan *int may be: - allocated here - allocated here - sent to, here - sent to, here - received from, here - received from, here - received from, here - received from, here - received from, here - closed, here - --------- @pointsto pointsto-rA -------- -this *int may point to these objects: - main.a2 - a1 - --------- @peers peer-recv-chB -------- -This channel of type chan *int may be: - allocated here - sent to, here - received from, here - received from, here - --------- @pointsto pointsto-rB -------- -this *int may point to these objects: - b - --------- @peers peer-recv-chA' -------- -This channel of type chan *int may be: - allocated here - allocated here - sent to, here - sent to, here - received from, here - received from, here - received from, here - received from, here - received from, here - closed, here - --------- @peers peer-send-chA' -------- -This channel of type chan *int may be: - allocated here - sent to, here - received from, here - received from, here - received from, here - received from, here - received from, here - closed, here - --------- @peers peer-close-chA -------- -This channel of type chan *int may be: - allocated here - allocated here - sent to, here - sent to, here - received from, here - received from, here - received from, here - received from, here - received from, here - closed, here - --------- @peers peer-close-chC -------- -This channel of type chan *int may be: - allocated here - sent to, here - received from, here - closed, here - --------- @peers peer-send-chC -------- -This channel of type chan *int may be: - allocated here - sent to, here - received from, here - closed, here - --------- @peers peer-recv-chC -------- -This channel of type chan *int may be: - allocated here - sent to, here - received from, here - closed, here - diff --git a/oracle/testdata/src/pointsto-json/main.go b/oracle/testdata/src/pointsto-json/main.go deleted file mode 100644 index 38f1148f..00000000 --- a/oracle/testdata/src/pointsto-json/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -// Tests of 'pointsto' queries, -format=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See pointsto-json.golden for expected query results. - -func main() { // - var s struct{ x [3]int } - p := &s.x[0] // @pointsto val-p "p" - _ = p - - var i I = C(0) - if i == nil { - i = new(D) - } - print(i) // @pointsto val-i "\\bi\\b" -} - -type I interface { - f() -} - -type C int -type D struct{} - -func (c C) f() {} -func (d *D) f() {} diff --git a/oracle/testdata/src/pointsto-json/main.golden b/oracle/testdata/src/pointsto-json/main.golden deleted file mode 100644 index 13ac1df9..00000000 --- a/oracle/testdata/src/pointsto-json/main.golden +++ /dev/null @@ -1,35 +0,0 @@ --------- @pointsto val-p -------- -{ - "mode": "pointsto", - "pointsto": [ - { - "type": "*int", - "labels": [ - { - "pos": "testdata/src/pointsto-json/main.go:8:6", - "desc": "s.x[*]" - } - ] - } - ] -} --------- @pointsto val-i -------- -{ - "mode": "pointsto", - "pointsto": [ - { - "type": "*D", - "namepos": "testdata/src/pointsto-json/main.go:24:6", - "labels": [ - { - "pos": "testdata/src/pointsto-json/main.go:14:10", - "desc": "new" - } - ] - }, - { - "type": "C", - "namepos": "testdata/src/pointsto-json/main.go:23:6" - } - ] -} diff --git a/oracle/testdata/src/pointsto/main.go b/oracle/testdata/src/pointsto/main.go deleted file mode 100644 index 9064e469..00000000 --- a/oracle/testdata/src/pointsto/main.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -// Tests of 'pointsto' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See pointsto.golden for expected query results. - -const pi = 3.141 // @pointsto const "pi" - -var global = new(string) // NB: ssa.Global is indirect, i.e. **string - -func main() { - livecode() - - // func objects - _ = main // @pointsto func-ref-main "main" - _ = (*C).f // @pointsto func-ref-*C.f "..C..f" - _ = D.f // @pointsto func-ref-D.f "D.f" - _ = I.f // @pointsto func-ref-I.f "I.f" - var d D - var i I - _ = d.f // @pointsto func-ref-d.f "d.f" - _ = i.f // @pointsto func-ref-i.f "i.f" - - // var objects - anon := func() { - _ = d.f // @pointsto ref-lexical-d.f "d.f" - } - _ = anon // @pointsto ref-anon "anon" - _ = global // @pointsto ref-global "global" - - // SSA affords some local flow sensitivity. - var a, b int - var x = &a // @pointsto var-def-x-1 "x" - _ = x // @pointsto var-ref-x-1 "x" - x = &b // @pointsto var-def-x-2 "x" - _ = x // @pointsto var-ref-x-2 "x" - - i = new(C) // @pointsto var-ref-i-C "i" - if i != nil { - i = D{} // @pointsto var-ref-i-D "i" - } - print(i) // @pointsto var-ref-i "\\bi\\b" - - m := map[string]*int{"a": &a} - mapval, _ := m["a"] // @pointsto map-lookup,ok "m..a.." - _ = mapval // @pointsto mapval "mapval" - _ = m // @pointsto m "m" - - if false { - panic(3) // @pointsto builtin-panic "panic" - } - - // NB: s.f is addressable per (*ssa.Program).VarValue, - // but our query concerns the object, not its address. - s := struct{ f interface{} }{f: make(chan bool)} - print(s.f) // @pointsto var-ref-s-f "s.f" -} - -func livecode() {} // @pointsto func-live "livecode" - -func deadcode() { // @pointsto func-dead "deadcode" - // Pointer analysis can't run on dead code. - var b = new(int) // @pointsto b "b" - _ = b -} - -type I interface { - f() -} - -type C int -type D struct{} - -func (c *C) f() {} -func (d D) f() {} diff --git a/oracle/testdata/src/pointsto/main.golden b/oracle/testdata/src/pointsto/main.golden deleted file mode 100644 index fd68bda6..00000000 --- a/oracle/testdata/src/pointsto/main.golden +++ /dev/null @@ -1,96 +0,0 @@ --------- @pointsto const -------- - -Error: pointer analysis wants an expression of reference type; got untyped float --------- @pointsto func-ref-main -------- -this func() may point to these objects: - main.main - --------- @pointsto func-ref-*C.f -------- -this func() may point to these objects: - (*main.C).f - --------- @pointsto func-ref-D.f -------- -this func() may point to these objects: - (main.D).f - --------- @pointsto func-ref-I.f -------- - -Error: func (main.I).f() is an interface method --------- @pointsto func-ref-d.f -------- -this func() may point to these objects: - (main.D).f - --------- @pointsto func-ref-i.f -------- - -Error: func (main.I).f() is an interface method --------- @pointsto ref-lexical-d.f -------- -this func() may point to these objects: - (main.D).f - --------- @pointsto ref-anon -------- -this func() may point to these objects: - main.main$1 - --------- @pointsto ref-global -------- -this *string may point to these objects: - new - --------- @pointsto var-def-x-1 -------- -this *int may point to these objects: - a - --------- @pointsto var-ref-x-1 -------- -this *int may point to these objects: - a - --------- @pointsto var-def-x-2 -------- -this *int may point to these objects: - b - --------- @pointsto var-ref-x-2 -------- -this *int may point to these objects: - b - --------- @pointsto var-ref-i-C -------- -this I may contain these dynamic types: - *C, may point to: - new - --------- @pointsto var-ref-i-D -------- -this I may contain these dynamic types: - D - --------- @pointsto var-ref-i -------- -this I may contain these dynamic types: - *C, may point to: - new - D - --------- @pointsto map-lookup,ok -------- - -Error: pointer analysis wants an expression of reference type; got (*int, bool) --------- @pointsto mapval -------- -this *int may point to these objects: - a - --------- @pointsto m -------- -this map[string]*int may point to these objects: - makemap - --------- @pointsto builtin-panic -------- - -Error: pointer analysis wants an expression of reference type; got () --------- @pointsto var-ref-s-f -------- -this interface{} may contain these dynamic types: - chan bool, may point to: - makechan - --------- @pointsto func-live -------- - -Error: pointer analysis did not find expression (dead code?) --------- @pointsto func-dead -------- - -Error: pointer analysis did not find expression (dead code?) --------- @pointsto b -------- - -Error: pointer analysis did not find expression (dead code?) diff --git a/oracle/testdata/src/referrers-json/main.go b/oracle/testdata/src/referrers-json/main.go deleted file mode 100644 index f551ee01..00000000 --- a/oracle/testdata/src/referrers-json/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -// Tests of 'referrers' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See referrers.golden for expected query results. - -import "lib" - -type s struct { - f int -} - -func main() { - var v lib.Type = lib.Const // @referrers ref-package "lib" - _ = v.Method // @referrers ref-method "Method" - _ = v.Method - v++ //@referrers ref-local "v" - v++ - - _ = s{}.f // @referrers ref-field "f" - - var s2 s - s2.f = 1 -} diff --git a/oracle/testdata/src/referrers-json/main.golden b/oracle/testdata/src/referrers-json/main.golden deleted file mode 100644 index 47a2d01b..00000000 --- a/oracle/testdata/src/referrers-json/main.golden +++ /dev/null @@ -1,59 +0,0 @@ --------- @referrers ref-package -------- -{ - "mode": "referrers", - "referrers": { - "pos": "testdata/src/referrers-json/main.go:14:8", - "objpos": "testdata/src/referrers-json/main.go:7:8", - "desc": "package lib", - "refs": [ - "testdata/src/referrers-json/main.go:14:8", - "testdata/src/referrers-json/main.go:14:19" - ] - } -} --------- @referrers ref-method -------- -{ - "mode": "referrers", - "referrers": { - "pos": "testdata/src/referrers-json/main.go:15:8", - "objpos": "testdata/src/lib/lib.go:5:13", - "desc": "func (lib.Type).Method(x *int) *int", - "refs": [ - "testdata/src/imports/main.go:22:9", - "testdata/src/referrers-json/main.go:15:8", - "testdata/src/referrers-json/main.go:16:8", - "testdata/src/referrers/ext_test.go:10:17", - "testdata/src/referrers/int_test.go:7:17", - "testdata/src/referrers/main.go:17:8", - "testdata/src/referrers/main.go:18:8" - ] - } -} --------- @referrers ref-local -------- -{ - "mode": "referrers", - "referrers": { - "pos": "testdata/src/referrers-json/main.go:17:2", - "objpos": "testdata/src/referrers-json/main.go:14:6", - "desc": "var v lib.Type", - "refs": [ - "testdata/src/referrers-json/main.go:15:6", - "testdata/src/referrers-json/main.go:16:6", - "testdata/src/referrers-json/main.go:17:2", - "testdata/src/referrers-json/main.go:18:2" - ] - } -} --------- @referrers ref-field -------- -{ - "mode": "referrers", - "referrers": { - "pos": "testdata/src/referrers-json/main.go:20:10", - "objpos": "testdata/src/referrers-json/main.go:10:2", - "desc": "field f int", - "refs": [ - "testdata/src/referrers-json/main.go:20:10", - "testdata/src/referrers-json/main.go:23:5" - ] - } -} diff --git a/oracle/testdata/src/referrers/ext_test.go b/oracle/testdata/src/referrers/ext_test.go deleted file mode 100644 index 35e3199a..00000000 --- a/oracle/testdata/src/referrers/ext_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package main_test - -import ( - "lib" - renamed "referrers" // package has name "main", path "referrers", local name "renamed" -) - -func _() { - // This reference should be found by the ref-method query. - _ = (lib.Type).Method // ref from external test package - var _ renamed.T -} diff --git a/oracle/testdata/src/referrers/int_test.go b/oracle/testdata/src/referrers/int_test.go deleted file mode 100644 index 9102cd6f..00000000 --- a/oracle/testdata/src/referrers/int_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import "lib" - -func _() { - // This reference should be found by the ref-method query. - _ = (lib.Type).Method // ref from internal test package -} diff --git a/oracle/testdata/src/referrers/main.go b/oracle/testdata/src/referrers/main.go deleted file mode 100644 index fef571ae..00000000 --- a/oracle/testdata/src/referrers/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main // @referrers package-decl "main" - -// Tests of 'referrers' query. -// See go.tools/oracle/oracle_test.go for explanation. -// See referrers.golden for expected query results. - -import "lib" - -type s struct { // @referrers type " s " - f int -} - -type T int - -func main() { - var v lib.Type = lib.Const // @referrers ref-package "lib" - _ = v.Method // @referrers ref-method "Method" - _ = v.Method - v++ //@referrers ref-local "v" - v++ - - _ = s{}.f // @referrers ref-field "f" - - var s2 s - s2.f = 1 -} - -// Test //line directives: - -type U int // @referrers ref-type-U "U" - -//line nosuchfile.y:123 -var u1 U -var u2 U diff --git a/oracle/testdata/src/referrers/main.golden b/oracle/testdata/src/referrers/main.golden deleted file mode 100644 index d98d5109..00000000 --- a/oracle/testdata/src/referrers/main.golden +++ /dev/null @@ -1,42 +0,0 @@ --------- @referrers package-decl -------- -1 references to package main ("referrers") - var _ renamed.T - --------- @referrers type -------- -2 references to type s struct{f int} - _ = s{}.f // @referrers ref-field "f" - var s2 s - --------- @referrers ref-package -------- -4 references to package lib - _ = (lib.Type).Method // ref from external test package - _ = (lib.Type).Method // ref from internal test package - var v lib.Type = lib.Const // @referrers ref-package "lib" - var v lib.Type = lib.Const // @referrers ref-package "lib" - --------- @referrers ref-method -------- -7 references to func (lib.Type).Method(x *int) *int - p := t.Method(&a) // @describe ref-method "Method" - _ = v.Method // @referrers ref-method "Method" - _ = v.Method - _ = (lib.Type).Method // ref from external test package - _ = (lib.Type).Method // ref from internal test package - _ = v.Method // @referrers ref-method "Method" - _ = v.Method - --------- @referrers ref-local -------- -4 references to var v lib.Type - _ = v.Method // @referrers ref-method "Method" - _ = v.Method - v++ //@referrers ref-local "v" - v++ - --------- @referrers ref-field -------- -2 references to field f int - _ = s{}.f // @referrers ref-field "f" - s2.f = 1 - --------- @referrers ref-type-U -------- -2 references to type U int -open testdata/src/referrers/nosuchfile.y: no such file or directory (+ 1 more refs in this file) - diff --git a/oracle/testdata/src/reflection/main.go b/oracle/testdata/src/reflection/main.go deleted file mode 100644 index 392643ba..00000000 --- a/oracle/testdata/src/reflection/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -// This is a test of 'pointsto', but we split it into a separate file -// so that pointsto.go doesn't have to import "reflect" each time. - -import "reflect" - -var a int -var b bool - -func main() { - m := make(map[*int]*bool) - m[&a] = &b - - mrv := reflect.ValueOf(m) - if a > 0 { - mrv = reflect.ValueOf(&b) - } - if a > 0 { - mrv = reflect.ValueOf(&a) - } - - _ = mrv // @pointsto mrv "mrv" - p1 := mrv.Interface() // @pointsto p1 "p1" - p2 := mrv.MapKeys() // @pointsto p2 "p2" - p3 := p2[0] // @pointsto p3 "p3" - p4 := reflect.TypeOf(p1) // @pointsto p4 "p4" - - _, _, _, _ = p1, p2, p3, p4 -} diff --git a/oracle/testdata/src/reflection/main.golden b/oracle/testdata/src/reflection/main.golden deleted file mode 100644 index 6190c065..00000000 --- a/oracle/testdata/src/reflection/main.golden +++ /dev/null @@ -1,34 +0,0 @@ --------- @pointsto mrv -------- -this reflect.Value may contain these dynamic types: - *bool, may point to: - main.b - *int, may point to: - main.a - map[*int]*bool, may point to: - makemap - --------- @pointsto p1 -------- -this interface{} may contain these dynamic types: - *bool, may point to: - main.b - *int, may point to: - main.a - map[*int]*bool, may point to: - makemap - --------- @pointsto p2 -------- -this []reflect.Value may point to these objects: - - --------- @pointsto p3 -------- -this reflect.Value may contain these dynamic types: - *int, may point to: - main.a - --------- @pointsto p4 -------- -this reflect.Type may contain these dynamic types: - *reflect.rtype, may point to: - *bool - *int - map[*int]*bool - diff --git a/oracle/testdata/src/what-json/main.go b/oracle/testdata/src/what-json/main.go deleted file mode 100644 index 8d578c73..00000000 --- a/oracle/testdata/src/what-json/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -// Tests of 'what' queries, -format=json. -// See go.tools/oracle/oracle_test.go for explanation. -// See what-json.golden for expected query results. - -func main() { - f() // @what call "f" -} diff --git a/oracle/testdata/src/what-json/main.golden b/oracle/testdata/src/what-json/main.golden deleted file mode 100644 index 602b4d55..00000000 --- a/oracle/testdata/src/what-json/main.golden +++ /dev/null @@ -1,51 +0,0 @@ --------- @what call -------- -{ - "mode": "what", - "what": { - "enclosing": [ - { - "desc": "identifier", - "start": 179, - "end": 180 - }, - { - "desc": "function call", - "start": 179, - "end": 182 - }, - { - "desc": "expression statement", - "start": 179, - "end": 182 - }, - { - "desc": "block", - "start": 176, - "end": 202 - }, - { - "desc": "function declaration", - "start": 164, - "end": 202 - }, - { - "desc": "source file", - "start": 0, - "end": 202 - } - ], - "modes": [ - "callees", - "callers", - "callstack", - "definition", - "describe", - "freevars", - "implements", - "pointsto", - "referrers" - ], - "srcdir": "testdata/src", - "importpath": "what-json" - } -} diff --git a/oracle/testdata/src/what/main.go b/oracle/testdata/src/what/main.go deleted file mode 100644 index 38e9dc7b..00000000 --- a/oracle/testdata/src/what/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main // @what pkgdecl "main" - -// Tests of 'what' queries. -// See go.tools/oracle/oracle_test.go for explanation. -// See what.golden for expected query results. - -func main() { - f() // @what call "f" - var ch chan int // @what var "var" - <-ch // @what recv "ch" -} diff --git a/oracle/testdata/src/what/main.golden b/oracle/testdata/src/what/main.golden deleted file mode 100644 index 620eec03..00000000 --- a/oracle/testdata/src/what/main.golden +++ /dev/null @@ -1,39 +0,0 @@ --------- @what pkgdecl -------- -identifier -source file -modes: [definition describe freevars implements pointsto referrers] -srcdir: testdata/src -import path: what - --------- @what call -------- -identifier -function call -expression statement -block -function declaration -source file -modes: [callees callers callstack definition describe freevars implements pointsto referrers] -srcdir: testdata/src -import path: what - --------- @what var -------- -variable declaration -variable declaration statement -block -function declaration -source file -modes: [callers callstack describe freevars pointsto] -srcdir: testdata/src -import path: what - --------- @what recv -------- -identifier -unary <- operation -expression statement -block -function declaration -source file -modes: [callers callstack definition describe freevars implements peers pointsto referrers] -srcdir: testdata/src -import path: what - diff --git a/oracle/testdata/src/whicherrs/main.go b/oracle/testdata/src/whicherrs/main.go deleted file mode 100644 index 27fe6b56..00000000 --- a/oracle/testdata/src/whicherrs/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -type errType string - -const constErr errType = "blah" - -func (et errType) Error() string { - return string(et) -} - -var errVar error = errType("foo") - -func genErr(i int) error { - switch i { - case 0: - return constErr - case 1: - return errVar - default: - return nil - } -} - -func main() { - err := genErr(0) // @whicherrs localerrs "err" - _ = err -} diff --git a/oracle/testdata/src/whicherrs/main.golden b/oracle/testdata/src/whicherrs/main.golden deleted file mode 100644 index 1118e0a8..00000000 --- a/oracle/testdata/src/whicherrs/main.golden +++ /dev/null @@ -1,8 +0,0 @@ --------- @whicherrs localerrs -------- -this error may point to these globals: - errVar -this error may contain these constants: - constErr -this error may contain these dynamic types: - errType - diff --git a/oracle/what.go b/oracle/what.go deleted file mode 100644 index 5a5c0cfa..00000000 --- a/oracle/what.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package oracle - -import ( - "fmt" - "go/ast" - "go/build" - "go/token" - "os" - "path/filepath" - "sort" - "strings" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/oracle/serial" -) - -// what reports all the information about the query selection that can be -// obtained from parsing only its containing source file. -// It is intended to be a very low-latency query callable from GUI -// tools, e.g. to populate a menu of options of slower queries about -// the selected location. -// -func what(q *Query) error { - qpos, err := fastQueryPos(q.Pos) - if err != nil { - return err - } - q.Fset = qpos.fset - - // (ignore errors) - srcdir, importPath, _ := guessImportPath(q.Fset.File(qpos.start).Name(), q.Build) - - // Determine which query modes are applicable to the selection. - enable := map[string]bool{ - "describe": true, // any syntax; always enabled - } - - if qpos.end > qpos.start { - enable["freevars"] = true // nonempty selection? - } - - for _, n := range qpos.path { - switch n := n.(type) { - case *ast.Ident: - enable["definition"] = true - enable["referrers"] = true - enable["implements"] = true - case *ast.CallExpr: - enable["callees"] = true - case *ast.FuncDecl: - enable["callers"] = true - enable["callstack"] = true - case *ast.SendStmt: - enable["peers"] = true - case *ast.UnaryExpr: - if n.Op == token.ARROW { - enable["peers"] = true - } - } - - // For implements, we approximate findInterestingNode. - if _, ok := enable["implements"]; !ok { - switch n.(type) { - case *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - enable["implements"] = true - } - } - - // For pointsto, we approximate findInterestingNode. - if _, ok := enable["pointsto"]; !ok { - switch n.(type) { - case ast.Stmt, - *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - enable["pointsto"] = false // not an expr - - case ast.Expr, ast.Decl, *ast.ValueSpec: - enable["pointsto"] = true // an expr, maybe - - default: - // Comment, Field, KeyValueExpr, etc: ascend. - } - } - } - - // If we don't have an exact selection, disable modes that need one. - if !qpos.exact { - enable["callees"] = false - enable["pointsto"] = false - enable["whicherrs"] = false - enable["describe"] = false - } - - var modes []string - for mode := range enable { - modes = append(modes, mode) - } - sort.Strings(modes) - - q.result = &whatResult{ - path: qpos.path, - srcdir: srcdir, - importPath: importPath, - modes: modes, - } - return nil -} - -// guessImportPath finds the package containing filename, and returns -// its source directory (an element of $GOPATH) and its import path -// relative to it. -// -// TODO(adonovan): what about _test.go files that are not part of the -// package? -// -func guessImportPath(filename string, buildContext *build.Context) (srcdir, importPath string, err error) { - absFile, err := filepath.Abs(filename) - if err != nil { - err = fmt.Errorf("can't form absolute path of %s", filename) - return - } - absFileDir := segments(filepath.Dir(absFile)) - - // Find the innermost directory in $GOPATH that encloses filename. - minD := 1024 - for _, gopathDir := range buildContext.SrcDirs() { - absDir, err := filepath.Abs(gopathDir) - if err != nil { - continue // e.g. non-existent dir on $GOPATH - } - d := prefixLen(segments(absDir), absFileDir) - // If there are multiple matches, - // prefer the innermost enclosing directory - // (smallest d). - if d >= 0 && d < minD { - minD = d - srcdir = gopathDir - importPath = strings.Join(absFileDir[len(absFileDir)-minD:], string(os.PathSeparator)) - } - } - if srcdir == "" { - err = fmt.Errorf("directory %s is not beneath any of these GOROOT/GOPATH directories: %s", - filepath.Dir(absFile), strings.Join(buildContext.SrcDirs(), ", ")) - } - return -} - -func segments(path string) []string { - return strings.Split(path, string(os.PathSeparator)) -} - -// prefixLen returns the length of the remainder of y if x is a prefix -// of y, a negative number otherwise. -func prefixLen(x, y []string) int { - d := len(y) - len(x) - if d >= 0 { - for i := range x { - if y[i] != x[i] { - return -1 // not a prefix - } - } - } - return d -} - -type whatResult struct { - path []ast.Node - modes []string - srcdir string - importPath string -} - -func (r *whatResult) display(printf printfFunc) { - for _, n := range r.path { - printf(n, "%s", astutil.NodeDescription(n)) - } - printf(nil, "modes: %s", r.modes) - printf(nil, "srcdir: %s", r.srcdir) - printf(nil, "import path: %s", r.importPath) -} - -func (r *whatResult) toSerial(res *serial.Result, fset *token.FileSet) { - var enclosing []serial.SyntaxNode - for _, n := range r.path { - enclosing = append(enclosing, serial.SyntaxNode{ - Description: astutil.NodeDescription(n), - Start: fset.Position(n.Pos()).Offset, - End: fset.Position(n.End()).Offset, - }) - } - res.What = &serial.What{ - Modes: r.modes, - SrcDir: r.srcdir, - ImportPath: r.importPath, - Enclosing: enclosing, - } -} diff --git a/oracle/whicherrs.go b/oracle/whicherrs.go deleted file mode 100644 index be43c390..00000000 --- a/oracle/whicherrs.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.5 - -package oracle - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "sort" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/oracle/serial" -) - -var builtinErrorType = types.Universe.Lookup("error").Type() - -// whicherrs takes an position to an error and tries to find all types, constants -// and global value which a given error can point to and which can be checked from the -// scope where the error lives. -// In short, it returns a list of things that can be checked against in order to handle -// an error properly. -// -// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err -// can be queried recursively somehow. -func whicherrs(q *Query) error { - lconf := loader.Config{Build: q.Build} - - if err := setPTAScope(&lconf, q.Scope); err != nil { - return err - } - - // Load/parse/type-check the program. - lprog, err := lconf.Load() - if err != nil { - return err - } - q.Fset = lprog.Fset - - qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos - if err != nil { - return err - } - - prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - - ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) - if err != nil { - return err - } - - path, action := findInterestingNode(qpos.info, qpos.path) - if action != actionExpr { - return fmt.Errorf("whicherrs wants an expression; got %s", - astutil.NodeDescription(qpos.path[0])) - } - var expr ast.Expr - var obj types.Object - switch n := path[0].(type) { - case *ast.ValueSpec: - // ambiguous ValueSpec containing multiple names - return fmt.Errorf("multiple value specification") - case *ast.Ident: - obj = qpos.info.ObjectOf(n) - expr = n - case ast.Expr: - expr = n - default: - return fmt.Errorf("unexpected AST for expr: %T", n) - } - - typ := qpos.info.TypeOf(expr) - if !types.Identical(typ, builtinErrorType) { - return fmt.Errorf("selection is not an expression of type 'error'") - } - // Determine the ssa.Value for the expression. - var value ssa.Value - if obj != nil { - // def/ref of func/var object - value, _, err = ssaValueForIdent(prog, qpos.info, obj, path) - } else { - value, _, err = ssaValueForExpr(prog, qpos.info, path) - } - if err != nil { - return err // e.g. trivially dead code - } - - // Defer SSA construction till after errors are reported. - prog.Build() - - globals := findVisibleErrs(prog, qpos) - constants := findVisibleConsts(prog, qpos) - - res := &whicherrsResult{ - qpos: qpos, - errpos: expr.Pos(), - } - - // TODO(adonovan): the following code is heavily duplicated - // w.r.t. "pointsto". Refactor? - - // Find the instruction which initialized the - // global error. If more than one instruction has stored to the global - // remove the global from the set of values that we want to query. - allFuncs := ssautil.AllFunctions(prog) - for fn := range allFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - store, ok := instr.(*ssa.Store) - if !ok { - continue - } - gval, ok := store.Addr.(*ssa.Global) - if !ok { - continue - } - gbl, ok := globals[gval] - if !ok { - continue - } - // we already found a store to this global - // The normal error define is just one store in the init - // so we just remove this global from the set we want to query - if gbl != nil { - delete(globals, gval) - } - globals[gval] = store.Val - } - } - } - - ptaConfig.AddQuery(value) - for _, v := range globals { - ptaConfig.AddQuery(v) - } - - ptares := ptrAnalysis(ptaConfig) - valueptr := ptares.Queries[value] - for g, v := range globals { - ptr, ok := ptares.Queries[v] - if !ok { - continue - } - if !ptr.MayAlias(valueptr) { - continue - } - res.globals = append(res.globals, g) - } - pts := valueptr.PointsTo() - dedup := make(map[*ssa.NamedConst]bool) - for _, label := range pts.Labels() { - // These values are either MakeInterfaces or reflect - // generated interfaces. For the purposes of this - // analysis, we don't care about reflect generated ones - makeiface, ok := label.Value().(*ssa.MakeInterface) - if !ok { - continue - } - constval, ok := makeiface.X.(*ssa.Const) - if !ok { - continue - } - c := constants[*constval] - if c != nil && !dedup[c] { - dedup[c] = true - res.consts = append(res.consts, c) - } - } - concs := pts.DynamicTypes() - concs.Iterate(func(conc types.Type, _ interface{}) { - // go/types is a bit annoying here. - // We want to find all the types that we can - // typeswitch or assert to. This means finding out - // if the type pointed to can be seen by us. - // - // For the purposes of this analysis, the type is always - // either a Named type or a pointer to one. - // There are cases where error can be implemented - // by unnamed types, but in that case, we can't assert to - // it, so we don't care about it for this analysis. - var name *types.TypeName - switch t := conc.(type) { - case *types.Pointer: - named, ok := t.Elem().(*types.Named) - if !ok { - return - } - name = named.Obj() - case *types.Named: - name = t.Obj() - default: - return - } - if !isAccessibleFrom(name, qpos.info.Pkg) { - return - } - res.types = append(res.types, &errorType{conc, name}) - }) - sort.Sort(membersByPosAndString(res.globals)) - sort.Sort(membersByPosAndString(res.consts)) - sort.Sort(sorterrorType(res.types)) - - q.result = res - return nil -} - -// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil. -func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value { - globals := make(map[*ssa.Global]ssa.Value) - for _, pkg := range prog.AllPackages() { - for _, mem := range pkg.Members { - gbl, ok := mem.(*ssa.Global) - if !ok { - continue - } - gbltype := gbl.Type() - // globals are always pointers - if !types.Identical(deref(gbltype), builtinErrorType) { - continue - } - if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) { - continue - } - globals[gbl] = nil - } - } - return globals -} - -// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil. -func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst { - constants := make(map[ssa.Const]*ssa.NamedConst) - for _, pkg := range prog.AllPackages() { - for _, mem := range pkg.Members { - obj, ok := mem.(*ssa.NamedConst) - if !ok { - continue - } - consttype := obj.Type() - if !types.AssignableTo(consttype, builtinErrorType) { - continue - } - if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) { - continue - } - constants[*obj.Value] = obj - } - } - - return constants -} - -type membersByPosAndString []ssa.Member - -func (a membersByPosAndString) Len() int { return len(a) } -func (a membersByPosAndString) Less(i, j int) bool { - cmp := a[i].Pos() - a[j].Pos() - return cmp < 0 || cmp == 0 && a[i].String() < a[j].String() -} -func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type sorterrorType []*errorType - -func (a sorterrorType) Len() int { return len(a) } -func (a sorterrorType) Less(i, j int) bool { - cmp := a[i].obj.Pos() - a[j].obj.Pos() - return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String() -} -func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type errorType struct { - typ types.Type // concrete type N or *N that implements error - obj *types.TypeName // the named type N -} - -type whicherrsResult struct { - qpos *queryPos - errpos token.Pos - globals []ssa.Member - consts []ssa.Member - types []*errorType -} - -func (r *whicherrsResult) display(printf printfFunc) { - if len(r.globals) > 0 { - printf(r.qpos, "this error may point to these globals:") - for _, g := range r.globals { - printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg)) - } - } - if len(r.consts) > 0 { - printf(r.qpos, "this error may contain these constants:") - for _, c := range r.consts { - printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg)) - } - } - if len(r.types) > 0 { - printf(r.qpos, "this error may contain these dynamic types:") - for _, t := range r.types { - printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ)) - } - } -} - -func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) { - we := &serial.WhichErrs{} - we.ErrPos = fset.Position(r.errpos).String() - for _, g := range r.globals { - we.Globals = append(we.Globals, fset.Position(g.Pos()).String()) - } - for _, c := range r.consts { - we.Constants = append(we.Constants, fset.Position(c.Pos()).String()) - } - for _, t := range r.types { - var et serial.WhichErrsType - et.Type = r.qpos.typeString(t.typ) - et.Position = fset.Position(t.obj.Pos()).String() - we.Types = append(we.Types, et) - } - res.WhichErrs = we -}