diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c3d1216f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.16-alpine as builder +WORKDIR /build +COPY . . +#RUN go mod download +RUN go build -o /example ./main.go +FROM alpine:3 +WORKDIR /app +COPY --from=builder /example /app/example +ENTRYPOINT ["/app/example"] \ No newline at end of file diff --git a/Dockerfile2 b/Dockerfile2 new file mode 100644 index 00000000..9b6fbb2a --- /dev/null +++ b/Dockerfile2 @@ -0,0 +1,172 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=alpine:3.17 +ARG JS_IMAGE=node:18-alpine3.17 +ARG JS_PLATFORM=linux/amd64 +ARG GO_IMAGE=golang:1.20.1-alpine3.17 + +ARG GO_SRC=go-builder +ARG JS_SRC=js-builder + +FROM --platform=${JS_PLATFORM} ${JS_IMAGE} as js-builder + +ENV NODE_OPTIONS=--max_old_space_size=8000 + +WORKDIR /tmp/grafana + +COPY package.json yarn.lock .yarnrc.yml ./ +COPY .yarn .yarn +COPY packages packages +COPY plugins-bundled plugins-bundled + +RUN yarn install --immutable + +COPY tsconfig.json .eslintrc .editorconfig .browserslistrc .prettierrc.js babel.config.json .linguirc ./ +COPY public public +COPY scripts scripts +COPY emails emails + +ENV NODE_ENV production +RUN yarn build + +FROM ${GO_IMAGE} as go-builder + +ARG GO_BUILD_TAGS="oss" +ARG WIRE_TAGS="oss" +ARG BINGO="true" + +# Install build dependencies +RUN if grep -i -q alpine /etc/issue; then \ + apk add --no-cache gcc g++ make git; \ + fi + +WORKDIR /tmp/grafana + +COPY go.* ./ +COPY .bingo .bingo + +RUN go mod download +RUN if [[ "$BINGO" = "true" ]]; then \ + go install github.com/bwplotka/bingo@latest && \ + bingo get -v; \ + fi + +COPY embed.go Makefile build.go package.json ./ +COPY cue.mod cue.mod +COPY kinds kinds +COPY local local +COPY packages/grafana-schema packages/grafana-schema +COPY public/app/plugins public/app/plugins +COPY public/api-merged.json public/api-merged.json +COPY pkg pkg +COPY scripts scripts +COPY conf conf +COPY .github .github +COPY .git .git + +RUN make build-go GO_BUILD_TAGS=${GO_BUILD_TAGS} WIRE_TAGS=${WIRE_TAGS} + +FROM ${BASE_IMAGE} as tgz-builder + +WORKDIR /tmp/grafana + +ARG GRAFANA_TGZ="grafana-latest.linux-x64-musl.tar.gz" + +COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz + +# add -v to make tar print every file it extracts +RUN tar x -z -f /tmp/grafana.tar.gz --strip-components=1 + +# helpers for COPY --from +FROM ${GO_SRC} as go-src +FROM ${JS_SRC} as js-src + +# Final stage +FROM ${BASE_IMAGE} + +LABEL maintainer="Grafana Labs " + +ARG GF_UID="472" +ARG GF_GID="0" + +ENV PATH="/usr/share/grafana/bin:$PATH" \ + GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \ + GF_PATHS_DATA="/var/lib/grafana" \ + GF_PATHS_HOME="/usr/share/grafana" \ + GF_PATHS_LOGS="/var/log/grafana" \ + GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \ + GF_PATHS_PROVISIONING="/etc/grafana/provisioning" + +WORKDIR $GF_PATHS_HOME + +# Install dependencies +RUN if grep -i -q alpine /etc/issue; then \ + apk add --no-cache ca-certificates bash curl tzdata musl-utils && \ + apk info -vv | sort; \ + elif grep -i -q ubuntu /etc/issue; then \ + DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y ca-certificates curl tzdata && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/*; \ + else \ + echo 'ERROR: Unsupported base image' && /bin/false; \ + fi + +# glibc support for alpine x86_64 only +RUN if grep -i -q alpine /etc/issue && [ `arch` = "x86_64" ]; then \ + wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ + wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk \ + -O /tmp/glibc-2.35-r0.apk && \ + wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-bin-2.35-r0.apk \ + -O /tmp/glibc-bin-2.35-r0.apk && \ + apk add --force-overwrite --no-cache /tmp/glibc-2.35-r0.apk /tmp/glibc-bin-2.35-r0.apk && \ + rm -f /lib64/ld-linux-x86-64.so.2 && \ + ln -s /usr/glibc-compat/lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 && \ + rm -f /tmp/glibc-2.35-r0.apk && \ + rm -f /tmp/glibc-bin-2.35-r0.apk && \ + rm -f /lib/ld-linux-x86-64.so.2 && \ + rm -f /etc/ld.so.cache; \ + fi + +COPY --from=go-src /tmp/grafana/conf ./conf + +RUN if [ ! $(getent group "$GF_GID") ]; then \ + if grep -i -q alpine /etc/issue; then \ + addgroup -S -g $GF_GID grafana; \ + else \ + addgroup --system --gid $GF_GID grafana; \ + fi; \ + fi && \ + GF_GID_NAME=$(getent group $GF_GID | cut -d':' -f1) && \ + mkdir -p "$GF_PATHS_HOME/.aws" && \ + if grep -i -q alpine /etc/issue; then \ + adduser -S -u $GF_UID -G "$GF_GID_NAME" grafana; \ + else \ + adduser --system --uid $GF_UID --ingroup "$GF_GID_NAME" grafana; \ + fi && \ + mkdir -p "$GF_PATHS_PROVISIONING/datasources" \ + "$GF_PATHS_PROVISIONING/dashboards" \ + "$GF_PATHS_PROVISIONING/notifiers" \ + "$GF_PATHS_PROVISIONING/plugins" \ + "$GF_PATHS_PROVISIONING/access-control" \ + "$GF_PATHS_PROVISIONING/alerting" \ + "$GF_PATHS_LOGS" \ + "$GF_PATHS_PLUGINS" \ + "$GF_PATHS_DATA" && \ + cp conf/sample.ini "$GF_PATHS_CONFIG" && \ + cp conf/ldap.toml /etc/grafana/ldap.toml && \ + chown -R "grafana:$GF_GID_NAME" "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \ + chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" + +COPY --from=go-src /tmp/grafana/bin/grafana* /tmp/grafana/bin/*/grafana* ./bin/ +COPY --from=js-src /tmp/grafana/public ./public + +EXPOSE 3000 + +ARG RUN_SH=./packaging/docker/run.sh + +COPY ${RUN_SH} /run.sh + +USER "$GF_UID" +ENTRYPOINT [ "/run.sh" ] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..04db6776 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,5 @@ +@Library('shared-pipeline@feature/test') _ + + + +testpipe() \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md deleted file mode 100644 index 13b9972f..00000000 --- a/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Go example projects - -[![Go Reference](https://pkg.go.dev/badge/golang.org/x/example.svg)](https://pkg.go.dev/golang.org/x/example) - -This repository contains a collection of Go programs and libraries that -demonstrate the language, standard libraries, and tools. - -## Clone the project - -``` -$ git clone https://go.googlesource.com/example -$ cd example -``` -https://go.googlesource.com/example is the canonical Git repository. -It is mirrored at https://github.com/golang/example. -## [hello](hello/) and [stringutil](stringutil/) - -``` -$ cd hello -$ go build -``` -A trivial "Hello, world" program that uses a stringutil package. - -Command [hello](hello/) covers: - -* The basic form of an executable command -* Importing packages (from the standard library and the local repository) -* Printing strings ([fmt](//golang.org/pkg/fmt/)) - -Library [stringutil](stringutil/) covers: - -* The basic form of a library -* Conversion between string and []rune -* Table-driven unit tests ([testing](//golang.org/pkg/testing/)) - -## [outyet](outyet/) - -``` -$ cd outyet -$ go build -``` -A web server that answers the question: "Is Go 1.x out yet?" - -Topics covered: - -* Command-line flags ([flag](//golang.org/pkg/flag/)) -* Web servers ([net/http](//golang.org/pkg/net/http/)) -* HTML Templates ([html/template](//golang.org/pkg/html/template/)) -* Logging ([log](//golang.org/pkg/log/)) -* Long-running background processes -* Synchronizing data access between goroutines ([sync](//golang.org/pkg/sync/)) -* Exporting server state for monitoring ([expvar](//golang.org/pkg/expvar/)) -* Unit and integration tests ([testing](//golang.org/pkg/testing/)) -* Dependency injection -* Time ([time](//golang.org/pkg/time/)) - -## [appengine-hello](appengine-hello/) - -A trivial "Hello, world" App Engine application intended to be used as the -starting point for your own code. Please see -[Google App Engine SDK for Go](https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go) -and [Quickstart for Go in the App Engine Standard Environment](https://cloud.google.com/appengine/docs/standard/go/quickstart). - -## [gotypes](gotypes/) - -The `go/types` package is a type-checker for Go programs. It is one of the most -complex packages in Go's standard library, so we have provided this tutorial to -help you find your bearings. It comes with several example programs that you -can obtain using `go get` and play with as you learn to build tools that analyze -or manipulate Go programs. - -## [template](template/) - -A trivial web server that demonstrates the use of the -[`template` package](https://golang.org/pkg/text/template/)'s `block` feature. diff --git a/appengine-hello/README.md b/appengine-hello/README.md deleted file mode 100644 index ace365ea..00000000 --- a/appengine-hello/README.md +++ /dev/null @@ -1,11 +0,0 @@ -This code is a starting point for your Google App Engine application in -Go. - -To run the application locally you need the install the [Go Cloud -SDK](https://cloud.google.com/appengine/docs/go/#Go_tools) and execute the next -command from the directory containing this file: - - $ goapp serve app.yaml - -To deploy the application you have to first create an App Engine project -and use it as the application file in all the yaml files. diff --git a/appengine-hello/app.go b/appengine-hello/app.go deleted file mode 100644 index 49b3a4f1..00000000 --- a/appengine-hello/app.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package hello is a simple App Engine application that replies to requests -// on /hello with a welcoming message. -package hello - -import ( - "fmt" - "net/http" -) - -// init is run before the application starts serving. -func init() { - // Handle all requests with path /hello with the helloHandler function. - http.HandleFunc("/hello", helloHandler) -} - -func helloHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "Hello from the Go app") -} diff --git a/appengine-hello/app.yaml b/appengine-hello/app.yaml deleted file mode 100644 index f8fa15c2..00000000 --- a/appengine-hello/app.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2015 Google Inc. All rights reserved. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http:#www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to writing, software distributed -# under the License is distributed on a "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. - -# See the License for the specific language governing permissions and -# limitations under the License. - -application: you-application-id -version: 1 -runtime: go -api_version: go1 - -handlers: -- url: /hello - script: _go_app - -- url: /favicon.ico - static_files: static/favicon.ico - upload: static/favicon.ico - -- url: / - static_files: static/index.html - upload: static/index.html - -- url: / - static_dir: static diff --git a/appengine-hello/static/favicon.ico b/appengine-hello/static/favicon.ico deleted file mode 100644 index d287722d..00000000 Binary files a/appengine-hello/static/favicon.ico and /dev/null differ diff --git a/appengine-hello/static/index.html b/appengine-hello/static/index.html deleted file mode 100644 index 612d2b06..00000000 --- a/appengine-hello/static/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - Hello, world - - - - - -

Hello, world

- - -

Click on the button to fetch the message.

- - - diff --git a/appengine-hello/static/script.js b/appengine-hello/static/script.js deleted file mode 100644 index 7bf47578..00000000 --- a/appengine-hello/static/script.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2015 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -"use strict"; - -function fetchMessage() { - var xmlHttp = new XMLHttpRequest(); - xmlHttp.open("GET", "/hello", false); - xmlHttp.send(null); - document.getElementById("message").innerHTML = xmlHttp.responseText; -} \ No newline at end of file diff --git a/appengine-hello/static/style.css b/appengine-hello/static/style.css deleted file mode 100644 index 9169ad58..00000000 --- a/appengine-hello/static/style.css +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2015 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -h1 { - text-align: center; -} \ No newline at end of file diff --git a/go.mod b/go.mod deleted file mode 100644 index c6b3f6b0..00000000 --- a/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module golang.org/x/example - -go 1.15 - -require golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0 diff --git a/go.sum b/go.sum deleted file mode 100644 index 18a9ad7a..00000000 --- a/go.sum +++ /dev/null @@ -1,24 +0,0 @@ -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0 h1:iZhiQWrjyEuXG495d9MXkcmhrlxbQyGp0uNBY+YBZDk= -golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gotypes/Makefile b/gotypes/Makefile deleted file mode 100644 index 8d40c92e..00000000 --- a/gotypes/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -all: README.md - go build ./... - -README.md: go-types.md weave.go $(wildcard */*.go) - go run weave.go go-types.md >README.md - -# This is for previewing using github. -# $HOME/markdown must be a github client. -test: README.md - cp README.md $$HOME/markdown/ - (cd $$HOME/markdown/ && git commit -m . README.md && git push) - diff --git a/gotypes/README.md b/gotypes/README.md deleted file mode 100644 index e6883f51..00000000 --- a/gotypes/README.md +++ /dev/null @@ -1,2554 +0,0 @@ - - -# `go/types`: The Go Type Checker - -This document is maintained by Alan Donovan `adonovan@google.com`. - -[October 2015 GothamGo talk on go/types](https://docs.google.com/presentation/d/13OvHYozAUBeISPRoLgG7kMBuja1NsU1D_mMlmbaYojk/view) - - -# Contents - -1. [Introduction](#introduction) -1. [An Example](#an-example) -1. [Objects](#objects) -1. [Identifier Resolution](#identifier-resolution) -1. [Scopes](#scopes) -1. [Initialization Order](#initialization-order) -1. [Types](#types) - 1. [Basic types](#basic-types) - 1. [Simple Composite Types](#simple-composite-types) - 1. [Struct Types](#struct-types) - 1. [Tuple Types](#tuple-types) - 1. [Function and Method Types](#function-and-method-types) - 1. [Named Types](#named-types) - 1. [Interface Types](#interface-types) - 1. [TypeAndValue](#typeandvalue) -1. [Selections](#selections) -1. [Ids](#ids) -1. [Method Sets](#method-sets) -1. [Constants](#constants) -1. [Size and Alignment](#size-and-alignment) -1. [Imports](#imports) -1. [Formatting support](#formatting-support) -1. [Getting from A to B](#getting-from-a-to-b) - -# Introduction - - -The [`go/types` package]('https://golang.org/pkg/go/types) is a -type-checker for Go programs, designed by Robert Griesemer. -It became part of Go's standard library in Go 1.5. -Measured by lines of code and by API surface area, it is one of the -most complex packages in Go's standard library, and using it requires -a firm grasp of the structure of Go programs. -This tutorial will help you find your bearings. -It comes with several example programs that you can obtain with `go get` and play with. -We assume you are a proficient Go programmer who wants to build tools -to analyze or manipulate Go programs and that you have some knowledge -of how a typical compiler works. - -The type checker complements several existing -standard packages for analyzing Go programs. -We've listed them below. - - - → go/types - go/constant - go/parser - go/ast - go/scanner - go/token - - -Starting at the bottom, the -[`go/token` package](http://golang.org/pkg/go/token) -defines the lexical tokens of Go. -The [`go/scanner` package](http://golang.org/pkg/go/scanner) tokenizes an input stream and records -file position information for use in diagnostics -or for file surgery in a refactoring tool. -The [`go/ast` package](http://golang.org/pkg/go/ast) -defines the data types of the abstract syntax tree (AST). -The [`go/parser` package](http://golang.org/pkg/go/parser) -provides a robust recursive-descent parser that constructs the AST. -And [`go/constant`](http://golang.org/pkg/go/constant) -provides representations and arithmetic operations for the values of compile-time -constant expressions, as we'll see in -[Constants](#constants). - - - -The [`golang.org/x/tools/go/loader` package](https://pkg.go.dev/golang.org/x/tools/go/loader) -from the `x/tools` repository is a client of the type -checker that loads, parses, and type-checks a complete Go program from -source code. -We use it in some of our examples and you may find it useful too. - - - -The Go type checker does three main things. -First, for every name in the program, it determines which declaration -the name refers to; this is known as _identifier resolution_. -Second, for every expression in the program, it determines what type -that expression has, or reports an error if the expression has no -type, or has an inappropriate type for its context; this is known as -_type deduction_. -Third, for every constant expression in the program, it determines the -value of that constant; this is known as _constant evaluation_. - - - -Superficially, it appears that these three processes could be done -sequentially, in the order above, but perhaps surprisingly, they must -be done together. -For example, the value of a constant may depend on the type of an -expression due to operators like `unsafe.Sizeof`. -Conversely, the type of an expression may depend on the value of a -constant, since array types contain constants. -As a result, type deduction and constant evaluation must be done -together. - - - -As another example, we cannot resolve the identifier `k` in the composite -literal `T{k: 0}` until we know whether `T` is a struct type. -If it is, then `k` must be found among `T`'s fields. -If not, then `k` is an ordinary reference -to a constant or variable in the lexical environment. -Consequently, identifier resolution and type deduction are also -inseparable in the general case. - - - -Nonetheless, the three processes of identifier resolution, type -deduction, and constant evaluation can be separated for the purpose of -explanation. - - -# An Example - - -The code below shows the most basic use of the type checker to check -the _hello, world_ program, supplied as a string. -Later examples will be variations on this one, and we'll often omit -boilerplate details such as parsing. -To check out and build the examples, -run `go get golang.org/x/example/gotypes/...`. - - - // go get golang.org/x/example/gotypes/pkginfo - -``` -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -const hello = `package main - -import "fmt" - -func main() { - fmt.Println("Hello, world") -}` - -func main() { - fset := token.NewFileSet() - - // Parse the input string, []byte, or io.Reader, - // recording position information in fset. - // ParseFile returns an *ast.File, a syntax tree. - f, err := parser.ParseFile(fset, "hello.go", hello, 0) - if err != nil { - log.Fatal(err) // parse error - } - - // A Config controls various options of the type checker. - // The defaults work fine except for one setting: - // we must specify how to deal with imports. - conf := types.Config{Importer: importer.Default()} - - // Type-check the package containing only file f. - // Check returns a *types.Package. - pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil) - if err != nil { - log.Fatal(err) // type error - } - - fmt.Printf("Package %q\n", pkg.Path()) - fmt.Printf("Name: %s\n", pkg.Name()) - fmt.Printf("Imports: %s\n", pkg.Imports()) - fmt.Printf("Scope: %s\n", pkg.Scope()) -} -``` - - -First, the program creates a -[`token.FileSet`](http://golang.org/pkg/go/token/#FileSet). -To avoid the need to store file names and line and column -numbers in every node of the syntax tree, the `go/token` package -provides `FileSet`, a data structure that stores this information -compactly for a sequence of files. -A `FileSet` records each file name only once, and records -only the byte offsets of each newline, allowing a position within -any file to be identified using a small integer called a -`token.Pos`. -Many tools create a single `FileSet` at startup. -Any part of the program that needs to convert a `token.Pos` into -an intelligible location---as part of an error message, for -instance---must have access to the `FileSet`. - - - -Second, the program parses the input string. -More realistic packages contain several source files, so the parsing -step must be repeated for each one, or better, done in parallel. -Third, it creates a `Config` that specifies type-checking options. -Since the _hello, world_ program uses imports, we must indicate -how to locate the imported packages. -Here we use `importer.Default()`, which loads compiler-generated -export data, but we'll explore alternatives in [Imports](#imports). - - - -Fourth, the program calls `Check`. -This creates a `Package` whose path is `"cmd/hello"`, and -type-checks each of the specified files---just one in this example. -The final (nil) argument is a pointer to an optional `Info` -struct that returns additional deductions from the type checker; more -on that later. -`Check` returns a `Package` even when it also returns an error. -The type checker is robust to ill-formed input, -and goes to great lengths to report accurate -partial information even in the vicinity of syntax or type errors. -`Package` has this definition: - - - type Package struct{ ... } - func (*Package) Path() string - func (*Package) Name() string - func (*Package) Scope() *Scope - func (*Package) Imports() []*Package - - -Finally, the program prints the attributes of the package, shown below. -(The hexadecimal number may vary from one run to the next.) - - -``` -$ go build golang.org/x/example/gotypes/pkginfo -$ ./pkginfo -Package "cmd/hello" -Name: main -Imports: [package fmt ("fmt")] -Scope: package "cmd/hello" scope 0x820533590 { -. func cmd/hello.main() -} -``` - - -A package's `Path`, such as `"encoding/json"`, is the string -by which import declarations identify it. -It is unique within a `$GOPATH` workspace, -and for published packages it must be globally unique. - - -A package's `Name` is the identifier in the `package` -declaration of each source file within the package, such as `json`. -The type checker reports an error if not all the package declarations in -the package agree. -The package name determines how the package is known when it is -imported into a file (unless a renaming import is used), -but is otherwise not visible to a program. - - -`Scope` returns the package's [_lexical block_](#scopes), -which provides access to all the named entities or -[_objects_](#objects) declared at package level. -`Imports` returns the set of packages directly imported by this -one, and may be useful for computing dependencies -([Initialization Order](#initialization-order)). - - - -# Objects - - -The task of identifier resolution is to map every identifier in the -syntax tree, that is, every `ast.Ident`, to an object. -For our purposes, an _object_ is a named entity created by a -declaration, such as a `var`, `type`, or `func` -declaration. -(This is different from the everyday meaning of object in -object-oriented programming.) - - - -Objects are represented by the `Object` interface: - - - type Object interface { - Name() string // package-local object name - Exported() bool // reports whether the name starts with a capital letter - Type() Type // object type - Pos() token.Pos // position of object identifier in declaration - - Parent() *Scope // scope in which this object is declared - Pkg() *Package // nil for objects in the Universe scope and labels - Id() string // object id (see Ids section below) - } - - -The first four methods are straightforward; we'll explain the other -three later. -`Name` returns the object's name---an identifier. -`Exported` is a convenience method that reports whether the first -letter of `Name` is a capital, indicating that the object may be -visible from outside the package. -It's a shorthand for `ast.IsExported(obj.Name())`. -`Type` returns the object's type; we'll come back to that in -[Types](#types). - - - -`Pos` returns the source position of the object's declaring identifier. -To make sense of a `token.Pos`, we need to call the -`(*token.FileSet).Position` method, which returns a struct with -individual fields for the file name, line number, column, and byte -offset, though usually we just call its `String` method: - - - fmt.Println(fset.Position(obj.Pos())) // "hello.go:10:6" - - -Not all objects carry position information. -Since the file format for compiler export data ([Imports](#imports)) -does not record position information, calling `Pos` on an object -imported from such a file returns zero, also known as -`token.NoPos`. - - - -There are eight kinds of objects in the Go type checker. -Most familiar are the kinds that can be declared at package level: -constants, variables, functions, and types. -Less familiar are statement labels, imported package names -(such as `json` in a file containing an `import "encoding/json"` -declaration), built-in functions (such as `append` and -`len`), and the pre-declared `nil`. -The eight types shown below are the only concrete types that satisfy -the `Object` interface. -In other words, `Object` is a _discriminated union_ of 8 -possible types, and we commonly use a type switch to distinguish them. - - - Object = *Func // function, concrete method, or abstract method - | *Var // variable, parameter, result, or struct field - | *Const // constant - | *TypeName // type name - | *Label // statement label - | *PkgName // package name, e.g. json after import "encoding/json" - | *Builtin // predeclared function such as append or len - | *Nil // predeclared nil - - -`Object`s are canonical. -That is, two `Object`s `x` and `y` denote the same -entity if and only if `x==y`. -Object identity is significant, and objects are routinely compared by -the addresses of the underlying pointers. -Although a package-level object is uniquely identified by its name -and enclosing package, for other objects there is no simple way to -obtain a string that uniquely identifies it. - - - -The `Parent` method returns the `Scope` (lexical block) in -which the object was declared; we'll come back to this in -[Scopes](#scopes). -Fields and methods are not found in the lexical environment, so -their objects have no `Parent`. - - - - -The `Pkg` method returns the `Package` to which this object -belongs, even for objects not declared at package level. -Only predeclared objects have no package. -The `Id` method will be explained in [Ids](#ids). - - - -Not all methods make sense for each kind of object. For instance, -the last four kinds above have no meaningful `Type` method. -And some kinds of objects have methods in addition to those required by the -`Object` interface: - - - func (*Func) Scope() *Scope - func (*Var) Anonymous() bool - func (*Var) IsField() bool - func (*Const) Val() constant.Value - func (*TypeName) IsAlias() bool - func (*PkgName) Imported() *Package - - -`(*Func).Scope` returns the [lexical block](#scopes) -containing the function's parameters, results, -and other local declarations. -`(*Var).IsField` distinguishes struct fields from ordinary -variables, and `(*Var).Anonymous` discriminates named fields like -the one in `struct{T T}` from anonymous fields like the one in `struct{T}`. -`(*Const).Val` returns the value of a named [constant](#constants). - - -`(*TypeName).IsAlias`, introduced in Go 1.9, reports whether the -type name is simply an alias for a type (as in `type I = int`), -as opposed to a definition of a [`Named`](#named-types) type, as -in `type Celsius float64`. - - -`(*PkgName).Imported` returns the package (for instance, -`encoding/json`) denoted by a given import name such as `json`. -Each time a package is imported, a new `PkgName` object is -created, usually with the same name as the `Package` it -denotes, but not always, as in the case of a renaming import. -`PkgName`s are objects, but `Package`s are not. -We'll look more closely at this in [Imports](#imports). - - - -All relationships between the syntax trees (`ast.Node`s) and type -checker data structures such as `Object`s and `Type`s are -stored in mappings outside the syntax tree itself. -Be aware that the `go/ast` package also defines a type called -`Object` that resembles---and predates---the type checker's -`Object`, and that `ast.Object`s are held directly by -identifiers in the AST. -They are created by the parser, which has a necessarily limited view -of the package, so the information they represent is at best partial and -in some cases wrong, as in the `T{k: 0}` example mentioned above. -If you are using the type checker, there is no reason to use the older -`ast.Object` mechanism. - - - -# Identifier Resolution - - -Identifier resolution computes the relationship between -identifiers and objects. -Its results are recorded in the `Info` struct optionally passed -to `Check`. -The fields related to identifier resolution are shown below. - - - type Info struct { - Defs map[*ast.Ident]Object - Uses map[*ast.Ident]Object - Implicits map[ast.Node]Object - Selections map[*ast.SelectorExpr]*Selection - Scopes map[ast.Node]*Scope - ... - } - - -Since not all facts computed by the type checker are needed by every -client, the API lets clients control which components of the result -should be recorded and which discarded: only fields that hold a -non-nil map will be populated during the call to `Check`. - - - -The two fields of type `map[*ast.Ident]Object` are the most important: -`Defs` records _declaring_ identifiers and -`Uses` records _referring_ identifiers. -In the example below, the comments indicate which identifiers are of -which kind. - - - var x int // def of x, use of int - fmt.Println(x) // uses of fmt, Println, and x - type T struct{U} // def of T, use of U (type), def of U (field) - - -The final line above illustrates why we don't combine `Defs` and -`Uses` into one map. -In the anonymous field declaration `struct{U}`, the -identifier `U` is both a use of the type `U` (a -`TypeName`) and a definition of the anonymous field (a -`Var`). - - - -The function below prints the location of each referring and defining -identifier in the input program, and the object it refers to. - - - // go get golang.org/x/example/gotypes/defsuses - -``` -func PrintDefsUses(fset *token.FileSet, files ...*ast.File) error { - conf := types.Config{Importer: importer.Default()} - info := &types.Info{ - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - } - _, err := conf.Check("hello", fset, files, info) - if err != nil { - return err // type error - } - - for id, obj := range info.Defs { - fmt.Printf("%s: %q defines %v\n", - fset.Position(id.Pos()), id.Name, obj) - } - for id, obj := range info.Uses { - fmt.Printf("%s: %q uses %v\n", - fset.Position(id.Pos()), id.Name, obj) - } - return nil -} -``` - - -Let's use the _hello, world_ program again as the input: - - - // go get golang.org/x/example/gotypes/hello - -``` -package main - -import "fmt" - -func main() { - fmt.Println("Hello, 世界") -} -``` - - -This is what it prints: - - -``` -$ go build golang.org/x/example/gotypes/defsuses -$ ./defsuses -hello.go:1:9: "main" defines -hello.go:5:6: "main" defines func hello.main() -hello.go:6:9: "fmt" uses package fmt -hello.go:6:13: "Println" uses func fmt.Println(a ...interface{}) (n int, err error) -``` - - -Notice that the `Defs` mapping may contain nil entries in a few -cases. -The first line of output reports that the package identifier -`main` is present in the `Defs` mapping, but has no -associated object. - - - -The `Implicits` mapping handles two cases of the syntax in -which an `Object` is declared without an `ast.Ident`, namely type -switches and import declarations. - -In the type switch below, which declares a local variable `y`, -the type of `y` is different in each case of the switch: - - - switch y := x.(type) { - case int: - fmt.Printf("%d", y) - case string: - fmt.Printf("%q", y) - default: - fmt.Print(y) - } - - -To represent this, for each single-type case, the type checker creates -a separate `Var` object for `y` with the appropriate type, -and `Implicits` maps each `ast.CaseClause` to the `Var` -for that case. -The `default` case, the `nil` case, and cases with more than one -type all use the regular `Var` object that is associated with the -identifier `y`, which is found in the `Defs` mapping. - - - -The import declaration below defines the name `json` without an -`ast.Ident`: - - - import "encoding/json" - - -`Implicits` maps this `ast.ImportSpec` to the `PkgName` -object named `json` that it implicitly declares. - - - -The `Selections` mapping, of type -`map[*ast.SelectorExpr]*Selection`, records the meaning of each -expression of the form _`expr`_`.f`, where _`expr`_ is -an expression or type and `f` is the name of a field or method. -These expressions, called _selections_, are represented by -`ast.SelectorExpr` nodes in the AST. -We'll talk more about the `Selection` type in [Selections](#selections). - - - -Not all `ast.SelectorExpr` nodes represent selections. -Expressions like `fmt.Println`, in which a package name precedes -the dot, are _qualified identifiers_. -They do not appear in the `Selections` mapping, but their -constituent identifiers (such as `fmt` and `Println`) both -appear in `Uses`. - - - -Referring identifiers that are not part of an `ast.SelectorExpr` -are _lexical references_. -That is, they are resolved to an object by searching for the -innermost enclosing lexical declaration of that name. -We'll see how that search works in the next section. - - -# Scopes - - -The `Scope` type is a mapping from names to objects. - - - type Scope struct{ ... } - - func (s *Scope) Names() []string - func (s *Scope) Lookup(name string) Object - - -`Names` returns the set of names in the mapping, in sorted order. -(It is not a simple accessor though, so call it sparingly.) -The `Lookup ` method returns the object for a given name, so we -can print all the entries or _bindings_ in a scope like this: - - - for _, name := range scope.Names() { - fmt.Println(scope.Lookup(name)) - } - - -The _scope_ of a declaration of a name is the region of -program source in which a reference to the name resolves to that -declaration. That is, scope is a property of a declaration. -However, in the `go/types` API, the `Scope` type represents -a _lexical block_, which is one component of the lexical -environment. -Consider the _hello, world_ program again: - - - package main - - import "fmt" - - func main() { - const message = "hello, world" - fmt.Println(message) - } - - -There are four lexical blocks in this program. -The outermost one is the _universe block_, which maps the -pre-declared names like `int`, `true`, and `append` to -their objects---a `TypeName`, a `Const`, and a -`Builtin`, respectively. -The universe block is represented by the global variable -`Universe`, of type `*Scope`, although it's logically a -constant so you shouldn't modify it. - - - -Next is the _package block_, which maps `"main"` to the -`main` function. -Following that is the _file block_, which maps `"fmt"` to -the `PkgName` object for this import of the `fmt` package. -And finally, the innermost block is that of function `main`, a -local block, which contains the declaration of `message`, a `Const`. -The `main` function is trivial, but many functions contain -several blocks since each `if`, `for`, `switch`, -`case`, or `select` statement creates at least one -additional block. -Local blocks nest to arbitrary depths. - - - -The structure of the lexical environment thus forms a tree, with the -universe block at the root, the package blocks beneath it, the file -blocks beneath them, and then any number of local blocks beneath the -files. -We can access and navigate this tree structure with the following -methods of `Scope`: - - - func (s *Scope) Parent() *Scope - func (s *Scope) NumChildren() int - func (s *Scope) Child(i int) *Scope - - -`Parent` lets us walk up the tree, and `Child` -lets us walk down it. -Note that although the `Parent` of every package `Scope` is -`Universe`, `Universe` has no children. -This asymmetry is a consequence of using a global variable to hold -`Universe`. - - - -To obtain the universe block, we use the `Universe` global variable. -To obtain the lexical block of a `Package`, we call its -`Scope` method. -To obtain the scope of a file (`*ast.File`), or any smaller piece -of syntax such as an `*ast.IfStmt`, we consult the `Scopes` -mapping in the `Info` struct, which maps each block-creating -syntax node to its block. -The lexical block of a named function or method can also be obtained -by calling its `(*Func).Scope` method. - - - - - -To look up a name in the lexical environment, we must search the tree -of lexical blocks, starting at a particular `Scope` and walking -up to the root until a declaration of the name is found. -For convenience, the `LookupParent` method does this, returning -not just the object, if found, but also the `Scope` in which it was -declared, which may be an ancestor of the initial one: - - - func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) - - -The `pos` parameter determines the position in the source code at -which the name should be resolved. -The effective lexical environment is different at each point in the -block because it depends on which local declarations appear -before or after that point. -(We'll see an illustration in a moment.) - - - -`Scope` has several other methods relating to source positions: - - - func (s *Scope) Pos() token.Pos - func (s *Scope) End() token.Pos - func (s *Scope) Contains(pos token.Pos) bool - func (s *Scope) Innermost(pos token.Pos) *Scope - - -`Pos` and `End` report the `Scope`'s start and end -position which, for explicit blocks, coincide with its curly -braces. -`Contains` is a convenience method that reports whether a -position lies in this interval. -`Innermost` returns the innermost scope containing the specified -position, which may be a child or other descendent of the initial -scope. - - - -These features are useful for tools that wish to resolve names or -evaluate constant expressions as if they had appeared at a particular -point within the program. -The next example program finds all the comments in the input, -treating the contents of each one as a name. It looks up each name in -the environment at the position of the comment, and prints what it -finds. -Observe that the `ParseComments` flag directs the parser to -preserve comments in the input. - - - // go get golang.org/x/example/gotypes/lookup - -``` -func main() { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments) - if err != nil { - log.Fatal(err) // parse error - } - - conf := types.Config{Importer: importer.Default()} - pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil) - if err != nil { - log.Fatal(err) // type error - } - - // Each comment contains a name. - // Look up that name in the innermost scope enclosing the comment. - for _, comment := range f.Comments { - pos := comment.Pos() - name := strings.TrimSpace(comment.Text()) - fmt.Printf("At %s,\t%q = ", fset.Position(pos), name) - inner := pkg.Scope().Innermost(pos) - if _, obj := inner.LookupParent(name, pos); obj != nil { - fmt.Println(obj) - } else { - fmt.Println("not found") - } - } -} -``` - - -The expression `pkg.Scope().Innermost(pos)` finds the innermost -`Scope` that encloses the comment, and `LookupParent(name, pos)` -does a name lookup at a specific position in that lexical block. - - - -A typical input is shown below. -The first comment causes a lookup of `"append"` in the file block. -The second comment looks up `"fmt"` in the `main` function's block, -and so on. - - -``` -const hello = ` -package main - -import "fmt" - -// append -func main() { - // fmt - fmt.Println("Hello, world") - // main - main, x := 1, 2 - // main - print(main, x) - // x -} -// x -` -``` - - -Here's the output: - - -``` -$ go build golang.org/x/example/gotypes/lookup -$ ./lookup -At hello.go:6:1, "append" = builtin append -At hello.go:8:9, "fmt" = package fmt -At hello.go:10:9, "main" = func cmd/hello.main() -At hello.go:12:9, "main" = var main int -At hello.go:14:9, "x" = var x int -At hello.go:16:1, "x" = not found -``` - - -Notice how the two lookups of `main` return different results, -even though they occur in the same block, because one precedes the -declaration of the local variable named `main` and the other -follows it. -Also notice that there are two lookups of the name `x` but only -the first one, in the function block, succeeds. - - - -Download the program and modify both the input program and -the set of comments to get a better feel for how name resolution works. - - - -The table below summarizes which kinds of objects may be declared at -each level of the tree of lexical blocks. - - - Universe File Package Local - Builtin ✔ - Nil ✔ - Const ✔ ✔ ✔ - TypeName ✔ ✔ ✔ - Func ✔ - Var ✔ ✔ - PkgName ✔ - Label ✔ - - -# Initialization Order - - -In the course of identifier resolution, the type checker constructs a -graph of references among declarations of package-level variables and -functions. -The type checker reports an error if the initializer expression for a -variable refers to that variable, whether directly or indirectly. - - - -The reference graph determines the initialization order of the -package-level variables, as required by the Go spec, using a -breadth-first algorithm. -First, variables in the graph with no successors are removed, sorted -into the order in which they appear in the source code, then added -to a list. This creates more variables that have no successors. -The process repeats until they have all been removed. - - - -The result is available in the `InitOrder` field of the -`Info` struct, whose type is `[]Initializer`. - - - type Info struct { - ... - InitOrder []Initializer - ... - } - - type Initializer struct { - Lhs []*Var // var Lhs = Rhs - Rhs ast.Expr - } - - -Each element of the list represents a single initializer expression -that must be executed, and the variables to which it is assigned. -The variables may number zero, one, or more, as in these examples: - - - var _ io.Writer = new(bytes.Buffer) - var rx = regexp.MustCompile("^b(an)*a$") - var cwd, cwdErr = os.Getwd() - - -This process governs the initialization order of variables within a -package. -Across packages, dependencies must be initialized first, although the -order among them is not specified. -That is, any topological order of the import graph will do. -The `(*Package).Imports` method returns the set of direct -dependencies of a package. - - -# Types - - -The main job of the type checker is, of course, to deduce the type -of each expression and to report type errors. -Like `Object`, `Type` is an interface type used as a -discriminated union of several concrete types but, unlike -`Object`, `Type` has very few methods because types have -little in common with each other. -Here is the interface: - - - type Type interface { - Underlying() Type - } - - -And here are the eleven concrete types that satisfy it: - - - Type = *Basic - | *Pointer - | *Array - | *Slice - | *Map - | *Chan - | *Struct - | *Tuple - | *Signature - | *Named - | *Interface - - -With the exception of `Named` types, instances of `Type` are -not canonical. -That is, it is usually a mistake to compare types using `t1==t2` -since this equivalence is not the same as the -[type identity relation](https://golang.org/ref/spec#Type_identity) -defined by the Go spec. -Use this function instead: - - - func Identical(t1, t2 Type) bool - - -For the same reason, you should not use a `Type` as a key in a map. -The [`golang.org/x/tools/go/types/typeutil` package](https://godoc.org/golang.org/x/tools/go/types/typeutil) -provides a map keyed by types that uses the correct -equivalence relation. - - -The Go spec defines three relations over types. -[_Assignability_](https://golang.org/ref/spec#Assignability) -governs which pairs of types may appear on the -left- and right-hand side of an assignment, including implicit -assignments such as function calls, map and channel operations, and so -on. -[_Comparability_](https://golang.org/ref/spec#Comparison_operators) -determines which types may appear in a comparison `x==y` or a -switch case or may be used as a map key. -[_Convertibility_](https://golang.org/ref/spec#Conversions) -governs which pairs of types are allowed in a conversion operation -`T(v)`. -You can query these relations with the following predicate functions: - - - func AssignableTo(V, T Type) bool - func Comparable(T Type) bool - func ConvertibleTo(V, T Type) bool - - -Let's take a look at each kind of type. - - -## Basic types - - -`Basic` represents all types that are not composed from simpler -types. -This is essentially the set of underlying types that a constant expression is -permitted to have--strings, booleans, and numbers---but it also -includes `unsafe.Pointer` and untyped nil. - - - type Basic struct{...} - func (*Basic) Kind() BasicKind - func (*Basic) Name() string - func (*Basic) Info() BasicInfo - - -The `Kind` method returns an "enum" value that indicates which -basic type this is. -The kinds `Bool`, `String`, `Int16`, and so on, -represent the corresponding predeclared boolean, string, or numeric -types. -There are two synonyms: `Byte` is equivalent to `Uint8` -and `Rune` is equivalent to `Int32`. -The kind `UnsafePointer` represents `unsafe.Pointer`. -The kinds `UntypedBool`, `UntypedInt` and so on represent -the six kinds of "untyped" constant types: boolean, integer, rune, -float, complex, and string. -The kind `UntypedNil` represents the type of the predeclared -`nil` value. -And the kind `Invalid` indicates the invalid type, which is used -for expressions containing errors, or for objects without types, like -`Label`, `Builtin`, or `PkgName`. - - - -The `Name` method returns the name of the type, such as -`"float64"`, and the `Info` method returns a bitfield that -encodes information about the type, such as whether it is signed or -unsigned, integer or floating point, or real or complex. - - - -`Typ` is a table of canonical basic types, indexed by -kind, so `Typ[String]` returns the `*Basic` that represents -`string`, for instance. -Like `Universe`, `Typ` is logically a constant, so don't -modify it. - - - -A few minor subtleties: -According to the Go spec, pre-declared types such as `int` are -named types for the purposes of assignability, even though the type -checker does not represent them using `Named`. -And `unsafe.Pointer` is a pointer type for the purpose of -determining whether the receiver type of a method is legal, even -though the type checker does not represent it using `Pointer`. - - - -The "untyped" types are usually only ascribed to constant expressions, -but there is one exception. -A comparison `x==y` has type "untyped bool", so the result of -this expression may be assigned to a variable of type `bool` or -any other named boolean type. - - - -## Simple Composite Types - - -The types `Pointer`, `Array`, `Slice`, `Map`, -and `Chan` are pretty self-explanatory. -All have an `Elem` method that returns the element type `T` -for a pointer `*T`, an array `[n]T`, a slice `[]T`, a -map `map[K]T`, or a channel `chan T`. -This should feel familiar if you've used the `reflect.Value` API. - - - -In addition, the `*Map`, `*Chan`, and `*Array` types -have accessor methods that return their key type, direction, and -length, respectively: - - - func (*Map) Key() Type - func (*Chan) Dir() ChanDir // = Send | Recv | SendRecv - func (*Array) Len() int64 - - - -## Struct Types - - -A struct type has an ordered list of fields and a corresponding -ordered list of field tags. - - - type Struct struct{ ... } - func (*Struct) NumFields() int - func (*Struct) Field(i int) *Var - func (*Struct) Tag(i int) string - - -Each field is a `Var` object whose `IsField` method returns true. -Field objects have no `Parent` scope, because they are -resolved through selections, not through the lexical environment. - - - - -Thanks to embedding, the expression `new(S).f` may be a shorthand -for a longer expression such as `new(S).d.e.f`, but in the -representation of `Struct` types, these field selection -operations are explicit. -That is, the set of fields of struct type `S` does not include `f`. -An anonymous field is represented like a regular field, but its -`Anonymous` method returns true. - - - -One subtlety is relevant to tools that generate documentation. -When analyzing a declaration such as this, - - - type T struct{x int} - - -it may be tempting to consider the `Var` object for field `x` as if it -had the name `"T.x"`, but beware: field objects do not have -canonical names and there is no way to obtain the name `"T"` -from the `Var` for `x`. -That's because several types may have the same underlying struct type, -as in this code: - - - type T struct{x int} - type U T - - -Here, the `Var` for field `x` belongs equally to `T` -and to `U`, and short of inspecting source positions or walking -the AST---neither of which is possible for objects loaded from compiler -export data---it is not possible to ascertain that `x` was declared as -part of `T`. -The type checker builds the exact same data structures given this input: - - - type T U - type U struct{x int} - - -A similar issue applies to the methods of named interface types. - - -## Tuple Types - - -Like a struct, a tuple type has an ordered list of fields, and fields -may be named. - - - type Tuple struct{ ... } - func (*Tuple) Len() int - func (*Tuple) At(i int) *Var - - -Although tuples are not the type of any variable in Go, they are -the type of some expressions, such as the right-hand sides of these -assignments: - - - v, ok = m[key] - v, ok = <-ch - v, ok = x.(T) - f, err = os.Open(filename) - - -Tuples also represent the types of the parameter list and the result -list of a function, as we will see. - - - -Since empty tuples are common, the nil `*Tuple` pointer is a valid empty tuple. - - - -## Function and Method Types - - -The types of functions and methods are represented by a `Signature`, -which has a tuple of parameter types and a tuple of result types. - - - type Signature struct{ ... } - func (*Signature) Recv() *Var - func (*Signature) Params() *Tuple - func (*Signature) Results() *Tuple - func (*Signature) Variadic() bool - - -Variadic functions such as `fmt.Println` have the `Variadic` -flag set. -The final parameter of such functions is always a slice, or in the -special case of certain calls to `append`, a string. - - - -A `Signature` for a method, whether concrete or abstract, has a -non-nil receiver parameter, `Recv`. -The type of the receiver is usually a named type or a pointer to a named type, -but it may be an unnamed struct or interface type in some cases. -Method types are rather second-class: they are only used for the -`Func` objects created by method declarations, and no Go -expression has a method type. -When printing a method type, the receiver does not appear, and the -`Identical` predicate ignores the receiver. - - - -The types of `Builtin` objects like `append` cannot be -expressed as a `Signature` since those types require parametric -polymorphism. -`Builtin` objects are thus ascribed the `Invalid` basic type. -However, the type of each _call_ to a built-in function has a specific -and expressible Go type. -These types are recorded during type checking for later use -([TypeAndValue](#typeandvalue)). - - - -## Named Types - - -Type declarations come in two forms. -The simplest kind, introduced in Go 1.9, -merely declares a (possibly alternative) name for an existing type. -Type names used in this way are informally called _type aliases_. -For example, this declaration lets you use the type -`Dictionary` as an alias for `map[string]string`: - - type Dictionary = map[string]string - -The declaration creates a `TypeName` object for `Dictionary`. The -object's `IsAlias` method returns true, and its `Type` method returns -a `Map` type that represents `map[string]string`. - - -The second form of type declaration, and the only kind prior to Go -1.9, does not use an equals sign: - - type Celsius float64 - -This declaration does more than just give a name to a type. -It first defines a new `Named` type -whose underlying type is `float64`; this `Named` type is different -from any other type, including `float64`. The declaration binds the -`TypeName` object to the `Named` type. - -Since Go 1.9, the Go language specification has used the term _defined -types_ instead of named types; -the essential property of a defined type is not that it has a name, -but that it is a distinct type with its own method set. -However, the type checker API predates that -change and instead calls defined types "named" types. - - type Named struct{ ... } - func (*Named) NumMethods() int - func (*Named) Method(i int) *Func - func (*Named) Obj() *TypeName - func (*Named) Underlying() Type - -The `Named` type's `Obj` method returns the `TypeName` object, which -provides the name, position, and other properties of the declaration. -Conversely, the `TypeName` object's `Type` method returns the `Named` type. - -A `Named` type may appear as the receiver type in a method declaration. -Methods are associated with the `Named` type, not the name (the -`TypeName` object); it's possible---though cryptic---to declare a -method on a `Named` type using one of its aliases. -The `NumMethods` and `Method` methods enumerate the declared -methods associated with this `Named` type (or a pointer to it), -in the order they were declared. -However, due to the subtleties of anonymous fields and the difference -between value and pointer receivers, a named type may have more or fewer -methods than this list. We'll return to this in [Method Sets](#method-sets). - - - -Every `Type` has an `Underlying` method, but for all of them -except `*Named`, it is simply the identity function. -For a named type, `Underlying` returns its underlying type, which -is always an unnamed type. -Thus `Underlying` returns `int` for both `T` and -`U` below. - - - type T int - type U T - - -Clients of the type checker often use type assertions or type switches -with a `Type` operand. -When doing so, it is often necessary to switch on the type that -_underlies_ the type of interest, and failure to do so may be a -bug. - -This is a common pattern: - - - // handle types of composite literal - switch u := t.Underlying().(type) { - case *Struct: // ... - case *Map: // ... - case *Array, *Slice: // ... - default: - panic("impossible") - } - -## Interface Types - - -Interface types are represented by `Interface`. - - - type Interface struct{ ... } - func (*Interface) Empty() bool - func (*Interface) NumMethods() int - func (*Interface) Method(i int) *Func - func (*Interface) NumEmbeddeds() int - func (*Interface) Embedded(i int) *Named - func (*Interface) NumExplicitMethods() int - func (*Interface) ExplicitMethod(i int) *Func - - -Syntactically, an interface type has a list of explicitly declared -methods (`ExplicitMethod`), and a list of embedded named -interface types (`Embedded`), but many clients care only about -the complete set of methods, which can be enumerated via -`Method`. -All three lists are ordered by name. -Since the empty interface is an important special case, the -`Empty` predicate provides a shorthand for `NumMethods() == -0`. - - - -As with the fields of structs (see above), the methods of interfaces -may belong equally to more than one interface type. -The `Func` object for method `f` in the code below is shared -by `I` and `J`: - - - type I interface { f() } - type J I - - -Because the difference between interface (abstract) and -non-interface (concrete) types is so important in Go, the -`IsInterface` predicate is provided for convenience. - - - func IsInterface(Type) bool - - -The type checker provides three utility methods relating to interface -satisfaction: - - - func Implements(V Type, T *Interface) bool - func AssertableTo(V *Interface, T Type) bool - func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) - - -The `Implements` predicate reports whether a type satisfies an -interface type. -`MissingMethod` is like `Implements`, but instead of -returning false, it explains why a type does not satisfy the -interface, for use in diagnostics. - - - -`AssertableTo` reports whether a type assertion `v.(T)` is legal. -If `T` is a concrete type that doesn't have all the methods of -interface `v`, then the type assertion is not legal, as in this example: - - - // error: io.Writer is not assertible to int - func f(w io.Writer) int { return w.(int) } - - - -## TypeAndValue - - -The type checker records the type of each expression in another field -of the `Info` struct, namely `Types`: - - - type Info struct { - ... - Types map[ast.Expr]TypeAndValue - } - - -No entries are recorded for identifiers since the `Defs` and -`Uses` maps provide more information about them. -Also, no entries are recorded for pseudo-expressions like -`*ast.KeyValuePair` or `*ast.Ellipsis`. - - - -The value of the `Types` map is a `TypeAndValue`, which -(unsurprisingly) holds the type and value of the expression, and in -addition, its _mode_. -The mode is opaque, but has predicates to answer questions such as: -Does this expression denote a value or a type? Does this value have an -address? Does this expression appear on the left-hand side of an -assignment? Does this expression appear in a context that expects two -results? -The comments in the code below give examples of expressions that -satisfy each predicate. - - - type TypeAndValue struct { - Type Type - Value constant.Value // for constant expressions only - ... - } - - func (TypeAndValue) IsVoid() bool // e.g. "main()" - func (TypeAndValue) IsType() bool // e.g. "*os.File" - func (TypeAndValue) IsBuiltin() bool // e.g. "len(x)" - func (TypeAndValue) IsValue() bool // e.g. "*os.Stdout" - func (TypeAndValue) IsNil() bool // e.g. "nil" - func (TypeAndValue) Addressable() bool // e.g. "a[i]" but not "f()", "m[key]" - func (TypeAndValue) Assignable() bool // e.g. "a[i]", "m[key]" - func (TypeAndValue) HasOk() bool // e.g. "<-ch", "m[key]" - - -The statement below inspects every expression within the AST of a single -type-checked file and prints its type, value, and mode: - - - // go get golang.org/x/example/gotypes/typeandvalue - -``` -// f is a parsed, type-checked *ast.File. -ast.Inspect(f, func(n ast.Node) bool { - if expr, ok := n.(ast.Expr); ok { - if tv, ok := info.Types[expr]; ok { - fmt.Printf("%-24s\tmode: %s\n", nodeString(expr), mode(tv)) - fmt.Printf("\t\t\t\ttype: %v\n", tv.Type) - if tv.Value != nil { - fmt.Printf("\t\t\t\tvalue: %v\n", tv.Value) - } - } - } - return true -}) -``` - - -It makes use of these two helper functions, which are not shown: - - - // nodeString formats a syntax tree in the style of gofmt. - func nodeString(n ast.Node) string - - // mode returns a string describing the mode of an expression. - func mode(tv types.TypeAndValue) string - - -Given this input: - - -``` -const input = ` -package main - -var m = make(map[string]int) - -func main() { - v, ok := m["hello, " + "world"] - print(rune(v), ok) -} -` -``` - - -the program prints: - - -``` -$ go build golang.org/x/example/gotypes/typeandvalue -$ ./typeandvalue -make(map[string]int) mode: value - type: map[string]int -make mode: builtin - type: func(map[string]int) map[string]int -map[string]int mode: type - type: map[string]int -string mode: type - type: string -int mode: type - type: int -m["hello, "+"world"] mode: value,assignable,ok - type: (int, bool) -m mode: value,addressable,assignable - type: map[string]int -"hello, " + "world" mode: value - type: string - value: "hello, world" -"hello, " mode: value - type: untyped string - value: "hello, " -"world" mode: value - type: untyped string - value: "world" -print(rune(v), ok) mode: void - type: () -print mode: builtin - type: func(rune, bool) -rune(v) mode: value - type: rune -rune mode: type - type: rune -...more not shown... -``` - - -Notice that the identifiers for the built-ins `make` and -`print` have types that are specific to the particular calls in -which they appear. -Also notice `m["hello"]` has a 2-tuple type `(int, bool)` -and that it is assignable, but unlike the variable `m`, it is not -addressable. - - - -Download the example and vary the inputs and see what the program prints. - - - -Here's another example, adapted from the `govet` static checking tool. -It checks for accidental uses of a method value `x.f` when a -call `x.f()` was intended; -comparing a method `x.f` against nil is a common mistake. - - - // go get golang.org/x/example/gotypes/nilfunc - -``` -// CheckNilFuncComparison reports unintended comparisons -// of functions against nil, e.g., "if x.Method == nil {". -func CheckNilFuncComparison(info *types.Info, n ast.Node) { - e, ok := n.(*ast.BinaryExpr) - if !ok { - return // not a binary operation - } - if e.Op != token.EQL && e.Op != token.NEQ { - return // not a comparison - } - - // If this is a comparison against nil, find the other operand. - var other ast.Expr - if info.Types[e.X].IsNil() { - other = e.Y - } else if info.Types[e.Y].IsNil() { - other = e.X - } else { - return // not a comparison against nil - } - - // Find the object. - var obj types.Object - switch v := other.(type) { - case *ast.Ident: - obj = info.Uses[v] - case *ast.SelectorExpr: - obj = info.Uses[v.Sel] - default: - return // not an identifier or selection - } - - if _, ok := obj.(*types.Func); !ok { - return // not a function or method - } - - fmt.Printf("%s: comparison of function %v %v nil is always %v\n", - fset.Position(e.Pos()), obj.Name(), e.Op, e.Op == token.NEQ) -} -``` - - -Given this input, - - -``` -const input = `package main - -import "bytes" - -func main() { - var buf bytes.Buffer - if buf.Bytes == nil && bytes.Repeat != nil && main == nil { - // ... - } -} -` -``` - - -the program reports these errors: - - -``` -$ go build golang.org/x/example/gotypes/nilfunc -$ ./nilfunc -input.go:7:5: comparison of function Bytes == nil is always false -input.go:7:25: comparison of function Repeat != nil is always true -input.go:7:48: comparison of function main == nil is always false -``` - -# Selections - - -A _selection_ is an expression _`expr`_`.f` in which -`f` denotes either a struct field or a method. -A selection is resolved not by looking for a name in the lexical -environment, but by looking within a _type_. -The type checker ascertains the meaning of each selection in the -package---a surprisingly tricky business---and records it in the -`Selections` mapping of the `Info` struct, whose values are -of type `Selection`: - - - type Selection struct{ ... } - func (s *Selection) Kind() SelectionKind // = FieldVal | MethodVal | MethodExpr - func (s *Selection) Recv() Type - func (s *Selection) Obj() Object - func (s *Selection) Type() Type - func (s *Selection) Index() []int - func (s *Selection) Indirect() bool - - -The `Kind` method discriminates between the three (legal) kinds -of selections, as indicated by the comments below. - - - type T struct{Field int} - func (T) Method() {} - var v T - - // Kind Type - var _ = v.Field // FieldVal int - var _ = v.Method // MethodVal func() - var _ = T.Method // MethodExpr func(T) - - -Because of embedding, a selection may denote more than one field or -method, in which case it is ambiguous, and no `Selection` is -recorded for it. - - - -The `Obj` method returns the `Object` for the selected field -(`*Var`) or method (`*Func`). -Due to embedding, the object may belong to a different type than that -of the receiver expression _`expr`_. -The `Type` method returns the type of the selection. For a field -selection, this is the type of the field, but for method selections, -the result is a function type that is not the same as the type of the -method. -For a `MethodVal`, the receiver parameter is dropped, and -for a `MethodExpr`, the receiver parameter becomes a regular -parameter, as shown in the example above. - - - -The `Index` and `Indirect` methods report information about -implicit operations occurring during the selection that a compiler -would need to know about. -Because of embedding, a selection _`expr`_`.f` may be -shorthand for a sequence containing several implicit field selections, -_`expr`_`.d.e.f`, and `Index` reports the complete -sequence. -And because of automatic pointer dereferencing during struct field -accesses and method calls, a selection may imply one or more indirect -loads from memory; `Indirect` reports whether this occurs. - - - -Clients of the type checker can call `LookupFieldOrMethod` to -look up a name within a type, as if by a selection. -This function has an intimidating signature, but conceptually it -accepts just a `Type` and a name, and returns a `Selection`: - - - func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) \ - (obj Object, index []int, indirect bool) - - -The result is not actually a `Selection`, but it contains the -three main components of one: `Obj`, `Index`, -and `Indirect`. - - - -The `addressable` flag should be set if the receiver is a -_variable_ of type `T`, since in a method selection on a -variable, an implicit address-of operation (`&`) may occur. -The flag indicates whether the methods of type `*T` should be -considered during the lookup. -(You may wonder why this parameter is necessary. Couldn't clients -instead call `LookupFieldOrMethod` on the pointer type `*T` -if the receiver is a `T` variable? The answer is that if -`T` is an interface type, the type `*T` has no methods at -all.) - - - -The final two parameters of `LookupFieldOrMethod` are `(pkg -*Package, name string)`. -Together they specify the name of the field or method to look up. -This brings us to `Id`s. - - - -# Ids - - -`LookupFieldOrMethod`'s need for a `Package` parameter -is a subtle consequence of the -[_Uniqueness of identifiers_](https://golang.org/ref/spec#Uniqueness_of_identifiers) -section in the Go spec: "Two -identifiers are different if they are spelled differently, or if they -appear in different packages and are not exported." -In practical terms, this means that a type may have two methods -(or two fields, or one of each) both named `f` so long as those -methods are defined in different packages, as in this example: - - - package a - type A int - func (A) f() - - package b - type B int - func (B) f() - - package c - import ( "a"; "b" ) - type C struct{a.A; b.B} // C has two methods called f - - -The type `c.C` has two methods named `f`, but there is -no ambiguity because the two `f`s are distinct -identifiers---think of them as `fᵃ` and `fᵇ`. -For an exported method, this situation _would_ be ambiguous -because there is no distinction between `Fᵃ` and `Fᵇ`; there -is only `F`. - - - -Despite having two methods called `f`, neither of them can be -called from within package `c` because `c` has no way to -identify them. -Within `c`, `f` is the identifier `fᶜ`, and -type `C` has no method of that name. -But if we pass an instance of `C` to code in package `a` -and call its `f` method via an interface, `fᵃ` is called. - - - -The practical consequence for tool builders is that any time you need -to look up a field or method by name, or construct a map of fields and/or -methods keyed by name, it is not sufficient to use the object's name -as a key. -Instead, you must call the `Object.Id` method, which returns -a string that incorporates the object name, and for unexported -objects, the package path too. -There is also a standalone function `Id` that combines a name and -the package path in the same way: - - - func Id(pkg *Package, name string) string - - -This distinction applies to selections _`expr`_`.f`, but not -to lexical references `x` because for unexported identifiers, -declarations and references always appear in the same package. - - - -Fun fact: the `reflect.StructField` type records both the -`Name` and the `PkgPath` strings for the same reason. -The `FieldByName` methods of `reflect.Value` and -`reflect.Type` match field names without regard to the package. -If there is more than one match, they return an invalid value. - - - - -# Method Sets - - -The _method set_ of a type is the set of methods that can be -called on any value of that type. -(A variable of type `T` has access to all the methods of type -`*T` as well, due to the implicit address-of operation during -method calls, but those extra methods are not part of the method set -of `T`.) - - - -Clients can request the method set of a type `T` by calling -`NewMethodSet(T)`: - - - type MethodSet struct{ ... } - func NewMethodSet(T Type) *MethodSet - func (s *MethodSet) Len() int - func (s *MethodSet) At(i int) *Selection - func (s *MethodSet) Lookup(pkg *Package, name string) *Selection - - -The `Len` and `At` methods access a list of -`Selections`, all of kind `MethodVal`, ordered by `Id`. -The `Lookup` function allows lookup of a single method by -name (and package path, as explained in the previous section). - - - -`NewMethodSet` can be expensive, so for applications that compute -method sets repeatedly, `golang.org/x/tools/go/types/typeutil` -provides a `MethodSetCache` type that records previous results. -If you only need a single method, don't construct the -`MethodSet` at all; it's cheaper to use -`LookupFieldOrMethod`. - - - -The next program generates a boilerplate -declaration of a new concrete type that satisfies an existing -interface. -Here's an example: - - -``` -$ ./skeleton io ReadWriteCloser buffer -// *buffer implements io.ReadWriteCloser. -type buffer struct{} -func (b *buffer) Close() error { - panic("unimplemented") -} -func (b *buffer) Read(p []byte) (n int, err error) { - panic("unimplemented") -} -func (b *buffer) Write(p []byte) (n int, err error) { - panic("unimplemented") -} -``` - - -The three arguments are the package and the name of the existing -interface type, and the name of the new concrete type. -The `main` function (not shown) loads the specified package and -calls `PrintSkeleton` with the remaining two arguments: - - - // go get golang.org/x/example/gotypes/skeleton - -``` -func PrintSkeleton(pkg *types.Package, ifacename, concname string) error { - obj := pkg.Scope().Lookup(ifacename) - if obj == nil { - return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename) - } - if _, ok := obj.(*types.TypeName); !ok { - return fmt.Errorf("%v is not a named type", obj) - } - iface, ok := obj.Type().Underlying().(*types.Interface) - if !ok { - return fmt.Errorf("type %v is a %T, not an interface", - obj, obj.Type().Underlying()) - } - // Use first letter of type name as receiver parameter. - if !isValidIdentifier(concname) { - return fmt.Errorf("invalid concrete type name: %q", concname) - } - r, _ := utf8.DecodeRuneInString(concname) - - fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename) - fmt.Printf("type %s struct{}\n", concname) - mset := types.NewMethodSet(iface) - for i := 0; i < mset.Len(); i++ { - meth := mset.At(i).Obj() - sig := types.TypeString(meth.Type(), (*types.Package).Name) - fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n", - r, concname, meth.Name(), - strings.TrimPrefix(sig, "func")) - } - return nil -} -``` - - -First, `PrintSkeleton` locates the package-level named interface -type, handling various error cases. -Then it chooses the name for the receiver of the new methods: the -first letter of the concrete type. -Finally, it iterates over the method set of the interface, printing -the corresponding concrete method declarations. - - - -There's a subtlety in the declaration of `sig`, which is the -string form of the method signature. -We could have obtained this string from `meth.Type().String()`, -but this would cause any named types within it to be formatted with -the complete package path, for instance -`net/http.ResponseWriter`, which is informative in diagnostics -but not legal Go syntax. -The `TypeString` function (explained in [Formatting Values](#formatting-values)) allows the -caller to control how packages are printed. -Passing `(*types.Package).Name` causes only the package name -`http` to be printed, not the complete path. -Here's another example that illustrates it: - - -``` -$ ./skeleton net/http Handler myHandler -// *myHandler implements net/http.Handler. -type myHandler struct{} -func (m *myHandler) ServeHTTP(http.ResponseWriter, *http.Request) { - panic("unimplemented") -} -``` - - -The following program inspects all pairs of package-level named types -in `pkg`, and reports the types that satisfy each interface type. - - - // go get golang.org/x/example/gotypes/implements - -``` -// Find all named types at package level. -var allNamed []*types.Named -for _, name := range pkg.Scope().Names() { - if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { - allNamed = append(allNamed, obj.Type().(*types.Named)) - } -} - -// Test assignability of all distinct pairs of -// named types (T, U) where U is an interface. -for _, T := range allNamed { - for _, U := range allNamed { - if T == U || !types.IsInterface(U) { - continue - } - if types.AssignableTo(T, U) { - fmt.Printf("%s satisfies %s\n", T, U) - } else if !types.IsInterface(T) && - types.AssignableTo(types.NewPointer(T), U) { - fmt.Printf("%s satisfies %s\n", types.NewPointer(T), U) - } - } -} -``` - - -Given this input, - - - // go get golang.org/x/example/gotypes/implements - -``` -const input = `package main - -type A struct{} -func (*A) f() - -type B int -func (B) f() -func (*B) g() - -type I interface { f() } -type J interface { g() } -` -``` - - -the program prints: - - -``` -$ go build golang.org/x/example/gotypes/implements -$ ./implements -*hello.A satisfies hello.I -hello.B satisfies hello.I -*hello.B satisfies hello.J -``` - - -Notice that the method set of `B` does not include `g`, but -the method set of `*B` does. -That's why we needed the second assignability check, using the pointer -type `types.NewPointer(T)`. - - - -# Constants - - -A constant expression is one whose value is guaranteed to be computed at -compile time. -Constant expressions may appear in types, specifically as the length -of an array type such as `[16]byte`, so one of the jobs of the -type checker is to compute the value of each constant expression. - - - -As we saw in the `typeandvalue` example, the type checker records -the value of each constant expression like `"Hello, " + "world"`, -storing it in the `Value` field of the `TypeAndValue` struct. -Constants are represented using the `Value` interface from the -`go/constant` package. - - - package constant // go/constant - - type Value interface { - Kind() Kind - } - - type Kind int // one of Unknown, Bool, String, Int, Float, Complex - - -The interface has only one method, for discriminating the various -kinds of constants, but the package provides many functions for -inspecting a value of a known kind, - - - // Accessors - func BoolVal(x Value) bool - func Float32Val(x Value) (float32, bool) - func Float64Val(x Value) (float64, bool) - func Int64Val(x Value) (int64, bool) - func StringVal(x Value) string - func Uint64Val(x Value) (uint64, bool) - func Bytes(x Value) []byte - func BitLen(x Value) int - func Sign(x Value) int - - -for performing arithmetic on values, - - - // Operations - func Compare(x Value, op token.Token, y Value) bool - func UnaryOp(op token.Token, y Value, prec uint) Value - func BinaryOp(x Value, op token.Token, y Value) Value - func Shift(x Value, op token.Token, s uint) Value - func Denom(x Value) Value - func Num(x Value) Value - func Real(x Value) Value - func Imag(x Value) Value - - -and for constructing new values: - - - // Constructors - func MakeBool(b bool) Value - func MakeFloat64(x float64) Value - func MakeFromBytes(bytes []byte) Value - func MakeFromLiteral(lit string, tok token.Token, prec uint) Value - func MakeImag(x Value) Value - func MakeInt64(x int64) Value - func MakeString(s string) Value - func MakeUint64(x uint64) Value - func MakeUnknown() Value - - -All numeric `Value`s, whether integer or floating-point, signed or -unsigned, or real or complex, are represented more precisely than -ordinary Go types like `int64` and `float64`. -Internally, the `go/constant` package uses multi-precision data types -like `Int`, `Rat`, and `Float` from the `math/big` package so that -`Values` and their arithmetic operations are accurate to at least 256 -bits, as required by the Go specification. - - - - - -# Size and Alignment - - -Because the calls `unsafe.Sizeof(v)`, `unsafe.Alignof(v)`, -and `unsafe.Offsetof(v.f)` are all constant expressions, the type -checker must be able to compute the memory layout of any value -`v`. - - - -By default, the type checker uses the same layout algorithm as the Go -1.5 `gc` compiler targeting `amd64`. -Clients can configure the type checker to use a different algorithm by -providing an instance of the `types.Sizes` interface in the -`types.Config` struct: - - - package types - - type Sizes interface { - Alignof(T Type) int64 - Offsetsof(fields []*Var) []int64 - Sizeof(T Type) int64 - } - - - -For common changes, like reducing the word size to 32 bits, clients -can use an instance of `StdSizes`: - - - type StdSizes struct { - WordSize int64 - MaxAlign int64 - } - - -This type has two basic size and alignment parameters from which it -derives all the other values using common assumptions. -For example, pointers, functions, maps, and channels fit in one word, -strings and interfaces require two words, and slices need three. -The default behaviour is equivalent to `StdSizes{8, 8}`. -For more esoteric layout changes, you'll need to write a new -implementation of the `Sizes` interface. - - - -The `hugeparam` program below prints all function parameters and -results whose size exceeds a threshold. -By default, the threshold is 48 bytes, but you can set it via the -`-bytes` command-line flag. -Such a tool could help identify inefficient parameter passing in your -programs. - - - // go get golang.org/x/example/gotypes/hugeparam - -``` -var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes") - -var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function - -func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) { - checkTuple := func(descr string, tuple *types.Tuple) { - for i := 0; i < tuple.Len(); i++ { - v := tuple.At(i) - if sz := sizeof(v.Type()); sz > int64(*bytesFlag) { - fmt.Printf("%s: %q %s: %s = %d bytes\n", - fset.Position(v.Pos()), - v.Name(), descr, v.Type(), sz) - } - } - } - checkSig := func(sig *types.Signature) { - checkTuple("parameter", sig.Params()) - checkTuple("result", sig.Results()) - } - for _, file := range files { - ast.Inspect(file, func(n ast.Node) bool { - switch n := n.(type) { - case *ast.FuncDecl: - checkSig(info.Defs[n.Name].Type().(*types.Signature)) - case *ast.FuncLit: - checkSig(info.Types[n.Type].Type.(*types.Signature)) - } - return true - }) - } -} -``` - - -As before, `Inspect` applies a function to every node in the AST. -The function cares about two kinds of nodes: declarations of named -functions and methods (`*ast.FuncDecl`) and function literals -(`*ast.FuncLit`). -Observe the two cases' different logic to obtain the type of each -function. - - - -Here's a typical invocation on the standard `encoding/xml` package. -It reports a number of places where the 7-word -[`StartElement` type](https://godoc.org/encoding/xml#StartElement) -is copied. - - -``` -% ./hugeparam encoding/xml -/go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:761:51: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:781:68: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/xml.go:72:30: "" result: encoding/xml.StartElement = 56 bytes -``` - -# Imports - - -The type checker's `Check` function processes a slice of parsed -files (`[]*ast.File`) that make up one package. -When the type checker encounters an import declaration, it needs the -type information for the objects in the imported package. -It gets it by calling the `Import` method of the `Importer` -interface shown below, an instance of which must be provided by the -`Config`. -This separation of concerns relieves the type checker from having to -know any of the details of Go workspace organization, `GOPATH`, -compiler file formats, and so on. - - - type Importer interface { - Import(path string) (*Package, error) - } - - -Most of our examples used the simplest `Importer` implementation, -`importer.Default()`, provided by the `go/importer` package. -This importer looks in `$GOROOT` and `$GOPATH` for `.a` -files written by the compiler (`gc` or `gccgo`) -that was used to build the program. -In addition to object code, these files contain _export data_, -that is, a description of all the objects declared by the package, and -also of any objects from other packages that were referred to indirectly. -Because export data includes information about dependencies, the type -checker need load at most one file per import, instead of one per -transitive dependency. - - - -Compiler export data is compact and efficient to locate, load, and -parse, but it has several shortcomings. -First, it does not contain position information for imported -objects, reducing the quality of certain diagnostic messages. -Second, it does not contain complete syntax trees nor semantic information -about the contents of function bodies, so it is not suitable for -interprocedural analyses. -Third, compiler object data may be stale. Nothing detects or ensures -that the object files are more recent than the source files from which -they were derived. -Generally, object data for standard packages is likely to be -up-to-date, but for user packages, it depends on how recently the user -ran a `go install` or `go build -i` command. - - - -The [`golang.org/tools/x/go/loader` package](https://godoc.org/golang.org/x/tools/go/loader) -provides an alternative `Importer` that addresses -some of these problems. -It loads a complete program from source, performing -[`cgo`](https://golang.org/cmd/cgo/cgo) preprocessing if -necessary, followed by parsing and type-checking. -It loads independent packages in parallel to hide I/O latency, and -detects and reports import cycles. -For each package, it provides the `types.Package` containing the -package's lexical environment, the list of `ast.File` syntax -trees for each file in the package, the `types.Info` containing -type information for each syntax node, and a list of type errors -associated with that package. -(Please be aware that the `go/loader` package's API is likely to -change before it finally stabilizes.) - - - -The `doc` program below demonstrates a simple use of the loader. -It is a rudimentary implementation of `go doc` that prints the type, -methods, and documentation of the package-level object specified on -the command line. -Here's an example: - - -``` -$ ./doc net/http File -type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader} -/go/src/io/io.go:92:2: method (net/http.File) Close() error -/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error) -/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error) -/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error) -/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error) - - A File is returned by a FileSystem's Open method and can be -served by the FileServer implementation. - -The methods should behave the same as those on an *os.File. -``` - - -Observe that it prints the correct location of each method -declaration, even though, due to embedding, some of -`http.File`'s methods were declared in another package. -Here's the first part of the program, showing how to load an entire -program starting from the single package, `pkgpath`: - - - // go get golang.org/x/example/gotypes/doc - -``` -pkgpath, name := os.Args[1], os.Args[2] - -// The loader loads a complete Go program from source code. -conf := loader.Config{ParserMode: parser.ParseComments} -conf.Import(pkgpath) -lprog, err := conf.Load() -if err != nil { - log.Fatal(err) // load error -} - -// Find the package and package-level object. -pkg := lprog.Package(pkgpath).Pkg -obj := pkg.Scope().Lookup(name) -if obj == nil { - log.Fatalf("%s.%s not found", pkg.Path(), name) -} -``` - - -Notice that we instructed the parser to retain comments during parsing. -The rest of the program prints the output: - - - // go get golang.org/x/example/gotypes/doc - -``` -// Print the object and its methods (incl. location of definition). -fmt.Println(obj) -for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { - fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel) -} - -// Find the path from the root of the AST to the object's position. -// Walk up to the enclosing ast.Decl for the doc comment. -_, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos()) -for _, n := range path { - switch n := n.(type) { - case *ast.GenDecl: - fmt.Println("\n", n.Doc.Text()) - return - case *ast.FuncDecl: - fmt.Println("\n", n.Doc.Text()) - return - } -} -``` - - -We used `IntuitiveMethodSet` to compute the method set, instead -of `NewMethodSet`. -The result of this convenience function, which is intended for use in -user interfaces, includes methods of `*T` as well as those of -`T`, since that matches most users' intuition about the method -set of a type. -(Our example, `http.File`, didn't illustrate the difference, but try -running it on a type with both value and pointer methods.) - - - -Also notice `PathEnclosingInterval`, which finds the set of AST -nodes that enclose a particular point, in this case, the object's -declaring identifier. -By walking up with path, we find the enclosing declaration, to which -the documentation is attached. - - - -# Formatting support - - -All types that satisfy `Type` or `Object` define a -`String` method that formats the type or object in a readable -notation. `Selection` also provides a `String` method. -All package-level objects within these data structures are -printed with the complete package path, as in these examples: - - - []encoding/json.Marshaler // a *Slice type - encoding/json.Marshal // a *Func object - (*encoding/json.Encoder).Encode // a *Func object (method) - func (enc *encoding/json.Encoder) Encode(v interface{}) error // a method *Signature - func NewEncoder(w io.Writer) *encoding/json.Encoder // a function *Signature - - -This notation is unambiguous, but it is not legal Go syntax. -Also, package paths may be long, and the same package path may appear -many times in a single string, for instance, when formatting a -function of several parameters. -Because these strings often form part of a tool's user interface---as -with the diagnostic messages of `hugeparam` or the code generated -by `skeleton`---many clients want more control over the -formatting of package names. - - - -The `go/types` package provides these alternatives to the -`String` methods: - - - func ObjectString(obj Object, qf Qualifier) string - func TypeString(typ Type, qf Qualifier) string - func SelectionString(s *Selection, qf Qualifier) string - - type Qualifier func(*Package) string - - -The `TypeString`, `ObjectString`, and `SelectionString` -functions are like the `String` methods of the respective types, -but they accept an additional argument, a `Qualifier`. - - - -A `Qualifier` is a client-provided function that determines how a -package name is rendered as a string. -If it is nil, the default behavior is to print the package's -path, just like the `String` methods do. -If a caller passes `(*Package).Name` as the qualifier, that is, a -function that accepts a package and returns its `Name`, then -objects are qualified only by the package name. -The above examples would look like this: - - - []json.Marshaler - json.Marshal - (*json.Encoder).Encode - func (enc *json.Encoder) Encode(v interface{}) error - func NewEncoder(w io.Writer) *json.Encoder - - -Often when a tool prints some output, it is implicitly in the -context of a particular package, perhaps one specified by the -command line or HTTP request. -In that case, it is more natural to omit the package qualification -altogether for objects belonging to that package, but to qualify all -other objects by their package's path. -That's what the `RelativeTo(pkg)` qualifier does: - - - func RelativeTo(pkg *Package) Qualifier - - -The examples below show how `json.NewEncoder` would be printed -using three qualifiers, each relative to a different package: - - - // RelativeTo "encoding/json": - func NewEncoder(w io.Writer) *Encoder - - // RelativeTo "io": - func NewEncoder(w Writer) *encoding/json.Encoder - - // RelativeTo any other package: - func NewEncoder(w io.Writer) *encoding/json.Encoder - - -Another qualifier that may be relevant to refactoring tools (but is -not currently provided by the type checker) is one that renders each -package name using the locally appropriate name within a given source -file. -Its behavior would depend on the set of import declarations, including -renaming imports, within that source file. - - - -# Getting from A to B - - -The type checker and its related packages represent many aspects of a -Go program in many different ways, and analysis tools must often map -between them. -For instance, a named entity may be identified by its `Object`; -by its declaring identifier (`ast.Ident`) or by any referring -identifier; by its declaring `ast.Node`; by the position -(`token.Pos`) of any those nodes; or by the filename and -line/column number (or byte offset) of those `token.Pos` values. - - - -In this section, we'll list solutions to a number of common problems -of the form "I have an A; I need the corresponding B". - - - -To map **from a `token.Pos` to an `ast.Node`**, call the -helper function -[`astutil.PathEnclosingInterval`](https://godoc.org/golang.org/x/tools/go/ast/astutil#PathEnclosingInterval). -It returns the enclosing `ast.Node`, and all its ancestors up to -the root of the file. -You must know which file `*ast.File` the `token.Pos` belongs to. -Alternatively, you can search an entire program loaded by the -`loader` package, using -[`(*loader.Program).PathEnclosingInterval`](https://godoc.org/golang.org/x/tools/go/loader#Program.PathEnclosingInterval). - - - -To map **from an `Object` to its declaring syntax**, call -`Pos` to get its position, then use `PathEnclosingInterval` as before. -This approach is suitable for a one-off query. For repeated use, it -may be more efficient to visit the syntax tree and construct the -mapping between declarations and objects. - - - -To map **from an `ast.Ident` to the `Object`** it refers to (or -declares), consult the `Uses` or `Defs` map for the -package, as shown in [Identifier Resolution](#identifier-resolution). - - - -To map **from an `Object` to its documentation**, find the -object's declaration, and look at the attached `Doc` field. -You must have set the parser's `ParseComments` flag. -See the `doc` example in [Imports](#imports). diff --git a/gotypes/defsuses/main.go b/gotypes/defsuses/main.go deleted file mode 100644 index 608c529a..00000000 --- a/gotypes/defsuses/main.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -const hello = `package main - -import "fmt" - -func main() { - fmt.Println("Hello, world") -} -` - -//!+ -func PrintDefsUses(fset *token.FileSet, files ...*ast.File) error { - conf := types.Config{Importer: importer.Default()} - info := &types.Info{ - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - } - _, err := conf.Check("hello", fset, files, info) - if err != nil { - return err // type error - } - - for id, obj := range info.Defs { - fmt.Printf("%s: %q defines %v\n", - fset.Position(id.Pos()), id.Name, obj) - } - for id, obj := range info.Uses { - fmt.Printf("%s: %q uses %v\n", - fset.Position(id.Pos()), id.Name, obj) - } - return nil -} - -//!- - -func main() { - // Parse one file. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "hello.go", hello, 0) - if err != nil { - log.Fatal(err) // parse error - } - if err := PrintDefsUses(fset, f); err != nil { - log.Fatal(err) // type error - } -} - -/* -//!+output -$ go build github.com/golang/example/gotypes/defsuses -$ ./defsuses -hello.go:1:9: "main" defines -hello.go:5:6: "main" defines func hello.main() -hello.go:6:9: "fmt" uses package fmt -hello.go:6:13: "Println" uses func fmt.Println(a ...interface{}) (n int, err error) -//!-output -*/ diff --git a/gotypes/doc/main.go b/gotypes/doc/main.go deleted file mode 100644 index 3140f473..00000000 --- a/gotypes/doc/main.go +++ /dev/null @@ -1,77 +0,0 @@ -// The doc command prints the doc comment of a package-level object. -package main - -import ( - "fmt" - "go/ast" - "go/parser" - "log" - "os" - - // TODO: these will use std go/types after Feb 2016 - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types/typeutil" -) - -func main() { - if len(os.Args) != 3 { - log.Fatal("Usage: doc ") - } - //!+part1 - pkgpath, name := os.Args[1], os.Args[2] - - // The loader loads a complete Go program from source code. - conf := loader.Config{ParserMode: parser.ParseComments} - conf.Import(pkgpath) - lprog, err := conf.Load() - if err != nil { - log.Fatal(err) // load error - } - - // Find the package and package-level object. - pkg := lprog.Package(pkgpath).Pkg - obj := pkg.Scope().Lookup(name) - if obj == nil { - log.Fatalf("%s.%s not found", pkg.Path(), name) - } - //!-part1 - //!+part2 - - // Print the object and its methods (incl. location of definition). - fmt.Println(obj) - for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { - fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel) - } - - // Find the path from the root of the AST to the object's position. - // Walk up to the enclosing ast.Decl for the doc comment. - _, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos()) - for _, n := range path { - switch n := n.(type) { - case *ast.GenDecl: - fmt.Println("\n", n.Doc.Text()) - return - case *ast.FuncDecl: - fmt.Println("\n", n.Doc.Text()) - return - } - } - //!-part2 -} - -/* -//!+output -$ ./doc net/http File -type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader} -/go/src/io/io.go:92:2: method (net/http.File) Close() error -/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error) -/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error) -/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error) -/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error) - - A File is returned by a FileSystem's Open method and can be -served by the FileServer implementation. - -The methods should behave the same as those on an *os.File. -//!-output -*/ diff --git a/gotypes/go-types.md b/gotypes/go-types.md deleted file mode 100644 index d59516ae..00000000 --- a/gotypes/go-types.md +++ /dev/null @@ -1,2055 +0,0 @@ - -# `go/types`: The Go Type Checker - -This document is maintained by Alan Donovan `adonovan@google.com`. - -[October 2015 GothamGo talk on go/types](https://docs.google.com/presentation/d/13OvHYozAUBeISPRoLgG7kMBuja1NsU1D_mMlmbaYojk/view) - - -# Contents - -%toc - -# Introduction - - -The [`go/types` package]('https://golang.org/pkg/go/types) is a -type-checker for Go programs, designed by Robert Griesemer. -It became part of Go's standard library in Go 1.5. -Measured by lines of code and by API surface area, it is one of the -most complex packages in Go's standard library, and using it requires -a firm grasp of the structure of Go programs. -This tutorial will help you find your bearings. -It comes with several example programs that you can obtain with `go get` and play with. -We assume you are a proficient Go programmer who wants to build tools -to analyze or manipulate Go programs and that you have some knowledge -of how a typical compiler works. - -The type checker complements several existing -standard packages for analyzing Go programs. -We've listed them below. - - - → go/types - go/constant - go/parser - go/ast - go/scanner - go/token - - -Starting at the bottom, the -[`go/token` package](http://golang.org/pkg/go/token) -defines the lexical tokens of Go. -The [`go/scanner` package](http://golang.org/pkg/go/scanner) tokenizes an input stream and records -file position information for use in diagnostics -or for file surgery in a refactoring tool. -The [`go/ast` package](http://golang.org/pkg/go/ast) -defines the data types of the abstract syntax tree (AST). -The [`go/parser` package](http://golang.org/pkg/go/parser) -provides a robust recursive-descent parser that constructs the AST. -And [`go/constant`](http://golang.org/pkg/go/constant) -provides representations and arithmetic operations for the values of compile-time -constant expressions, as we'll see in -[Constants](#constants). - - - -The [`golang.org/x/tools/go/loader` package](https://godoc.org/golang.org/x/tools/go/loader) -from the `x/tools` repository is a client of the type -checker that loads, parses, and type-checks a complete Go program from -source code. -We use it in some of our examples and you may find it useful too. - - - -The Go type checker does three main things. -First, for every name in the program, it determines which declaration -the name refers to; this is known as _identifier resolution_. -Second, for every expression in the program, it determines what type -that expression has, or reports an error if the expression has no -type, or has an inappropriate type for its context; this is known as -_type deduction_. -Third, for every constant expression in the program, it determines the -value of that constant; this is known as _constant evaluation_. - - - -Superficially, it appears that these three processes could be done -sequentially, in the order above, but perhaps surprisingly, they must -be done together. -For example, the value of a constant may depend on the type of an -expression due to operators like `unsafe.Sizeof`. -Conversely, the type of an expression may depend on the value of a -constant, since array types contain constants. -As a result, type deduction and constant evaluation must be done -together. - - - -As another example, we cannot resolve the identifier `k` in the composite -literal `T{k: 0}` until we know whether `T` is a struct type. -If it is, then `k` must be found among `T`'s fields. -If not, then `k` is an ordinary reference -to a constant or variable in the lexical environment. -Consequently, identifier resolution and type deduction are also -inseparable in the general case. - - - -Nonetheless, the three processes of identifier resolution, type -deduction, and constant evaluation can be separated for the purpose of -explanation. - - -# An Example - - -The code below shows the most basic use of the type checker to check -the _hello, world_ program, supplied as a string. -Later examples will be variations on this one, and we'll often omit -boilerplate details such as parsing. -To check out and build the examples, -run `go get github.com/golang/example/gotypes/...`. - - -%include pkginfo/main.go - - -First, the program creates a -[`token.FileSet`](http://golang.org/pkg/go/token/#FileSet). -To avoid the need to store file names and line and column -numbers in every node of the syntax tree, the `go/token` package -provides `FileSet`, a data structure that stores this information -compactly for a sequence of files. -A `FileSet` records each file name only once, and records -only the byte offsets of each newline, allowing a position within -any file to be identified using a small integer called a -`token.Pos`. -Many tools create a single `FileSet` at startup. -Any part of the program that needs to convert a `token.Pos` into -an intelligible location---as part of an error message, for -instance---must have access to the `FileSet`. - - - -Second, the program parses the input string. -More realistic packages contain several source files, so the parsing -step must be repeated for each one, or better, done in parallel. -Third, it creates a `Config` that specifies type-checking options. -Since the _hello, world_ program uses imports, we must indicate -how to locate the imported packages. -Here we use `importer.Default()`, which loads compiler-generated -export data, but we'll explore alternatives in [Imports](#imports). - - - -Fourth, the program calls `Check`. -This creates a `Package` whose path is `"cmd/hello"`, and -type-checks each of the specified files---just one in this example. -The final (nil) argument is a pointer to an optional `Info` -struct that returns additional deductions from the type checker; more -on that later. -`Check` returns a `Package` even when it also returns an error. -The type checker is robust to ill-formed input, -and goes to great lengths to report accurate -partial information even in the vicinity of syntax or type errors. -`Package` has this definition: - - - type Package struct{ ... } - func (*Package) Path() string - func (*Package) Name() string - func (*Package) Scope() *Scope - func (*Package) Imports() []*Package - - -Finally, the program prints the attributes of the package, shown below. -(The hexadecimal number may vary from one run to the next.) - - -%include pkginfo/main.go output - - - -A package's `Path`, such as `"encoding/json"`, is the string -by which import declarations identify it. -It is unique within a `$GOPATH` workspace, -and for published packages it must be globally unique. - - -A package's `Name` is the identifier in the `package` -declaration of each source file within the package, such as `json`. -The type checker reports an error if not all the package declarations in -the package agree. -The package name determines how the package is known when it is -imported into a file (unless a renaming import is used), -but is otherwise not visible to a program. - - -`Scope` returns the package's [_lexical block_](#scopes), -which provides access to all the named entities or -[_objects_](#objects) declared at package level. -`Imports` returns the set of packages directly imported by this -one, and may be useful for computing dependencies -([Initialization Order](#initialization-order)). - - - -# Objects - - -The task of identifier resolution is to map every identifier in the -syntax tree, that is, every `ast.Ident`, to an object. -For our purposes, an _object_ is a named entity created by a -declaration, such as a `var`, `type`, or `func` -declaration. -(This is different from the everyday meaning of object in -object-oriented programming.) - - - -Objects are represented by the `Object` interface: - - - type Object interface { - Name() string // package-local object name - Exported() bool // reports whether the name starts with a capital letter - Type() Type // object type - Pos() token.Pos // position of object identifier in declaration - - Parent() *Scope // scope in which this object is declared - Pkg() *Package // nil for objects in the Universe scope and labels - Id() string // object id (see Ids section below) - } - - -The first four methods are straightforward; we'll explain the other -three later. -`Name` returns the object's name---an identifier. -`Exported` is a convenience method that reports whether the first -letter of `Name` is a capital, indicating that the object may be -visible from outside the package. -It's a shorthand for `ast.IsExported(obj.Name())`. -`Type` returns the object's type; we'll come back to that in -[Types](#types). - - - -`Pos` returns the source position of the object's declaring identifier. -To make sense of a `token.Pos`, we need to call the -`(*token.FileSet).Position` method, which returns a struct with -individual fields for the file name, line number, column, and byte -offset, though usually we just call its `String` method: - - - fmt.Println(fset.Position(obj.Pos())) // "hello.go:10:6" - - -Not all objects carry position information. -Since the file format for compiler export data ([Imports](#imports)) -does not record position information, calling `Pos` on an object -imported from such a file returns zero, also known as -`token.NoPos`. - - - -There are eight kinds of objects in the Go type checker. -Most familiar are the kinds that can be declared at package level: -constants, variables, functions, and types. -Less familiar are statement labels, imported package names -(such as `json` in a file containing an `import "encoding/json"` -declaration), built-in functions (such as `append` and -`len`), and the pre-declared `nil`. -The eight types shown below are the only concrete types that satisfy -the `Object` interface. -In other words, `Object` is a _discriminated union_ of 8 -possible types, and we commonly use a type switch to distinguish them. - - - Object = *Func // function, concrete method, or abstract method - | *Var // variable, parameter, result, or struct field - | *Const // constant - | *TypeName // type name - | *Label // statement label - | *PkgName // package name, e.g. json after import "encoding/json" - | *Builtin // predeclared function such as append or len - | *Nil // predeclared nil - - -`Object`s are canonical. -That is, two `Object`s `x` and `y` denote the same -entity if and only if `x==y`. -Object identity is significant, and objects are routinely compared by -the addresses of the underlying pointers. -Although a package-level object is uniquely identified by its name -and enclosing package, for other objects there is no simple way to -obtain a string that uniquely identifies it. - - - -The `Parent` method returns the `Scope` (lexical block) in -which the object was declared; we'll come back to this in -[Scopes](#scopes). -Fields and methods are not found in the lexical environment, so -their objects have no `Parent`. - - - - -The `Pkg` method returns the `Package` to which this object -belongs, even for objects not declared at package level. -Only predeclared objects have no package. -The `Id` method will be explained in [Ids](#ids). - - - -Not all methods make sense for each kind of object. For instance, -the last four kinds above have no meaningful `Type` method. -And some kinds of objects have methods in addition to those required by the -`Object` interface: - - - func (*Func) Scope() *Scope - func (*Var) Anonymous() bool - func (*Var) IsField() bool - func (*Const) Val() constant.Value - func (*TypeName) IsAlias() bool - func (*PkgName) Imported() *Package - - -`(*Func).Scope` returns the [lexical block](#scopes) -containing the function's parameters, results, -and other local declarations. -`(*Var).IsField` distinguishes struct fields from ordinary -variables, and `(*Var).Anonymous` discriminates named fields like -the one in `struct{T T}` from anonymous fields like the one in `struct{T}`. -`(*Const).Val` returns the value of a named [constant](#constants). - - -`(*TypeName).IsAlias`, introduced in Go 1.9, reports whether the -type name is simply an alias for a type (as in `type I = int`), -as opposed to a definition of a [`Named`](#named-types) type, as -in `type Celsius float64`. - - -`(*PkgName).Imported` returns the package (for instance, -`encoding/json`) denoted by a given import name such as `json`. -Each time a package is imported, a new `PkgName` object is -created, usually with the same name as the `Package` it -denotes, but not always, as in the case of a renaming import. -`PkgName`s are objects, but `Package`s are not. -We'll look more closely at this in [Imports](#imports). - - - -All relationships between the syntax trees (`ast.Node`s) and type -checker data structures such as `Object`s and `Type`s are -stored in mappings outside the syntax tree itself. -Be aware that the `go/ast` package also defines a type called -`Object` that resembles---and predates---the type checker's -`Object`, and that `ast.Object`s are held directly by -identifiers in the AST. -They are created by the parser, which has a necessarily limited view -of the package, so the information they represent is at best partial and -in some cases wrong, as in the `T{k: 0}` example mentioned above. -If you are using the type checker, there is no reason to use the older -`ast.Object` mechanism. - - - -# Identifier Resolution - - -Identifier resolution computes the relationship between -identifiers and objects. -Its results are recorded in the `Info` struct optionally passed -to `Check`. -The fields related to identifier resolution are shown below. - - - type Info struct { - Defs map[*ast.Ident]Object - Uses map[*ast.Ident]Object - Implicits map[ast.Node]Object - Selections map[*ast.SelectorExpr]*Selection - Scopes map[ast.Node]*Scope - ... - } - - -Since not all facts computed by the type checker are needed by every -client, the API lets clients control which components of the result -should be recorded and which discarded: only fields that hold a -non-nil map will be populated during the call to `Check`. - - - -The two fields of type `map[*ast.Ident]Object` are the most important: -`Defs` records _declaring_ identifiers and -`Uses` records _referring_ identifiers. -In the example below, the comments indicate which identifiers are of -which kind. - - - var x int // def of x, use of int - fmt.Println(x) // uses of fmt, Println, and x - type T struct{U} // def of T, use of U (type), def of U (field) - - -The final line above illustrates why we don't combine `Defs` and -`Uses` into one map. -In the anonymous field declaration `struct{U}`, the -identifier `U` is both a use of the type `U` (a -`TypeName`) and a definition of the anonymous field (a -`Var`). - - - -The function below prints the location of each referring and defining -identifier in the input program, and the object it refers to. - - -%include defsuses/main.go - - -Let's use the _hello, world_ program again as the input: - - -%include hello/hello.go - - -This is what it prints: - - -%include defsuses/main.go output - - - -Notice that the `Defs` mapping may contain nil entries in a few -cases. -The first line of output reports that the package identifier -`main` is present in the `Defs` mapping, but has no -associated object. - - - -The `Implicits` mapping handles two cases of the syntax in -which an `Object` is declared without an `ast.Ident`, namely type -switches and import declarations. - -In the type switch below, which declares a local variable `y`, -the type of `y` is different in each case of the switch: - - - switch y := x.(type) { - case int: - fmt.Printf("%d", y) - case string: - fmt.Printf("%q", y) - default: - fmt.Print(y) - } - - -To represent this, for each single-type case, the type checker creates -a separate `Var` object for `y` with the appropriate type, -and `Implicits` maps each `ast.CaseClause` to the `Var` -for that case. -The `default` case, the `nil` case, and cases with more than one -type all use the regular `Var` object that is associated with the -identifier `y`, which is found in the `Defs` mapping. - - - -The import declaration below defines the name `json` without an -`ast.Ident`: - - - import "encoding/json" - - -`Implicits` maps this `ast.ImportSpec` to the `PkgName` -object named `json` that it implicitly declares. - - - -The `Selections` mapping, of type -`map[*ast.SelectorExpr]*Selection`, records the meaning of each -expression of the form _`expr`_`.f`, where _`expr`_ is -an expression or type and `f` is the name of a field or method. -These expressions, called _selections_, are represented by -`ast.SelectorExpr` nodes in the AST. -We'll talk more about the `Selection` type in [Selections](#selections). - - - -Not all `ast.SelectorExpr` nodes represent selections. -Expressions like `fmt.Println`, in which a package name precedes -the dot, are _qualified identifiers_. -They do not appear in the `Selections` mapping, but their -constituent identifiers (such as `fmt` and `Println`) both -appear in `Uses`. - - - -Referring identifiers that are not part of an `ast.SelectorExpr` -are _lexical references_. -That is, they are resolved to an object by searching for the -innermost enclosing lexical declaration of that name. -We'll see how that search works in the next section. - - -# Scopes - - -The `Scope` type is a mapping from names to objects. - - - type Scope struct{ ... } - - func (s *Scope) Names() []string - func (s *Scope) Lookup(name string) Object - - -`Names` returns the set of names in the mapping, in sorted order. -(It is not a simple accessor though, so call it sparingly.) -The `Lookup ` method returns the object for a given name, so we -can print all the entries or _bindings_ in a scope like this: - - - for _, name := range scope.Names() { - fmt.Println(scope.Lookup(name)) - } - - -The _scope_ of a declaration of a name is the region of -program source in which a reference to the name resolves to that -declaration. That is, scope is a property of a declaration. -However, in the `go/types` API, the `Scope` type represents -a _lexical block_, which is one component of the lexical -environment. -Consider the _hello, world_ program again: - - - package main - - import "fmt" - - func main() { - const message = "hello, world" - fmt.Println(message) - } - - -There are four lexical blocks in this program. -The outermost one is the _universe block_, which maps the -pre-declared names like `int`, `true`, and `append` to -their objects---a `TypeName`, a `Const`, and a -`Builtin`, respectively. -The universe block is represented by the global variable -`Universe`, of type `*Scope`, although it's logically a -constant so you shouldn't modify it. - - - -Next is the _package block_, which maps `"main"` to the -`main` function. -Following that is the _file block_, which maps `"fmt"` to -the `PkgName` object for this import of the `fmt` package. -And finally, the innermost block is that of function `main`, a -local block, which contains the declaration of `message`, a `Const`. -The `main` function is trivial, but many functions contain -several blocks since each `if`, `for`, `switch`, -`case`, or `select` statement creates at least one -additional block. -Local blocks nest to arbitrary depths. - - - -The structure of the lexical environment thus forms a tree, with the -universe block at the root, the package blocks beneath it, the file -blocks beneath them, and then any number of local blocks beneath the -files. -We can access and navigate this tree structure with the following -methods of `Scope`: - - - func (s *Scope) Parent() *Scope - func (s *Scope) NumChildren() int - func (s *Scope) Child(i int) *Scope - - -`Parent` lets us walk up the tree, and `Child` -lets us walk down it. -Note that although the `Parent` of every package `Scope` is -`Universe`, `Universe` has no children. -This asymmetry is a consequence of using a global variable to hold -`Universe`. - - - -To obtain the universe block, we use the `Universe` global variable. -To obtain the lexical block of a `Package`, we call its -`Scope` method. -To obtain the scope of a file (`*ast.File`), or any smaller piece -of syntax such as an `*ast.IfStmt`, we consult the `Scopes` -mapping in the `Info` struct, which maps each block-creating -syntax node to its block. -The lexical block of a named function or method can also be obtained -by calling its `(*Func).Scope` method. - - - - - -To look up a name in the lexical environment, we must search the tree -of lexical blocks, starting at a particular `Scope` and walking -up to the root until a declaration of the name is found. -For convenience, the `LookupParent` method does this, returning -not just the object, if found, but also the `Scope` in which it was -declared, which may be an ancestor of the initial one: - - - func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) - - -The `pos` parameter determines the position in the source code at -which the name should be resolved. -The effective lexical environment is different at each point in the -block because it depends on which local declarations appear -before or after that point. -(We'll see an illustration in a moment.) - - - -`Scope` has several other methods relating to source positions: - - - func (s *Scope) Pos() token.Pos - func (s *Scope) End() token.Pos - func (s *Scope) Contains(pos token.Pos) bool - func (s *Scope) Innermost(pos token.Pos) *Scope - - -`Pos` and `End` report the `Scope`'s start and end -position which, for explicit blocks, coincide with its curly -braces. -`Contains` is a convenience method that reports whether a -position lies in this interval. -`Innermost` returns the innermost scope containing the specified -position, which may be a child or other descendent of the initial -scope. - - - -These features are useful for tools that wish to resolve names or -evaluate constant expressions as if they had appeared at a particular -point within the program. -The next example program finds all the comments in the input, -treating the contents of each one as a name. It looks up each name in -the environment at the position of the comment, and prints what it -finds. -Observe that the `ParseComments` flag directs the parser to -preserve comments in the input. - - -%include lookup/lookup.go main - - -The expression `pkg.Scope().Innermost(pos)` finds the innermost -`Scope` that encloses the comment, and `LookupParent(name, pos)` -does a name lookup at a specific position in that lexical block. - - - -A typical input is shown below. -The first comment causes a lookup of `"append"` in the file block. -The second comment looks up `"fmt"` in the `main` function's block, -and so on. - - -%include lookup/lookup.go input - - - -Here's the output: - - -%include lookup/lookup.go output - - - -Notice how the two lookups of `main` return different results, -even though they occur in the same block, because one precedes the -declaration of the local variable named `main` and the other -follows it. -Also notice that there are two lookups of the name `x` but only -the first one, in the function block, succeeds. - - - -Download the program and modify both the input program and -the set of comments to get a better feel for how name resolution works. - - - -The table below summarizes which kinds of objects may be declared at -each level of the tree of lexical blocks. - - - Universe File Package Local - Builtin ✔ - Nil ✔ - Const ✔ ✔ ✔ - TypeName ✔ ✔ ✔ - Func ✔ - Var ✔ ✔ - PkgName ✔ - Label ✔ - - -# Initialization Order - - -In the course of identifier resolution, the type checker constructs a -graph of references among declarations of package-level variables and -functions. -The type checker reports an error if the initializer expression for a -variable refers to that variable, whether directly or indirectly. - - - -The reference graph determines the initialization order of the -package-level variables, as required by the Go spec, using a -breadth-first algorithm. -First, variables in the graph with no successors are removed, sorted -into the order in which they appear in the source code, then added -to a list. This creates more variables that have no successors. -The process repeats until they have all been removed. - - - -The result is available in the `InitOrder` field of the -`Info` struct, whose type is `[]Initializer`. - - - type Info struct { - ... - InitOrder []Initializer - ... - } - - type Initializer struct { - Lhs []*Var // var Lhs = Rhs - Rhs ast.Expr - } - - -Each element of the list represents a single initializer expression -that must be executed, and the variables to which it is assigned. -The variables may number zero, one, or more, as in these examples: - - - var _ io.Writer = new(bytes.Buffer) - var rx = regexp.MustCompile("^b(an)*a$") - var cwd, cwdErr = os.Getwd() - - -This process governs the initialization order of variables within a -package. -Across packages, dependencies must be initialized first, although the -order among them is not specified. -That is, any topological order of the import graph will do. -The `(*Package).Imports` method returns the set of direct -dependencies of a package. - - -# Types - - -The main job of the type checker is, of course, to deduce the type -of each expression and to report type errors. -Like `Object`, `Type` is an interface type used as a -discriminated union of several concrete types but, unlike -`Object`, `Type` has very few methods because types have -little in common with each other. -Here is the interface: - - - type Type interface { - Underlying() Type - } - - -And here are the eleven concrete types that satisfy it: - - - Type = *Basic - | *Pointer - | *Array - | *Slice - | *Map - | *Chan - | *Struct - | *Tuple - | *Signature - | *Named - | *Interface - - -With the exception of `Named` types, instances of `Type` are -not canonical. -That is, it is usually a mistake to compare types using `t1==t2` -since this equivalence is not the same as the -[type identity relation](https://golang.org/ref/spec#Type_identity) -defined by the Go spec. -Use this function instead: - - - func Identical(t1, t2 Type) bool - - -For the same reason, you should not use a `Type` as a key in a map. -The [`golang.org/x/tools/go/types/typeutil` package](https://godoc.org/golang.org/x/tools/go/types/typeutil) -provides a map keyed by types that uses the correct -equivalence relation. - - -The Go spec defines three relations over types. -[_Assignability_](https://golang.org/ref/spec#Assignability) -governs which pairs of types may appear on the -left- and right-hand side of an assignment, including implicit -assignments such as function calls, map and channel operations, and so -on. -[_Comparability_](https://golang.org/ref/spec#Comparison_operators) -determines which types may appear in a comparison `x==y` or a -switch case or may be used as a map key. -[_Convertibility_](https://golang.org/ref/spec#Conversions) -governs which pairs of types are allowed in a conversion operation -`T(v)`. -You can query these relations with the following predicate functions: - - - func AssignableTo(V, T Type) bool - func Comparable(T Type) bool - func ConvertibleTo(V, T Type) bool - - -Let's take a look at each kind of type. - - -## Basic types - - -`Basic` represents all types that are not composed from simpler -types. -This is essentially the set of underlying types that a constant expression is -permitted to have--strings, booleans, and numbers---but it also -includes `unsafe.Pointer` and untyped nil. - - - type Basic struct{...} - func (*Basic) Kind() BasicKind - func (*Basic) Name() string - func (*Basic) Info() BasicInfo - - -The `Kind` method returns an "enum" value that indicates which -basic type this is. -The kinds `Bool`, `String`, `Int16`, and so on, -represent the corresponding predeclared boolean, string, or numeric -types. -There are two synonyms: `Byte` is equivalent to `Uint8` -and `Rune` is equivalent to `Int32`. -The kind `UnsafePointer` represents `unsafe.Pointer`. -The kinds `UntypedBool`, `UntypedInt` and so on represent -the six kinds of "untyped" constant types: boolean, integer, rune, -float, complex, and string. -The kind `UntypedNil` represents the type of the predeclared -`nil` value. -And the kind `Invalid` indicates the invalid type, which is used -for expressions containing errors, or for objects without types, like -`Label`, `Builtin`, or `PkgName`. - - - -The `Name` method returns the name of the type, such as -`"float64"`, and the `Info` method returns a bitfield that -encodes information about the type, such as whether it is signed or -unsigned, integer or floating point, or real or complex. - - - -`Typ` is a table of canonical basic types, indexed by -kind, so `Typ[String]` returns the `*Basic` that represents -`string`, for instance. -Like `Universe`, `Typ` is logically a constant, so don't -modify it. - - - -A few minor subtleties: -According to the Go spec, pre-declared types such as `int` are -named types for the purposes of assignability, even though the type -checker does not represent them using `Named`. -And `unsafe.Pointer` is a pointer type for the purpose of -determining whether the receiver type of a method is legal, even -though the type checker does not represent it using `Pointer`. - - - -The "untyped" types are usually only ascribed to constant expressions, -but there is one exception. -A comparison `x==y` has type "untyped bool", so the result of -this expression may be assigned to a variable of type `bool` or -any other named boolean type. - - - -## Simple Composite Types - - -The types `Pointer`, `Array`, `Slice`, `Map`, -and `Chan` are pretty self-explanatory. -All have an `Elem` method that returns the element type `T` -for a pointer `*T`, an array `[n]T`, a slice `[]T`, a -map `map[K]T`, or a channel `chan T`. -This should feel familiar if you've used the `reflect.Value` API. - - - -In addition, the `*Map`, `*Chan`, and `*Array` types -have accessor methods that return their key type, direction, and -length, respectively: - - - func (*Map) Key() Type - func (*Chan) Dir() ChanDir // = Send | Recv | SendRecv - func (*Array) Len() int64 - - - -## Struct Types - - -A struct type has an ordered list of fields and a corresponding -ordered list of field tags. - - - type Struct struct{ ... } - func (*Struct) NumFields() int - func (*Struct) Field(i int) *Var - func (*Struct) Tag(i int) string - - -Each field is a `Var` object whose `IsField` method returns true. -Field objects have no `Parent` scope, because they are -resolved through selections, not through the lexical environment. - - - - -Thanks to embedding, the expression `new(S).f` may be a shorthand -for a longer expression such as `new(S).d.e.f`, but in the -representation of `Struct` types, these field selection -operations are explicit. -That is, the set of fields of struct type `S` does not include `f`. -An anonymous field is represented like a regular field, but its -`Anonymous` method returns true. - - - -One subtlety is relevant to tools that generate documentation. -When analyzing a declaration such as this, - - - type T struct{x int} - - -it may be tempting to consider the `Var` object for field `x` as if it -had the name `"T.x"`, but beware: field objects do not have -canonical names and there is no way to obtain the name `"T"` -from the `Var` for `x`. -That's because several types may have the same underlying struct type, -as in this code: - - - type T struct{x int} - type U T - - -Here, the `Var` for field `x` belongs equally to `T` -and to `U`, and short of inspecting source positions or walking -the AST---neither of which is possible for objects loaded from compiler -export data---it is not possible to ascertain that `x` was declared as -part of `T`. -The type checker builds the exact same data structures given this input: - - - type T U - type U struct{x int} - - -A similar issue applies to the methods of named interface types. - - -## Tuple Types - - -Like a struct, a tuple type has an ordered list of fields, and fields -may be named. - - - type Tuple struct{ ... } - func (*Tuple) Len() int - func (*Tuple) At(i int) *Var - - -Although tuples are not the type of any variable in Go, they are -the type of some expressions, such as the right-hand sides of these -assignments: - - - v, ok = m[key] - v, ok = <-ch - v, ok = x.(T) - f, err = os.Open(filename) - - -Tuples also represent the types of the parameter list and the result -list of a function, as we will see. - - - -Since empty tuples are common, the nil `*Tuple` pointer is a valid empty tuple. - - - -## Function and Method Types - - -The types of functions and methods are represented by a `Signature`, -which has a tuple of parameter types and a tuple of result types. - - - type Signature struct{ ... } - func (*Signature) Recv() *Var - func (*Signature) Params() *Tuple - func (*Signature) Results() *Tuple - func (*Signature) Variadic() bool - - -Variadic functions such as `fmt.Println` have the `Variadic` -flag set. -The final parameter of such functions is always a slice, or in the -special case of certain calls to `append`, a string. - - - -A `Signature` for a method, whether concrete or abstract, has a -non-nil receiver parameter, `Recv`. -The type of the receiver is usually a named type or a pointer to a named type, -but it may be an unnamed struct or interface type in some cases. -Method types are rather second-class: they are only used for the -`Func` objects created by method declarations, and no Go -expression has a method type. -When printing a method type, the receiver does not appear, and the -`Identical` predicate ignores the receiver. - - - -The types of `Builtin` objects like `append` cannot be -expressed as a `Signature` since those types require parametric -polymorphism. -`Builtin` objects are thus ascribed the `Invalid` basic type. -However, the type of each _call_ to a built-in function has a specific -and expressible Go type. -These types are recorded during type checking for later use -([TypeAndValue](#typeandvalue)). - - - -## Named Types - - -Type declarations come in two forms. -The simplest kind, introduced in Go 1.9, -merely declares a (possibly alternative) name for an existing type. -Type names used in this way are informally called _type aliases_. -For example, this declaration lets you use the type -`Dictionary` as an alias for `map[string]string`: - - type Dictionary = map[string]string - -The declaration creates a `TypeName` object for `Dictionary`. The -object's `IsAlias` method returns true, and its `Type` method returns -a `Map` type that represents `map[string]string`. - - -The second form of type declaration, and the only kind prior to Go -1.9, does not use an equals sign: - - type Celsius float64 - -This declaration does more than just give a name to a type. -It first defines a new `Named` type -whose underlying type is `float64`; this `Named` type is different -from any other type, including `float64`. The declaration binds the -`TypeName` object to the `Named` type. - -Since Go 1.9, the Go language specification has used the term _defined -types_ instead of named types; -the essential property of a defined type is not that it has a name, -but that it is a distinct type with its own method set. -However, the type checker API predates that -change and instead calls defined types "named" types. - - type Named struct{ ... } - func (*Named) NumMethods() int - func (*Named) Method(i int) *Func - func (*Named) Obj() *TypeName - func (*Named) Underlying() Type - -The `Named` type's `Obj` method returns the `TypeName` object, which -provides the name, position, and other properties of the declaration. -Conversely, the `TypeName` object's `Type` method returns the `Named` type. - -A `Named` type may appear as the receiver type in a method declaration. -Methods are associated with the `Named` type, not the name (the -`TypeName` object); it's possible---though cryptic---to declare a -method on a `Named` type using one of its aliases. -The `NumMethods` and `Method` methods enumerate the declared -methods associated with this `Named` type (or a pointer to it), -in the order they were declared. -However, due to the subtleties of anonymous fields and the difference -between value and pointer receivers, a named type may have more or fewer -methods than this list. We'll return to this in [Method Sets](#method-sets). - - - -Every `Type` has an `Underlying` method, but for all of them -except `*Named`, it is simply the identity function. -For a named type, `Underlying` returns its underlying type, which -is always an unnamed type. -Thus `Underlying` returns `int` for both `T` and -`U` below. - - - type T int - type U T - - -Clients of the type checker often use type assertions or type switches -with a `Type` operand. -When doing so, it is often necessary to switch on the type that -_underlies_ the type of interest, and failure to do so may be a -bug. - -This is a common pattern: - - - // handle types of composite literal - switch u := t.Underlying().(type) { - case *Struct: // ... - case *Map: // ... - case *Array, *Slice: // ... - default: - panic("impossible") - } - -## Interface Types - - -Interface types are represented by `Interface`. - - - type Interface struct{ ... } - func (*Interface) Empty() bool - func (*Interface) NumMethods() int - func (*Interface) Method(i int) *Func - func (*Interface) NumEmbeddeds() int - func (*Interface) Embedded(i int) *Named - func (*Interface) NumExplicitMethods() int - func (*Interface) ExplicitMethod(i int) *Func - - -Syntactically, an interface type has a list of explicitly declared -methods (`ExplicitMethod`), and a list of embedded named -interface types (`Embedded`), but many clients care only about -the complete set of methods, which can be enumerated via -`Method`. -All three lists are ordered by name. -Since the empty interface is an important special case, the -`Empty` predicate provides a shorthand for `NumMethods() == -0`. - - - -As with the fields of structs (see above), the methods of interfaces -may belong equally to more than one interface type. -The `Func` object for method `f` in the code below is shared -by `I` and `J`: - - - type I interface { f() } - type J I - - -Because the difference between interface (abstract) and -non-interface (concrete) types is so important in Go, the -`IsInterface` predicate is provided for convenience. - - - func IsInterface(Type) bool - - -The type checker provides three utility methods relating to interface -satisfaction: - - - func Implements(V Type, T *Interface) bool - func AssertableTo(V *Interface, T Type) bool - func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) - - -The `Implements` predicate reports whether a type satisfies an -interface type. -`MissingMethod` is like `Implements`, but instead of -returning false, it explains why a type does not satisfy the -interface, for use in diagnostics. - - - -`AssertableTo` reports whether a type assertion `v.(T)` is legal. -If `T` is a concrete type that doesn't have all the methods of -interface `v`, then the type assertion is not legal, as in this example: - - - // error: io.Writer is not assertible to int - func f(w io.Writer) int { return w.(int) } - - - -## TypeAndValue - - -The type checker records the type of each expression in another field -of the `Info` struct, namely `Types`: - - - type Info struct { - ... - Types map[ast.Expr]TypeAndValue - } - - -No entries are recorded for identifiers since the `Defs` and -`Uses` maps provide more information about them. -Also, no entries are recorded for pseudo-expressions like -`*ast.KeyValuePair` or `*ast.Ellipsis`. - - - -The value of the `Types` map is a `TypeAndValue`, which -(unsurprisingly) holds the type and value of the expression, and in -addition, its _mode_. -The mode is opaque, but has predicates to answer questions such as: -Does this expression denote a value or a type? Does this value have an -address? Does this expression appear on the left-hand side of an -assignment? Does this expression appear in a context that expects two -results? -The comments in the code below give examples of expressions that -satisfy each predicate. - - - type TypeAndValue struct { - Type Type - Value constant.Value // for constant expressions only - ... - } - - func (TypeAndValue) IsVoid() bool // e.g. "main()" - func (TypeAndValue) IsType() bool // e.g. "*os.File" - func (TypeAndValue) IsBuiltin() bool // e.g. "len(x)" - func (TypeAndValue) IsValue() bool // e.g. "*os.Stdout" - func (TypeAndValue) IsNil() bool // e.g. "nil" - func (TypeAndValue) Addressable() bool // e.g. "a[i]" but not "f()", "m[key]" - func (TypeAndValue) Assignable() bool // e.g. "a[i]", "m[key]" - func (TypeAndValue) HasOk() bool // e.g. "<-ch", "m[key]" - - -The statement below inspects every expression within the AST of a single -type-checked file and prints its type, value, and mode: - - -%include typeandvalue/main.go inspect - - -It makes use of these two helper functions, which are not shown: - - - // nodeString formats a syntax tree in the style of gofmt. - func nodeString(n ast.Node) string - - // mode returns a string describing the mode of an expression. - func mode(tv types.TypeAndValue) string - - -Given this input: - - -%include typeandvalue/main.go input - - - -the program prints: - - -%include typeandvalue/main.go output - - - -Notice that the identifiers for the built-ins `make` and -`print` have types that are specific to the particular calls in -which they appear. -Also notice `m["hello"]` has a 2-tuple type `(int, bool)` -and that it is assignable, but unlike the variable `m`, it is not -addressable. - - - -Download the example and vary the inputs and see what the program prints. - - - -Here's another example, adapted from the `govet` static checking tool. -It checks for accidental uses of a method value `x.f` when a -call `x.f()` was intended; -comparing a method `x.f` against nil is a common mistake. - - -%include nilfunc/main.go - - -Given this input, - - -%include nilfunc/main.go input - - - -the program reports these errors: - - -%include nilfunc/main.go output - - -# Selections - - -A _selection_ is an expression _`expr`_`.f` in which -`f` denotes either a struct field or a method. -A selection is resolved not by looking for a name in the lexical -environment, but by looking within a _type_. -The type checker ascertains the meaning of each selection in the -package---a surprisingly tricky business---and records it in the -`Selections` mapping of the `Info` struct, whose values are -of type `Selection`: - - - type Selection struct{ ... } - func (s *Selection) Kind() SelectionKind // = FieldVal | MethodVal | MethodExpr - func (s *Selection) Recv() Type - func (s *Selection) Obj() Object - func (s *Selection) Type() Type - func (s *Selection) Index() []int - func (s *Selection) Indirect() bool - - -The `Kind` method discriminates between the three (legal) kinds -of selections, as indicated by the comments below. - - - type T struct{Field int} - func (T) Method() {} - var v T - - // Kind Type - var _ = v.Field // FieldVal int - var _ = v.Method // MethodVal func() - var _ = T.Method // MethodExpr func(T) - - -Because of embedding, a selection may denote more than one field or -method, in which case it is ambiguous, and no `Selection` is -recorded for it. - - - -The `Obj` method returns the `Object` for the selected field -(`*Var`) or method (`*Func`). -Due to embedding, the object may belong to a different type than that -of the receiver expression _`expr`_. -The `Type` method returns the type of the selection. For a field -selection, this is the type of the field, but for method selections, -the result is a function type that is not the same as the type of the -method. -For a `MethodVal`, the receiver parameter is dropped, and -for a `MethodExpr`, the receiver parameter becomes a regular -parameter, as shown in the example above. - - - -The `Index` and `Indirect` methods report information about -implicit operations occurring during the selection that a compiler -would need to know about. -Because of embedding, a selection _`expr`_`.f` may be -shorthand for a sequence containing several implicit field selections, -_`expr`_`.d.e.f`, and `Index` reports the complete -sequence. -And because of automatic pointer dereferencing during struct field -accesses and method calls, a selection may imply one or more indirect -loads from memory; `Indirect` reports whether this occurs. - - - -Clients of the type checker can call `LookupFieldOrMethod` to -look up a name within a type, as if by a selection. -This function has an intimidating signature, but conceptually it -accepts just a `Type` and a name, and returns a `Selection`: - - - func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) \ - (obj Object, index []int, indirect bool) - - -The result is not actually a `Selection`, but it contains the -three main components of one: `Obj`, `Index`, -and `Indirect`. - - - -The `addressable` flag should be set if the receiver is a -_variable_ of type `T`, since in a method selection on a -variable, an implicit address-of operation (`&`) may occur. -The flag indicates whether the methods of type `*T` should be -considered during the lookup. -(You may wonder why this parameter is necessary. Couldn't clients -instead call `LookupFieldOrMethod` on the pointer type `*T` -if the receiver is a `T` variable? The answer is that if -`T` is an interface type, the type `*T` has no methods at -all.) - - - -The final two parameters of `LookupFieldOrMethod` are `(pkg -*Package, name string)`. -Together they specify the name of the field or method to look up. -This brings us to `Id`s. - - - -# Ids - - -`LookupFieldOrMethod`'s need for a `Package` parameter -is a subtle consequence of the -[_Uniqueness of identifiers_](https://golang.org/ref/spec#Uniqueness_of_identifiers) -section in the Go spec: "Two -identifiers are different if they are spelled differently, or if they -appear in different packages and are not exported." -In practical terms, this means that a type may have two methods -(or two fields, or one of each) both named `f` so long as those -methods are defined in different packages, as in this example: - - - package a - type A int - func (A) f() - - package b - type B int - func (B) f() - - package c - import ( "a"; "b" ) - type C struct{a.A; b.B} // C has two methods called f - - -The type `c.C` has two methods named `f`, but there is -no ambiguity because the two `f`s are distinct -identifiers---think of them as `fᵃ` and `fᵇ`. -For an exported method, this situation _would_ be ambiguous -because there is no distinction between `Fᵃ` and `Fᵇ`; there -is only `F`. - - - -Despite having two methods called `f`, neither of them can be -called from within package `c` because `c` has no way to -identify them. -Within `c`, `f` is the identifier `fᶜ`, and -type `C` has no method of that name. -But if we pass an instance of `C` to code in package `a` -and call its `f` method via an interface, `fᵃ` is called. - - - -The practical consequence for tool builders is that any time you need -to look up a field or method by name, or construct a map of fields and/or -methods keyed by name, it is not sufficient to use the object's name -as a key. -Instead, you must call the `Object.Id` method, which returns -a string that incorporates the object name, and for unexported -objects, the package path too. -There is also a standalone function `Id` that combines a name and -the package path in the same way: - - - func Id(pkg *Package, name string) string - - -This distinction applies to selections _`expr`_`.f`, but not -to lexical references `x` because for unexported identifiers, -declarations and references always appear in the same package. - - - -Fun fact: the `reflect.StructField` type records both the -`Name` and the `PkgPath` strings for the same reason. -The `FieldByName` methods of `reflect.Value` and -`reflect.Type` match field names without regard to the package. -If there is more than one match, they return an invalid value. - - - - -# Method Sets - - -The _method set_ of a type is the set of methods that can be -called on any value of that type. -(A variable of type `T` has access to all the methods of type -`*T` as well, due to the implicit address-of operation during -method calls, but those extra methods are not part of the method set -of `T`.) - - - -Clients can request the method set of a type `T` by calling -`NewMethodSet(T)`: - - - type MethodSet struct{ ... } - func NewMethodSet(T Type) *MethodSet - func (s *MethodSet) Len() int - func (s *MethodSet) At(i int) *Selection - func (s *MethodSet) Lookup(pkg *Package, name string) *Selection - - -The `Len` and `At` methods access a list of -`Selections`, all of kind `MethodVal`, ordered by `Id`. -The `Lookup` function allows lookup of a single method by -name (and package path, as explained in the previous section). - - - -`NewMethodSet` can be expensive, so for applications that compute -method sets repeatedly, `golang.org/x/tools/go/types/typeutil` -provides a `MethodSetCache` type that records previous results. -If you only need a single method, don't construct the -`MethodSet` at all; it's cheaper to use -`LookupFieldOrMethod`. - - - -The next program generates a boilerplate -declaration of a new concrete type that satisfies an existing -interface. -Here's an example: - - -%include skeleton/main.go output1 - - - -The three arguments are the package and the name of the existing -interface type, and the name of the new concrete type. -The `main` function (not shown) loads the specified package and -calls `PrintSkeleton` with the remaining two arguments: - - -%include skeleton/main.go - - -First, `PrintSkeleton` locates the package-level named interface -type, handling various error cases. -Then it chooses the name for the receiver of the new methods: the -first letter of the concrete type. -Finally, it iterates over the method set of the interface, printing -the corresponding concrete method declarations. - - - -There's a subtlety in the declaration of `sig`, which is the -string form of the method signature. -We could have obtained this string from `meth.Type().String()`, -but this would cause any named types within it to be formatted with -the complete package path, for instance -`net/http.ResponseWriter`, which is informative in diagnostics -but not legal Go syntax. -The `TypeString` function (explained in [Formatting Values](#formatting-values)) allows the -caller to control how packages are printed. -Passing `(*types.Package).Name` causes only the package name -`http` to be printed, not the complete path. -Here's another example that illustrates it: - - -%include skeleton/main.go output2 - - - -The following program inspects all pairs of package-level named types -in `pkg`, and reports the types that satisfy each interface type. - - -%include implements/main.go implements - - -Given this input, - - -%include implements/main.go input - - -the program prints: - - -%include implements/main.go output - - - -Notice that the method set of `B` does not include `g`, but -the method set of `*B` does. -That's why we needed the second assignability check, using the pointer -type `types.NewPointer(T)`. - - - -# Constants - - -A constant expression is one whose value is guaranteed to be computed at -compile time. -Constant expressions may appear in types, specifically as the length -of an array type such as `[16]byte`, so one of the jobs of the -type checker is to compute the value of each constant expression. - - - -As we saw in the `typeandvalue` example, the type checker records -the value of each constant expression like `"Hello, " + "world"`, -storing it in the `Value` field of the `TypeAndValue` struct. -Constants are represented using the `Value` interface from the -`go/constant` package. - - - package constant // go/constant - - type Value interface { - Kind() Kind - } - - type Kind int // one of Unknown, Bool, String, Int, Float, Complex - - -The interface has only one method, for discriminating the various -kinds of constants, but the package provides many functions for -inspecting a value of a known kind, - - - // Accessors - func BoolVal(x Value) bool - func Float32Val(x Value) (float32, bool) - func Float64Val(x Value) (float64, bool) - func Int64Val(x Value) (int64, bool) - func StringVal(x Value) string - func Uint64Val(x Value) (uint64, bool) - func Bytes(x Value) []byte - func BitLen(x Value) int - func Sign(x Value) int - - -for performing arithmetic on values, - - - // Operations - func Compare(x Value, op token.Token, y Value) bool - func UnaryOp(op token.Token, y Value, prec uint) Value - func BinaryOp(x Value, op token.Token, y Value) Value - func Shift(x Value, op token.Token, s uint) Value - func Denom(x Value) Value - func Num(x Value) Value - func Real(x Value) Value - func Imag(x Value) Value - - -and for constructing new values: - - - // Constructors - func MakeBool(b bool) Value - func MakeFloat64(x float64) Value - func MakeFromBytes(bytes []byte) Value - func MakeFromLiteral(lit string, tok token.Token, prec uint) Value - func MakeImag(x Value) Value - func MakeInt64(x int64) Value - func MakeString(s string) Value - func MakeUint64(x uint64) Value - func MakeUnknown() Value - - -All numeric `Value`s, whether integer or floating-point, signed or -unsigned, or real or complex, are represented more precisely than -ordinary Go types like `int64` and `float64`. -Internally, the `go/constant` package uses multi-precision data types -like `Int`, `Rat`, and `Float` from the `math/big` package so that -`Values` and their arithmetic operations are accurate to at least 256 -bits, as required by the Go specification. - - - - - -# Size and Alignment - - -Because the calls `unsafe.Sizeof(v)`, `unsafe.Alignof(v)`, -and `unsafe.Offsetof(v.f)` are all constant expressions, the type -checker must be able to compute the memory layout of any value -`v`. - - - -By default, the type checker uses the same layout algorithm as the Go -1.5 `gc` compiler targeting `amd64`. -Clients can configure the type checker to use a different algorithm by -providing an instance of the `types.Sizes` interface in the -`types.Config` struct: - - - package types - - type Sizes interface { - Alignof(T Type) int64 - Offsetsof(fields []*Var) []int64 - Sizeof(T Type) int64 - } - - - -For common changes, like reducing the word size to 32 bits, clients -can use an instance of `StdSizes`: - - - type StdSizes struct { - WordSize int64 - MaxAlign int64 - } - - -This type has two basic size and alignment parameters from which it -derives all the other values using common assumptions. -For example, pointers, functions, maps, and channels fit in one word, -strings and interfaces require two words, and slices need three. -The default behaviour is equivalent to `StdSizes{8, 8}`. -For more esoteric layout changes, you'll need to write a new -implementation of the `Sizes` interface. - - - -The `hugeparam` program below prints all function parameters and -results whose size exceeds a threshold. -By default, the threshold is 48 bytes, but you can set it via the -`-bytes` command-line flag. -Such a tool could help identify inefficient parameter passing in your -programs. - - -%include hugeparam/main.go - - -As before, `Inspect` applies a function to every node in the AST. -The function cares about two kinds of nodes: declarations of named -functions and methods (`*ast.FuncDecl`) and function literals -(`*ast.FuncLit`). -Observe the two cases' different logic to obtain the type of each -function. - - - -Here's a typical invocation on the standard `encoding/xml` package. -It reports a number of places where the 7-word -[`StartElement` type](https://godoc.org/encoding/xml#StartElement) -is copied. - - -%include hugeparam/main.go output - - -# Imports - - -The type checker's `Check` function processes a slice of parsed -files (`[]*ast.File`) that make up one package. -When the type checker encounters an import declaration, it needs the -type information for the objects in the imported package. -It gets it by calling the `Import` method of the `Importer` -interface shown below, an instance of which must be provided by the -`Config`. -This separation of concerns relieves the type checker from having to -know any of the details of Go workspace organization, `GOPATH`, -compiler file formats, and so on. - - - type Importer interface { - Import(path string) (*Package, error) - } - - -Most of our examples used the simplest `Importer` implementation, -`importer.Default()`, provided by the `go/importer` package. -This importer looks in `$GOROOT` and `$GOPATH` for `.a` -files written by the compiler (`gc` or `gccgo`) -that was used to build the program. -In addition to object code, these files contain _export data_, -that is, a description of all the objects declared by the package, and -also of any objects from other packages that were referred to indirectly. -Because export data includes information about dependencies, the type -checker need load at most one file per import, instead of one per -transitive dependency. - - - -Compiler export data is compact and efficient to locate, load, and -parse, but it has several shortcomings. -First, it does not contain position information for imported -objects, reducing the quality of certain diagnostic messages. -Second, it does not contain complete syntax trees nor semantic information -about the contents of function bodies, so it is not suitable for -interprocedural analyses. -Third, compiler object data may be stale. Nothing detects or ensures -that the object files are more recent than the source files from which -they were derived. -Generally, object data for standard packages is likely to be -up-to-date, but for user packages, it depends on how recently the user -ran a `go install` or `go build -i` command. - - - -The [`golang.org/tools/x/go/loader` package](https://godoc.org/golang.org/x/tools/go/loader) -provides an alternative `Importer` that addresses -some of these problems. -It loads a complete program from source, performing -[`cgo`](https://golang.org/cmd/cgo/cgo) preprocessing if -necessary, followed by parsing and type-checking. -It loads independent packages in parallel to hide I/O latency, and -detects and reports import cycles. -For each package, it provides the `types.Package` containing the -package's lexical environment, the list of `ast.File` syntax -trees for each file in the package, the `types.Info` containing -type information for each syntax node, and a list of type errors -associated with that package. -(Please be aware that the `go/loader` package's API is likely to -change before it finally stabilizes.) - - - -The `doc` program below demonstrates a simple use of the loader. -It is a rudimentary implementation of `go doc` that prints the type, -methods, and documentation of the package-level object specified on -the command line. -Here's an example: - - -%include doc/main.go output - - - -Observe that it prints the correct location of each method -declaration, even though, due to embedding, some of -`http.File`'s methods were declared in another package. -Here's the first part of the program, showing how to load an entire -program starting from the single package, `pkgpath`: - - -%include doc/main.go part1 - - -Notice that we instructed the parser to retain comments during parsing. -The rest of the program prints the output: - - -%include doc/main.go part2 - - -We used `IntuitiveMethodSet` to compute the method set, instead -of `NewMethodSet`. -The result of this convenience function, which is intended for use in -user interfaces, includes methods of `*T` as well as those of -`T`, since that matches most users' intuition about the method -set of a type. -(Our example, `http.File`, didn't illustrate the difference, but try -running it on a type with both value and pointer methods.) - - - -Also notice `PathEnclosingInterval`, which finds the set of AST -nodes that enclose a particular point, in this case, the object's -declaring identifier. -By walking up with path, we find the enclosing declaration, to which -the documentation is attached. - - - -# Formatting support - - -All types that satisfy `Type` or `Object` define a -`String` method that formats the type or object in a readable -notation. `Selection` also provides a `String` method. -All package-level objects within these data structures are -printed with the complete package path, as in these examples: - - - []encoding/json.Marshaler // a *Slice type - encoding/json.Marshal // a *Func object - (*encoding/json.Encoder).Encode // a *Func object (method) - func (enc *encoding/json.Encoder) Encode(v interface{}) error // a method *Signature - func NewEncoder(w io.Writer) *encoding/json.Encoder // a function *Signature - - -This notation is unambiguous, but it is not legal Go syntax. -Also, package paths may be long, and the same package path may appear -many times in a single string, for instance, when formatting a -function of several parameters. -Because these strings often form part of a tool's user interface---as -with the diagnostic messages of `hugeparam` or the code generated -by `skeleton`---many clients want more control over the -formatting of package names. - - - -The `go/types` package provides these alternatives to the -`String` methods: - - - func ObjectString(obj Object, qf Qualifier) string - func TypeString(typ Type, qf Qualifier) string - func SelectionString(s *Selection, qf Qualifier) string - - type Qualifier func(*Package) string - - -The `TypeString`, `ObjectString`, and `SelectionString` -functions are like the `String` methods of the respective types, -but they accept an additional argument, a `Qualifier`. - - - -A `Qualifier` is a client-provided function that determines how a -package name is rendered as a string. -If it is nil, the default behavior is to print the package's -path, just like the `String` methods do. -If a caller passes `(*Package).Name` as the qualifier, that is, a -function that accepts a package and returns its `Name`, then -objects are qualified only by the package name. -The above examples would look like this: - - - []json.Marshaler - json.Marshal - (*json.Encoder).Encode - func (enc *json.Encoder) Encode(v interface{}) error - func NewEncoder(w io.Writer) *json.Encoder - - -Often when a tool prints some output, it is implicitly in the -context of a particular package, perhaps one specified by the -command line or HTTP request. -In that case, it is more natural to omit the package qualification -altogether for objects belonging to that package, but to qualify all -other objects by their package's path. -That's what the `RelativeTo(pkg)` qualifier does: - - - func RelativeTo(pkg *Package) Qualifier - - -The examples below show how `json.NewEncoder` would be printed -using three qualifiers, each relative to a different package: - - - // RelativeTo "encoding/json": - func NewEncoder(w io.Writer) *Encoder - - // RelativeTo "io": - func NewEncoder(w Writer) *encoding/json.Encoder - - // RelativeTo any other package: - func NewEncoder(w io.Writer) *encoding/json.Encoder - - -Another qualifier that may be relevant to refactoring tools (but is -not currently provided by the type checker) is one that renders each -package name using the locally appropriate name within a given source -file. -Its behavior would depend on the set of import declarations, including -renaming imports, within that source file. - - - -# Getting from A to B - - -The type checker and its related packages represent many aspects of a -Go program in many different ways, and analysis tools must often map -between them. -For instance, a named entity may be identified by its `Object`; -by its declaring identifier (`ast.Ident`) or by any referring -identifier; by its declaring `ast.Node`; by the position -(`token.Pos`) of any those nodes; or by the filename and -line/column number (or byte offset) of those `token.Pos` values. - - - -In this section, we'll list solutions to a number of common problems -of the form "I have an A; I need the corresponding B". - - - -To map **from a `token.Pos` to an `ast.Node`**, call the -helper function -[`astutil.PathEnclosingInterval`](https://godoc.org/golang.org/x/tools/go/ast/astutil#PathEnclosingInterval). -It returns the enclosing `ast.Node`, and all its ancestors up to -the root of the file. -You must know which file `*ast.File` the `token.Pos` belongs to. -Alternatively, you can search an entire program loaded by the -`loader` package, using -[`(*loader.Program).PathEnclosingInterval`](https://godoc.org/golang.org/x/tools/go/loader#Program.PathEnclosingInterval). - - - -To map **from an `Object` to its declaring syntax**, call -`Pos` to get its position, then use `PathEnclosingInterval` as before. -This approach is suitable for a one-off query. For repeated use, it -may be more efficient to visit the syntax tree and construct the -mapping between declarations and objects. - - - -To map **from an `ast.Ident` to the `Object`** it refers to (or -declares), consult the `Uses` or `Defs` map for the -package, as shown in [Identifier Resolution](#identifier-resolution). - - - -To map **from an `Object` to its documentation**, find the -object's declaration, and look at the attached `Doc` field. -You must have set the parser's `ParseComments` flag. -See the `doc` example in [Imports](#imports). diff --git a/gotypes/hello/hello.go b/gotypes/hello/hello.go deleted file mode 100644 index 01a28623..00000000 --- a/gotypes/hello/hello.go +++ /dev/null @@ -1,10 +0,0 @@ -//!+ -package main - -import "fmt" - -func main() { - fmt.Println("Hello, 世界") -} - -//!- diff --git a/gotypes/hugeparam/main.go b/gotypes/hugeparam/main.go deleted file mode 100644 index 1474403a..00000000 --- a/gotypes/hugeparam/main.go +++ /dev/null @@ -1,81 +0,0 @@ -// The hugeparam command identifies by-value parameters that are larger than n bytes. -// -// Example: -// $ ./hugeparams encoding/xml -package main - -import ( - "flag" - "fmt" - "go/ast" - "go/token" - "go/types" - "log" - - "golang.org/x/tools/go/loader" -) - -//!+ -var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes") - -var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function - -func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) { - checkTuple := func(descr string, tuple *types.Tuple) { - for i := 0; i < tuple.Len(); i++ { - v := tuple.At(i) - if sz := sizeof(v.Type()); sz > int64(*bytesFlag) { - fmt.Printf("%s: %q %s: %s = %d bytes\n", - fset.Position(v.Pos()), - v.Name(), descr, v.Type(), sz) - } - } - } - checkSig := func(sig *types.Signature) { - checkTuple("parameter", sig.Params()) - checkTuple("result", sig.Results()) - } - for _, file := range files { - ast.Inspect(file, func(n ast.Node) bool { - switch n := n.(type) { - case *ast.FuncDecl: - checkSig(info.Defs[n.Name].Type().(*types.Signature)) - case *ast.FuncLit: - checkSig(info.Types[n.Type].Type.(*types.Signature)) - } - return true - }) - } -} - -//!- - -func main() { - flag.Parse() - - // The loader loads a complete Go program from source code. - var conf loader.Config - _, err := conf.FromArgs(flag.Args(), false) - if err != nil { - log.Fatal(err) // command syntax error - } - lprog, err := conf.Load() - if err != nil { - log.Fatal(err) // load error - } - - for _, info := range lprog.InitialPackages() { - PrintHugeParams(lprog.Fset, &info.Info, info.Files) - } -} - -/* -//!+output -% ./hugeparam encoding/xml -/go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:761:51: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/marshal.go:781:68: "start" parameter: encoding/xml.StartElement = 56 bytes -/go/src/encoding/xml/xml.go:72:30: "" result: encoding/xml.StartElement = 56 bytes -//!-output -*/ diff --git a/gotypes/implements/main.go b/gotypes/implements/main.go deleted file mode 100644 index d7e5701b..00000000 --- a/gotypes/implements/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -//!+input -const input = `package main - -type A struct{} -func (*A) f() - -type B int -func (B) f() -func (*B) g() - -type I interface { f() } -type J interface { g() } -` - -//!-input - -func main() { - // Parse one file. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "input.go", input, 0) - if err != nil { - log.Fatal(err) // parse error - } - conf := types.Config{Importer: importer.Default()} - pkg, err := conf.Check("hello", fset, []*ast.File{f}, nil) - if err != nil { - log.Fatal(err) // type error - } - - //!+implements - // Find all named types at package level. - var allNamed []*types.Named - for _, name := range pkg.Scope().Names() { - if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { - allNamed = append(allNamed, obj.Type().(*types.Named)) - } - } - - // Test assignability of all distinct pairs of - // named types (T, U) where U is an interface. - for _, T := range allNamed { - for _, U := range allNamed { - if T == U || !types.IsInterface(U) { - continue - } - if types.AssignableTo(T, U) { - fmt.Printf("%s satisfies %s\n", T, U) - } else if !types.IsInterface(T) && - types.AssignableTo(types.NewPointer(T), U) { - fmt.Printf("%s satisfies %s\n", types.NewPointer(T), U) - } - } - } - //!-implements -} - -/* -//!+output -$ go build github.com/golang/example/gotypes/implements -$ ./implements -*hello.A satisfies hello.I -hello.B satisfies hello.I -*hello.B satisfies hello.J -//!-output -*/ diff --git a/gotypes/lookup/lookup.go b/gotypes/lookup/lookup.go deleted file mode 100644 index 9bdc97d2..00000000 --- a/gotypes/lookup/lookup.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" - "strings" -) - -//!+input -const hello = ` -package main - -import "fmt" - -// append -func main() { - // fmt - fmt.Println("Hello, world") - // main - main, x := 1, 2 - // main - print(main, x) - // x -} -// x -` - -//!-input - -//!+main -func main() { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments) - if err != nil { - log.Fatal(err) // parse error - } - - conf := types.Config{Importer: importer.Default()} - pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil) - if err != nil { - log.Fatal(err) // type error - } - - // Each comment contains a name. - // Look up that name in the innermost scope enclosing the comment. - for _, comment := range f.Comments { - pos := comment.Pos() - name := strings.TrimSpace(comment.Text()) - fmt.Printf("At %s,\t%q = ", fset.Position(pos), name) - inner := pkg.Scope().Innermost(pos) - if _, obj := inner.LookupParent(name, pos); obj != nil { - fmt.Println(obj) - } else { - fmt.Println("not found") - } - } -} - -//!-main - -/* -//!+output -$ go build github.com/golang/example/gotypes/lookup -$ ./lookup -At hello.go:6:1, "append" = builtin append -At hello.go:8:9, "fmt" = package fmt -At hello.go:10:9, "main" = func cmd/hello.main() -At hello.go:12:9, "main" = var main int -At hello.go:14:9, "x" = var x int -At hello.go:16:1, "x" = not found -//!-output -*/ diff --git a/gotypes/nilfunc/main.go b/gotypes/nilfunc/main.go deleted file mode 100644 index da4b79b7..00000000 --- a/gotypes/nilfunc/main.go +++ /dev/null @@ -1,104 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -//!+input -const input = `package main - -import "bytes" - -func main() { - var buf bytes.Buffer - if buf.Bytes == nil && bytes.Repeat != nil && main == nil { - // ... - } -} -` - -//!-input - -var fset = token.NewFileSet() - -func main() { - f, err := parser.ParseFile(fset, "input.go", input, 0) - if err != nil { - log.Fatal(err) // parse error - } - conf := types.Config{Importer: importer.Default()} - info := &types.Info{ - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Types: make(map[ast.Expr]types.TypeAndValue), - } - if _, err = conf.Check("cmd/hello", fset, []*ast.File{f}, info); err != nil { - log.Fatal(err) // type error - } - - ast.Inspect(f, func(n ast.Node) bool { - if n != nil { - CheckNilFuncComparison(info, n) - } - return true - }) -} - -//!+ -// CheckNilFuncComparison reports unintended comparisons -// of functions against nil, e.g., "if x.Method == nil {". -func CheckNilFuncComparison(info *types.Info, n ast.Node) { - e, ok := n.(*ast.BinaryExpr) - if !ok { - return // not a binary operation - } - if e.Op != token.EQL && e.Op != token.NEQ { - return // not a comparison - } - - // If this is a comparison against nil, find the other operand. - var other ast.Expr - if info.Types[e.X].IsNil() { - other = e.Y - } else if info.Types[e.Y].IsNil() { - other = e.X - } else { - return // not a comparison against nil - } - - // Find the object. - var obj types.Object - switch v := other.(type) { - case *ast.Ident: - obj = info.Uses[v] - case *ast.SelectorExpr: - obj = info.Uses[v.Sel] - default: - return // not an identifier or selection - } - - if _, ok := obj.(*types.Func); !ok { - return // not a function or method - } - - fmt.Printf("%s: comparison of function %v %v nil is always %v\n", - fset.Position(e.Pos()), obj.Name(), e.Op, e.Op == token.NEQ) -} - -//!- - -/* -//!+output -$ go build github.com/golang/example/gotypes/nilfunc -$ ./nilfunc -input.go:7:5: comparison of function Bytes == nil is always false -input.go:7:25: comparison of function Repeat != nil is always true -input.go:7:48: comparison of function main == nil is always false -//!-output -*/ diff --git a/gotypes/pkginfo/main.go b/gotypes/pkginfo/main.go deleted file mode 100644 index 3050f80d..00000000 --- a/gotypes/pkginfo/main.go +++ /dev/null @@ -1,64 +0,0 @@ -//!+ -package main - -import ( - "fmt" - "go/ast" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -const hello = `package main - -import "fmt" - -func main() { - fmt.Println("Hello, world") -}` - -func main() { - fset := token.NewFileSet() - - // Parse the input string, []byte, or io.Reader, - // recording position information in fset. - // ParseFile returns an *ast.File, a syntax tree. - f, err := parser.ParseFile(fset, "hello.go", hello, 0) - if err != nil { - log.Fatal(err) // parse error - } - - // A Config controls various options of the type checker. - // The defaults work fine except for one setting: - // we must specify how to deal with imports. - conf := types.Config{Importer: importer.Default()} - - // Type-check the package containing only file f. - // Check returns a *types.Package. - pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil) - if err != nil { - log.Fatal(err) // type error - } - - fmt.Printf("Package %q\n", pkg.Path()) - fmt.Printf("Name: %s\n", pkg.Name()) - fmt.Printf("Imports: %s\n", pkg.Imports()) - fmt.Printf("Scope: %s\n", pkg.Scope()) -} - -//!- - -/* -//!+output -$ go build github.com/golang/example/gotypes/pkginfo -$ ./pkginfo -Package "cmd/hello" -Name: main -Imports: [package fmt ("fmt")] -Scope: package "cmd/hello" scope 0x820533590 { -. func cmd/hello.main() -} -//!-output -*/ diff --git a/gotypes/skeleton/main.go b/gotypes/skeleton/main.go deleted file mode 100644 index c3961e20..00000000 --- a/gotypes/skeleton/main.go +++ /dev/null @@ -1,115 +0,0 @@ -// The skeleton command prints the boilerplate for a concrete type -// that implements the specified interface type. -// -// Example: -// $ ./skeleton io ReadWriteCloser buffer -// // *buffer implements io.ReadWriteCloser. -// type buffer struct{ /* ... */ } -// func (b *buffer) Close() error { panic("unimplemented") } -// func (b *buffer) Read(p []byte) (n int, err error) { panic("unimplemented") } -// func (b *buffer) Write(p []byte) (n int, err error) { panic("unimplemented") } -package main - -import ( - "fmt" - "go/types" - "log" - "os" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/tools/go/loader" -) - -const usage = "Usage: skeleton " - -//!+ -func PrintSkeleton(pkg *types.Package, ifacename, concname string) error { - obj := pkg.Scope().Lookup(ifacename) - if obj == nil { - return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename) - } - if _, ok := obj.(*types.TypeName); !ok { - return fmt.Errorf("%v is not a named type", obj) - } - iface, ok := obj.Type().Underlying().(*types.Interface) - if !ok { - return fmt.Errorf("type %v is a %T, not an interface", - obj, obj.Type().Underlying()) - } - // Use first letter of type name as receiver parameter. - if !isValidIdentifier(concname) { - return fmt.Errorf("invalid concrete type name: %q", concname) - } - r, _ := utf8.DecodeRuneInString(concname) - - fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename) - fmt.Printf("type %s struct{}\n", concname) - mset := types.NewMethodSet(iface) - for i := 0; i < mset.Len(); i++ { - meth := mset.At(i).Obj() - sig := types.TypeString(meth.Type(), (*types.Package).Name) - fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n", - r, concname, meth.Name(), - strings.TrimPrefix(sig, "func")) - } - return nil -} - -//!- - -func isValidIdentifier(id string) bool { - for i, r := range id { - if !unicode.IsLetter(r) && - !(i > 0 && unicode.IsDigit(r)) { - return false - } - } - return id != "" -} - -func main() { - if len(os.Args) != 4 { - log.Fatal(usage) - } - pkgpath, ifacename, concname := os.Args[1], os.Args[2], os.Args[3] - - // The loader loads a complete Go program from source code. - var conf loader.Config - conf.Import(pkgpath) - lprog, err := conf.Load() - if err != nil { - log.Fatal(err) // load error - } - pkg := lprog.Package(pkgpath).Pkg - if err := PrintSkeleton(pkg, ifacename, concname); err != nil { - log.Fatal(err) - } -} - -/* -//!+output1 -$ ./skeleton io ReadWriteCloser buffer -// *buffer implements io.ReadWriteCloser. -type buffer struct{} -func (b *buffer) Close() error { - panic("unimplemented") -} -func (b *buffer) Read(p []byte) (n int, err error) { - panic("unimplemented") -} -func (b *buffer) Write(p []byte) (n int, err error) { - panic("unimplemented") -} -//!-output1 - -//!+output2 -$ ./skeleton net/http Handler myHandler -// *myHandler implements net/http.Handler. -type myHandler struct{} -func (m *myHandler) ServeHTTP(http.ResponseWriter, *http.Request) { - panic("unimplemented") -} -//!-output2 -*/ diff --git a/gotypes/typeandvalue/main.go b/gotypes/typeandvalue/main.go deleted file mode 100644 index 8a6fc798..00000000 --- a/gotypes/typeandvalue/main.go +++ /dev/null @@ -1,134 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/format" - "go/importer" - "go/parser" - "go/token" - "go/types" - "log" -) - -//!+input -const input = ` -package main - -var m = make(map[string]int) - -func main() { - v, ok := m["hello, " + "world"] - print(rune(v), ok) -} -` - -//!-input - -var fset = token.NewFileSet() - -func main() { - f, err := parser.ParseFile(fset, "hello.go", input, 0) - if err != nil { - log.Fatal(err) // parse error - } - - conf := types.Config{Importer: importer.Default()} - info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} - if _, err := conf.Check("cmd/hello", fset, []*ast.File{f}, info); err != nil { - log.Fatal(err) // type error - } - - //!+inspect - // f is a parsed, type-checked *ast.File. - ast.Inspect(f, func(n ast.Node) bool { - if expr, ok := n.(ast.Expr); ok { - if tv, ok := info.Types[expr]; ok { - fmt.Printf("%-24s\tmode: %s\n", nodeString(expr), mode(tv)) - fmt.Printf("\t\t\t\ttype: %v\n", tv.Type) - if tv.Value != nil { - fmt.Printf("\t\t\t\tvalue: %v\n", tv.Value) - } - } - } - return true - }) - //!-inspect -} - -// nodeString formats a syntax tree in the style of gofmt. -func nodeString(n ast.Node) string { - var buf bytes.Buffer - format.Node(&buf, fset, n) - return buf.String() -} - -// mode returns a string describing the mode of an expression. -func mode(tv types.TypeAndValue) string { - s := "" - if tv.IsVoid() { - s += ",void" - } - if tv.IsType() { - s += ",type" - } - if tv.IsBuiltin() { - s += ",builtin" - } - if tv.IsValue() { - s += ",value" - } - if tv.IsNil() { - s += ",nil" - } - if tv.Addressable() { - s += ",addressable" - } - if tv.Assignable() { - s += ",assignable" - } - if tv.HasOk() { - s += ",ok" - } - return s[1:] -} - -/* -//!+output -$ go build github.com/golang/example/gotypes/typeandvalue -$ ./typeandvalue -make(map[string]int) mode: value - type: map[string]int -make mode: builtin - type: func(map[string]int) map[string]int -map[string]int mode: type - type: map[string]int -string mode: type - type: string -int mode: type - type: int -m["hello, "+"world"] mode: value,assignable,ok - type: (int, bool) -m mode: value,addressable,assignable - type: map[string]int -"hello, " + "world" mode: value - type: string - value: "hello, world" -"hello, " mode: value - type: untyped string - value: "hello, " -"world" mode: value - type: untyped string - value: "world" -print(rune(v), ok) mode: void - type: () -print mode: builtin - type: func(rune, bool) -rune(v) mode: value - type: rune -rune mode: type - type: rune -...more not shown... -//!-output -*/ diff --git a/gotypes/weave.go b/gotypes/weave.go deleted file mode 100644 index cfaa57b9..00000000 --- a/gotypes/weave.go +++ /dev/null @@ -1,191 +0,0 @@ -// The weave command is a simple preprocessor for markdown files. -// It builds a table of contents and processes %include directives. -// -// Example usage: -// $ go run weave.go go-types.md > README.md -package main - -import ( - "bufio" - "bytes" - "fmt" - "log" - "os" - "path/filepath" - "regexp" - "strings" -) - -func main() { - log.SetFlags(0) - log.SetPrefix("weave: ") - if len(os.Args) != 2 { - log.Fatal("usage: weave input.md\n") - } - - f, err := os.Open(os.Args[1]) - if err != nil { - log.Fatal(err) - } - defer f.Close() - - fmt.Println("") - - // Pass 1. - var toc []string - in := bufio.NewScanner(f) - for in.Scan() { - line := in.Text() - if line == "" || (line[0] != '#' && line[0] != '%') { - continue - } - line = strings.TrimSpace(line) - if line == "%toc" { - toc = nil - } else if strings.HasPrefix(line, "# ") || strings.HasPrefix(line, "## ") { - words := strings.Fields(line) - depth := len(words[0]) - words = words[1:] - text := strings.Join(words, " ") - for i := range words { - words[i] = strings.ToLower(words[i]) - } - line = fmt.Sprintf("%s1. [%s](#%s)", - strings.Repeat("\t", depth-1), text, strings.Join(words, "-")) - toc = append(toc, line) - } - } - - // Pass 2. - if _, err := f.Seek(0, os.SEEK_SET); err != nil { - log.Fatalf("can't rewind input: %v", err) - } - in = bufio.NewScanner(f) - for in.Scan() { - line := in.Text() - switch { - case strings.HasPrefix(line, "%toc"): // ToC - for _, h := range toc { - fmt.Println(h) - } - case strings.HasPrefix(line, "%include"): - words := strings.Fields(line) - if len(words) < 2 { - log.Fatal(line) - } - filename := words[1] - - // Show caption unless '-' follows. - if len(words) < 4 || words[3] != "-" { - fmt.Printf(" // go get golang.org/x/example/gotypes/%s\n\n", - filepath.Dir(filename)) - } - - section := "" - if len(words) > 2 { - section = words[2] - } - s, err := include(filename, section) - if err != nil { - log.Fatal(err) - } - fmt.Println("```") - fmt.Println(cleanListing(s)) // TODO(adonovan): escape /^```/ in s - fmt.Println("```") - default: - fmt.Println(line) - } - - } -} - -// include processes an included file, and returns the included text. -// Only lines between those matching !+tag and !-tag will be returned. -// This is true even if tag=="". -func include(file, tag string) (string, error) { - f, err := os.Open(file) - if err != nil { - return "", err - } - defer f.Close() - - startre, err := regexp.Compile("!\\+" + tag + "$") - if err != nil { - return "", err - } - endre, err := regexp.Compile("!\\-" + tag + "$") - if err != nil { - return "", err - } - - var text bytes.Buffer - in := bufio.NewScanner(f) - var on bool - for in.Scan() { - line := in.Text() - switch { - case startre.MatchString(line): - on = true - case endre.MatchString(line): - on = false - case on: - text.WriteByte('\t') - text.WriteString(line) - text.WriteByte('\n') - } - } - if text.Len() == 0 { - return "", fmt.Errorf("no lines of %s matched tag %q", file, tag) - } - return text.String(), nil -} - -func isBlank(line string) bool { return strings.TrimSpace(line) == "" } - -func indented(line string) bool { - return strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") -} - -// cleanListing removes entirely blank leading and trailing lines from -// text, and removes n leading tabs. -func cleanListing(text string) string { - lines := strings.Split(text, "\n") - - // remove minimum number of leading tabs from all non-blank lines - tabs := 999 - for i, line := range lines { - if strings.TrimSpace(line) == "" { - lines[i] = "" - } else { - if n := leadingTabs(line); n < tabs { - tabs = n - } - } - } - for i, line := range lines { - if line != "" { - line := line[tabs:] - lines[i] = line // remove leading tabs - } - } - - // remove leading blank lines - for len(lines) > 0 && lines[0] == "" { - lines = lines[1:] - } - // remove trailing blank lines - for len(lines) > 0 && lines[len(lines)-1] == "" { - lines = lines[:len(lines)-1] - } - return strings.Join(lines, "\n") -} - -func leadingTabs(s string) int { - var i int - for i = 0; i < len(s); i++ { - if s[i] != '\t' { - break - } - } - return i -} diff --git a/hello/hello.go b/hello/hello.go deleted file mode 100644 index e76ed81b..00000000 --- a/hello/hello.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2014 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - - "golang.org/x/example/stringutil" -) - -func main() { - fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH")) -} diff --git a/main.go b/main.go new file mode 100644 index 00000000..85210dd0 --- /dev/null +++ b/main.go @@ -0,0 +1,42 @@ +package main + +import "fmt" + +func main() { + asciiArt := +` + .... + .'' .''' +. .' : +\\ .: : + \\ _: : ..----.._ + \\ .:::.....:::.. .' ''. + \\ .' #-. .-######' # '. + \\ '.##'/ ' ################ : + \\ ##################### : + \\ ..##.-.#### .''''###'.._ : + \\ :--:########: '. .' : + \\..__...--.. :--:#######.' '. '. : + : : : : '':'-:'':':: . '. .' + '---'''..: : ': '..'''. '. :' + \\ :: : : ' ''''''. '. .: + \\ :: : : ' '. ' : + \\:: : : ....' ..: ' '. + \\:: : : .....####\\ .~~.:. : + \\':.:.:.:'#########.===. ~ |.'-. . '''.. : + \\ .' ########## \ \ _.' '. '-. '''. + :\\ : ######## \ \ '. '-. : + : \\' ' #### : \ \ :. '-. : + : .'\\ :' : : \ \ : '-. : + : .' .\\ ' : : :\ \ : '. : + :: : \\' :. : : \ \ : '. : + ::. : \\ : : : ; \ \ : '.: + : ': '\\ : : : : \:\ : ..' + : ' \\ : : ; \| : .''' + '. ' \\: :.'' + .:..... \\: : ..'' + '._____|'.\\......'''''''.:..''' + \\ +` + fmt.Println(asciiArt) +} diff --git a/outyet/Dockerfile b/outyet/Dockerfile deleted file mode 100644 index 3fa58ff7..00000000 --- a/outyet/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM golang:onbuild -EXPOSE 8080 diff --git a/outyet/containers.yaml b/outyet/containers.yaml deleted file mode 100644 index 44ba78a6..00000000 --- a/outyet/containers.yaml +++ /dev/null @@ -1,8 +0,0 @@ -version: v1beta2 -containers: -- name: outyet - image: adg1/outyet - ports: - - name: http - hostPort: 80 - containerPort: 8080 diff --git a/outyet/main.go b/outyet/main.go deleted file mode 100644 index 96f77673..00000000 --- a/outyet/main.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2014 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// outyet is a web server that announces whether or not a particular Go version -// has been tagged. -package main - -import ( - "expvar" - "flag" - "fmt" - "html/template" - "log" - "net/http" - "sync" - "time" -) - -// Command-line flags. -var ( - httpAddr = flag.String("http", ":8080", "Listen address") - pollPeriod = flag.Duration("poll", 5*time.Second, "Poll period") - version = flag.String("version", "1.4", "Go version") -) - -const baseChangeURL = "https://go.googlesource.com/go/+/" - -func main() { - flag.Parse() - changeURL := fmt.Sprintf("%sgo%s", baseChangeURL, *version) - http.Handle("/", NewServer(*version, changeURL, *pollPeriod)) - log.Fatal(http.ListenAndServe(*httpAddr, nil)) -} - -// Exported variables for monitoring the server. -// These are exported via HTTP as a JSON object at /debug/vars. -var ( - hitCount = expvar.NewInt("hitCount") - pollCount = expvar.NewInt("pollCount") - pollError = expvar.NewString("pollError") - pollErrorCount = expvar.NewInt("pollErrorCount") -) - -// Server implements the outyet server. -// It serves the user interface (it's an http.Handler) -// and polls the remote repository for changes. -type Server struct { - version string - url string - period time.Duration - - mu sync.RWMutex // protects the yes variable - yes bool -} - -// NewServer returns an initialized outyet server. -func NewServer(version, url string, period time.Duration) *Server { - s := &Server{version: version, url: url, period: period} - go s.poll() - return s -} - -// poll polls the change URL for the specified period until the tag exists. -// Then it sets the Server's yes field true and exits. -func (s *Server) poll() { - for !isTagged(s.url) { - pollSleep(s.period) - } - s.mu.Lock() - s.yes = true - s.mu.Unlock() - pollDone() -} - -// Hooks that may be overridden for integration tests. -var ( - pollSleep = time.Sleep - pollDone = func() {} -) - -// isTagged makes an HTTP HEAD request to the given URL and reports whether it -// returned a 200 OK response. -func isTagged(url string) bool { - pollCount.Add(1) - r, err := http.Head(url) - if err != nil { - log.Print(err) - pollError.Set(err.Error()) - pollErrorCount.Add(1) - return false - } - return r.StatusCode == http.StatusOK -} - -// ServeHTTP implements the HTTP user interface. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - hitCount.Add(1) - s.mu.RLock() - data := struct { - URL string - Version string - Yes bool - }{ - s.url, - s.version, - s.yes, - } - s.mu.RUnlock() - err := tmpl.Execute(w, data) - if err != nil { - log.Print(err) - } -} - -// tmpl is the HTML template that drives the user interface. -var tmpl = template.Must(template.New("tmpl").Parse(` -
-

Is Go {{.Version}} out yet?

-

- {{if .Yes}} - YES! - {{else}} - No. :-( - {{end}} -

-
-`)) diff --git a/outyet/main_test.go b/outyet/main_test.go deleted file mode 100644 index d21a22ce..00000000 --- a/outyet/main_test.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2014 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" -) - -// statusHandler is an http.Handler that writes an empty response using itself -// as the response status code. -type statusHandler int - -func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(int(*h)) -} - -func TestIsTagged(t *testing.T) { - // Set up a fake "Google Code" web server reporting 404 not found. - status := statusHandler(http.StatusNotFound) - s := httptest.NewServer(&status) - defer s.Close() - - if isTagged(s.URL) { - t.Fatal("isTagged == true, want false") - } - - // Change fake server status to 200 OK and try again. - status = http.StatusOK - - if !isTagged(s.URL) { - t.Fatal("isTagged == false, want true") - } -} - -func TestIntegration(t *testing.T) { - status := statusHandler(http.StatusNotFound) - ts := httptest.NewServer(&status) - defer ts.Close() - - // Replace the pollSleep with a closure that we can block and unblock. - sleep := make(chan bool) - pollSleep = func(time.Duration) { - sleep <- true - sleep <- true - } - - // Replace pollDone with a closure that will tell us when the poller is - // exiting. - done := make(chan bool) - pollDone = func() { done <- true } - - // Put things as they were when the test finishes. - defer func() { - pollSleep = time.Sleep - pollDone = func() {} - }() - - s := NewServer("1.x", ts.URL, 1*time.Millisecond) - - <-sleep // Wait for poll loop to start sleeping. - - // Make first request to the server. - r, _ := http.NewRequest("GET", "/", nil) - w := httptest.NewRecorder() - s.ServeHTTP(w, r) - if b := w.Body.String(); !strings.Contains(b, "No.") { - t.Fatalf("body = %s, want no", b) - } - - status = http.StatusOK - - <-sleep // Permit poll loop to stop sleeping. - <-done // Wait for poller to see the "OK" status and exit. - - // Make second request to the server. - w = httptest.NewRecorder() - s.ServeHTTP(w, r) - if b := w.Body.String(); !strings.Contains(b, "YES!") { - t.Fatalf("body = %q, want yes", b) - } -} diff --git a/photo_2023-04-24_12-34-10.jpg b/photo_2023-04-24_12-34-10.jpg new file mode 100644 index 00000000..f268e071 Binary files /dev/null and b/photo_2023-04-24_12-34-10.jpg differ diff --git a/stringutil/reverse.go b/stringutil/reverse.go deleted file mode 100644 index d0bfd614..00000000 --- a/stringutil/reverse.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2014 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package stringutil contains utility functions for working with strings. -package stringutil - -// Reverse returns its argument string reversed rune-wise left to right. -func Reverse(s string) string { - r := []rune(s) - for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { - r[i], r[j] = r[j], r[i] - } - return string(r) -} diff --git a/stringutil/reverse_test.go b/stringutil/reverse_test.go deleted file mode 100644 index 3a2337f1..00000000 --- a/stringutil/reverse_test.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2014 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package stringutil - -import "testing" - -func TestReverse(t *testing.T) { - for _, c := range []struct { - in, want string - }{ - {"Hello, world", "dlrow ,olleH"}, - {"Hello, 世界", "界世 ,olleH"}, - {"", ""}, - } { - got := Reverse(c.in) - if got != c.want { - t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want) - } - } -} diff --git a/template/image.tmpl b/template/image.tmpl deleted file mode 100644 index 14d1097c..00000000 --- a/template/image.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -{{define "content"}} -
- {{.Title}} -
-{{end}} - -{{define "sidebar"}} -Back -{{end}} diff --git a/template/index.tmpl b/template/index.tmpl deleted file mode 100644 index 24ed3ffb..00000000 --- a/template/index.tmpl +++ /dev/null @@ -1,41 +0,0 @@ - - - - - {{.Title}} - - - -

{{.Title}}

- - - - {{block "content" .}} -
- {{.Body}} -
- {{end}} - - diff --git a/template/main.go b/template/main.go deleted file mode 100644 index 5da54752..00000000 --- a/template/main.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2016 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Command template is a trivial web server that uses the text/template (and -// html/template) package's "block" feature to implement a kind of template -// inheritance. -// -// It should be executed from the directory in which the source resides, -// as it will look for its template files in the current directory. -package main - -import ( - "html/template" - "log" - "net/http" - "strings" -) - -func main() { - http.HandleFunc("/", indexHandler) - http.HandleFunc("/image/", imageHandler) - log.Fatal(http.ListenAndServe("localhost:8080", nil)) -} - -// indexTemplate is the main site template. -// The default template includes two template blocks ("sidebar" and "content") -// that may be replaced in templates derived from this one. -var indexTemplate = template.Must(template.ParseFiles("index.tmpl")) - -// Index is a data structure used to populate an indexTemplate. -type Index struct { - Title string - Body string - Links []Link -} - -type Link struct { - URL, Title string -} - -// indexHandler is an HTTP handler that serves the index page. -func indexHandler(w http.ResponseWriter, r *http.Request) { - data := &Index{ - Title: "Image gallery", - Body: "Welcome to the image gallery.", - } - for name, img := range images { - data.Links = append(data.Links, Link{ - URL: "/image/" + name, - Title: img.Title, - }) - } - if err := indexTemplate.Execute(w, data); err != nil { - log.Println(err) - } -} - -// imageTemplate is a clone of indexTemplate that provides -// alternate "sidebar" and "content" templates. -var imageTemplate = template.Must(template.Must(indexTemplate.Clone()).ParseFiles("image.tmpl")) - -// Image is a data structure used to populate an imageTemplate. -type Image struct { - Title string - URL string -} - -// imageHandler is an HTTP handler that serves the image pages. -func imageHandler(w http.ResponseWriter, r *http.Request) { - data, ok := images[strings.TrimPrefix(r.URL.Path, "/image/")] - if !ok { - http.NotFound(w, r) - return - } - if err := imageTemplate.Execute(w, data); err != nil { - log.Println(err) - } -} - -// images specifies the site content: a collection of images. -var images = map[string]*Image{ - "go": {"The Go Gopher", "https://golang.org/doc/gopher/frontpage.png"}, - "google": {"The Google Logo", "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"}, -}