diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 363c8b2cd30..35b75d6a205 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -1,4 +1,4 @@ -# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT. +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT. # All tools are designed to be build inside $GOBIN. BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) GOPATH ?= $(shell go env GOPATH) @@ -59,9 +59,3 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod @echo "(re)installing $(GOBIN)/swagger-v0.30.2" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.2 "github.com/go-swagger/go-swagger/cmd/swagger" -WIRE := $(GOBIN)/wire-v0.6.0 -$(WIRE): $(BINGO_DIR)/wire.mod - @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/wire-v0.6.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=wire.mod -o=$(GOBIN)/wire-v0.6.0 "github.com/google/wire/cmd/wire" - diff --git a/.bingo/variables.env b/.bingo/variables.env index dc64e5f430b..d41c6f92197 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -1,4 +1,4 @@ -# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT. +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT. # All tools are designed to be build inside $GOBIN. # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. GOBIN=${GOBIN:=$(go env GOBIN)} @@ -22,5 +22,3 @@ LEFTHOOK="${GOBIN}/lefthook-v1.4.8" SWAGGER="${GOBIN}/swagger-v0.30.2" -WIRE="${GOBIN}/wire-v0.6.0" - diff --git a/.bingo/wire.mod b/.bingo/wire.mod deleted file mode 100644 index fdfc21a466f..00000000000 --- a/.bingo/wire.mod +++ /dev/null @@ -1,5 +0,0 @@ -module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT - -go 1.16 - -require github.com/google/wire v0.6.0 // cmd/wire diff --git a/Makefile b/Makefile index b8a938afa78..081a2459630 100644 --- a/Makefile +++ b/Makefile @@ -117,9 +117,9 @@ gen-feature-toggles: go test -v ./pkg/services/featuremgmt/...; \ fi -gen-go: $(WIRE) +gen-go: @echo "generate go files" - $(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server + $(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags $(WIRE_TAGS) ./pkg/server fix-cue: $(CUE) @echo "formatting cue files" diff --git a/go.work b/go.work index 3bb653aaf42..7ccf184922b 100644 --- a/go.work +++ b/go.work @@ -4,6 +4,7 @@ use ( . ./pkg/apimachinery ./pkg/apiserver + ./pkg/build/wire ./pkg/promlib ./pkg/util/xorm ) diff --git a/pkg/build/wire/AUTHORS b/pkg/build/wire/AUTHORS new file mode 100644 index 00000000000..4d8d4b3197f --- /dev/null +++ b/pkg/build/wire/AUTHORS @@ -0,0 +1,18 @@ +# This is the official list of Wire authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as one of +# Organization's name +# Individual's name +# Individual's name +# See CONTRIBUTORS for the meaning of multiple email addresses. + +# Please keep the list sorted. + +Google LLC +ktr +Kumbirai Tanekha +Oleg Kovalov +Yoichiro Shimizu +Zachary Romero diff --git a/pkg/build/wire/CODE_OF_CONDUCT.md b/pkg/build/wire/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..3a8545eccf6 --- /dev/null +++ b/pkg/build/wire/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Code of Conduct + +This project is covered under the [Go Code of Conduct][]. In summary: + +- Treat everyone with respect and kindness. +- Be thoughtful in how you communicate. +- Don’t be destructive or inflammatory. +- If you encounter an issue, please mail conduct@golang.org. + +[Go Code of Conduct]: https://golang.org/conduct diff --git a/pkg/build/wire/CONTRIBUTING.md b/pkg/build/wire/CONTRIBUTING.md new file mode 100644 index 00000000000..ee19499f4e5 --- /dev/null +++ b/pkg/build/wire/CONTRIBUTING.md @@ -0,0 +1,152 @@ +# How to Contribute + +We would love to accept your patches and contributions to this project. Here is +how you can help. + +## Filing issues + +Filing issues is an important way you can contribute to the Wire Project. We +want your feedback on things like bugs, desired API changes, or just anything +that isn't working for you. + +### Bugs + +If your issue is a bug, open one +[here](https://github.com/google/wire/issues/new). The easiest way to file an +issue with all the right information is to run `go bug`. `go bug` will print out +a handy template of questions and system information that will help us get to +the root of the issue quicker. + +### Changes + +Unlike the core Go project, we do not have a formal proposal process for +changes. If you have a change you would like to see in Wire, please file an +issue with the necessary details. + +### Triaging + +The Go Cloud team triages issues at least every two weeks, but usually within +two business days. Bugs or feature requests are either placed into a **Sprint** +milestone which means the issue is intended to be worked on. Issues that we +would like to address but do not have time for are placed into the [Unplanned][] +milestone. + +[Unplanned]: https://github.com/google/wire/milestone/1 + +## Contributing Code + +We love accepting contributions! If your change is minor, please feel free +submit a [pull request](https://help.github.com/articles/about-pull-requests/). +If your change is larger, or adds a feature, please file an issue beforehand so +that we can discuss the change. You're welcome to file an implementation pull +request immediately as well, although we generally lean towards discussing the +change and then reviewing the implementation separately. + +### Finding something to work on + +If you want to write some code, but don't know where to start or what you might +want to do, take a look at our [Unplanned][] milestone. This is where you can +find issues we would like to address but can't currently find time for. See if +any of the latest ones look interesting! If you need help before you can start +work, you can comment on the issue and we will try to help as best we can. + +### Contributor License Agreement + +Contributions to this project can only be made by those who have signed Google's +Contributor License Agreement. You (or your employer) retain the copyright to +your contribution, this simply gives us permission to use and redistribute your +contributions as part of the project. Head over to + to see your current agreements on file or +to sign a new one. + +As a personal contributor, you only need to sign the Google CLA once across all +Google projects. If you've already signed the CLA, there is no need to do it +again. If you are submitting code on behalf of your employer, there's +[a separate corporate CLA that your employer manages for you](https://opensource.google.com/docs/cla/#external-contributors). + +## Making a pull request + +* Follow the normal + [pull request flow](https://help.github.com/articles/creating-a-pull-request/) +* Build your changes using Go 1.11 with Go modules enabled. Wire's continuous + integration uses Go modules in order to ensure + [reproducible builds](https://research.swtch.com/vgo-repro). +* Test your changes using `go test ./...`. Please add tests that show the + change does what it says it does, even if there wasn't a test in the first + place. +* Feel free to make as many commits as you want; we will squash them all into + a single commit before merging your change. +* Check the diffs, write a useful description (including something like + `Fixes #123` if it's fixing a bug) and send the PR out. +* Github will run tests against the PR. This should + happen within 10 minutes or so. If a test fails, go back to the coding stage + and try to fix the test and push the same branch again. You won't need to + make a new pull request, the changes will be rolled directly into the PR you + already opened. Wait for the tests again. There is no need to assign a reviewer + to the PR, the project team will assign someone for review during the + standard [triage](#triaging) process. + +## Code review + +All submissions, including submissions by project members, require review. It is +almost never the case that a pull request is accepted without some changes +requested, so please do not be offended! + +When you have finished making requested changes to your pull request, please +make a comment containing "PTAL" (Please Take Another Look) on your pull +request. GitHub notifications can be noisy, and it is unfortunately easy for +things to be lost in the shuffle. + +Once your PR is approved (hooray!) the reviewer will squash your commits into a +single commit, and then merge the commit onto the Wire master branch. Thank you! + +## Github code review workflow conventions + +(For project members and frequent contributors.) + +As a contributor: + +- Try hard to make each Pull Request as small and focused as possible. In + particular, this means that if a reviewer asks you to do something that is + beyond the scope of the Pull Request, the best practice is to file another + issue and reference it from the Pull Request rather than just adding more + commits to the existing PR. +- Adding someone as a Reviewer means "please feel free to look and comment"; + the review is optional. Choose as many Reviewers as you'd like. +- Adding someone as an Assignee means that the Pull Request should not be + submitted until they approve. If you choose multiple Assignees, wait until + all of them approve. It is fine to ask someone if they are OK with being + removed as an Assignee. + - Note that if you don't select any assignees, ContributeBot will turn all + of your Reviewers into Assignees. +- Make as many commits as you want locally, but try not to push them to Github + until you've addressed comments; this allows the email notification about + the push to be a signal to reviewers that the PR is ready to be looked at + again. +- When there may be confusion about what should happen next for a PR, be + explicit; add a "PTAL" comment if it is ready for review again, or a "Please + hold off on reviewing for now" if you are still working on addressing + comments. +- "Resolve" comments that you are sure you've addressed; let your reviewers + resolve ones that you're not sure about. +- Do not use `git push --force`; this can cause comments from your reviewers + that are associated with a specific commit to be lost. This implies that + once you've sent a Pull Request, you should use `git merge` instead of `git + rebase` to incorporate commits from the master branch. + +As a reviewer: + +- Be timely in your review process, especially if you are an Assignee. +- Try to use `Start a Review` instead of single comments, to reduce email + spam. +- "Resolve" your own comments if they have been addressed. +- If you want your review to be blocking, and are not currently an Assignee, + add yourself as an Assignee. + +When squashing-and-merging: + +- Ensure that **all** of the Assignees have approved. +- Do a final review of the one-line PR summary, ensuring that it accurately + describes the change. +- Delete the automatically added commit lines; these are generally not + interesting and make commit history harder to read. diff --git a/pkg/build/wire/CONTRIBUTORS b/pkg/build/wire/CONTRIBUTORS new file mode 100644 index 00000000000..00a94f89caa --- /dev/null +++ b/pkg/build/wire/CONTRIBUTORS @@ -0,0 +1,43 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Wire repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Individual's name +# Individual's name +# +# An entry with multiple email addresses specifies that the +# first address should be used in the submit logs and +# that the other addresses should be recognized as the +# same person when interacting with Git. + +# Please keep the list sorted. + +Chris Lewis +Christina Austin <4240737+clausti@users.noreply.github.com> +Eno Compton +Issac Trotts +ktr +Kumbirai Tanekha +Oleg Kovalov +Robert van Gent +Ross Light +Tuo Shan +Yoichiro Shimizu +Zachary Romero diff --git a/pkg/build/wire/LICENSE b/pkg/build/wire/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/pkg/build/wire/LICENSE @@ -0,0 +1,202 @@ + + 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/pkg/build/wire/README.md b/pkg/build/wire/README.md new file mode 100644 index 00000000000..ccafb0e27f2 --- /dev/null +++ b/pkg/build/wire/README.md @@ -0,0 +1,60 @@ +# Wire: Automated Initialization in Go + +[![Build Status](https://github.com/google/wire/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/google/wire/actions) +[![godoc](https://godoc.org/github.com/google/wire?status.svg)][godoc] +[![Coverage](https://codecov.io/gh/google/wire/branch/master/graph/badge.svg)](https://codecov.io/gh/google/wire) + + +Wire is a code generation tool that automates connecting components using +[dependency injection][]. Dependencies between components are represented in +Wire as function parameters, encouraging explicit initialization instead of +global variables. Because Wire operates without runtime state or reflection, +code written to be used with Wire is useful even for hand-written +initialization. + +For an overview, see the [introductory blog post][]. + +[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection +[introductory blog post]: https://blog.golang.org/wire +[godoc]: https://godoc.org/github.com/google/wire +[travis]: https://travis-ci.com/google/wire + +## Installing + +Install Wire by running: + +```shell +go install github.com/google/wire/cmd/wire@latest +``` + +and ensuring that `$GOPATH/bin` is added to your `$PATH`. + +## Documentation + +- [Tutorial][] +- [User Guide][] +- [Best Practices][] +- [FAQ][] + +[Tutorial]: ./_tutorial/README.md +[Best Practices]: ./docs/best-practices.md +[FAQ]: ./docs/faq.md +[User Guide]: ./docs/guide.md + +## Project status + +As of version v0.3.0, Wire is *beta* and is considered feature complete. It +works well for the tasks it was designed to perform, and we prefer to keep it +as simple as possible. + +We'll not be accepting new features at this time, but will gladly accept bug +reports and fixes. + +## Community + +For questions, please use [GitHub Discussions](https://github.com/google/wire/discussions). + +This project is covered by the Go [Code of Conduct][]. + +[Code of Conduct]: ./CODE_OF_CONDUCT.md +[go-cloud mailing list]: https://groups.google.com/forum/#!forum/go-cloud diff --git a/pkg/build/wire/_tutorial/README.md b/pkg/build/wire/_tutorial/README.md new file mode 100644 index 00000000000..f39fba2bb6e --- /dev/null +++ b/pkg/build/wire/_tutorial/README.md @@ -0,0 +1,419 @@ +# Wire Tutorial + +Let's learn to use Wire by example. The [Wire guide][guide] provides thorough +documentation of the tool's usage. For readers eager to see Wire applied to a +larger server, the [guestbook sample in Go Cloud][guestbook] uses Wire to +initialize its components. Here we are going to build a small greeter program to +understand how to use Wire. The finished product may be found in the same +directory as this README. + +[guestbook]: https://github.com/google/go-cloud/tree/master/samples/guestbook +[guide]: https://github.com/google/wire/blob/master/docs/guide.md + +## A First Pass of Building the Greeter Program + +Let's create a small program that simulates an event with a greeter greeting +guests with a particular message. + +To start, we will create three types: 1) a message for a greeter, 2) a greeter +who conveys that message, and 3) an event that starts with the greeter greeting +guests. In this design, we have three `struct` types: + +``` go +type Message string + +type Greeter struct { + // ... TBD +} + +type Event struct { + // ... TBD +} +``` + +The `Message` type just wraps a string. For now, we will create a simple +initializer that always returns a hard-coded message: + +``` go +func NewMessage() Message { + return Message("Hi there!") +} +``` + +Our `Greeter` will need reference to the `Message`. So let's create an +initializer for our `Greeter` as well. + +``` go +func NewGreeter(m Message) Greeter { + return Greeter{Message: m} +} + +type Greeter struct { + Message Message // <- adding a Message field +} +``` + +In the initializer we assign a `Message` field to `Greeter`. Now, we can use the +`Message` when we create a `Greet` method on `Greeter`: + +``` go +func (g Greeter) Greet() Message { + return g.Message +} +``` + +Next, we need our `Event` to have a `Greeter`, so we will create an initializer +for it as well. + +``` go +func NewEvent(g Greeter) Event { + return Event{Greeter: g} +} + +type Event struct { + Greeter Greeter // <- adding a Greeter field +} +``` + +Then we add a method to start the `Event`: + +``` go +func (e Event) Start() { + msg := e.Greeter.Greet() + fmt.Println(msg) +} +``` + +The `Start` method holds the core of our small application: it tells the +greeter to issue a greeting and then prints that message to the screen. + +Now that we have all the components of our application ready, let's see what it +takes to initialize all the components without using Wire. Our main function +would look like this: + +``` go +func main() { + message := NewMessage() + greeter := NewGreeter(message) + event := NewEvent(greeter) + + event.Start() +} +``` + +First we create a message, then we create a greeter with that message, and +finally we create an event with that greeter. With all the initialization done, +we're ready to start our event. + +We are using the [dependency injection][di] design principle. In practice, that +means we pass in whatever each component needs. This style of design lends +itself to writing easily tested code and makes it easy to swap out one +dependency with another. + +[di]: https://stackoverflow.com/questions/130794/what-is-dependency-injection + +## Using Wire to Generate Code + +One downside to dependency injection is the need for so many initialization +steps. Let's see how we can use Wire to make the process of initializing our +components smoother. + +Let's start by changing our `main` function to look like this: + +``` go +func main() { + e := InitializeEvent() + + e.Start() +} +``` + +Next, in a separate file called `wire.go` we will define `InitializeEvent`. +This is where things get interesting: + +``` go +// wire.go + +func InitializeEvent() Event { + wire.Build(NewEvent, NewGreeter, NewMessage) + return Event{} +} +``` + +Rather than go through the trouble of initializing each component in turn and +passing it into the next one, we instead have a single call to `wire.Build` +passing in the initializers we want to use. In Wire, initializers are known as +"providers," functions which provide a particular type. We add a zero value for +`Event` as a return value to satisfy the compiler. Note that even if we add +values to `Event`, Wire will ignore them. In fact, the injector's purpose is to +provide information about which providers to use to construct an `Event` and so +we will exclude it from our final binary with a build constraint at the top of +the file: + +``` go +//+build wireinject + +``` + +Note, a [build constraint][constraint] requires a blank, trailing line. + +In Wire parlance, `InitializeEvent` is an "injector." Now that we have our +injector complete, we are ready to use the `wire` command line tool. + +Install the tool with: + +``` shell +go install github.com/google/wire/cmd/wire@latest +``` + +Then in the same directory with the above code, simply run `wire`. Wire will +find the `InitializeEvent` injector and generate a function whose body is +filled out with all the necessary initialization steps. The result will be +written to a file named `wire_gen.go`. + +Let's take a look at what Wire did for us: + +``` go +// wire_gen.go + +func InitializeEvent() Event { + message := NewMessage() + greeter := NewGreeter(message) + event := NewEvent(greeter) + return event +} +``` + +It looks just like what we wrote above! Now this is a simple example with just +three components, so writing the initializer by hand isn't too painful. Imagine +how useful Wire is for components that are much more complex. When working with +Wire, we will commit both `wire.go` and `wire_gen.go` to source control. + +[constraint]: https://godoc.org/go/build#hdr-Build_Constraints + +## Making Changes with Wire + +To show a small part of how Wire handles more complex setups, let's refactor +our initializer for `Event` to return an error and see what happens. + +``` go +func NewEvent(g Greeter) (Event, error) { + if g.Grumpy { + return Event{}, errors.New("could not create event: event greeter is grumpy") + } + return Event{Greeter: g}, nil +} +``` + +We'll say that sometimes a `Greeter` might be grumpy and so we cannot create +an `Event`. The `NewGreeter` initializer now looks like this: + +``` go +func NewGreeter(m Message) Greeter { + var grumpy bool + if time.Now().Unix()%2 == 0 { + grumpy = true + } + return Greeter{Message: m, Grumpy: grumpy} +} +``` + +We have added a `Grumpy` field to `Greeter` struct and if the invocation time +of the initializer is an even number of seconds since the Unix epoch, we will +create a grumpy greeter instead of a friendly one. + +The `Greet` method then becomes: + +``` go +func (g Greeter) Greet() Message { + if g.Grumpy { + return Message("Go away!") + } + return g.Message +} +``` + +Now you see how a grumpy `Greeter` is no good for an `Event`. So `NewEvent` may +fail. Our `main` must now take into account that `InitializeEvent` may in fact +fail: + +``` go +func main() { + e, err := InitializeEvent() + if err != nil { + fmt.Printf("failed to create event: %s\n", err) + os.Exit(2) + } + e.Start() +} +``` + +We also need to update `InitializeEvent` to add an `error` type to the return value: + +``` go +// wire.go + +func InitializeEvent() (Event, error) { + wire.Build(NewEvent, NewGreeter, NewMessage) + return Event{}, nil +} +``` + +With the setup complete, we are ready to invoke the `wire` command again. Note, +that after running `wire` once to produce a `wire_gen.go` file, we may also use +`go generate`. Having run the command, our `wire_gen.go` file looks like +this: + +``` go +// wire_gen.go + +func InitializeEvent() (Event, error) { + message := NewMessage() + greeter := NewGreeter(message) + event, err := NewEvent(greeter) + if err != nil { + return Event{}, err + } + return event, nil +} +``` + +Wire has detected that the `NewEvent` provider may fail and has done the right +thing inside the generated code: it checks the error and returns early if one +is present. + +## Changing the Injector Signature + +As another improvement, let's look at how Wire generates code based on the +signature of the injector. Presently, we have hard-coded the message inside +`NewMessage`. In practice, it's much nicer to allow callers to change that +message however they see fit. So let's change `InitializeEvent` to look like +this: + +``` go +func InitializeEvent(phrase string) (Event, error) { + wire.Build(NewEvent, NewGreeter, NewMessage) + return Event{}, nil +} +``` + +Now `InitializeEvent` allows callers to pass in the `phrase` for a `Greeter` to +use. We also add a `phrase` argument to `NewMessage`: + +``` go +func NewMessage(phrase string) Message { + return Message(phrase) +} +``` + +After we run `wire` again, we will see that the tool has generated an +initializer which passes the `phrase` value as a `Message` into `Greeter`. +Neat! + +``` go +// wire_gen.go + +func InitializeEvent(phrase string) (Event, error) { + message := NewMessage(phrase) + greeter := NewGreeter(message) + event, err := NewEvent(greeter) + if err != nil { + return Event{}, err + } + return event, nil +} +``` + +Wire inspects the arguments to the injector, sees that we added a string to the +list of arguments (e.g., `phrase`), and likewise sees that among all the +providers, `NewMessage` takes a string, and so it passes `phrase` into +`NewMessage`. + +## Catching Mistakes with Helpful Errors + +Let's also look at what happens when Wire detects mistakes in our code and see +how Wire's error messages help us correct any problems. + +For example, when writing our injector `InitializeEvent`, let's say we forget +to add a provider for `Greeter`. Let's see what happens: + +``` go +func InitializeEvent(phrase string) (Event, error) { + wire.Build(NewEvent, NewMessage) // woops! We forgot to add a provider for Greeter + return Event{}, nil +} +``` + +Running `wire`, we see the following: + +``` shell +# wrapping the error across lines for readability +$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1: +inject InitializeEvent: no provider found for github.com/google/wire/_tutorial.Greeter +(required by provider of github.com/google/wire/_tutorial.Event) +wire: generate failed +``` + +Wire is telling us some useful information: it cannot find a provider for +`Greeter`. Note that the error message prints out the full path to the +`Greeter` type. It's also telling us the line number and injector name where +the problem occurred: line 24 inside `InitializeEvent`. In addition, the error +message tells us which provider needs a `Greeter`. It's the `Event` type. Once +we pass in a provider of `Greeter`, the problem will be solved. + +Alternatively, what happens if we provide one too many providers to `wire.Build`? + +``` go +func NewEventNumber() int { + return 1 +} + +func InitializeEvent(phrase string) (Event, error) { + // woops! NewEventNumber is unused. + wire.Build(NewEvent, NewGreeter, NewMessage, NewEventNumber) + return Event{}, nil +} +``` + +Wire helpfully tells us that we have an unused provider: + +``` shell +$GOPATH/src/github.com/google/wire/_tutorial/wire.go:24:1: +inject InitializeEvent: unused provider "NewEventNumber" +wire: generate failed +``` + +Deleting the unused provider from the call to `wire.Build` resolves the error. + +## Conclusion + +Let's summarize what we have done here. First, we wrote a number of components +with corresponding initializers, or providers. Next, we created an injector +function, specifying which arguments it receives and which types it returns. +Then, we filled in the injector function with a call to `wire.Build` supplying +all necessary providers. Finally, we ran the `wire` command to generate code +that wires up all the different initializers. When we added an argument to the +injector and an error return value, running `wire` again made all the necessary +updates to our generated code. + +The example here is small, but it demonstrates some of the power of Wire, and +how it takes much of the pain out of initializing code using dependency +injection. Furthermore, using Wire produced code that looks much like what we +would otherwise write. There are no bespoke types that commit a user to Wire. +Instead it's just generated code. We may do with it what we will. Finally, +another point worth considering is how easy it is to add new dependencies to +our component initialization. As long as we tell Wire how to provide (i.e., +initialize) a component, we may add that component anywhere in the dependency +graph and Wire will handle the rest. + +In closing, it is worth mentioning that Wire supports a number of additional +features not discussed here. Providers may be grouped in [provider sets][sets]. +There is support for [binding interfaces][interfaces], [binding +values][values], as well as support for [cleanup functions][cleanup]. See the +[Advanced Features][advanced] section for more. + +[advanced]: https://github.com/google/wire/blob/master/docs/guide.md#advanced-features +[cleanup]: https://github.com/google/wire/blob/master/docs/guide.md#cleanup-functions +[interfaces]: https://github.com/google/wire/blob/master/docs/guide.md#binding-interfaces +[sets]: https://github.com/google/wire/blob/master/docs/guide.md#defining-providers +[values]: https://github.com/google/wire/blob/master/docs/guide.md#binding-values diff --git a/pkg/build/wire/_tutorial/main.go b/pkg/build/wire/_tutorial/main.go new file mode 100644 index 00000000000..b00e5b8a406 --- /dev/null +++ b/pkg/build/wire/_tutorial/main.go @@ -0,0 +1,83 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +// The greeter binary simulates an event with greeters greeting guests. +package main + +import ( + "errors" + "fmt" + "os" + "time" +) + +// Message is what greeters will use to greet guests. +type Message string + +// NewMessage creates a default Message. +func NewMessage(phrase string) Message { + return Message(phrase) +} + +// NewGreeter initializes a Greeter. If the current epoch time is an even +// number, NewGreeter will create a grumpy Greeter. +func NewGreeter(m Message) Greeter { + var grumpy bool + if time.Now().Unix()%2 == 0 { + grumpy = true + } + return Greeter{Message: m, Grumpy: grumpy} +} + +// Greeter is the type charged with greeting guests. +type Greeter struct { + Grumpy bool + Message Message +} + +// Greet produces a greeting for guests. +func (g Greeter) Greet() Message { + if g.Grumpy { + return Message("Go away!") + } + return g.Message +} + +// NewEvent creates an event with the specified greeter. +func NewEvent(g Greeter) (Event, error) { + if g.Grumpy { + return Event{}, errors.New("could not create event: event greeter is grumpy") + } + return Event{Greeter: g}, nil +} + +// Event is a gathering with greeters. +type Event struct { + Greeter Greeter +} + +// Start ensures the event starts with greeting all guests. +func (e Event) Start() { + msg := e.Greeter.Greet() + fmt.Println(msg) +} + +func main() { + e, err := InitializeEvent("hi there!") + if err != nil { + fmt.Printf("failed to create event: %s\n", err) + os.Exit(2) + } + e.Start() +} diff --git a/pkg/build/wire/_tutorial/wire.go b/pkg/build/wire/_tutorial/wire.go new file mode 100644 index 00000000000..2b467dd6791 --- /dev/null +++ b/pkg/build/wire/_tutorial/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//go:build wireinject +// +build wireinject + +// The build tag makes sure the stub is not built in the final build. +package main + +import "github.com/grafana/grafana/pkg/build/wire" + +// InitializeEvent creates an Event. It will error if the Event is staffed with +// a grumpy greeter. +func InitializeEvent(phrase string) (Event, error) { + wire.Build(NewEvent, NewGreeter, NewMessage) + return Event{}, nil +} diff --git a/pkg/build/wire/_tutorial/wire_gen.go b/pkg/build/wire/_tutorial/wire_gen.go new file mode 100644 index 00000000000..fe54ab77917 --- /dev/null +++ b/pkg/build/wire/_tutorial/wire_gen.go @@ -0,0 +1,19 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func InitializeEvent(phrase string) (Event, error) { + message := NewMessage(phrase) + greeter := NewGreeter(message) + event, err := NewEvent(greeter) + if err != nil { + return Event{}, err + } + return event, nil +} diff --git a/pkg/build/wire/cmd/wire/main.go b/pkg/build/wire/cmd/wire/main.go new file mode 100644 index 00000000000..af79d29115c --- /dev/null +++ b/pkg/build/wire/cmd/wire/main.go @@ -0,0 +1,609 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +// Wire is a compile-time dependency injection tool. +// +// For an overview, see https://github.com/google/wire/blob/master/README.md +package main + +import ( + "context" + "flag" + "fmt" + "go/token" + "go/types" + "io/ioutil" + "log" + "os" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/google/subcommands" + "github.com/grafana/grafana/pkg/build/wire/internal/wire" + "github.com/pmezard/go-difflib/difflib" + "golang.org/x/tools/go/types/typeutil" +) + +func main() { + subcommands.Register(subcommands.CommandsCommand(), "") + subcommands.Register(subcommands.FlagsCommand(), "") + subcommands.Register(subcommands.HelpCommand(), "") + subcommands.Register(&checkCmd{}, "") + subcommands.Register(&diffCmd{}, "") + subcommands.Register(&genCmd{}, "") + subcommands.Register(&showCmd{}, "") + flag.Parse() + + // Initialize the default logger to log to stderr. + log.SetFlags(0) + log.SetPrefix("wire: ") + log.SetOutput(os.Stderr) + + // TODO(rvangent): Use subcommands's VisitCommands instead of hardcoded map, + // once there is a release that contains it: + // allCmds := map[string]bool{} + // subcommands.DefaultCommander.VisitCommands(func(_ *subcommands.CommandGroup, cmd subcommands.Command) { allCmds[cmd.Name()] = true }) + allCmds := map[string]bool{ + "commands": true, // builtin + "help": true, // builtin + "flags": true, // builtin + "check": true, + "diff": true, + "gen": true, + "show": true, + } + // Default to running the "gen" command. + if args := flag.Args(); len(args) == 0 || !allCmds[args[0]] { + genCmd := &genCmd{} + os.Exit(int(genCmd.Execute(context.Background(), flag.CommandLine))) + } + os.Exit(int(subcommands.Execute(context.Background()))) +} + +// packages returns the slice of packages to run wire over based on f. +// It defaults to ".". +func packages(f *flag.FlagSet) []string { + pkgs := f.Args() + if len(pkgs) == 0 { + pkgs = []string{"."} + } + return pkgs +} + +// newGenerateOptions returns an initialized wire.GenerateOptions, possibly +// with the Header option set. +func newGenerateOptions(headerFile string) (*wire.GenerateOptions, error) { + opts := new(wire.GenerateOptions) + if headerFile != "" { + var err error + opts.Header, err = ioutil.ReadFile(headerFile) + if err != nil { + return nil, fmt.Errorf("failed to read header file %q: %v", headerFile, err) + } + } + return opts, nil +} + +type genCmd struct { + headerFile string + prefixFileName string + tags string +} + +func (*genCmd) Name() string { return "gen" } +func (*genCmd) Synopsis() string { + return "generate the wire_gen.go file for each package" +} +func (*genCmd) Usage() string { + return `gen [packages] + + Given one or more packages, gen creates the wire_gen.go file for each. + + If no packages are listed, it defaults to ".". +` +} +func (cmd *genCmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go") + f.StringVar(&cmd.prefixFileName, "output_file_prefix", "", "string to prepend to output file names.") + f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild") +} + +func (cmd *genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + wd, err := os.Getwd() + if err != nil { + log.Println("failed to get working directory: ", err) + return subcommands.ExitFailure + } + opts, err := newGenerateOptions(cmd.headerFile) + if err != nil { + log.Println(err) + return subcommands.ExitFailure + } + + opts.PrefixOutputFile = cmd.prefixFileName + opts.Tags = cmd.tags + + outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts) + if len(errs) > 0 { + logErrors(errs) + log.Println("generate failed") + return subcommands.ExitFailure + } + if len(outs) == 0 { + return subcommands.ExitSuccess + } + success := true + for _, out := range outs { + if len(out.Errs) > 0 { + logErrors(out.Errs) + log.Printf("%s: generate failed\n", out.PkgPath) + success = false + } + if len(out.Content) == 0 { + // No Wire output. Maybe errors, maybe no Wire directives. + continue + } + if err := out.Commit(); err == nil { + log.Printf("%s: wrote %s\n", out.PkgPath, out.OutputPath) + } else { + log.Printf("%s: failed to write %s: %v\n", out.PkgPath, out.OutputPath, err) + success = false + } + } + if !success { + log.Println("at least one generate failure") + return subcommands.ExitFailure + } + return subcommands.ExitSuccess +} + +type diffCmd struct { + headerFile string + tags string +} + +func (*diffCmd) Name() string { return "diff" } +func (*diffCmd) Synopsis() string { + return "output a diff between existing wire_gen.go files and what gen would generate" +} +func (*diffCmd) Usage() string { + return `diff [packages] + + Given one or more packages, diff generates the content for their wire_gen.go + files and outputs the diff against the existing files. + + If no packages are listed, it defaults to ".". + + Similar to the diff command, it returns 0 if no diff, 1 if different, 2 + plus an error if trouble. +` +} +func (cmd *diffCmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go") + f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild") +} +func (cmd *diffCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + const ( + errReturn = subcommands.ExitStatus(2) + diffReturn = subcommands.ExitStatus(1) + ) + wd, err := os.Getwd() + if err != nil { + log.Println("failed to get working directory: ", err) + return errReturn + } + opts, err := newGenerateOptions(cmd.headerFile) + if err != nil { + log.Println(err) + return subcommands.ExitFailure + } + + opts.Tags = cmd.tags + + outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts) + if len(errs) > 0 { + logErrors(errs) + log.Println("generate failed") + return errReturn + } + if len(outs) == 0 { + return subcommands.ExitSuccess + } + success := true + hadDiff := false + for _, out := range outs { + if len(out.Errs) > 0 { + logErrors(out.Errs) + log.Printf("%s: generate failed\n", out.PkgPath) + success = false + } + if len(out.Content) == 0 { + // No Wire output. Maybe errors, maybe no Wire directives. + continue + } + // Assumes the current file is empty if we can't read it. + cur, _ := ioutil.ReadFile(out.OutputPath) + if diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(string(cur)), + B: difflib.SplitLines(string(out.Content)), + }); err == nil { + if diff != "" { + // Print the actual diff to stdout, not stderr. + fmt.Printf("%s: diff from %s:\n%s\n", out.PkgPath, out.OutputPath, diff) + hadDiff = true + } + } else { + log.Printf("%s: failed to diff %s: %v\n", out.PkgPath, out.OutputPath, err) + success = false + } + } + if !success { + log.Println("at least one generate failure") + return errReturn + } + if hadDiff { + return diffReturn + } + return subcommands.ExitSuccess +} + +type showCmd struct { + tags string +} + +func (*showCmd) Name() string { return "show" } +func (*showCmd) Synopsis() string { + return "describe all top-level provider sets" +} +func (*showCmd) Usage() string { + return `show [packages] + + Given one or more packages, show finds all the provider sets declared as + top-level variables and prints what other provider sets they import and what + outputs they can produce, given possible inputs. It also lists any injector + functions defined in the package. + + If no packages are listed, it defaults to ".". +` +} +func (cmd *showCmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild") +} +func (cmd *showCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + wd, err := os.Getwd() + if err != nil { + log.Println("failed to get working directory: ", err) + return subcommands.ExitFailure + } + info, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f)) + if info != nil { + keys := make([]wire.ProviderSetID, 0, len(info.Sets)) + for k := range info.Sets { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + if keys[i].ImportPath == keys[j].ImportPath { + return keys[i].VarName < keys[j].VarName + } + return keys[i].ImportPath < keys[j].ImportPath + }) + for i, k := range keys { + if i > 0 { + fmt.Println() + } + outGroups, imports := gather(info, k) + fmt.Println(k) + for _, imp := range sortSet(imports) { + fmt.Printf("\t%s\n", imp) + } + for i := range outGroups { + fmt.Printf("\tOutputs given %s:\n", outGroups[i].name) + out := make(map[string]token.Pos, outGroups[i].outputs.Len()) + outGroups[i].outputs.Iterate(func(t types.Type, v interface{}) { + switch v := v.(type) { + case *wire.Provider: + out[types.TypeString(t, nil)] = v.Pos + case *wire.Value: + out[types.TypeString(t, nil)] = v.Pos + case *wire.Field: + out[types.TypeString(t, nil)] = v.Pos + default: + panic("unreachable") + } + }) + for _, t := range sortSet(out) { + fmt.Printf("\t\t%s\n", t) + fmt.Printf("\t\t\tat %v\n", info.Fset.Position(out[t])) + } + } + } + if len(info.Injectors) > 0 { + injectors := append([]*wire.Injector(nil), info.Injectors...) + sort.Slice(injectors, func(i, j int) bool { + if injectors[i].ImportPath == injectors[j].ImportPath { + return injectors[i].FuncName < injectors[j].FuncName + } + return injectors[i].ImportPath < injectors[j].ImportPath + }) + fmt.Println("\nInjectors:") + for _, in := range injectors { + fmt.Printf("\t%v\n", in) + } + } + } + if len(errs) > 0 { + logErrors(errs) + log.Println("error loading packages") + return subcommands.ExitFailure + } + return subcommands.ExitSuccess +} + +type checkCmd struct { + tags string +} + +func (*checkCmd) Name() string { return "check" } +func (*checkCmd) Synopsis() string { + return "print any Wire errors found" +} +func (*checkCmd) Usage() string { + return `check [-tags tag,list] [packages] + + Given one or more packages, check prints any type-checking or Wire errors + found with top-level variable provider sets or injector functions. + + If no packages are listed, it defaults to ".". +` +} +func (cmd *checkCmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild") +} +func (cmd *checkCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + wd, err := os.Getwd() + if err != nil { + log.Println("failed to get working directory: ", err) + return subcommands.ExitFailure + } + _, errs := wire.Load(ctx, wd, os.Environ(), cmd.tags, packages(f)) + if len(errs) > 0 { + logErrors(errs) + log.Println("error loading packages") + return subcommands.ExitFailure + } + return subcommands.ExitSuccess +} + +type outGroup struct { + name string + inputs *typeutil.Map // values are not important + outputs *typeutil.Map // values are *wire.Provider, *wire.Value, or *wire.Field +} + +// gather flattens a provider set into outputs grouped by the inputs +// required to create them. As it flattens the provider set, it records +// the visited named provider sets as imports. +func gather(info *wire.Info, key wire.ProviderSetID) (_ []outGroup, imports map[string]struct{}) { + set := info.Sets[key] + hash := typeutil.MakeHasher() + + // Find imports. + next := []*wire.ProviderSet{info.Sets[key]} + visited := make(map[*wire.ProviderSet]struct{}) + imports = make(map[string]struct{}) + for len(next) > 0 { + curr := next[len(next)-1] + next = next[:len(next)-1] + if _, found := visited[curr]; found { + continue + } + visited[curr] = struct{}{} + if curr.VarName != "" && !(curr.PkgPath == key.ImportPath && curr.VarName == key.VarName) { + imports[formatProviderSetName(curr.PkgPath, curr.VarName)] = struct{}{} + } + next = append(next, curr.Imports...) + } + + // Depth-first search to build groups. + var groups []outGroup + inputVisited := new(typeutil.Map) // values are int, indices into groups or -1 for input. + inputVisited.SetHasher(hash) + var stk []types.Type + for _, k := range set.Outputs() { + // Start a DFS by picking a random unvisited node. + if inputVisited.At(k) == nil { + stk = append(stk, k) + } + + // Run DFS + dfs: + for len(stk) > 0 { + curr := stk[len(stk)-1] + stk = stk[:len(stk)-1] + if inputVisited.At(curr) != nil { + continue + } + switch pv := set.For(curr); { + case pv.IsNil(): + // This is an input. + inputVisited.Set(curr, -1) + case pv.IsArg(): + // This is an injector argument. + inputVisited.Set(curr, -1) + case pv.IsProvider(): + // Try to see if any args haven't been visited. + p := pv.Provider() + allPresent := true + for _, arg := range p.Args { + if inputVisited.At(arg.Type) == nil { + allPresent = false + } + } + if !allPresent { + stk = append(stk, curr) + for _, arg := range p.Args { + if inputVisited.At(arg.Type) == nil { + stk = append(stk, arg.Type) + } + } + continue dfs + } + + // Build up set of input types, match to a group. + in := new(typeutil.Map) + in.SetHasher(hash) + for _, arg := range p.Args { + i := inputVisited.At(arg.Type).(int) + if i == -1 { + in.Set(arg.Type, true) + } else { + mergeTypeSets(in, groups[i].inputs) + } + } + for i := range groups { + if sameTypeKeys(groups[i].inputs, in) { + groups[i].outputs.Set(curr, p) + inputVisited.Set(curr, i) + continue dfs + } + } + out := new(typeutil.Map) + out.SetHasher(hash) + out.Set(curr, p) + inputVisited.Set(curr, len(groups)) + groups = append(groups, outGroup{ + inputs: in, + outputs: out, + }) + case pv.IsValue(): + v := pv.Value() + for i := range groups { + if groups[i].inputs.Len() == 0 { + groups[i].outputs.Set(curr, v) + inputVisited.Set(curr, i) + continue dfs + } + } + in := new(typeutil.Map) + in.SetHasher(hash) + out := new(typeutil.Map) + out.SetHasher(hash) + out.Set(curr, v) + inputVisited.Set(curr, len(groups)) + groups = append(groups, outGroup{ + inputs: in, + outputs: out, + }) + case pv.IsField(): + // Try to see if the parent struct hasn't been visited. + f := pv.Field() + if inputVisited.At(f.Parent) == nil { + stk = append(stk, curr, f.Parent) + continue + } + // Build the input map for the parent struct. + in := new(typeutil.Map) + in.SetHasher(hash) + i := inputVisited.At(f.Parent).(int) + if i == -1 { + in.Set(f.Parent, true) + } else { + mergeTypeSets(in, groups[i].inputs) + } + // Group all fields together under the same parent struct. + for i := range groups { + if sameTypeKeys(groups[i].inputs, in) { + groups[i].outputs.Set(curr, f) + inputVisited.Set(curr, i) + continue dfs + } + } + out := new(typeutil.Map) + out.SetHasher(hash) + out.Set(curr, f) + inputVisited.Set(curr, len(groups)) + groups = append(groups, outGroup{ + inputs: in, + outputs: out, + }) + default: + panic("unreachable") + } + } + } + + // Name and sort groups. + for i := range groups { + if groups[i].inputs.Len() == 0 { + groups[i].name = "no inputs" + continue + } + instr := make([]string, 0, groups[i].inputs.Len()) + groups[i].inputs.Iterate(func(k types.Type, _ interface{}) { + instr = append(instr, types.TypeString(k, nil)) + }) + sort.Strings(instr) + groups[i].name = strings.Join(instr, ", ") + } + sort.Slice(groups, func(i, j int) bool { + if groups[i].inputs.Len() == groups[j].inputs.Len() { + return groups[i].name < groups[j].name + } + return groups[i].inputs.Len() < groups[j].inputs.Len() + }) + return groups, imports +} + +func mergeTypeSets(dst, src *typeutil.Map) { + src.Iterate(func(k types.Type, _ interface{}) { + dst.Set(k, true) + }) +} + +func sameTypeKeys(a, b *typeutil.Map) bool { + if a.Len() != b.Len() { + return false + } + same := true + a.Iterate(func(k types.Type, _ interface{}) { + if b.At(k) == nil { + same = false + } + }) + return same +} + +func sortSet(set interface{}) []string { + rv := reflect.ValueOf(set) + a := make([]string, 0, rv.Len()) + keys := rv.MapKeys() + for _, k := range keys { + a = append(a, k.String()) + } + sort.Strings(a) + return a +} + +func formatProviderSetName(importPath, varName string) string { + // Since varName is an identifier, it doesn't make sense to quote. + return strconv.Quote(importPath) + "." + varName +} + +func logErrors(errs []error) { + for _, err := range errs { + log.Println(strings.Replace(err.Error(), "\n", "\n\t", -1)) + } +} diff --git a/pkg/build/wire/cmd/wire/wire b/pkg/build/wire/cmd/wire/wire new file mode 100755 index 00000000000..f07aa2e0841 Binary files /dev/null and b/pkg/build/wire/cmd/wire/wire differ diff --git a/pkg/build/wire/docs/best-practices.md b/pkg/build/wire/docs/best-practices.md new file mode 100644 index 00000000000..470a4cd4375 --- /dev/null +++ b/pkg/build/wire/docs/best-practices.md @@ -0,0 +1,121 @@ +# Best Practices + +The following are practices we recommend for using Wire. This list will grow +over time. + +## Distinguishing Types + +If you need to inject a common type like `string`, create a new string type to +avoid conflicts with other providers. For example: + +```go +type MySQLConnectionString string +``` + +## Options Structs + +A provider function that includes many dependencies can pair the function with +an options struct. + +```go +type Options struct { + // Messages is the set of recommended greetings. + Messages []Message + // Writer is the location to send greetings. nil goes to stdout. + Writer io.Writer +} + +func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) { + // ... +} + +var GreeterSet = wire.NewSet(wire.Struct(new(Options), "*"), NewGreeter) +``` + +## Provider Sets in Libraries + +When creating a provider set for use in a library, the only changes you can make +without breaking compatibility are: + +- Change which provider a provider set uses to provide a specific output, as + long as it does not introduce a new input to the provider set. It may remove + inputs. However, note that existing injectors will use the old provider + until they are regenerated. +- Introduce a new output type into the provider set, but only if the type + itself is newly added. If the type is not new, it is possible that some + injector already has the output type included, which would cause a conflict. + +All other changes are not safe. This includes: + +- Requiring a new input in the provider set. +- Removing an output type from a provider set. +- Adding an existing output type into the provider set. + +Instead of making one of these breaking changes, consider adding a new provider +set. + +As an example, if you have a provider set like this: + +```go +var GreeterSet = wire.NewSet(NewStdoutGreeter) + +func DefaultGreeter(ctx context.Context) *Greeter { + // ... +} + +func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter { + // ... +} + +func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) { + // ... +} +``` + +You may: + +- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. +- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as + `T` is introduced in the same commit/release as the provider is added. + +You may not: + +- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both + adds an input type (`io.Writer`) and requires injectors to return an `error` + where the provider of `*Greeter` did not require this before. +- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on + `*Greeter` will be broken. +- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have + a provider for `io.Writer` which might conflict with this one. + +As such, you should pick the output types in a library provider set carefully. +In general, prefer small provider sets in a library. For example, it is common +for a library provider set to contain a single provider function along with a +`wire.Bind` to the interface the return type implements. Avoiding larger +provider sets reduces the likelihood that applications will encounter conflicts. +To illustrate, imagine your library provides a client for a web service. While +it may be tempting to bundle a provider for `*http.Client` in a provider set for +your library's client, doing so would cause conflicts if every library did the +same. Instead, the library's provider set should only include the provider for +the API client, and let `*http.Client` be an input of the provider set. + +## Mocking + +There are two approaches for creating an injected app with mocked dependencies. +Examples of both approaches are shown +[here](https://github.com/google/wire/tree/master/internal/wire/testdata/ExampleWithMocks/foo). + +### Approach A: Pass mocks to the injector + +Create a test-only injector that takes all of the mocks as arguments; the +argument types must be the interface types the mocks are mocking. `wire.Build` +can't include providers for the mocked dependencies without creating conflicts, +so if you're using provider set(s) you will need to define one that doesn't +include the mocked types. + +### Approach B: Return the mocks from the injector + +Create a new struct that includes the app plus all of the dependencies you want +to mock. Create a test-only injector that returns this struct, give it providers +for the concrete mock types, and use `wire.Bind` to tell Wire that the concrete +mock types should be used to fulfill the appropriate interface. diff --git a/pkg/build/wire/docs/faq.md b/pkg/build/wire/docs/faq.md new file mode 100644 index 00000000000..28a45d59562 --- /dev/null +++ b/pkg/build/wire/docs/faq.md @@ -0,0 +1,130 @@ +# Frequently Asked Questions + +## How does Wire relate to other Go dependency injection tools? + +Other dependency injection tools for Go like [dig][] or [facebookgo/inject][] +are based on reflection. Wire runs as a code generator, which means that the +injector works without making calls to a runtime library. This enables easier +introspection of initialization and correct cross-references for tooling like +[guru][]. + +[dig]: https://github.com/uber-go/dig +[facebookgo/inject]: https://github.com/facebookgo/inject +[guru]: https://golang.org/s/using-guru + +## How does Wire relate to other non-Go dependency injection tools (like Dagger 2)? + +Wire's approach was inspired by [Dagger 2][]. However, it is not the aim of Wire +to emulate dependency injection tools from other languages: the design space and +requirements are quite different. For example, the Go compiler does not support +anything like Java's annotation processing mechanisms. The difference in +languages and their idioms necessarily requires different approaches in +primitives and API. + +[Dagger 2]: https://google.github.io/dagger/ + +## Why use pseudo-functions to create provider sets or injectors? + +In the early prototypes, Wire directives were specially formatted comments. This +seemed appealing at first glance as this meant no compile-time or runtime +impact. However, this unstructured approach becomes opaque to other tooling not +written for Wire. Tools like [`gorename`][] or [guru][] would not be able to +recognize references to the identifiers existing in comments without being +specially modified to understand Wire's comment format. By moving the references +into no-op function calls, Wire interoperates seamlessly with other Go tooling. + +[`gorename`]: https://godoc.org/golang.org/x/tools/cmd/gorename + +## What if my dependency graph has two dependencies of the same type? + +This most frequently appears with common types like `string`. An example of this +problem would be: + +```go +type Foo struct { /* ... */ } +type Bar struct { /* ... */ } + +func newFoo1() *Foo { /* ... */ } +func newFoo2() *Foo { /* ... */ } +func newBar(foo1 *Foo, foo2 *Foo) *Bar { /* ... */ } + +func inject() *Bar { + // ERROR! Multiple providers for *Foo. + wire.Build(newFoo1, newFoo2, newBar) + return nil +} +``` + +Wire does not allow multiple providers for one type to exist in the transitive +closure of the providers presented to `wire.Build`, as this is usually a +mistake. For legitimate cases where you need multiple dependencies of the same +type, you need to invent a new type to call this other dependency. For example, +you can name OAuth credentials after the service they connect to. Once you have +a suitable different type, you can wrap and unwrap the type when plumbing it +through Wire. Continuing our above example: + +```go +type OtherFoo Foo + +func newOtherFoo() *OtherFoo { + // Call the original provider... + foo := newFoo2() + // ...then convert it to the new type. + return (*OtherFoo)(foo) +} + +func provideBar(foo1 *Foo, otherFoo *OtherFoo) *Bar { + // Convert the new type into the unwrapped type... + foo2 := (*Foo)(otherFoo) + // ...then use it to call the original provider. + return newBar(foo1, foo2) +} + +func inject() *Bar { + wire.Build(newFoo1, newOtherFoo, provideBar) + return nil +} +``` + +## Why does Wire forbid including the same provider multiple times? + +Wire forbids this to remain consistent with the principle that specifying +multiple providers for the same type is an error. On the surface, Wire could +permit duplication, but this would introduce a few unintended consequences: + +- Wire would have to specify what kinds of duplicates are permissible: are two + `wire.Value` calls ever considered to be the "same"? +- If a provider set changes the function it uses to provide a type, then this + could break an application, since it may introduce a new conflict between + another provider set that was specifying the "same" provider. + +As such, we decided that the simpler behavior would be for this case to be an +error, knowing we can always relax this restriction later. The user can always +create a new provider set that does not have the conflicting type. A [proposed +subtract command][] would automate the toil in this process. + +[proposed subtract command]: https://github.com/google/wire/issues/8 + +## Why does Wire require explicitly declare that a type provides an interface type? + +The reason the binding is explicit is to avoid scenarios where adding a new type +to the provider graph that implements the same interface causes the graph to +break, because that can be surprising. While this does result in more typing, +the end-effect is that the developer's intent is more explicit in the code, +which we felt was most consistent with the Go philosophy. + +There is an [open issue](https://github.com/google/wire/issues/242) to consider +improving this. + +## Should I use Wire for small applications? + +Probably not. Wire is designed to automate more intricate setup code found in +larger applications. For small applications, hand-wiring dependencies is +simpler. + +## Who is using Wire? + +Wire is still fairly new and doesn't have a large user base yet. However, we +have heard a lot of interest from Go users wanting to simplify their +applications. If your project or company uses Wire, please let us know by either +emailing us or sending a pull request amending this section. diff --git a/pkg/build/wire/docs/guide.md b/pkg/build/wire/docs/guide.md new file mode 100644 index 00000000000..530650a110a --- /dev/null +++ b/pkg/build/wire/docs/guide.md @@ -0,0 +1,465 @@ +# Wire User Guide + +## Basics + +Wire has two core concepts: providers and injectors. + +### Defining Providers + +The primary mechanism in Wire is the **provider**: a function that can produce a +value. These functions are ordinary Go code. + +```go +package foobarbaz + +type Foo struct { + X int +} + +// ProvideFoo returns a Foo. +func ProvideFoo() Foo { + return Foo{X: 42} +} +``` + +Provider functions must be exported in order to be used from other packages, +just like ordinary functions. + +Providers can specify dependencies with parameters: + +```go +package foobarbaz + +// ... + +type Bar struct { + X int +} + +// ProvideBar returns a Bar: a negative Foo. +func ProvideBar(foo Foo) Bar { + return Bar{X: -foo.X} +} +``` + +Providers can also return errors: + +```go +package foobarbaz + +import ( + "context" + "errors" +) + +// ... + +type Baz struct { + X int +} + +// ProvideBaz returns a value if Bar is not zero. +func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) { + if bar.X == 0 { + return Baz{}, errors.New("cannot provide baz when bar is zero") + } + return Baz{X: bar.X}, nil +} +``` + +Providers can be grouped into **provider sets**. This is useful if several +providers will frequently be used together. To add these providers to a new set +called `SuperSet`, use the `wire.NewSet` function: + +```go +package foobarbaz + +import ( + // ... + "github.com/google/wire" +) + +// ... + +var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz) +``` + +You can also add other provider sets into a provider set. + +```go +package foobarbaz + +import ( + // ... + "example.com/some/other/pkg" +) + +// ... + +var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet) +``` + +### Injectors + +An application wires up these providers with an **injector**: a function that +calls providers in dependency order. With Wire, you write the injector's +signature, then Wire generates the function's body. + +An injector is declared by writing a function declaration whose body is a call +to `wire.Build`. The return values don't matter as long as they are of the +correct type. The values themselves will be ignored in the generated code. Let's +say that the above providers were defined in a package called +`example.com/foobarbaz`. The following would declare an injector to obtain a +`Baz`: + +```go +// +build wireinject +// The build tag makes sure the stub is not built in the final build. + +package main + +import ( + "context" + + "github.com/google/wire" + "example.com/foobarbaz" +) + +func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) { + wire.Build(foobarbaz.MegaSet) + return foobarbaz.Baz{}, nil +} +``` + +Like providers, injectors can be parameterized on inputs (which then get sent to +providers) and can return errors. Arguments to `wire.Build` are the same as +`wire.NewSet`: they form a provider set. This is the provider set that gets used +during code generation for that injector. + +Any non-injector declarations found in a file with injectors will be copied into +the generated file. + +You can generate the injector by invoking Wire in the package directory: + +```shell +wire +``` + +Wire will produce an implementation of the injector in a file called +`wire_gen.go` that looks something like this: + +```go +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//+build !wireinject + +package main + +import ( + "example.com/foobarbaz" +) + +func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) { + foo := foobarbaz.ProvideFoo() + bar := foobarbaz.ProvideBar(foo) + baz, err := foobarbaz.ProvideBaz(ctx, bar) + if err != nil { + return foobarbaz.Baz{}, err + } + return baz, nil +} +``` + +As you can see, the output is very close to what a developer would write +themselves. Further, there is little dependency on Wire at runtime: all of the +written code is just normal Go code, and can be used without Wire. + +Once `wire_gen.go` is created, you can regenerate it by running [`go generate`]. + +[`go generate`]: https://blog.golang.org/generate + +## Advanced Features + +The following features all build on top of the concepts of providers and +injectors. + +### Binding Interfaces + +Frequently, dependency injection is used to bind a concrete implementation for +an interface. Wire matches inputs to outputs via [type identity][], so the +inclination might be to create a provider that returns an interface type. +However, this would not be idiomatic, since the Go best practice is to +[return concrete types][]. Instead, you can declare an interface binding in a +provider set: + +```go +type Fooer interface { + Foo() string +} + +type MyFooer string + +func (b *MyFooer) Foo() string { + return string(*b) +} + +func provideMyFooer() *MyFooer { + b := new(MyFooer) + *b = "Hello, World!" + return b +} + +type Bar string + +func provideBar(f Fooer) string { + // f will be a *MyFooer. + return f.Foo() +} + +var Set = wire.NewSet( + provideMyFooer, + wire.Bind(new(Fooer), new(*MyFooer)), + provideBar) +``` + +The first argument to `wire.Bind` is a pointer to a value of the desired +interface type and the second argument is a pointer to a value of the type that +implements the interface. Any set that includes an interface binding must also +have a provider in the same set that provides the concrete type. + +[type identity]: https://golang.org/ref/spec#Type_identity +[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces + +### Struct Providers + +Structs can be constructed using provided types. Use the `wire.Struct` function +to construct a struct type and tell the injector which field(s) should be injected. +The injector will fill in each field using the provider for the field's type. +For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For +example, given the following providers: + +```go +type Foo int +type Bar int + +func ProvideFoo() Foo {/* ... */} + +func ProvideBar() Bar {/* ... */} + +type FooBar struct { + MyFoo Foo + MyBar Bar +} + +var Set = wire.NewSet( + ProvideFoo, + ProvideBar, + wire.Struct(new(FooBar), "MyFoo", "MyBar")) +``` + +A generated injector for `FooBar` would look like this: + +```go +func injectFooBar() FooBar { + foo := ProvideFoo() + bar := ProvideBar() + fooBar := FooBar{ + MyFoo: foo, + MyBar: bar, + } + return fooBar +} +``` + +The first argument to `wire.Struct` is a pointer to the desired struct type and +the subsequent arguments are the names of fields to be injected. A special +string `"*"` can be used as a shortcut to tell the injector to inject all +fields. So `wire.Struct(new(FooBar), "*")` produces the same result as above. + +For the above example, you can specify only injecting `"MyFoo"` by changing the +`Set` to: + +```go +var Set = wire.NewSet( + ProvideFoo, + wire.Struct(new(FooBar), "MyFoo")) +``` + +Then the generated injector for `FooBar` would look like this: + +```go +func injectFooBar() FooBar { + foo := ProvideFoo() + fooBar := FooBar{ + MyFoo: foo, + } + return fooBar +} +``` + +If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector +would look like this: + +```go +func injectFooBar() *FooBar { + foo := ProvideFoo() + fooBar := &FooBar{ + MyFoo: foo, + } + return fooBar +} +``` + +It is sometimes useful to prevent certain fields from being filled in by the +injector, especially when passing `*` to `wire.Struct`. You can tag a field with +`` `wire:"-"` `` to have Wire ignore such fields. For example: + +```go +type Foo struct { + mu sync.Mutex `wire:"-"` + Bar Bar +} +``` + +When you provide the `Foo` type using `wire.Struct(new(Foo), "*")`, Wire will +automatically omit the `mu` field. Additionally, it is an error to explicitly +specify a prevented field as in `wire.Struct(new(Foo), "mu")`. + +### Binding Values + +Occasionally, it is useful to bind a basic value (usually `nil`) to a type. +Instead of having injectors depend on a throwaway provider function, you can add +a value expression to a provider set. + +```go +type Foo struct { + X int +} + +func injectFoo() Foo { + wire.Build(wire.Value(Foo{X: 42})) + return Foo{} +} +``` + +The generated injector would look like this: + +```go +func injectFoo() Foo { + foo := _wireFooValue + return foo +} + +var ( + _wireFooValue = Foo{X: 42} +) +``` + +It's important to note that the expression will be copied to the injector's +package; references to variables will be evaluated during the injector package's +initialization. Wire will emit an error if the expression calls any functions or +receives from any channels. + +For interface values, use `InterfaceValue`: + +```go +func injectReader() io.Reader { + wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin)) + return nil +} +``` + +### Use Fields of a Struct as Providers + +Sometimes the providers the user wants are some fields of a struct. If you find +yourself writing a provider like `getS` in the example below to promote struct +fields into provided types: + +```go +type Foo struct { + S string + N int + F float64 +} + +func getS(foo Foo) string { + // Bad! Use wire.FieldsOf instead. + return foo.S +} + +func provideFoo() Foo { + return Foo{ S: "Hello, World!", N: 1, F: 3.14 } +} + +func injectedMessage() string { + wire.Build( + provideFoo, + getS) + return "" +} +``` + +You can instead use `wire.FieldsOf` to use those fields directly without writing +`getS`: + +```go +func injectedMessage() string { + wire.Build( + provideFoo, + wire.FieldsOf(new(Foo), "S")) + return "" +} +``` + +The generated injector would look like this: + +```go +func injectedMessage() string { + foo := provideFoo() + string2 := foo.S + return string2 +} +``` + +You can add as many field names to a `wire.FieldsOf` function as you like. +For a given field type `T`, `FieldsOf` provides at least `T`; if the struct +argument is a pointer to a struct, then `FieldsOf` also provides `*T`. + +### Cleanup functions + +If a provider creates a value that needs to be cleaned up (e.g. closing a file), +then it can return a closure to clean up the resource. The injector will use +this to either return an aggregated cleanup function to the caller or to clean +up the resource if a provider called later in the injector's implementation +returns an error. + +```go +func provideFile(log Logger, path Path) (*os.File, func(), error) { + f, err := os.Open(string(path)) + if err != nil { + return nil, nil, err + } + cleanup := func() { + if err := f.Close(); err != nil { + log.Log(err) + } + } + return f, cleanup, nil +} +``` + +A cleanup function is guaranteed to be called before the cleanup function of any +of the provider's inputs and must have the signature `func()`. + +### Alternate Injector Syntax + +If you grow weary of writing `return foobarbaz.Foo{}, nil` at the end of your +injector function declaration, you can instead write it more concisely with a +`panic`: + +```go +func injectFoo() Foo { + panic(wire.Build(/* ... */)) +} +``` diff --git a/pkg/build/wire/go.mod b/pkg/build/wire/go.mod new file mode 100644 index 00000000000..0b2868ee974 --- /dev/null +++ b/pkg/build/wire/go.mod @@ -0,0 +1,10 @@ +module github.com/grafana/grafana/pkg/build/wire + +go 1.12 + +require ( + github.com/google/go-cmp v0.6.0 + github.com/google/subcommands v1.2.0 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 + golang.org/x/tools v0.17.0 +) diff --git a/.bingo/wire.sum b/pkg/build/wire/go.sum similarity index 81% rename from .bingo/wire.sum rename to pkg/build/wire/go.sum index be6e646d063..d24a2f2fe06 100644 --- a/.bingo/wire.sum +++ b/pkg/build/wire/go.sum @@ -1,14 +1,7 @@ -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= -github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= -github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= -github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -19,7 +12,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -31,6 +23,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -55,8 +48,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s= -golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/pkg/build/wire/internal/alldeps b/pkg/build/wire/internal/alldeps new file mode 100644 index 00000000000..41e98ac7c96 --- /dev/null +++ b/pkg/build/wire/internal/alldeps @@ -0,0 +1,5 @@ +github.com/google/subcommands +github.com/google/wire +github.com/pmezard/go-difflib +golang.org/x/mod +golang.org/x/tools diff --git a/pkg/build/wire/internal/check_api_change.sh b/pkg/build/wire/internal/check_api_change.sh new file mode 100755 index 00000000000..ad2f00c4d03 --- /dev/null +++ b/pkg/build/wire/internal/check_api_change.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Copyright 2019 The Wire Authors +# +# 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 +# +# https://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. + +# This script checks to see if there are any incompatible API changes on the +# current branch relative to the upstream branch. +# It fails if it finds any, unless there is a commit with BREAKING_CHANGE_OK +# in the first line of the commit message. + +# This script expects: +# a) to be run at the root of the repository +# b) HEAD is pointing to a commit that merges between the pull request and the +# upstream branch (GITHUB_BASE_REF). + +set -euo pipefail + +UPSTREAM_BRANCH="${GITHUB_BASE_REF:-master}" +echo "Checking for incompatible API changes relative to ${UPSTREAM_BRANCH}..." + +MASTER_CLONE_DIR="$(mktemp -d)" +PKGINFO_BRANCH=$(mktemp) +PKGINFO_MASTER=$(mktemp) + +function cleanup() { + rm -rf "$MASTER_CLONE_DIR" + rm -f "$PKGINFO_BRANCH" + rm -f "$PKGINFO_MASTER" +} +trap cleanup EXIT + +# Install apidiff. +go install golang.org/x/exp/cmd/apidiff@latest + +git clone -b "$UPSTREAM_BRANCH" . "$MASTER_CLONE_DIR" &> /dev/null + +incompatible_change_pkgs=() +PKGS=$(cd "$MASTER_CLONE_DIR"; go list ./... | grep -v test | grep -v internal) +for pkg in $PKGS; do + echo " Testing ${pkg}..." + + # Compute export data for the current branch. + package_deleted=0 + apidiff -w "$PKGINFO_BRANCH" "$pkg" || package_deleted=1 + if [[ $package_deleted -eq 1 ]]; then + echo " Package ${pkg} was deleted! Recording as an incompatible change."; + incompatible_change_pkgs+=(${pkg}); + continue; + fi + + # Compute export data for master@HEAD. + (cd "$MASTER_CLONE_DIR"; apidiff -w "$PKGINFO_MASTER" "$pkg") + + # Print all changes for posterity. + apidiff "$PKGINFO_MASTER" "$PKGINFO_BRANCH" + + # Note if there's an incompatible change. + ic=$(apidiff -incompatible "$PKGINFO_MASTER" "$PKGINFO_BRANCH") + if [ ! -z "$ic" ]; then + incompatible_change_pkgs+=("$pkg"); + fi +done + +if [ ${#incompatible_change_pkgs[@]} -eq 0 ]; then + # No incompatible changes, we are good. + echo "OK: No incompatible changes found." + exit 0; +fi +echo "Found breaking API change(s) in: ${incompatible_change_pkgs[*]}." + +# Found incompatible changes; see if they were declared as OK via a commit. +if git cherry -v master | grep -q "BREAKING_CHANGE_OK"; then + echo "Allowing them due to a commit message with BREAKING_CHANGE_OK."; + exit 0; +fi + +echo "FAIL. If this is expected and OK, you can pass this check by adding a commit with BREAKING_CHANGE_OK in the first line of the message." +exit 1 + diff --git a/pkg/build/wire/internal/listdeps.sh b/pkg/build/wire/internal/listdeps.sh new file mode 100755 index 00000000000..fc99ade9c6d --- /dev/null +++ b/pkg/build/wire/internal/listdeps.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright 2019 The Wire Authors +# +# 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 +# +# https://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. + +set -euo pipefail + +# To run this script manually to update alldeps: +# +# $ internal/listdeps.sh > internal/alldeps +# +# Important note: there are changes in module tooling behavior between go 1.11 +# and go 1.12; please make sure to use the same version of Go as used by Github +# Actions (see .github/workflows/tests.yml) when updating the alldeps file. +go list -deps -f '{{with .Module}}{{.Path}}{{end}}' ./... | sort | uniq diff --git a/pkg/build/wire/internal/runtests.sh b/pkg/build/wire/internal/runtests.sh new file mode 100755 index 00000000000..dd5647329fc --- /dev/null +++ b/pkg/build/wire/internal/runtests.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Copyright 2019 The Wire Authors +# +# 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 +# +# https://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. + +# https://coderwall.com/p/fkfaqq/safer-bash-scripts-with-set-euxo-pipefail +set -euo pipefail + +if [[ $# -gt 0 ]]; then + echo "usage: runtests.sh" 1>&2 + exit 64 +fi + +# Run Go tests. Only do coverage for the Linux build +# because it is slow, and codecov will only save the last one anyway. +result=0 +if [[ "${RUNNER_OS:-}" == "Linux" ]]; then + echo "Running Go tests (with coverage)..." + go test -mod=readonly -race -coverpkg=./... -coverprofile=coverage.out ./... || result=1 + if [ -f coverage.out ] && [ $result -eq 0 ]; then + bash <(curl -s https://codecov.io/bash) + fi +else + echo "Running Go tests..." + go test -mod=readonly -race ./... || result=1 +fi + +# No need to run other checks on OSs other than linux. +# We default RUNNER_OS to "Linux" so that we don't abort here when run locally. +if [[ "${RUNNER_OS:-Linux}" != "Linux" ]]; then + exit $result +fi + +echo +echo "Ensuring .go files are formatted with gofmt -s..." +mapfile -t go_files < <(find . -name '*.go' -type f | grep -v testdata) +DIFF="$(gofmt -s -d "${go_files[@]}")" +if [ -n "$DIFF" ]; then + echo "FAIL: please run gofmt -s and commit the result" + echo "$DIFF"; + result=1; +else + echo "OK" +fi; + + +# Ensure that the code has no extra dependencies (including transitive +# dependencies) that we're not already aware of by comparing with +# ./internal/alldeps +# +# Whenever project dependencies change, rerun ./internal/listdeps.sh +echo +echo "Ensuring that there are no dependencies not listed in ./internal/alldeps..." +(./internal/listdeps.sh | diff ./internal/alldeps - && echo "OK") || { + echo "FAIL: dependencies changed; run: internal/listdeps.sh > internal/alldeps" + # Module behavior may differ across versions. + echo "using the latest go version." + result=1 +} + + +# For pull requests, check if there are undeclared incompatible API changes. +# Skip this if we're already going to fail since it is expensive. +# CURRENTLY BROKEN +# if [[ ${result} -eq 0 ]] && [[ ! -z "${GITHUB_HEAD_REF:-x}" ]]; then + # echo + # ./internal/check_api_change.sh || result=1; +# fi + +exit $result diff --git a/pkg/build/wire/internal/wire/analyze.go b/pkg/build/wire/internal/wire/analyze.go new file mode 100644 index 00000000000..9650ef16728 --- /dev/null +++ b/pkg/build/wire/internal/wire/analyze.go @@ -0,0 +1,521 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire + +import ( + "errors" + "fmt" + "go/ast" + "go/token" + "go/types" + "sort" + "strings" + + "golang.org/x/tools/go/types/typeutil" +) + +type callKind int + +const ( + funcProviderCall callKind = iota + structProvider + valueExpr + selectorExpr +) + +// A call represents a step of an injector function. It may be either a +// function call or a composite struct literal, depending on the value +// of kind. +type call struct { + // kind indicates the code pattern to use. + kind callKind + + // out is the type this step produces. + out types.Type + + // pkg and name identify one of the following: + // 1) the provider to call for kind == funcProviderCall; + // 2) the type to construct for kind == structProvider; + // 3) the name to select for kind == selectorExpr. + pkg *types.Package + name string + + // args is a list of arguments to call the provider with. Each element is: + // a) one of the givens (args[i] < len(given)), + // b) the result of a previous provider call (args[i] >= len(given)) + // + // This will be nil for kind == valueExpr. + // + // If kind == selectorExpr, then the length of this slice will be 1 and the + // "argument" will be the value to access fields from. + args []int + + // varargs is true if the provider function is variadic. + varargs bool + + // fieldNames maps the arguments to struct field names. + // This will only be set if kind == structProvider. + fieldNames []string + + // ins is the list of types this call receives as arguments. + // This will be nil for kind == valueExpr. + ins []types.Type + + // The following are only set for kind == funcProviderCall: + + // hasCleanup is true if the provider call returns a cleanup function. + hasCleanup bool + // hasErr is true if the provider call returns an error. + hasErr bool + + // The following are only set for kind == valueExpr: + + valueExpr ast.Expr + valueTypeInfo *types.Info + + // The following are only set for kind == selectorExpr: + + ptrToField bool +} + +// solve finds the sequence of calls required to produce an output type +// with an optional set of provided inputs. +func solve(fset *token.FileSet, out types.Type, given *types.Tuple, set *ProviderSet) ([]call, []error) { + ec := new(errorCollector) + + // Start building the mapping of type to local variable of the given type. + // The first len(given) local variables are the given types. + index := new(typeutil.Map) + for i := 0; i < given.Len(); i++ { + index.Set(given.At(i).Type(), i) + } + + // Topological sort of the directed graph defined by the providers + // using a depth-first search using a stack. Provider set graphs are + // guaranteed to be acyclic. An index value of errAbort indicates that + // the type was visited, but failed due to an error added to ec. + errAbort := errors.New("failed to visit") + var used []*providerSetSrc + var calls []call + type frame struct { + t types.Type + from types.Type + up *frame + } + stk := []frame{{t: out}} +dfs: + for len(stk) > 0 { + curr := stk[len(stk)-1] + stk = stk[:len(stk)-1] + if index.At(curr.t) != nil { + continue + } + + pv := set.For(curr.t) + if pv.IsNil() { + if curr.from == nil { + ec.add(fmt.Errorf("no provider found for %s, output of injector", types.TypeString(curr.t, nil))) + index.Set(curr.t, errAbort) + continue + } + sb := new(strings.Builder) + fmt.Fprintf(sb, "no provider found for %s", types.TypeString(curr.t, nil)) + for f := curr.up; f != nil; f = f.up { + fmt.Fprintf(sb, "\nneeded by %s in %s", types.TypeString(f.t, nil), set.srcMap.At(f.t).(*providerSetSrc).description(fset, f.t)) + } + ec.add(errors.New(sb.String())) + index.Set(curr.t, errAbort) + continue + } + src := set.srcMap.At(curr.t).(*providerSetSrc) + used = append(used, src) + if concrete := pv.Type(); !types.Identical(concrete, curr.t) { + // Interface binding does not create a call. + i := index.At(concrete) + if i == nil { + stk = append(stk, curr, frame{t: concrete, from: curr.t, up: &curr}) + continue + } + index.Set(curr.t, i) + continue + } + + switch pv := set.For(curr.t); { + case pv.IsArg(): + // Continue, already added to stk. + case pv.IsProvider(): + p := pv.Provider() + // Ensure that all argument types have been visited. If not, push them + // on the stack in reverse order so that calls are added in argument + // order. + visitedArgs := true + for i := len(p.Args) - 1; i >= 0; i-- { + a := p.Args[i] + if index.At(a.Type) == nil { + if visitedArgs { + // Make sure to re-visit this type after visiting all arguments. + stk = append(stk, curr) + visitedArgs = false + } + stk = append(stk, frame{t: a.Type, from: curr.t, up: &curr}) + } + } + if !visitedArgs { + continue + } + args := make([]int, len(p.Args)) + ins := make([]types.Type, len(p.Args)) + for i := range p.Args { + ins[i] = p.Args[i].Type + v := index.At(p.Args[i].Type) + if v == errAbort { + index.Set(curr.t, errAbort) + continue dfs + } + args[i] = v.(int) + } + index.Set(curr.t, given.Len()+len(calls)) + kind := funcProviderCall + fieldNames := []string(nil) + if p.IsStruct { + kind = structProvider + for _, arg := range p.Args { + fieldNames = append(fieldNames, arg.FieldName) + } + } + calls = append(calls, call{ + kind: kind, + pkg: p.Pkg, + name: p.Name, + args: args, + varargs: p.Varargs, + fieldNames: fieldNames, + ins: ins, + out: curr.t, + hasCleanup: p.HasCleanup, + hasErr: p.HasErr, + }) + case pv.IsValue(): + v := pv.Value() + index.Set(curr.t, given.Len()+len(calls)) + calls = append(calls, call{ + kind: valueExpr, + out: curr.t, + valueExpr: v.expr, + valueTypeInfo: v.info, + }) + case pv.IsField(): + f := pv.Field() + if index.At(f.Parent) == nil { + // Fields have one dependency which is the parent struct. Make + // sure to visit it first if it is not already visited. + stk = append(stk, curr, frame{t: f.Parent, from: curr.t, up: &curr}) + continue + } + index.Set(curr.t, given.Len()+len(calls)) + v := index.At(f.Parent) + if v == errAbort { + index.Set(curr.t, errAbort) + continue dfs + } + // Use args[0] to store the position of the parent struct. + args := []int{v.(int)} + // If f.Out has 2 elements and curr.t is the 2nd one, then the call must + // provide a pointer to the field. + ptrToField := len(f.Out) == 2 && types.Identical(curr.t, f.Out[1]) + calls = append(calls, call{ + kind: selectorExpr, + pkg: f.Pkg, + name: f.Name, + out: curr.t, + args: args, + ptrToField: ptrToField, + }) + default: + panic("unknown return value from ProviderSet.For") + } + } + if len(ec.errors) > 0 { + return nil, ec.errors + } + if errs := verifyArgsUsed(set, used); len(errs) > 0 { + return nil, errs + } + return calls, nil +} + +// verifyArgsUsed ensures that all of the arguments in set were used during solve. +func verifyArgsUsed(set *ProviderSet, used []*providerSetSrc) []error { + var errs []error + for _, imp := range set.Imports { + found := false + for _, u := range used { + if u.Import == imp { + found = true + break + } + } + if !found { + if imp.VarName == "" { + errs = append(errs, errors.New("unused provider set")) + } else { + errs = append(errs, fmt.Errorf("unused provider set %q", imp.VarName)) + } + } + } + for _, p := range set.Providers { + found := false + for _, u := range used { + if u.Provider == p { + found = true + break + } + } + if !found { + errs = append(errs, fmt.Errorf("unused provider %q", p.Pkg.Name()+"."+p.Name)) + } + } + for _, v := range set.Values { + found := false + for _, u := range used { + if u.Value == v { + found = true + break + } + } + if !found { + errs = append(errs, fmt.Errorf("unused value of type %s", types.TypeString(v.Out, nil))) + } + } + for _, b := range set.Bindings { + found := false + for _, u := range used { + if u.Binding == b { + found = true + break + } + } + if !found { + errs = append(errs, fmt.Errorf("unused interface binding to type %s", types.TypeString(b.Iface, nil))) + } + } + for _, f := range set.Fields { + found := false + for _, u := range used { + if u.Field == f { + found = true + break + } + } + if !found { + errs = append(errs, fmt.Errorf("unused field %q.%s", f.Parent, f.Name)) + } + } + return errs +} + +// buildProviderMap creates the providerMap and srcMap fields for a given +// provider set. The given provider set's providerMap and srcMap fields are +// ignored. +func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *ProviderSet) (*typeutil.Map, *typeutil.Map, []error) { + providerMap := new(typeutil.Map) + providerMap.SetHasher(hasher) + srcMap := new(typeutil.Map) // to *providerSetSrc + srcMap.SetHasher(hasher) + + ec := new(errorCollector) + // Process injector arguments. + if set.InjectorArgs != nil { + givens := set.InjectorArgs.Tuple + for i := 0; i < givens.Len(); i++ { + typ := givens.At(i).Type() + arg := &InjectorArg{Args: set.InjectorArgs, Index: i} + src := &providerSetSrc{InjectorArg: arg} + if prevSrc := srcMap.At(typ); prevSrc != nil { + ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc))) + continue + } + providerMap.Set(typ, &ProvidedType{t: typ, a: arg}) + srcMap.Set(typ, src) + } + } + // Process imports, verifying that there are no conflicts between sets. + for _, imp := range set.Imports { + src := &providerSetSrc{Import: imp} + imp.providerMap.Iterate(func(k types.Type, v interface{}) { + if prevSrc := srcMap.At(k); prevSrc != nil { + ec.add(bindingConflictError(fset, k, set, src, prevSrc.(*providerSetSrc))) + return + } + providerMap.Set(k, v) + srcMap.Set(k, src) + }) + } + if len(ec.errors) > 0 { + return nil, nil, ec.errors + } + + // Process non-binding providers in new set. + for _, p := range set.Providers { + src := &providerSetSrc{Provider: p} + for _, typ := range p.Out { + if prevSrc := srcMap.At(typ); prevSrc != nil { + ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc))) + continue + } + providerMap.Set(typ, &ProvidedType{t: typ, p: p}) + srcMap.Set(typ, src) + } + } + for _, v := range set.Values { + src := &providerSetSrc{Value: v} + if prevSrc := srcMap.At(v.Out); prevSrc != nil { + ec.add(bindingConflictError(fset, v.Out, set, src, prevSrc.(*providerSetSrc))) + continue + } + providerMap.Set(v.Out, &ProvidedType{t: v.Out, v: v}) + srcMap.Set(v.Out, src) + } + for _, f := range set.Fields { + src := &providerSetSrc{Field: f} + for _, typ := range f.Out { + if prevSrc := srcMap.At(typ); prevSrc != nil { + ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc))) + continue + } + providerMap.Set(typ, &ProvidedType{t: typ, f: f}) + srcMap.Set(typ, src) + } + } + if len(ec.errors) > 0 { + return nil, nil, ec.errors + } + + // Process bindings in set. Must happen after the other providers to + // ensure the concrete type is being provided. + for _, b := range set.Bindings { + src := &providerSetSrc{Binding: b} + if prevSrc := srcMap.At(b.Iface); prevSrc != nil { + ec.add(bindingConflictError(fset, b.Iface, set, src, prevSrc.(*providerSetSrc))) + continue + } + concrete := providerMap.At(b.Provided) + if concrete == nil { + setName := set.VarName + if setName == "" { + setName = "provider set" + } + ec.add(notePosition(fset.Position(b.Pos), fmt.Errorf("wire.Bind of concrete type %q to interface %q, but %s does not include a provider for %q", b.Provided, b.Iface, setName, b.Provided))) + continue + } + providerMap.Set(b.Iface, concrete) + srcMap.Set(b.Iface, src) + } + if len(ec.errors) > 0 { + return nil, nil, ec.errors + } + return providerMap, srcMap, nil +} + +func verifyAcyclic(providerMap *typeutil.Map, hasher typeutil.Hasher) []error { + // We must visit every provider type inside provider map, but we don't + // have a well-defined starting point and there may be several + // distinct graphs. Thus, we start a depth-first search at every + // provider, but keep a shared record of visited providers to avoid + // duplicating work. + visited := new(typeutil.Map) // to bool + visited.SetHasher(hasher) + ec := new(errorCollector) + // Sort output types so that errors about cycles are consistent. + outputs := providerMap.Keys() + sort.Slice(outputs, func(i, j int) bool { return types.TypeString(outputs[i], nil) < types.TypeString(outputs[j], nil) }) + for _, root := range outputs { + // Depth-first search using a stack of trails through the provider map. + stk := [][]types.Type{{root}} + for len(stk) > 0 { + curr := stk[len(stk)-1] + stk = stk[:len(stk)-1] + head := curr[len(curr)-1] + if v, _ := visited.At(head).(bool); v { + continue + } + visited.Set(head, true) + x := providerMap.At(head) + if x == nil { + // Leaf: input. + continue + } + pt := x.(*ProvidedType) + switch { + case pt.IsValue(): + // Leaf: values do not have dependencies. + case pt.IsArg(): + // Injector arguments do not have dependencies. + case pt.IsProvider() || pt.IsField(): + var args []types.Type + if pt.IsProvider() { + for _, arg := range pt.Provider().Args { + args = append(args, arg.Type) + } + } else { + args = append(args, pt.Field().Parent) + } + for _, a := range args { + hasCycle := false + for i, b := range curr { + if types.Identical(a, b) { + sb := new(strings.Builder) + fmt.Fprintf(sb, "cycle for %s:\n", types.TypeString(a, nil)) + for j := i; j < len(curr); j++ { + t := providerMap.At(curr[j]).(*ProvidedType) + if t.IsProvider() { + p := t.Provider() + fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Pkg.Path(), p.Name) + } else { + p := t.Field() + fmt.Fprintf(sb, "%s (%s.%s) ->\n", types.TypeString(curr[j], nil), p.Parent, p.Name) + } + } + fmt.Fprintf(sb, "%s", types.TypeString(a, nil)) + ec.add(errors.New(sb.String())) + hasCycle = true + break + } + } + if !hasCycle { + next := append(append([]types.Type(nil), curr...), a) + stk = append(stk, next) + } + } + default: + panic("invalid provider map value") + } + } + } + return ec.errors +} + +// bindingConflictError creates a new error describing multiple bindings +// for the same output type. +func bindingConflictError(fset *token.FileSet, typ types.Type, set *ProviderSet, cur, prev *providerSetSrc) error { + sb := new(strings.Builder) + if set.VarName != "" { + fmt.Fprintf(sb, "%s has ", set.VarName) + } + fmt.Fprintf(sb, "multiple bindings for %s\n", types.TypeString(typ, nil)) + fmt.Fprintf(sb, "current:\n<- %s\n", strings.Join(cur.trace(fset, typ), "\n<- ")) + fmt.Fprintf(sb, "previous:\n<- %s", strings.Join(prev.trace(fset, typ), "\n<- ")) + return notePosition(fset.Position(set.Pos), errors.New(sb.String())) +} diff --git a/pkg/build/wire/internal/wire/copyast.go b/pkg/build/wire/internal/wire/copyast.go new file mode 100644 index 00000000000..179d1c64349 --- /dev/null +++ b/pkg/build/wire/internal/wire/copyast.go @@ -0,0 +1,493 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/go/ast/astutil" +) + +// copyAST performs a deep copy of an AST. *ast.Ident identity will be +// preserved. +// +// This allows using astutil.Apply to rewrite an AST without modifying +// the original AST. +func copyAST(original ast.Node) ast.Node { + // This function is necessarily long. No utility function exists to do this + // clone, as most any attempt would need to have customization options, which + // would need to be as expressive as Apply. A possibility to shorten the code + // here would be to use reflection, but that trades clarity for shorter code. + + m := make(map[ast.Node]ast.Node) + astutil.Apply(original, nil, func(c *astutil.Cursor) bool { + switch node := c.Node().(type) { + case nil: + // No-op. + case *ast.ArrayType: + m[node] = &ast.ArrayType{ + Lbrack: node.Lbrack, + Len: exprFromMap(m, node.Len), + Elt: exprFromMap(m, node.Elt), + } + case *ast.AssignStmt: + m[node] = &ast.AssignStmt{ + Lhs: copyExprList(m, node.Lhs), + TokPos: node.TokPos, + Tok: node.Tok, + Rhs: copyExprList(m, node.Rhs), + } + case *ast.BadDecl: + m[node] = &ast.BadDecl{ + From: node.From, + To: node.To, + } + case *ast.BadExpr: + m[node] = &ast.BadExpr{ + From: node.From, + To: node.To, + } + case *ast.BadStmt: + m[node] = &ast.BadStmt{ + From: node.From, + To: node.To, + } + case *ast.BasicLit: + m[node] = &ast.BasicLit{ + ValuePos: node.ValuePos, + Kind: node.Kind, + Value: node.Value, + } + case *ast.BinaryExpr: + m[node] = &ast.BinaryExpr{ + X: exprFromMap(m, node.X), + OpPos: node.OpPos, + Op: node.Op, + Y: exprFromMap(m, node.Y), + } + case *ast.BlockStmt: + m[node] = &ast.BlockStmt{ + Lbrace: node.Lbrace, + List: copyStmtList(m, node.List), + Rbrace: node.Rbrace, + } + case *ast.BranchStmt: + m[node] = &ast.BranchStmt{ + TokPos: node.TokPos, + Tok: node.Tok, + Label: identFromMap(m, node.Label), + } + case *ast.CallExpr: + m[node] = &ast.CallExpr{ + Fun: exprFromMap(m, node.Fun), + Lparen: node.Lparen, + Args: copyExprList(m, node.Args), + Ellipsis: node.Ellipsis, + Rparen: node.Rparen, + } + case *ast.CaseClause: + m[node] = &ast.CaseClause{ + Case: node.Case, + List: copyExprList(m, node.List), + Colon: node.Colon, + Body: copyStmtList(m, node.Body), + } + case *ast.ChanType: + m[node] = &ast.ChanType{ + Begin: node.Begin, + Arrow: node.Arrow, + Dir: node.Dir, + Value: exprFromMap(m, node.Value), + } + case *ast.CommClause: + m[node] = &ast.CommClause{ + Case: node.Case, + Comm: stmtFromMap(m, node.Comm), + Colon: node.Colon, + Body: copyStmtList(m, node.Body), + } + case *ast.Comment: + m[node] = &ast.Comment{ + Slash: node.Slash, + Text: node.Text, + } + case *ast.CommentGroup: + cg := new(ast.CommentGroup) + if node.List != nil { + cg.List = make([]*ast.Comment, len(node.List)) + for i := range node.List { + cg.List[i] = m[node.List[i]].(*ast.Comment) + } + } + m[node] = cg + case *ast.CompositeLit: + m[node] = &ast.CompositeLit{ + Type: exprFromMap(m, node.Type), + Lbrace: node.Lbrace, + Elts: copyExprList(m, node.Elts), + Rbrace: node.Rbrace, + } + case *ast.DeclStmt: + m[node] = &ast.DeclStmt{ + Decl: m[node.Decl].(ast.Decl), + } + case *ast.DeferStmt: + m[node] = &ast.DeferStmt{ + Defer: node.Defer, + Call: callExprFromMap(m, node.Call), + } + case *ast.Ellipsis: + m[node] = &ast.Ellipsis{ + Ellipsis: node.Ellipsis, + Elt: exprFromMap(m, node.Elt), + } + case *ast.EmptyStmt: + m[node] = &ast.EmptyStmt{ + Semicolon: node.Semicolon, + Implicit: node.Implicit, + } + case *ast.ExprStmt: + m[node] = &ast.ExprStmt{ + X: exprFromMap(m, node.X), + } + case *ast.Field: + m[node] = &ast.Field{ + Doc: commentGroupFromMap(m, node.Doc), + Names: copyIdentList(m, node.Names), + Type: exprFromMap(m, node.Type), + Tag: basicLitFromMap(m, node.Tag), + Comment: commentGroupFromMap(m, node.Comment), + } + case *ast.FieldList: + fl := &ast.FieldList{ + Opening: node.Opening, + Closing: node.Closing, + } + if node.List != nil { + fl.List = make([]*ast.Field, len(node.List)) + for i := range node.List { + fl.List[i] = m[node.List[i]].(*ast.Field) + } + } + m[node] = fl + case *ast.ForStmt: + m[node] = &ast.ForStmt{ + For: node.For, + Init: stmtFromMap(m, node.Init), + Cond: exprFromMap(m, node.Cond), + Post: stmtFromMap(m, node.Post), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.FuncDecl: + m[node] = &ast.FuncDecl{ + Doc: commentGroupFromMap(m, node.Doc), + Recv: fieldListFromMap(m, node.Recv), + Name: identFromMap(m, node.Name), + Type: funcTypeFromMap(m, node.Type), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.FuncLit: + m[node] = &ast.FuncLit{ + Type: funcTypeFromMap(m, node.Type), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.FuncType: + m[node] = &ast.FuncType{ + Func: node.Func, + Params: fieldListFromMap(m, node.Params), + Results: fieldListFromMap(m, node.Results), + } + case *ast.GenDecl: + decl := &ast.GenDecl{ + Doc: commentGroupFromMap(m, node.Doc), + TokPos: node.TokPos, + Tok: node.Tok, + Lparen: node.Lparen, + Rparen: node.Rparen, + } + if node.Specs != nil { + decl.Specs = make([]ast.Spec, len(node.Specs)) + for i := range node.Specs { + decl.Specs[i] = m[node.Specs[i]].(ast.Spec) + } + } + m[node] = decl + case *ast.GoStmt: + m[node] = &ast.GoStmt{ + Go: node.Go, + Call: callExprFromMap(m, node.Call), + } + case *ast.Ident: + // Keep identifiers the same identity so they can be conveniently + // used with the original *types.Info. + m[node] = node + case *ast.IfStmt: + m[node] = &ast.IfStmt{ + If: node.If, + Init: stmtFromMap(m, node.Init), + Cond: exprFromMap(m, node.Cond), + Body: blockStmtFromMap(m, node.Body), + Else: stmtFromMap(m, node.Else), + } + case *ast.ImportSpec: + m[node] = &ast.ImportSpec{ + Doc: commentGroupFromMap(m, node.Doc), + Name: identFromMap(m, node.Name), + Path: basicLitFromMap(m, node.Path), + Comment: commentGroupFromMap(m, node.Comment), + EndPos: node.EndPos, + } + case *ast.IncDecStmt: + m[node] = &ast.IncDecStmt{ + X: exprFromMap(m, node.X), + TokPos: node.TokPos, + Tok: node.Tok, + } + case *ast.IndexExpr: + m[node] = &ast.IndexExpr{ + X: exprFromMap(m, node.X), + Lbrack: node.Lbrack, + Index: exprFromMap(m, node.Index), + Rbrack: node.Rbrack, + } + case *ast.InterfaceType: + m[node] = &ast.InterfaceType{ + Interface: node.Interface, + Methods: fieldListFromMap(m, node.Methods), + Incomplete: node.Incomplete, + } + case *ast.KeyValueExpr: + m[node] = &ast.KeyValueExpr{ + Key: exprFromMap(m, node.Key), + Colon: node.Colon, + Value: exprFromMap(m, node.Value), + } + case *ast.LabeledStmt: + m[node] = &ast.LabeledStmt{ + Label: identFromMap(m, node.Label), + Colon: node.Colon, + Stmt: stmtFromMap(m, node.Stmt), + } + case *ast.MapType: + m[node] = &ast.MapType{ + Map: node.Map, + Key: exprFromMap(m, node.Key), + Value: exprFromMap(m, node.Value), + } + case *ast.ParenExpr: + m[node] = &ast.ParenExpr{ + Lparen: node.Lparen, + X: exprFromMap(m, node.X), + Rparen: node.Rparen, + } + case *ast.RangeStmt: + m[node] = &ast.RangeStmt{ + For: node.For, + Key: exprFromMap(m, node.Key), + Value: exprFromMap(m, node.Value), + TokPos: node.TokPos, + Tok: node.Tok, + X: exprFromMap(m, node.X), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.ReturnStmt: + m[node] = &ast.ReturnStmt{ + Return: node.Return, + Results: copyExprList(m, node.Results), + } + case *ast.SelectStmt: + m[node] = &ast.SelectStmt{ + Select: node.Select, + Body: blockStmtFromMap(m, node.Body), + } + case *ast.SelectorExpr: + m[node] = &ast.SelectorExpr{ + X: exprFromMap(m, node.X), + Sel: identFromMap(m, node.Sel), + } + case *ast.SendStmt: + m[node] = &ast.SendStmt{ + Chan: exprFromMap(m, node.Chan), + Arrow: node.Arrow, + Value: exprFromMap(m, node.Value), + } + case *ast.SliceExpr: + m[node] = &ast.SliceExpr{ + X: exprFromMap(m, node.X), + Lbrack: node.Lbrack, + Low: exprFromMap(m, node.Low), + High: exprFromMap(m, node.High), + Max: exprFromMap(m, node.Max), + Slice3: node.Slice3, + Rbrack: node.Rbrack, + } + case *ast.StarExpr: + m[node] = &ast.StarExpr{ + Star: node.Star, + X: exprFromMap(m, node.X), + } + case *ast.StructType: + m[node] = &ast.StructType{ + Struct: node.Struct, + Fields: fieldListFromMap(m, node.Fields), + Incomplete: node.Incomplete, + } + case *ast.SwitchStmt: + m[node] = &ast.SwitchStmt{ + Switch: node.Switch, + Init: stmtFromMap(m, node.Init), + Tag: exprFromMap(m, node.Tag), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.TypeAssertExpr: + m[node] = &ast.TypeAssertExpr{ + X: exprFromMap(m, node.X), + Lparen: node.Lparen, + Type: exprFromMap(m, node.Type), + Rparen: node.Rparen, + } + case *ast.TypeSpec: + m[node] = &ast.TypeSpec{ + Doc: commentGroupFromMap(m, node.Doc), + Name: identFromMap(m, node.Name), + Assign: node.Assign, + Type: exprFromMap(m, node.Type), + Comment: commentGroupFromMap(m, node.Comment), + } + case *ast.TypeSwitchStmt: + m[node] = &ast.TypeSwitchStmt{ + Switch: node.Switch, + Init: stmtFromMap(m, node.Init), + Assign: stmtFromMap(m, node.Assign), + Body: blockStmtFromMap(m, node.Body), + } + case *ast.UnaryExpr: + m[node] = &ast.UnaryExpr{ + OpPos: node.OpPos, + Op: node.Op, + X: exprFromMap(m, node.X), + } + case *ast.ValueSpec: + m[node] = &ast.ValueSpec{ + Doc: commentGroupFromMap(m, node.Doc), + Names: copyIdentList(m, node.Names), + Type: exprFromMap(m, node.Type), + Values: copyExprList(m, node.Values), + Comment: commentGroupFromMap(m, node.Comment), + } + default: + panic(fmt.Sprintf("unhandled AST node: %T", node)) + } + return true + }) + return m[original] +} + +func commentGroupFromMap(m map[ast.Node]ast.Node, key *ast.CommentGroup) *ast.CommentGroup { + if key == nil { + return nil + } + return m[key].(*ast.CommentGroup) +} + +func exprFromMap(m map[ast.Node]ast.Node, key ast.Expr) ast.Expr { + if key == nil { + return nil + } + return m[key].(ast.Expr) +} + +func stmtFromMap(m map[ast.Node]ast.Node, key ast.Stmt) ast.Stmt { + if key == nil { + return nil + } + return m[key].(ast.Stmt) +} + +func identFromMap(m map[ast.Node]ast.Node, key *ast.Ident) *ast.Ident { + if key == nil { + return nil + } + return m[key].(*ast.Ident) +} + +func blockStmtFromMap(m map[ast.Node]ast.Node, key *ast.BlockStmt) *ast.BlockStmt { + if key == nil { + return nil + } + return m[key].(*ast.BlockStmt) +} + +func fieldListFromMap(m map[ast.Node]ast.Node, key *ast.FieldList) *ast.FieldList { + if key == nil { + return nil + } + return m[key].(*ast.FieldList) +} + +func callExprFromMap(m map[ast.Node]ast.Node, key *ast.CallExpr) *ast.CallExpr { + if key == nil { + return nil + } + return m[key].(*ast.CallExpr) +} + +func basicLitFromMap(m map[ast.Node]ast.Node, key *ast.BasicLit) *ast.BasicLit { + if key == nil { + return nil + } + return m[key].(*ast.BasicLit) +} + +func funcTypeFromMap(m map[ast.Node]ast.Node, key *ast.FuncType) *ast.FuncType { + if key == nil { + return nil + } + return m[key].(*ast.FuncType) +} + +func copyExprList(m map[ast.Node]ast.Node, exprs []ast.Expr) []ast.Expr { + if exprs == nil { + return nil + } + newExprs := make([]ast.Expr, len(exprs)) + for i := range exprs { + newExprs[i] = m[exprs[i]].(ast.Expr) + } + return newExprs +} + +func copyStmtList(m map[ast.Node]ast.Node, stmts []ast.Stmt) []ast.Stmt { + if stmts == nil { + return nil + } + newStmts := make([]ast.Stmt, len(stmts)) + for i := range stmts { + newStmts[i] = m[stmts[i]].(ast.Stmt) + } + return newStmts +} + +func copyIdentList(m map[ast.Node]ast.Node, idents []*ast.Ident) []*ast.Ident { + if idents == nil { + return nil + } + newIdents := make([]*ast.Ident, len(idents)) + for i := range idents { + newIdents[i] = m[idents[i]].(*ast.Ident) + } + return newIdents +} diff --git a/pkg/build/wire/internal/wire/errors.go b/pkg/build/wire/internal/wire/errors.go new file mode 100644 index 00000000000..73c7d1f50e5 --- /dev/null +++ b/pkg/build/wire/internal/wire/errors.go @@ -0,0 +1,84 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire + +import ( + "go/token" +) + +// errorCollector manages a list of errors. The zero value is an empty list. +type errorCollector struct { + errors []error +} + +// add appends any non-nil errors to the collector. +func (ec *errorCollector) add(errs ...error) { + for _, e := range errs { + if e != nil { + ec.errors = append(ec.errors, e) + } + } +} + +// mapErrors returns a new slice that wraps any errors using the given function. +func mapErrors(errs []error, f func(error) error) []error { + if len(errs) == 0 { + return nil + } + newErrs := make([]error, len(errs)) + for i := range errs { + newErrs[i] = f(errs[i]) + } + return newErrs +} + +// A wireErr is an error with an optional position. +type wireErr struct { + error error + position token.Position +} + +// notePosition wraps an error with position information if it doesn't already +// have it. +// +// notePosition is usually called multiple times as an error goes up the call +// stack, so calling notePosition on an existing *wireErr will not modify the +// position, as the assumption is that deeper calls have more precise position +// information about the source of the error. +func notePosition(p token.Position, e error) error { + switch e.(type) { + case nil: + return nil + case *wireErr: + return e + default: + return &wireErr{error: e, position: p} + } +} + +// notePositionAll wraps a list of errors with the given position. +func notePositionAll(p token.Position, errs []error) []error { + return mapErrors(errs, func(e error) error { + return notePosition(p, e) + }) +} + +// Error returns the error message prefixed by the position if valid. +func (w *wireErr) Error() string { + if !w.position.IsValid() { + return w.error.Error() + } + return w.position.String() + ": " + w.error.Error() +} diff --git a/pkg/build/wire/internal/wire/parse.go b/pkg/build/wire/internal/wire/parse.go new file mode 100644 index 00000000000..3952e6fdaab --- /dev/null +++ b/pkg/build/wire/internal/wire/parse.go @@ -0,0 +1,1247 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire + +import ( + "context" + "errors" + "fmt" + "go/ast" + "go/token" + "go/types" + "os" + "reflect" + "strconv" + "strings" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/types/typeutil" +) + +// A providerSetSrc captures the source for a type provided by a ProviderSet. +// Exactly one of the fields will be set. +type providerSetSrc struct { + Provider *Provider + Binding *IfaceBinding + Value *Value + Import *ProviderSet + InjectorArg *InjectorArg + Field *Field +} + +// description returns a string describing the source of p, including line numbers. +func (p *providerSetSrc) description(fset *token.FileSet, typ types.Type) string { + quoted := func(s string) string { + if s == "" { + return "" + } + return fmt.Sprintf("%q ", s) + } + switch { + case p.Provider != nil: + kind := "provider" + if p.Provider.IsStruct { + kind = "struct provider" + } + return fmt.Sprintf("%s %s(%s)", kind, quoted(p.Provider.Name), fset.Position(p.Provider.Pos)) + case p.Binding != nil: + return fmt.Sprintf("wire.Bind (%s)", fset.Position(p.Binding.Pos)) + case p.Value != nil: + return fmt.Sprintf("wire.Value (%s)", fset.Position(p.Value.Pos)) + case p.Import != nil: + return fmt.Sprintf("provider set %s(%s)", quoted(p.Import.VarName), fset.Position(p.Import.Pos)) + case p.InjectorArg != nil: + args := p.InjectorArg.Args + return fmt.Sprintf("argument %s to injector function %s (%s)", args.Tuple.At(p.InjectorArg.Index).Name(), args.Name, fset.Position(args.Pos)) + case p.Field != nil: + return fmt.Sprintf("wire.FieldsOf (%s)", fset.Position(p.Field.Pos)) + } + panic("providerSetSrc with no fields set") +} + +// trace returns a slice of strings describing the (possibly recursive) source +// of p, including line numbers. +func (p *providerSetSrc) trace(fset *token.FileSet, typ types.Type) []string { + var retval []string + // Only Imports need recursion. + if p.Import != nil { + if parent := p.Import.srcMap.At(typ); parent != nil { + retval = append(retval, parent.(*providerSetSrc).trace(fset, typ)...) + } + } + retval = append(retval, p.description(fset, typ)) + return retval +} + +// A ProviderSet describes a set of providers. The zero value is an empty +// ProviderSet. +type ProviderSet struct { + // Pos is the position of the call to wire.NewSet or wire.Build that + // created the set. + Pos token.Pos + // PkgPath is the import path of the package that declared this set. + PkgPath string + // VarName is the variable name of the set, if it came from a package + // variable. + VarName string + + Providers []*Provider + Bindings []*IfaceBinding + Values []*Value + Fields []*Field + Imports []*ProviderSet + // InjectorArgs is only filled in for wire.Build. + InjectorArgs *InjectorArgs + + // providerMap maps from provided type to a *ProvidedType. + // It includes all of the imported types. + providerMap *typeutil.Map + + // srcMap maps from provided type to a *providerSetSrc capturing the + // Provider, Binding, Value, or Import that provided the type. + srcMap *typeutil.Map +} + +// Outputs returns a new slice containing the set of possible types the +// provider set can produce. The order is unspecified. +func (set *ProviderSet) Outputs() []types.Type { + return set.providerMap.Keys() +} + +// For returns a ProvidedType for the given type, or the zero ProvidedType. +func (set *ProviderSet) For(t types.Type) ProvidedType { + pt := set.providerMap.At(t) + if pt == nil { + return ProvidedType{} + } + return *pt.(*ProvidedType) +} + +// An IfaceBinding declares that a type should be used to satisfy inputs +// of the given interface type. +type IfaceBinding struct { + // Iface is the interface type, which is what can be injected. + Iface types.Type + + // Provided is always a type that is assignable to Iface. + Provided types.Type + + // Pos is the position where the binding was declared. + Pos token.Pos +} + +// Provider records the signature of a provider. A provider is a +// single Go object, either a function or a named struct type. +type Provider struct { + // Pkg is the package that the Go object resides in. + Pkg *types.Package + + // Name is the name of the Go object. + Name string + + // Pos is the source position of the func keyword or type spec + // defining this provider. + Pos token.Pos + + // Args is the list of data dependencies this provider has. + Args []ProviderInput + + // Varargs is true if the provider function is variadic. + Varargs bool + + // IsStruct is true if this provider is a named struct type. + // Otherwise it's a function. + IsStruct bool + + // Out is the set of types this provider produces. It will always + // contain at least one type. + Out []types.Type + + // HasCleanup reports whether the provider function returns a cleanup + // function. (Always false for structs.) + HasCleanup bool + + // HasErr reports whether the provider function can return an error. + // (Always false for structs.) + HasErr bool +} + +// ProviderInput describes an incoming edge in the provider graph. +type ProviderInput struct { + Type types.Type + + // If the provider is a struct, FieldName will be the field name to set. + FieldName string +} + +// Value describes a value expression. +type Value struct { + // Pos is the source position of the expression defining this value. + Pos token.Pos + + // Out is the type this value produces. + Out types.Type + + // expr is the expression passed to wire.Value. + expr ast.Expr + + // info is the type info for the expression. + info *types.Info +} + +// InjectorArg describes a specific argument passed to an injector function. +type InjectorArg struct { + // Args is the full set of arguments. + Args *InjectorArgs + // Index is the index into Args.Tuple for this argument. + Index int +} + +// InjectorArgs describes the arguments passed to an injector function. +type InjectorArgs struct { + // Name is the name of the injector function. + Name string + // Tuple represents the arguments. + Tuple *types.Tuple + // Pos is the source position of the injector function. + Pos token.Pos +} + +// Field describes a specific field selected from a struct. +type Field struct { + // Parent is the struct or pointer to the struct that the field belongs to. + Parent types.Type + // Name is the field name. + Name string + // Pkg is the package that the struct resides in. + Pkg *types.Package + // Pos is the source position of the field declaration. + // defining these fields. + Pos token.Pos + // Out is the field's provided types. The first element provides the + // field type. If the field is coming from a pointer to a struct, + // there will be a second element providing a pointer to the field. + Out []types.Type +} + +// Load finds all the provider sets in the packages that match the given +// patterns, as well as the provider sets' transitive dependencies. It +// may return both errors and Info. The patterns are defined by the +// underlying build system. For the go tool, this is described at +// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns +// +// wd is the working directory and env is the set of environment +// variables to use when loading the packages specified by patterns. If +// env is nil or empty, it is interpreted as an empty set of variables. +// In case of duplicate environment variables, the last one in the list +// takes precedence. +func Load(ctx context.Context, wd string, env []string, tags string, patterns []string) (*Info, []error) { + pkgs, errs := load(ctx, wd, env, tags, patterns) + if len(errs) > 0 { + return nil, errs + } + if len(pkgs) == 0 { + return new(Info), nil + } + fset := pkgs[0].Fset + info := &Info{ + Fset: fset, + Sets: make(map[ProviderSetID]*ProviderSet), + } + oc := newObjectCache(pkgs) + ec := new(errorCollector) + for _, pkg := range pkgs { + if isWireImport(pkg.PkgPath) { + // The marker function package confuses analysis. + continue + } + scope := pkg.Types.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + if !isProviderSetType(obj.Type()) { + continue + } + item, errs := oc.get(obj) + if len(errs) > 0 { + ec.add(notePositionAll(fset.Position(obj.Pos()), errs)...) + continue + } + pset := item.(*ProviderSet) + // pset.Name may not equal name, since it could be an alias to + // another provider set. + id := ProviderSetID{ImportPath: pset.PkgPath, VarName: name} + info.Sets[id] = pset + } + for _, f := range pkg.Syntax { + for _, decl := range f.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + buildCall, err := findInjectorBuild(pkg.TypesInfo, fn) + if err != nil { + ec.add(notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err))) + continue + } + if buildCall == nil { + continue + } + sig := pkg.TypesInfo.ObjectOf(fn.Name).Type().(*types.Signature) + ins, out, err := injectorFuncSignature(sig) + if err != nil { + if w, ok := err.(*wireErr); ok { + ec.add(notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error))) + } else { + ec.add(notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err))) + } + continue + } + injectorArgs := &InjectorArgs{ + Name: fn.Name.Name, + Tuple: ins, + Pos: fn.Pos(), + } + set, errs := oc.processNewSet(pkg.TypesInfo, pkg.PkgPath, buildCall, injectorArgs, "") + if len(errs) > 0 { + ec.add(notePositionAll(fset.Position(fn.Pos()), errs)...) + continue + } + _, errs = solve(fset, out.out, ins, set) + if len(errs) > 0 { + ec.add(mapErrors(errs, func(e error) error { + if w, ok := e.(*wireErr); ok { + return notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error)) + } + return notePosition(fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, e)) + })...) + continue + } + info.Injectors = append(info.Injectors, &Injector{ + ImportPath: pkg.PkgPath, + FuncName: fn.Name.Name, + }) + } + } + } + return info, ec.errors +} + +// load typechecks the packages that match the given patterns and +// includes source for all transitive dependencies. The patterns are +// defined by the underlying build system. For the go tool, this is +// described at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns +// +// wd is the working directory and env is the set of environment +// variables to use when loading the packages specified by patterns. If +// env is nil or empty, it is interpreted as an empty set of variables. +// In case of duplicate environment variables, the last one in the list +// takes precedence. +func load(ctx context.Context, wd string, env []string, tags string, patterns []string) ([]*packages.Package, []error) { + cfg := &packages.Config{ + Context: ctx, + Mode: packages.LoadAllSyntax, + Dir: wd, + Env: env, + BuildFlags: []string{"-tags=wireinject"}, + // TODO(light): Use ParseFile to skip function bodies and comments in indirect packages. + } + if len(tags) > 0 { + cfg.BuildFlags[0] += " " + tags + } + escaped := make([]string, len(patterns)) + for i := range patterns { + escaped[i] = "pattern=" + patterns[i] + } + pkgs, err := packages.Load(cfg, escaped...) + if err != nil { + return nil, []error{err} + } + var errs []error + for _, p := range pkgs { + for _, e := range p.Errors { + errs = append(errs, e) + } + } + if len(errs) > 0 { + return nil, errs + } + return pkgs, nil +} + +// Info holds the result of Load. +type Info struct { + Fset *token.FileSet + + // Sets contains all the provider sets in the initial packages. + Sets map[ProviderSetID]*ProviderSet + + // Injectors contains all the injector functions in the initial packages. + // The order is undefined. + Injectors []*Injector +} + +// A ProviderSetID identifies a named provider set. +type ProviderSetID struct { + ImportPath string + VarName string +} + +// String returns the ID as ""path/to/pkg".Foo". +func (id ProviderSetID) String() string { + return strconv.Quote(id.ImportPath) + "." + id.VarName +} + +// An Injector describes an injector function. +type Injector struct { + ImportPath string + FuncName string +} + +// String returns the injector name as ""path/to/pkg".Foo". +func (in *Injector) String() string { + return strconv.Quote(in.ImportPath) + "." + in.FuncName +} + +// objectCache is a lazily evaluated mapping of objects to Wire structures. +type objectCache struct { + fset *token.FileSet + packages map[string]*packages.Package + objects map[objRef]objCacheEntry + hasher typeutil.Hasher +} + +type objRef struct { + importPath string + name string +} + +type objCacheEntry struct { + val interface{} // *Provider, *ProviderSet, *IfaceBinding, or *Value + errs []error +} + +func newObjectCache(pkgs []*packages.Package) *objectCache { + if len(pkgs) == 0 { + panic("object cache must have packages to draw from") + } + oc := &objectCache{ + fset: pkgs[0].Fset, + packages: make(map[string]*packages.Package), + objects: make(map[objRef]objCacheEntry), + hasher: typeutil.MakeHasher(), + } + // Depth-first search of all dependencies to gather import path to + // packages.Package mapping. go/packages guarantees that for a single + // call to packages.Load and an import path X, there will exist only + // one *packages.Package value with PkgPath X. + stk := append([]*packages.Package(nil), pkgs...) + for len(stk) > 0 { + p := stk[len(stk)-1] + stk = stk[:len(stk)-1] + if oc.packages[p.PkgPath] != nil { + continue + } + oc.packages[p.PkgPath] = p + for _, imp := range p.Imports { + stk = append(stk, imp) + } + } + return oc +} + +// get converts a Go object into a Wire structure. It may return a *Provider, an +// *IfaceBinding, a *ProviderSet, a *Value, or a []*Field. +func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) { + ref := objRef{ + importPath: obj.Pkg().Path(), + name: obj.Name(), + } + if ent, cached := oc.objects[ref]; cached { + return ent.val, append([]error(nil), ent.errs...) + } + defer func() { + oc.objects[ref] = objCacheEntry{ + val: val, + errs: append([]error(nil), errs...), + } + }() + switch obj := obj.(type) { + case *types.Var: + spec := oc.varDecl(obj) + if spec == nil || len(spec.Values) == 0 { + return nil, []error{fmt.Errorf("%v is not a provider or a provider set", obj)} + } + var i int + for i = range spec.Names { + if spec.Names[i].Name == obj.Name() { + break + } + } + pkgPath := obj.Pkg().Path() + return oc.processExpr(oc.packages[pkgPath].TypesInfo, pkgPath, spec.Values[i], obj.Name()) + case *types.Func: + return processFuncProvider(oc.fset, obj) + default: + return nil, []error{fmt.Errorf("%v is not a provider or a provider set", obj)} + } +} + +// varDecl finds the declaration that defines the given variable. +func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec { + // TODO(light): Walk files to build object -> declaration mapping, if more performant. + // Recommended by https://golang.org/s/types-tutorial + pkg := oc.packages[obj.Pkg().Path()] + pos := obj.Pos() + for _, f := range pkg.Syntax { + tokenFile := oc.fset.File(f.Pos()) + if base := tokenFile.Base(); base <= int(pos) && int(pos) < base+tokenFile.Size() { + path, _ := astutil.PathEnclosingInterval(f, pos, pos) + for _, node := range path { + if spec, ok := node.(*ast.ValueSpec); ok { + return spec + } + } + } + } + return nil +} + +// processExpr converts an expression into a Wire structure. It may return a +// *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a []*Field. +func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Expr, varName string) (interface{}, []error) { + exprPos := oc.fset.Position(expr.Pos()) + expr = astutil.Unparen(expr) + if obj := qualifiedIdentObject(info, expr); obj != nil { + item, errs := oc.get(obj) + return item, mapErrors(errs, func(err error) error { + return notePosition(exprPos, err) + }) + } + if call, ok := expr.(*ast.CallExpr); ok { + fnObj := qualifiedIdentObject(info, call.Fun) + if fnObj == nil { + return nil, []error{notePosition(exprPos, errors.New("unknown pattern fnObj nil"))} + } + pkg := fnObj.Pkg() + if pkg == nil { + return nil, []error{notePosition(exprPos, fmt.Errorf("unknown pattern - pkg in fnObj is nil - %s", fnObj))} + } + if !isWireImport(pkg.Path()) { + return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))} + } + switch fnObj.Name() { + case "NewSet": + pset, errs := oc.processNewSet(info, pkgPath, call, nil, varName) + return pset, notePositionAll(exprPos, errs) + case "Bind": + b, err := processBind(oc.fset, info, call) + if err != nil { + return nil, []error{notePosition(exprPos, err)} + } + return b, nil + case "Value": + v, err := processValue(oc.fset, info, call) + if err != nil { + return nil, []error{notePosition(exprPos, err)} + } + return v, nil + case "InterfaceValue": + v, err := processInterfaceValue(oc.fset, info, call) + if err != nil { + return nil, []error{notePosition(exprPos, err)} + } + return v, nil + case "Struct": + s, err := processStructProvider(oc.fset, info, call) + if err != nil { + return nil, []error{notePosition(exprPos, err)} + } + return s, nil + case "FieldsOf": + v, err := processFieldsOf(oc.fset, info, call) + if err != nil { + return nil, []error{notePosition(exprPos, err)} + } + return v, nil + default: + return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))} + } + } + if tn := structArgType(info, expr); tn != nil { + p, errs := processStructLiteralProvider(oc.fset, tn) + if len(errs) > 0 { + return nil, notePositionAll(exprPos, errs) + } + return p, nil + } + return nil, []error{notePosition(exprPos, errors.New("unknown pattern"))} +} + +func (oc *objectCache) processNewSet(info *types.Info, pkgPath string, call *ast.CallExpr, args *InjectorArgs, varName string) (*ProviderSet, []error) { + // Assumes that call.Fun is wire.NewSet or wire.Build. + + pset := &ProviderSet{ + Pos: call.Pos(), + InjectorArgs: args, + PkgPath: pkgPath, + VarName: varName, + } + ec := new(errorCollector) + for _, arg := range call.Args { + item, errs := oc.processExpr(info, pkgPath, arg, "") + if len(errs) > 0 { + ec.add(errs...) + continue + } + switch item := item.(type) { + case *Provider: + pset.Providers = append(pset.Providers, item) + case *ProviderSet: + pset.Imports = append(pset.Imports, item) + case *IfaceBinding: + pset.Bindings = append(pset.Bindings, item) + case *Value: + pset.Values = append(pset.Values, item) + case []*Field: + pset.Fields = append(pset.Fields, item...) + default: + panic("unknown item type") + } + } + if len(ec.errors) > 0 { + return nil, ec.errors + } + var errs []error + pset.providerMap, pset.srcMap, errs = buildProviderMap(oc.fset, oc.hasher, pset) + if len(errs) > 0 { + return nil, errs + } + if errs := verifyAcyclic(pset.providerMap, oc.hasher); len(errs) > 0 { + return nil, errs + } + return pset, nil +} + +// structArgType attempts to interpret an expression as a simple struct type. +// It assumes any parentheses have been stripped. +func structArgType(info *types.Info, expr ast.Expr) *types.TypeName { + lit, ok := expr.(*ast.CompositeLit) + if !ok { + return nil + } + tn, ok := qualifiedIdentObject(info, lit.Type).(*types.TypeName) + if !ok { + return nil + } + if _, isStruct := tn.Type().Underlying().(*types.Struct); !isStruct { + return nil + } + return tn +} + +// qualifiedIdentObject finds the object for an identifier or a +// qualified identifier, or nil if the object could not be found. +func qualifiedIdentObject(info *types.Info, expr ast.Expr) types.Object { + switch expr := expr.(type) { + case *ast.Ident: + return info.ObjectOf(expr) + case *ast.SelectorExpr: + pkgName, ok := expr.X.(*ast.Ident) + if !ok { + return nil + } + if _, ok := info.ObjectOf(pkgName).(*types.PkgName); !ok { + return nil + } + return info.ObjectOf(expr.Sel) + default: + return nil + } +} + +// processFuncProvider creates a provider for a function declaration. +func processFuncProvider(fset *token.FileSet, fn *types.Func) (*Provider, []error) { + sig := fn.Type().(*types.Signature) + fpos := fn.Pos() + providerSig, err := funcOutput(sig) + if err != nil { + return nil, []error{notePosition(fset.Position(fpos), fmt.Errorf("wrong signature for provider %s: %v", fn.Name(), err))} + } + params := sig.Params() + provider := &Provider{ + Pkg: fn.Pkg(), + Name: fn.Name(), + Pos: fn.Pos(), + Args: make([]ProviderInput, params.Len()), + Varargs: sig.Variadic(), + Out: []types.Type{providerSig.out}, + HasCleanup: providerSig.cleanup, + HasErr: providerSig.err, + } + for i := 0; i < params.Len(); i++ { + provider.Args[i] = ProviderInput{ + Type: params.At(i).Type(), + } + for j := 0; j < i; j++ { + if types.Identical(provider.Args[i].Type, provider.Args[j].Type) { + return nil, []error{notePosition(fset.Position(fpos), fmt.Errorf("provider has multiple parameters of type %s", types.TypeString(provider.Args[j].Type, nil)))} + } + } + } + return provider, nil +} + +func injectorFuncSignature(sig *types.Signature) (*types.Tuple, outputSignature, error) { + out, err := funcOutput(sig) + if err != nil { + return nil, outputSignature{}, err + } + return sig.Params(), out, nil +} + +type outputSignature struct { + out types.Type + cleanup bool + err bool +} + +// funcOutput validates an injector or provider function's return signature. +func funcOutput(sig *types.Signature) (outputSignature, error) { + results := sig.Results() + switch results.Len() { + case 0: + return outputSignature{}, errors.New("no return values") + case 1: + return outputSignature{out: results.At(0).Type()}, nil + case 2: + out := results.At(0).Type() + switch t := results.At(1).Type(); { + case types.Identical(t, errorType): + return outputSignature{out: out, err: true}, nil + case types.Identical(t, cleanupType): + return outputSignature{out: out, cleanup: true}, nil + default: + return outputSignature{}, fmt.Errorf("second return type is %s; must be error or func()", types.TypeString(t, nil)) + } + case 3: + if t := results.At(1).Type(); !types.Identical(t, cleanupType) { + return outputSignature{}, fmt.Errorf("second return type is %s; must be func()", types.TypeString(t, nil)) + } + if t := results.At(2).Type(); !types.Identical(t, errorType) { + return outputSignature{}, fmt.Errorf("third return type is %s; must be error", types.TypeString(t, nil)) + } + return outputSignature{ + out: results.At(0).Type(), + cleanup: true, + err: true, + }, nil + default: + return outputSignature{}, errors.New("too many return values") + } +} + +// processStructLiteralProvider creates a provider for a named struct type. +// It produces pointer and non-pointer variants via two values in Out. +// +// This is a copy of the old processStructProvider, which is deprecated now. +// It will not support any new feature introduced after v0.2. Please use the new +// wire.Struct syntax for those. +func processStructLiteralProvider(fset *token.FileSet, typeName *types.TypeName) (*Provider, []error) { + out := typeName.Type() + st, ok := out.Underlying().(*types.Struct) + if !ok { + return nil, []error{fmt.Errorf("%v does not name a struct", typeName)} + } + + pos := typeName.Pos() + fmt.Fprintf(os.Stderr, + "Warning: %v, see https://godoc.org/github.com/google/wire#Struct for more information.\n", + notePosition(fset.Position(pos), + fmt.Errorf("using struct literal to inject %s is deprecated and will be removed in the next release; use wire.Struct instead", + typeName.Type()))) + provider := &Provider{ + Pkg: typeName.Pkg(), + Name: typeName.Name(), + Pos: pos, + Args: make([]ProviderInput, st.NumFields()), + IsStruct: true, + Out: []types.Type{out, types.NewPointer(out)}, + } + for i := 0; i < st.NumFields(); i++ { + f := st.Field(i) + provider.Args[i] = ProviderInput{ + Type: f.Type(), + FieldName: f.Name(), + } + for j := 0; j < i; j++ { + if types.Identical(provider.Args[i].Type, provider.Args[j].Type) { + return nil, []error{notePosition(fset.Position(pos), fmt.Errorf("provider struct has multiple fields of type %s", types.TypeString(provider.Args[j].Type, nil)))} + } + } + } + return provider, nil +} + +// processStructProvider creates a provider for a named struct type. +// It produces pointer and non-pointer variants via two values in Out. +func processStructProvider(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*Provider, error) { + // Assumes that call.Fun is wire.Struct. + + if len(call.Args) < 1 { + return nil, notePosition(fset.Position(call.Pos()), + errors.New("call to Struct must specify the struct to be injected")) + } + const firstArgReqFormat = "first argument to Struct must be a pointer to a named struct; found %s" + structType := info.TypeOf(call.Args[0]) + structPtr, ok := structType.(*types.Pointer) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf(firstArgReqFormat, types.TypeString(structType, nil))) + } + + st, ok := structPtr.Elem().Underlying().(*types.Struct) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf(firstArgReqFormat, types.TypeString(structPtr, nil))) + } + + stExpr := call.Args[0].(*ast.CallExpr) + typeName := qualifiedIdentObject(info, stExpr.Args[0]) // should be either an identifier or selector + provider := &Provider{ + Pkg: typeName.Pkg(), + Name: typeName.Name(), + Pos: typeName.Pos(), + IsStruct: true, + Out: []types.Type{structPtr.Elem(), structPtr}, + } + if allFields(call) { + for i := 0; i < st.NumFields(); i++ { + if isPrevented(st.Tag(i)) { + continue + } + f := st.Field(i) + provider.Args = append(provider.Args, ProviderInput{ + Type: f.Type(), + FieldName: f.Name(), + }) + } + } else { + provider.Args = make([]ProviderInput, len(call.Args)-1) + for i := 1; i < len(call.Args); i++ { + v, err := checkField(call.Args[i], st) + if err != nil { + return nil, notePosition(fset.Position(call.Pos()), err) + } + provider.Args[i-1] = ProviderInput{ + Type: v.Type(), + FieldName: v.Name(), + } + } + } + for i := 0; i < len(provider.Args); i++ { + for j := 0; j < i; j++ { + if types.Identical(provider.Args[i].Type, provider.Args[j].Type) { + f := st.Field(j) + return nil, notePosition(fset.Position(f.Pos()), fmt.Errorf("provider struct has multiple fields of type %s", types.TypeString(provider.Args[j].Type, nil))) + } + } + } + return provider, nil +} + +func allFields(call *ast.CallExpr) bool { + if len(call.Args) != 2 { + return false + } + b, ok := call.Args[1].(*ast.BasicLit) + if !ok { + return false + } + return strings.EqualFold(strconv.Quote("*"), b.Value) +} + +// isPrevented checks whether field i is prevented by tag "-". +// Since this is the only tag used by wire, we can do string comparison +// without using reflect. +func isPrevented(tag string) bool { + return reflect.StructTag(tag).Get("wire") == "-" +} + +// processBind creates an interface binding from a wire.Bind call. +func processBind(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*IfaceBinding, error) { + // Assumes that call.Fun is wire.Bind. + + if len(call.Args) != 2 { + return nil, notePosition(fset.Position(call.Pos()), + errors.New("call to Bind takes exactly two arguments")) + } + // TODO(light): Verify that arguments are simple expressions. + ifaceArgType := info.TypeOf(call.Args[0]) + ifacePtr, ok := ifaceArgType.(*types.Pointer) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf("first argument to Bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + } + iface := ifacePtr.Elem() + methodSet, ok := iface.Underlying().(*types.Interface) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf("first argument to Bind must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + } + + provided := info.TypeOf(call.Args[1]) + if bindShouldUsePointer(info, call) { + providedPtr, ok := provided.(*types.Pointer) + if !ok { + return nil, notePosition(fset.Position(call.Args[0].Pos()), + fmt.Errorf("second argument to Bind must be a pointer or a pointer to a pointer; found %s", types.TypeString(provided, nil))) + } + provided = providedPtr.Elem() + } + if types.Identical(iface, provided) { + return nil, notePosition(fset.Position(call.Pos()), + errors.New("cannot bind interface to itself")) + } + if !types.Implements(provided, methodSet) { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(iface, nil))) + } + return &IfaceBinding{ + Pos: call.Pos(), + Iface: iface, + Provided: provided, + }, nil +} + +// processValue creates a value from a wire.Value call. +func processValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*Value, error) { + // Assumes that call.Fun is wire.Value. + + if len(call.Args) != 1 { + return nil, notePosition(fset.Position(call.Pos()), errors.New("call to Value takes exactly one argument")) + } + ok := true + ast.Inspect(call.Args[0], func(node ast.Node) bool { + switch expr := node.(type) { + case nil, *ast.ArrayType, *ast.BasicLit, *ast.BinaryExpr, *ast.ChanType, *ast.CompositeLit, *ast.FuncType, *ast.Ident, *ast.IndexExpr, *ast.InterfaceType, *ast.KeyValueExpr, *ast.MapType, *ast.ParenExpr, *ast.SelectorExpr, *ast.SliceExpr, *ast.StarExpr, *ast.StructType, *ast.TypeAssertExpr: + // Good! + case *ast.UnaryExpr: + if expr.Op == token.ARROW { + ok = false + return false + } + case *ast.CallExpr: + // Only acceptable if it's a type conversion. + if _, isFunc := info.TypeOf(expr.Fun).(*types.Signature); isFunc { + ok = false + return false + } + default: + ok = false + return false + } + return true + }) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), errors.New("argument to Value is too complex")) + } + // Result type can't be an interface type; use wire.InterfaceValue for that. + argType := info.TypeOf(call.Args[0]) + if _, isInterfaceType := argType.Underlying().(*types.Interface); isInterfaceType { + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("argument to Value may not be an interface value (found %s); use InterfaceValue instead", types.TypeString(argType, nil))) + } + return &Value{ + Pos: call.Args[0].Pos(), + Out: info.TypeOf(call.Args[0]), + expr: call.Args[0], + info: info, + }, nil +} + +// processInterfaceValue creates a value from a wire.InterfaceValue call. +func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.CallExpr) (*Value, error) { + // Assumes that call.Fun is wire.InterfaceValue. + + if len(call.Args) != 2 { + return nil, notePosition(fset.Position(call.Pos()), errors.New("call to InterfaceValue takes exactly two arguments")) + } + ifaceArgType := info.TypeOf(call.Args[0]) + ifacePtr, ok := ifaceArgType.(*types.Pointer) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to InterfaceValue must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + } + iface := ifacePtr.Elem() + methodSet, ok := iface.Underlying().(*types.Interface) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("first argument to InterfaceValue must be a pointer to an interface type; found %s", types.TypeString(ifaceArgType, nil))) + } + provided := info.TypeOf(call.Args[1]) + if !types.Implements(provided, methodSet) { + return nil, notePosition(fset.Position(call.Pos()), fmt.Errorf("%s does not implement %s", types.TypeString(provided, nil), types.TypeString(iface, nil))) + } + return &Value{ + Pos: call.Args[1].Pos(), + Out: iface, + expr: call.Args[1], + info: info, + }, nil +} + +// processFieldsOf creates a slice of fields from a wire.FieldsOf call. +func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) ([]*Field, error) { + // Assumes that call.Fun is wire.FieldsOf. + + if len(call.Args) < 2 { + return nil, notePosition(fset.Position(call.Pos()), + errors.New("call to FieldsOf must specify fields to be extracted")) + } + const firstArgReqFormat = "first argument to FieldsOf must be a pointer to a struct or a pointer to a pointer to a struct; found %s" + structType := info.TypeOf(call.Args[0]) + structPtr, ok := structType.(*types.Pointer) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf(firstArgReqFormat, types.TypeString(structType, nil))) + } + + var struc *types.Struct + isPtrToStruct := false + switch t := structPtr.Elem().Underlying().(type) { + case *types.Pointer: + struc, ok = t.Elem().Underlying().(*types.Struct) + if !ok { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf(firstArgReqFormat, types.TypeString(struc, nil))) + } + isPtrToStruct = true + case *types.Struct: + struc = t + default: + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf(firstArgReqFormat, types.TypeString(t, nil))) + } + if struc.NumFields() < len(call.Args)-1 { + return nil, notePosition(fset.Position(call.Pos()), + fmt.Errorf("fields number exceeds the number available in the struct which has %d fields", struc.NumFields())) + } + + fields := make([]*Field, 0, len(call.Args)-1) + for i := 1; i < len(call.Args); i++ { + v, err := checkField(call.Args[i], struc) + if err != nil { + return nil, notePosition(fset.Position(call.Pos()), err) + } + out := []types.Type{v.Type()} + if isPtrToStruct { + // If the field is from a pointer to a struct, then + // wire.Fields also provides a pointer to the field. + out = append(out, types.NewPointer(v.Type())) + } + fields = append(fields, &Field{ + Parent: structPtr.Elem(), + Name: v.Name(), + Pkg: v.Pkg(), + Pos: v.Pos(), + Out: out, + }) + } + return fields, nil +} + +// checkField reports whether f is a field of st. f should be a string with the +// field name. +func checkField(f ast.Expr, st *types.Struct) (*types.Var, error) { + b, ok := f.(*ast.BasicLit) + if !ok { + return nil, fmt.Errorf("%v must be a string with the field name", f) + } + for i := 0; i < st.NumFields(); i++ { + if strings.EqualFold(strconv.Quote(st.Field(i).Name()), b.Value) { + if isPrevented(st.Tag(i)) { + return nil, fmt.Errorf("%s is prevented from injecting by wire", b.Value) + } + return st.Field(i), nil + } + } + return nil, fmt.Errorf("%s is not a field of %s", b.Value, st.String()) +} + +// findInjectorBuild returns the wire.Build call if fn is an injector template. +// It returns nil if the function is not an injector template. +func findInjectorBuild(info *types.Info, fn *ast.FuncDecl) (*ast.CallExpr, error) { + if fn.Body == nil { + return nil, nil + } + numStatements := 0 + invalid := false + var wireBuildCall *ast.CallExpr + for _, stmt := range fn.Body.List { + switch stmt := stmt.(type) { + case *ast.ExprStmt: + numStatements++ + if numStatements > 1 { + invalid = true + } + call, ok := stmt.X.(*ast.CallExpr) + if !ok { + continue + } + if qualifiedIdentObject(info, call.Fun) == types.Universe.Lookup("panic") { + if len(call.Args) != 1 { + continue + } + call, ok = call.Args[0].(*ast.CallExpr) + if !ok { + continue + } + } + buildObj := qualifiedIdentObject(info, call.Fun) + if buildObj == nil || buildObj.Pkg() == nil || !isWireImport(buildObj.Pkg().Path()) || buildObj.Name() != "Build" { + continue + } + wireBuildCall = call + case *ast.EmptyStmt: + // Do nothing. + case *ast.ReturnStmt: + // Allow the function to end in a return. + if numStatements == 0 { + return nil, nil + } + default: + invalid = true + } + + } + if wireBuildCall == nil { + return nil, nil + } + if invalid { + return nil, errors.New("a call to wire.Build indicates that this function is an injector, but injectors must consist of only the wire.Build call and an optional return") + } + return wireBuildCall, nil +} + +func isWireImport(path string) bool { + // TODO(light): This is depending on details of the current loader. + const vendorPart = "vendor/" + if i := strings.LastIndex(path, vendorPart); i != -1 && (i == 0 || path[i-1] == '/') { + path = path[i+len(vendorPart):] + } + return path == "github.com/grafana/grafana/pkg/build/wire" || path == "github.com/google/wire" +} + +func isProviderSetType(t types.Type) bool { + n, ok := t.(*types.Named) + if !ok { + return false + } + obj := n.Obj() + return obj.Pkg() != nil && isWireImport(obj.Pkg().Path()) && obj.Name() == "ProviderSet" +} + +// ProvidedType represents a type provided from a source. The source +// can be a *Provider (a provider function), a *Value (wire.Value), or an +// *InjectorArgs (arguments to the injector function). The zero value has +// none of the above, and returns true for IsNil. +type ProvidedType struct { + // t is the provided concrete type. + t types.Type + p *Provider + v *Value + a *InjectorArg + f *Field +} + +// IsNil reports whether pt is the zero value. +func (pt ProvidedType) IsNil() bool { + return pt.p == nil && pt.v == nil && pt.a == nil && pt.f == nil +} + +// Type returns the output type. +// +// - For a function provider, this is the first return value type. +// - For a struct provider, this is either the struct type or the pointer type +// whose element type is the struct type. +// - For a value, this is the type of the expression. +// - For an argument, this is the type of the argument. +func (pt ProvidedType) Type() types.Type { + return pt.t +} + +// IsProvider reports whether pt points to a Provider. +func (pt ProvidedType) IsProvider() bool { + return pt.p != nil +} + +// IsValue reports whether pt points to a Value. +func (pt ProvidedType) IsValue() bool { + return pt.v != nil +} + +// IsArg reports whether pt points to an injector argument. +func (pt ProvidedType) IsArg() bool { + return pt.a != nil +} + +// IsField reports whether pt points to a Fields. +func (pt ProvidedType) IsField() bool { + return pt.f != nil +} + +// Provider returns pt as a Provider pointer. It panics if pt does not point +// to a Provider. +func (pt ProvidedType) Provider() *Provider { + if pt.p == nil { + panic("ProvidedType does not hold a Provider") + } + return pt.p +} + +// Value returns pt as a Value pointer. It panics if pt does not point +// to a Value. +func (pt ProvidedType) Value() *Value { + if pt.v == nil { + panic("ProvidedType does not hold a Value") + } + return pt.v +} + +// Arg returns pt as an *InjectorArg representing an injector argument. It +// panics if pt does not point to an arg. +func (pt ProvidedType) Arg() *InjectorArg { + if pt.a == nil { + panic("ProvidedType does not hold an Arg") + } + return pt.a +} + +// Field returns pt as a Field pointer. It panics if pt does not point to a +// struct Field. +func (pt ProvidedType) Field() *Field { + if pt.f == nil { + panic("ProvidedType does not hold a Field") + } + return pt.f +} + +// bindShouldUsePointer loads the wire package the user is importing from their +// injector. The call is a wire marker function call. +func bindShouldUsePointer(info *types.Info, call *ast.CallExpr) bool { + // These type assertions should not fail, otherwise panic. + fun := call.Fun.(*ast.SelectorExpr) // wire.Bind + pkgName := fun.X.(*ast.Ident) // wire + wireName := info.ObjectOf(pkgName).(*types.PkgName) // wire package + return wireName.Imported().Scope().Lookup("bindToUsePointer") != nil +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/foo.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/foo.go new file mode 100644 index 00000000000..5e39698c6cf --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/foo.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(inject(Foo{"hello"}).Name) +} + +type Fooer interface { + Foo() string +} + +type Foo struct { + f string +} + +func (f Foo) Foo() string { + return f.f +} + +type Bar struct { + Name string +} + +func NewBar(fooer Fooer) *Bar { + return &Bar{Name: fooer.Foo()} +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go new file mode 100644 index 00000000000..bd7d773b79b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. +// +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject(foo Foo) *Bar { + wire.Build( + NewBar, + wire.Bind(new(Fooer), new(Foo)), + ) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/pkg b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/program_out.txt new file mode 100644 index 00000000000..ce013625030 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/program_out.txt @@ -0,0 +1 @@ +hello diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go new file mode 100644 index 00000000000..5f5b21068ef --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func inject(foo Foo) *Bar { + bar := NewBar(foo) + return bar +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/foo.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/foo.go new file mode 100644 index 00000000000..d8b1edd1c1c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/foo.go @@ -0,0 +1,43 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(inject(&Foo{"hello"}).Name) +} + +type Fooer interface { + Foo() string +} + +type Foo struct { + f string +} + +func (f *Foo) Foo() string { + return f.f +} + +type Bar struct { + Name string +} + +func NewBar(fooer Fooer) *Bar { + return &Bar{Name: fooer.Foo()} +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go new file mode 100644 index 00000000000..3fc9ca38046 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. +// +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject(foo *Foo) *Bar { + wire.Build( + NewBar, + wire.Bind(new(Fooer), new(*Foo)), + ) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/pkg b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt new file mode 100644 index 00000000000..ce013625030 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/program_out.txt @@ -0,0 +1 @@ +hello diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go new file mode 100644 index 00000000000..15d6f0e335b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func inject(foo *Foo) *Bar { + bar := NewBar(foo) + return bar +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/foo.go new file mode 100644 index 00000000000..82381664e52 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/foo.go @@ -0,0 +1,20 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 + +func main() { + w := inject() + w.Write([]byte("Hello, World!")) +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go new file mode 100644 index 00000000000..54c19ba97b0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. +// +//+build wireinject + +package main + +import ( + "io" + "os" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject() io.Writer { + wire.Build( + wire.Value(os.Stdout), + wire.Bind(new(io.Writer), new(*os.File)), + ) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/pkg b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/program_out.txt new file mode 100644 index 00000000000..b45ef6fec89 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go new file mode 100644 index 00000000000..58d82bf4006 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go @@ -0,0 +1,23 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "io" + "os" +) + +// Injectors from wire.go: + +func inject() io.Writer { + file := _wireFileValue + return file +} + +var ( + _wireFileValue = os.Stdout +) diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go new file mode 100644 index 00000000000..97c2561ba9d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build !wireinject + +// Package bar includes both wireinject and non-wireinject variants. +package bar + +import "github.com/grafana/grafana/pkg/build/wire" + +// Set provides an unfriendly user greeting. +var Set = wire.NewSet(wire.Value("Bah humbug! This is the wrong variant!")) diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go new file mode 100644 index 00000000000..aa40270918c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go @@ -0,0 +1,22 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package bar + +import "github.com/grafana/grafana/pkg/build/wire" + +// Set provides a friendly user greeting. +var Set = wire.NewSet(wire.Value("Hello, World!")) diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/foo.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go new file mode 100644 index 00000000000..32cc0f80743 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(bar.Set) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/pkg b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go new file mode 100644 index 00000000000..16c0df257c2 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := _wireStringValue + return string2 +} + +var ( + _wireStringValue = "Hello, World!" +) diff --git a/pkg/build/wire/internal/wire/testdata/Chain/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Chain/foo/foo.go new file mode 100644 index 00000000000..16942364d6f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Chain/foo/foo.go @@ -0,0 +1,40 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type FooBar int + +var Set = wire.NewSet( + provideFoo, + provideFooBar) + +func provideFoo() Foo { + return 41 +} + +func provideFooBar(foo Foo) FooBar { + return FooBar(foo) + 1 +} diff --git a/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go new file mode 100644 index 00000000000..4106dd8d91f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/Chain/pkg b/pkg/build/wire/internal/wire/testdata/Chain/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Chain/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Chain/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/Chain/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Chain/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go new file mode 100644 index 00000000000..cf9e9eeaaea --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go @@ -0,0 +1,15 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() FooBar { + foo := provideFoo() + fooBar := provideFooBar(foo) + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/foo.go new file mode 100644 index 00000000000..56f02151161 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/foo.go @@ -0,0 +1,46 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + bar, cleanup := injectBar() + fmt.Println(*bar) + cleanup() + fmt.Println(*bar) +} + +type Foo int +type Bar int + +func provideFoo() (*Foo, func()) { + foo := new(Foo) + *foo = 42 + return foo, func() { *foo = 0 } +} + +func provideBar(foo *Foo) (*Bar, func()) { + bar := new(Bar) + *bar = 77 + return bar, func() { + if *foo == 0 { + panic("foo cleaned up before bar") + } + *bar = 0 + } +} diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go new file mode 100644 index 00000000000..49867e8c1b1 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectBar() (*Bar, func()) { + wire.Build(provideFoo, provideBar) + return nil, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/pkg b/pkg/build/wire/internal/wire/testdata/Cleanup/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/Cleanup/want/program_out.txt new file mode 100644 index 00000000000..d77064277c0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/want/program_out.txt @@ -0,0 +1,2 @@ +77 +0 diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go new file mode 100644 index 00000000000..1519c104c52 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectBar() (*Bar, func()) { + foo, cleanup := provideFoo() + bar, cleanup2 := provideBar(foo) + return bar, func() { + cleanup2() + cleanup() + } +} diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go new file mode 100644 index 00000000000..e8045b254e8 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go @@ -0,0 +1,40 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +// All of the declarations are in one file. +// Wire should copy non-injectors over, preserving imports. + +package main + +import ( + "fmt" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectedMessage()) +} + +// provideMessage provides a friendly user greeting. +func provideMessage() string { + return "Hello, World!" +} + +func injectedMessage() string { + wire.Build(provideMessage) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/pkg b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go new file mode 100644 index 00000000000..c654540b4ed --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go @@ -0,0 +1,29 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "fmt" +) + +// Injectors from foo.go: + +func injectedMessage() string { + string2 := provideMessage() + return string2 +} + +// foo.go: + +func main() { + fmt.Println(injectedMessage()) +} + +// provideMessage provides a friendly user greeting. +func provideMessage() string { + return "Hello, World!" +} diff --git a/pkg/build/wire/internal/wire/testdata/Cycle/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Cycle/foo/foo.go new file mode 100644 index 00000000000..be718f78a76 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cycle/foo/foo.go @@ -0,0 +1,37 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedBaz()) +} + +type Foo int +type Bar int +type Baz int + +func provideFoo(_ Baz) Foo { + return 0 +} + +func provideBar(_ Foo) Bar { + return 0 +} + +func provideBaz(_ Bar) Baz { + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go new file mode 100644 index 00000000000..b89cdff87d2 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedBaz() Baz { + wire.Build(provideFoo, provideBar, provideBaz) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/Cycle/pkg b/pkg/build/wire/internal/wire/testdata/Cycle/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cycle/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Cycle/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/Cycle/want/wire_errs.txt new file mode 100644 index 00000000000..eed476641c8 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Cycle/want/wire_errs.txt @@ -0,0 +1,5 @@ +example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar: +example.com/foo.Bar (example.com/foo.provideBar) -> +example.com/foo.Foo (example.com/foo.provideFoo) -> +example.com/foo.Baz (example.com/foo.provideBaz) -> +example.com/foo.Bar \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/foo/foo.go b/pkg/build/wire/internal/wire/testdata/DocComment/foo/foo.go new file mode 100644 index 00000000000..c545d3fa414 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/DocComment/foo/foo.go @@ -0,0 +1,28 @@ +// Copyright 2020 The Wire Authors +// +// 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 +// +// https://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" +) + +type ( + Bar struct{} + Foo struct{} +) + +func main() { + fmt.Println("Hello, World") +} diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go b/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go new file mode 100644 index 00000000000..9a17664592d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go @@ -0,0 +1,31 @@ +// Copyright 2020 The Wire Authors +// +// 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 +// +// https://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. +// +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +/* blockComment returns Foo and has a /*- style doc comment */ +func blockComment() *Foo { + panic(wire.Build(wire.Struct(new(Foo)))) +} + +// lineComment returns Bar and has a //- style doc comment +func lineComment() *Bar { + panic(wire.Build(wire.Struct(new(Bar)))) +} diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/pkg b/pkg/build/wire/internal/wire/testdata/DocComment/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/DocComment/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/DocComment/want/program_out.txt new file mode 100644 index 00000000000..3fa0d4b9828 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/DocComment/want/program_out.txt @@ -0,0 +1 @@ +Hello, World diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go new file mode 100644 index 00000000000..3efb22ac5de --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go @@ -0,0 +1,21 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +/* blockComment returns Foo and has a /*- style doc comment */ +func blockComment() *Foo { + foo := &Foo{} + return foo +} + +// lineComment returns Bar and has a //- style doc comment +func lineComment() *Bar { + bar := &Bar{} + return bar +} diff --git a/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/foo.go b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/foo.go new file mode 100644 index 00000000000..24dae647591 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/foo.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectedMessage()) +} + +var myFakeSet struct{} diff --git a/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go new file mode 100644 index 00000000000..f1db1a4db8f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(myFakeSet) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/EmptyVar/pkg b/pkg/build/wire/internal/wire/testdata/EmptyVar/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/EmptyVar/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/EmptyVar/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/EmptyVar/want/wire_errs.txt new file mode 100644 index 00000000000..d7e1d21dbeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/EmptyVar/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: var example.com/foo.myFakeSet struct{} is not a provider or a provider set \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/foo.go new file mode 100644 index 00000000000..555ef60c621 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/foo.go @@ -0,0 +1,120 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +// This test demonstrates how to use mocks with wire. + +package main + +import ( + "fmt" + "time" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + // Create a "real" greeter. + // Greet() will include the real current time, so elide it for repeatable + // tests. + fmt.Printf("Real time greeting: %s [current time elided]\n", initApp().Greet()[0:15]) + + // There are two approaches for creating an app with mocks. + + // Approach A: create the mocks manually, and pass them to an injector. + // This approach is useful if you need to prime the mocks beforehand. + fmt.Println("Approach A") + mt := newMockTimer() + mockedApp := initMockedAppFromArgs(mt) + fmt.Println(mockedApp.Greet()) // prints greeting with time = zero time + mt.T = mt.T.AddDate(1999, 0, 0) + fmt.Println(mockedApp.Greet()) // prints greeting with time = year 2000 + + // Approach B: allow the injector to create the mocks, and return a struct + // that includes the resulting app plus the mocks. + fmt.Println("Approach B") + appWithMocks := initMockedApp() + fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = zero time + appWithMocks.mt.T = appWithMocks.mt.T.AddDate(999, 0, 0) + fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = year 1000 +} + +// appSet is a provider set for creating a real app. +var appSet = wire.NewSet( + wire.Struct(new(app), "*"), + wire.Struct(new(greeter), "*"), + wire.InterfaceValue(new(timer), realTime{}), +) + +// appSetWithoutMocks is a provider set for creating an app with mocked +// dependencies. The mocked dependencies are omitted and must be provided as +// arguments to the injector. +// It is used for Approach A. +var appSetWithoutMocks = wire.NewSet( + wire.Struct(new(app), "*"), + wire.Struct(new(greeter), "*"), +) + +// mockAppSet is a provider set for creating a mocked app, including the mocked +// dependencies. +// It is used for Approach B. +var mockAppSet = wire.NewSet( + wire.Struct(new(app), "*"), + wire.Struct(new(greeter), "*"), + wire.Struct(new(appWithMocks), "*"), + // For each mocked dependency, add a provider and use wire.Bind to bind + // the concrete type to the relevant interface. + newMockTimer, + wire.Bind(new(timer), new(*mockTimer)), +) + +type timer interface { + Now() time.Time +} + +// realTime implements timer with the real time. +type realTime struct{} + +func (realTime) Now() time.Time { return time.Now() } + +// mockTimer implements timer using a mocked time. +type mockTimer struct { + T time.Time +} + +func newMockTimer() *mockTimer { return &mockTimer{} } +func (m *mockTimer) Now() time.Time { return m.T } + +// greeter issues greetings with the time provided by T. +type greeter struct { + T timer +} + +func (g greeter) Greet() string { + return fmt.Sprintf("Good day! It is %v", g.T.Now()) +} + +type app struct { + g greeter +} + +func (a app) Greet() string { + return a.g.Greet() +} + +// appWithMocks is used for Approach B, to return the app plus its mocked +// dependencies. +type appWithMocks struct { + app app + mt *mockTimer +} diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go new file mode 100644 index 00000000000..bdec16a6806 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +// initApp returns a real app. +func initApp() *app { + wire.Build(appSet) + return nil +} + +// initMockedAppFromArgs returns an app with mocked dependencies provided via +// arguments (Approach A). Note that the argument's type is the interface +// type (timer), but the concrete mock type should be passed. +func initMockedAppFromArgs(mt timer) *app { + wire.Build(appSetWithoutMocks) + return nil +} + +// initMockedApp returns an app with its mocked dependencies, created +// via providers (Approach B). +func initMockedApp() *appWithMocks { + wire.Build(mockAppSet) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/pkg b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/program_out.txt new file mode 100644 index 00000000000..8f74a8e69bf --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/program_out.txt @@ -0,0 +1,7 @@ +Real time greeting: Good day! It is [current time elided] +Approach A +Good day! It is 0001-01-01 00:00:00 +0000 UTC +Good day! It is 2000-01-01 00:00:00 +0000 UTC +Approach B +Good day! It is 0001-01-01 00:00:00 +0000 UTC +Good day! It is 1000-01-01 00:00:00 +0000 UTC diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go new file mode 100644 index 00000000000..9cb7b01cb12 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go @@ -0,0 +1,55 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +// initApp returns a real app. +func initApp() *app { + mainTimer := _wireRealTimeValue + mainGreeter := greeter{ + T: mainTimer, + } + mainApp := &app{ + g: mainGreeter, + } + return mainApp +} + +var ( + _wireRealTimeValue = realTime{} +) + +// initMockedAppFromArgs returns an app with mocked dependencies provided via +// arguments (Approach A). Note that the argument's type is the interface +// type (timer), but the concrete mock type should be passed. +func initMockedAppFromArgs(mt timer) *app { + mainGreeter := greeter{ + T: mt, + } + mainApp := &app{ + g: mainGreeter, + } + return mainApp +} + +// initMockedApp returns an app with its mocked dependencies, created +// via providers (Approach B). +func initMockedApp() *appWithMocks { + mainMockTimer := newMockTimer() + mainGreeter := greeter{ + T: mainMockTimer, + } + mainApp := app{ + g: mainGreeter, + } + mainAppWithMocks := &appWithMocks{ + app: mainApp, + mt: mainMockTimer, + } + return mainAppWithMocks +} diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/bar/bar.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/bar/bar.go new file mode 100644 index 00000000000..c003b9d97a9 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/bar/bar.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +import "github.com/grafana/grafana/pkg/build/wire" + +var Value = wire.Value(PublicMsg) + +var PublicMsg = "Hello, World!" diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go new file mode 100644 index 00000000000..46f7af5bf5f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(bar.Value) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/pkg b/pkg/build/wire/internal/wire/testdata/ExportedValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go new file mode 100644 index 00000000000..7563ca26af5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go @@ -0,0 +1,22 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/bar" +) + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := _wireStringValue + return string2 +} + +var ( + _wireStringValue = bar.PublicMsg +) diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/bar/bar.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/bar/bar.go new file mode 100644 index 00000000000..a2368e8b199 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/bar/bar.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +import ( + "os" + + "github.com/grafana/grafana/pkg/build/wire" +) + +var Value = wire.Value(os.Stdout) diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/foo.go new file mode 100644 index 00000000000..50f40a18721 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Fprintln(injectedFile(), "Hello, World!") +} diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go new file mode 100644 index 00000000000..1da10a3455b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "os" + + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedFile() *os.File { + wire.Build(bar.Value) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/pkg b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go new file mode 100644 index 00000000000..d8c74d4cdb0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go @@ -0,0 +1,22 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "os" +) + +// Injectors from wire.go: + +func injectedFile() *os.File { + file := _wireFileValue + return file +} + +var ( + _wireFileValue = os.Stdout +) diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/foo.go new file mode 100644 index 00000000000..2f4b61bb1b3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/foo.go @@ -0,0 +1,38 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectedBaz()) +} + +type Foo int +type Baz int + +type Bar struct { + Bz Baz +} + +func provideFoo(_ Baz) Foo { + return 0 +} + +func provideBar(_ Foo) Bar { + return Bar{} +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go new file mode 100644 index 00000000000..b49dc720a42 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedBaz() Baz { + wire.Build(provideFoo, provideBar, wire.FieldsOf(new(Bar), "Bz")) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/want/wire_errs.txt new file mode 100644 index 00000000000..de958768592 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/want/wire_errs.txt @@ -0,0 +1,5 @@ +example.com/foo/wire.go:x:y: cycle for example.com/foo.Bar: +example.com/foo.Bar (example.com/foo.provideBar) -> +example.com/foo.Foo (example.com/foo.provideFoo) -> +example.com/foo.Baz (example.com/foo.Bar.Bz) -> +example.com/foo.Bar \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/bar/bar.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/bar/bar.go new file mode 100644 index 00000000000..c7cec395a6d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/bar/bar.go @@ -0,0 +1,32 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 bar + +import ( + "example.com/foo" +) + +type Config struct { + V int +} + +type Service struct { + Cfg *Config + F *foo.Service +} + +func New(cfg *Config, f *foo.Service) *Service { + return &Service{Cfg: cfg, F: f} +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/baz/baz.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/baz/baz.go new file mode 100644 index 00000000000..c573fe95c97 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/baz/baz.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 baz + +import ( + "fmt" + + "example.com/bar" + "example.com/foo" +) + +type Config struct { + Foo *foo.Config + Bar *bar.Config +} + +type Service struct { + Foo *foo.Service + Bar *bar.Service +} + +func (m *Service) String() string { + return fmt.Sprintf("%d %d", m.Foo.Cfg.V, m.Bar.Cfg.V) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/foo/foo.go new file mode 100644 index 00000000000..73afd912c06 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 foo + +type Config struct { + V int +} + +type Service struct { + Cfg *Config +} + +func New(cfg *Config) *Service { + return &Service{Cfg: cfg} +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go new file mode 100644 index 00000000000..15646542ee2 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "fmt" + + "example.com/bar" + "example.com/baz" + "example.com/foo" + "github.com/grafana/grafana/pkg/build/wire" +) + +func newBazService(*baz.Config) *baz.Service { + wire.Build( + wire.Struct(new(baz.Service), "*"), + wire.FieldsOf( + new(*baz.Config), + "Foo", + "Bar", + ), + foo.New, + bar.New, + ) + return nil +} + +func main() { + cfg := &baz.Config{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + } + svc := newBazService(cfg) + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/pkg new file mode 100644 index 00000000000..b7badd92db3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/pkg @@ -0,0 +1 @@ +example.com/main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/program_out.txt new file mode 100644 index 00000000000..8d04f961a03 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/program_out.txt @@ -0,0 +1 @@ +1 2 diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go new file mode 100644 index 00000000000..8467ee37d89 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go @@ -0,0 +1,39 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/bar" + "example.com/baz" + "example.com/foo" + "fmt" +) + +// Injectors from wire.go: + +func newBazService(config *baz.Config) *baz.Service { + fooConfig := config.Foo + service := foo.New(fooConfig) + barConfig := config.Bar + barService := bar.New(barConfig, service) + bazService := &baz.Service{ + Foo: service, + Bar: barService, + } + return bazService +} + +// wire.go: + +func main() { + cfg := &baz.Config{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + } + svc := newBazService(cfg) + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/foo.go new file mode 100644 index 00000000000..09e0eb062f6 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/foo.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +type S struct { + Foo string +} + +func provideS() S { + return S{Foo: "Hello, World!"} +} + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go new file mode 100644 index 00000000000..7b35bd86b56 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build( + provideS, + wire.FieldsOf(new(S), "Foo")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go new file mode 100644 index 00000000000..5b8ca2f3c07 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go @@ -0,0 +1,15 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + s := provideS() + string2 := s.Foo + return string2 +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go new file mode 100644 index 00000000000..fee0f71a0ca --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/foo.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" + +type S struct { + Foo string +} + +func provideS() S { + return S{Foo: "Hello, World!"} +} + +func main() { + fmt.Println("pointer to " + *injectedMessagePtr()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go new file mode 100644 index 00000000000..271ee817094 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go @@ -0,0 +1,32 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessagePtr() *string { + // This shouldn't work; FieldsOf provides a pointer to the + // field only when the struct type is a pointer to a struct. + // See FieldsOfStructPointer for a working example using + // a pointer to a struct. + wire.Build( + provideS, + wire.FieldsOf(new(S), "Foo")) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt new file mode 100644 index 00000000000..deee37e9c9c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectedMessagePtr: no provider found for *string, output of injector \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go new file mode 100644 index 00000000000..213d871fee6 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/foo.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +type S struct { + Foo string +} + +func provideS() *S { + return &S{Foo: "Hello, World!"} +} + +func main() { + fmt.Println(injectedMessage()) + fmt.Println("pointer to " + *injectedMessagePtr()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go new file mode 100644 index 00000000000..04ebb253f10 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build( + provideS, + wire.FieldsOf(new(*S), "Foo")) + return "" +} + +func injectedMessagePtr() *string { + wire.Build( + provideS, + wire.FieldsOf(new(*S), "Foo")) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt new file mode 100644 index 00000000000..82baaaf71ab --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/program_out.txt @@ -0,0 +1,2 @@ +Hello, World! +pointer to Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go new file mode 100644 index 00000000000..c9fe7955491 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go @@ -0,0 +1,21 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + s := provideS() + string2 := s.Foo + return string2 +} + +func injectedMessagePtr() *string { + s := provideS() + string2 := &s.Foo + return string2 +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/bar/bar.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/bar/bar.go new file mode 100644 index 00000000000..c7cec395a6d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/bar/bar.go @@ -0,0 +1,32 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 bar + +import ( + "example.com/foo" +) + +type Config struct { + V int +} + +type Service struct { + Cfg *Config + F *foo.Service +} + +func New(cfg *Config, f *foo.Service) *Service { + return &Service{Cfg: cfg, F: f} +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/baz/baz.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/baz/baz.go new file mode 100644 index 00000000000..c573fe95c97 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/baz/baz.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 baz + +import ( + "fmt" + + "example.com/bar" + "example.com/foo" +) + +type Config struct { + Foo *foo.Config + Bar *bar.Config +} + +type Service struct { + Foo *foo.Service + Bar *bar.Service +} + +func (m *Service) String() string { + return fmt.Sprintf("%d %d", m.Foo.Cfg.V, m.Bar.Cfg.V) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/foo/foo.go new file mode 100644 index 00000000000..73afd912c06 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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 foo + +type Config struct { + V int +} + +type Service struct { + Cfg *Config +} + +func New(cfg *Config) *Service { + return &Service{Cfg: cfg} +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go new file mode 100644 index 00000000000..d8ccb5caaeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "fmt" + + "example.com/bar" + "example.com/baz" + "example.com/foo" + "github.com/grafana/grafana/pkg/build/wire" +) + +func newBazService() *baz.Service { + wire.Build( + wire.Struct(new(baz.Service), "*"), + wire.Value(&baz.Config{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + }), + wire.FieldsOf( + new(*baz.Config), + "Foo", + "Bar", + ), + foo.New, + bar.New, + ) + return nil +} + +func main() { + svc := newBazService() + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/pkg b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/pkg new file mode 100644 index 00000000000..b7badd92db3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/pkg @@ -0,0 +1 @@ +example.com/main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/program_out.txt new file mode 100644 index 00000000000..8d04f961a03 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/program_out.txt @@ -0,0 +1 @@ +1 2 diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go new file mode 100644 index 00000000000..793c04d6b21 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go @@ -0,0 +1,43 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/bar" + "example.com/baz" + "example.com/foo" + "fmt" +) + +// Injectors from wire.go: + +func newBazService() *baz.Service { + config := _wireConfigValue + fooConfig := config.Foo + service := foo.New(fooConfig) + barConfig := config.Bar + barService := bar.New(barConfig, service) + bazService := &baz.Service{ + Foo: service, + Bar: barService, + } + return bazService +} + +var ( + _wireConfigValue = &baz.Config{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + } +) + +// wire.go: + +func main() { + svc := newBazService() + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/foo.go b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/foo.go new file mode 100644 index 00000000000..c8af9b268a4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + bar := injectBar(func() *Foo { return &Foo{42} }) + fmt.Println(bar.Name) +} + +type Foo struct { + Val int +} + +type Bar struct { + Name string +} + +func NewBar(f *Foo) *Bar { + return &Bar{Name: fmt.Sprintf("foo value %d", f.Val)} +} diff --git a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go new file mode 100644 index 00000000000..ae1011bd26c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectBar(fn func() *Foo) *Bar { + // fails because it doesn't identify fn as a Provider; see #723. + panic(wire.Build(fn, NewBar)) +} diff --git a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/pkg b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/want/wire_errs.txt new file mode 100644 index 00000000000..7155e1426a0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: var fn func() *example.com/foo.Foo is not a provider or a provider set \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/Header/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Header/foo/foo.go new file mode 100644 index 00000000000..67bc8a3e332 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/foo/foo.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectFoo()) +} + +type Foo int + +func provideFoo() Foo { + return 41 +} diff --git a/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go new file mode 100644 index 00000000000..4ad2705fc59 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() Foo { + wire.Build(provideFoo) + return Foo(0) +} diff --git a/pkg/build/wire/internal/wire/testdata/Header/header b/pkg/build/wire/internal/wire/testdata/Header/header new file mode 100644 index 00000000000..5ad71526f36 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/header @@ -0,0 +1,2 @@ +// This is a sample header file. +// diff --git a/pkg/build/wire/internal/wire/testdata/Header/pkg b/pkg/build/wire/internal/wire/testdata/Header/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Header/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/Header/want/program_out.txt new file mode 100644 index 00000000000..87523dd7a06 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/want/program_out.txt @@ -0,0 +1 @@ +41 diff --git a/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go new file mode 100644 index 00000000000..2f8c5c622f4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go @@ -0,0 +1,16 @@ +// This is a sample header file. +// +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFoo() Foo { + foo := provideFoo() + return foo +} diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/bar.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/bar.go new file mode 100644 index 00000000000..f2a04f2bc88 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/bar.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "example.com/foo" + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Bar string + +func (b *Bar) Foo() string { + return string(*b) +} + +func provideBar() *Bar { + b := new(Bar) + *b = "Hello, World!" + return b +} + +var Set = wire.NewSet( + provideBar, + wire.Bind(new(foo.Fooer), new(*Bar))) diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go new file mode 100644 index 00000000000..952f0753b09 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "example.com/foo" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() foo.Fooer { + wire.Build(Set) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/foo/foo.go new file mode 100644 index 00000000000..c89835732c5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/foo/foo.go @@ -0,0 +1,19 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 foo + +type Fooer interface { + Foo() string +} diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/pkg b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/pkg new file mode 100644 index 00000000000..923cd077cc7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/pkg @@ -0,0 +1 @@ +example.com/bar diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go new file mode 100644 index 00000000000..b68a1dbf2b4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/foo" +) + +// Injectors from wire.go: + +func injectFooer() foo.Fooer { + bar := provideBar() + return bar +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/foo.go new file mode 100644 index 00000000000..9d8116eeb65 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/foo.go @@ -0,0 +1,41 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar(40)) +} + +type Foo int +type Bar int +type FooBar int + +var Set = wire.NewSet( + provideBar, + provideFooBar) + +func provideBar() Bar { + return 2 +} + +func provideFooBar(foo Foo, bar Bar) FooBar { + return FooBar(foo) + FooBar(bar) +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go new file mode 100644 index 00000000000..0c1bf839fdd --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar(foo Foo) FooBar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/pkg b/pkg/build/wire/internal/wire/testdata/InjectInput/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/InjectInput/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go new file mode 100644 index 00000000000..9471cf6f381 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go @@ -0,0 +1,15 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar(foo Foo) FooBar { + bar := provideBar() + fooBar := provideFooBar(foo, bar) + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/foo.go new file mode 100644 index 00000000000..7006505c7cf --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/foo.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + // I'm on the fence as to whether this should be an error (versus an + // override). For now, I will make it an error that can be relaxed + // later. + fmt.Println(injectBar(40)) +} + +type Foo int +type Bar int + +var Set = wire.NewSet( + provideFoo, + provideBar) + +func provideFoo() Foo { + return -888 +} + +func provideBar(foo Foo) Bar { + return 2 +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go new file mode 100644 index 00000000000..24308cb35b7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectBar(foo Foo) Bar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/pkg b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/want/wire_errs.txt new file mode 100644 index 00000000000..988de2f1f6b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/want/wire_errs.txt @@ -0,0 +1,6 @@ +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo +current: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +<- provider set "Set" (example.com/foo/foo.go:x:y) +previous: +<- argument foo to injector function injectBar (example.com/foo/wire.go:x:y) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/foo.go new file mode 100644 index 00000000000..e94ff6b91ff --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} + +// provideMessage provides a friendly user greeting. +func provideMessage() string { + return "Hello, World!" +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go new file mode 100644 index 00000000000..349b7746904 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + panic(wire.Build(provideMessage)) +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/pkg b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go new file mode 100644 index 00000000000..8b124e562d8 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := provideMessage() + return string2 +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/foo.go new file mode 100644 index 00000000000..6166922f89d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/foo.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + foo := injectFoo() + fmt.Println(foo) +} + +type Foo int + +func provideFoo() (Foo, func()) { + return Foo(42), func() {} +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go new file mode 100644 index 00000000000..932a9fa7914 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() Foo { + // provideFoo returns a cleanup, but injectFoo does not. + wire.Build(provideFoo) + return Foo(0) +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/pkg b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/want/wire_errs.txt new file mode 100644 index 00000000000..a72614a1501 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectFoo: provider for example.com/foo.Foo returns cleanup but injection does not return cleanup function \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/foo.go new file mode 100644 index 00000000000..977126bfbd3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/foo.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + foo := injectFoo() + fmt.Println(foo) +} + +type Foo int + +func provideFoo() (Foo, error) { + return Foo(42), nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go new file mode 100644 index 00000000000..2dced82e731 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() Foo { + // provideFoo returns an error, but injectFoo does not. + wire.Build(provideFoo) + return Foo(0) +} diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/pkg b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/want/wire_errs.txt new file mode 100644 index 00000000000..7e2aad10b01 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectFoo: provider for example.com/foo.Foo returns error but injection not allowed to fail \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/foo.go new file mode 100644 index 00000000000..47f10493ba1 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/foo.go @@ -0,0 +1,45 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} + +type Bar string + +func (b *Bar) Foo() string { + return string(*b) +} + +func provideBar() *Bar { + b := new(Bar) + *b = "Hello, World!" + return b +} + +var Set = wire.NewSet( + provideBar, + wire.Bind(new(Fooer), new(*Bar))) diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go new file mode 100644 index 00000000000..250d8adc18e --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() Fooer { + wire.Build(Set) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go new file mode 100644 index 00000000000..2c1a67b261e --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooer() Fooer { + bar := provideBar() + return bar +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go new file mode 100644 index 00000000000..ad9b9c281ef --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go new file mode 100644 index 00000000000..492e3c2a5b7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() Fooer { + // wrong: string doesn't implement Fooer. + wire.Build(wire.Bind(new(Fooer), new(string))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt new file mode 100644 index 00000000000..647d06feb3a --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: string does not implement example.com/foo.Fooer \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go new file mode 100644 index 00000000000..ad9b9c281ef --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go new file mode 100644 index 00000000000..97f6b77ffdd --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() Fooer { + // wrong: arg0 must be a pointer to an interface. + wire.Build(wire.Bind("foo", "bar")) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt new file mode 100644 index 00000000000..4f3e78fb22b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: first argument to Bind must be a pointer to an interface type; found string \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go new file mode 100644 index 00000000000..ad9b9c281ef --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go new file mode 100644 index 00000000000..15a3322e589 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() Fooer { + // wrong: wire.Bind requires 2 args. + wire.Build(wire.Bind(new(Fooer))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt new file mode 100644 index 00000000000..01a2f6ccdb0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/want/wire_errs.txt @@ -0,0 +1,3 @@ +example.com/foo/wire.go:x:y: not enough arguments in call to wire.Bind + have (*Fooer) + want (interface{}, interface{}) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/foo.go new file mode 100644 index 00000000000..5d8e1b5c986 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/foo.go @@ -0,0 +1,61 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +// This test verifies that the concrete type is provided only once, even if an +// interface additionally depends on it. + +package main + +import ( + "fmt" + "sync" +) + +func main() { + injectFooBar() + fmt.Println(provideBarCalls) +} + +type Fooer interface { + Foo() string +} + +type Bar string + +type FooBar struct { + Fooer Fooer + Bar *Bar +} + +func (b *Bar) Foo() string { + return string(*b) +} + +func provideBar() *Bar { + mu.Lock() + provideBarCalls++ + mu.Unlock() + b := new(Bar) + *b = "Hello, World!" + return b +} + +var ( + mu sync.Mutex + provideBarCalls int +) + +func provideFooBar(fooer Fooer, bar *Bar) FooBar { + return FooBar{fooer, bar} +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go new file mode 100644 index 00000000000..b342b3b6f16 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build( + provideBar, + provideFooBar, + wire.Bind(new(Fooer), new(*Bar)), + ) + return FooBar{} +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/program_out.txt new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/program_out.txt @@ -0,0 +1 @@ +1 diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go new file mode 100644 index 00000000000..1c437713599 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go @@ -0,0 +1,15 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() FooBar { + bar := provideBar() + fooBar := provideFooBar(bar, bar) + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/foo.go new file mode 100644 index 00000000000..4e7edcdde6c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + "io/ioutil" +) + +func main() { + r := injectedReader() + buf, _ := ioutil.ReadAll(r) + fmt.Println(string(buf)) +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go new file mode 100644 index 00000000000..5ed782b930d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "io" + "strings" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedReader() io.Reader { + wire.Build(wire.InterfaceValue(new(io.Reader), strings.NewReader("hello world"))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/program_out.txt new file mode 100644 index 00000000000..3b18e512dba --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/program_out.txt @@ -0,0 +1 @@ +hello world diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go new file mode 100644 index 00000000000..9868a9023f5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go @@ -0,0 +1,23 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "io" + "strings" +) + +// Injectors from wire.go: + +func injectedReader() io.Reader { + reader := _wireReaderValue + return reader +} + +var ( + _wireReaderValue = strings.NewReader("hello world") +) diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go new file mode 100644 index 00000000000..c9ce4a302fc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" + "io" +) + +func injectedMessage() string { + // wrong: string doesn't implement io.Reader. + wire.Build(wire.InterfaceValue(new(io.Reader), "bar")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt new file mode 100644 index 00000000000..5357ddfc521 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: string does not implement io.Reader \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go new file mode 100644 index 00000000000..69985206fb5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + // wrong: arg0 must be a pointer to an interface. + wire.Build(wire.InterfaceValue("foo", "bar")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/want/wire_errs.txt new file mode 100644 index 00000000000..89cc2e7e322 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: first argument to InterfaceValue must be a pointer to an interface type; found string \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go new file mode 100644 index 00000000000..8d7b884d461 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + // wrong: InterfaceValue requires 2 args. + wire.Build(wire.InterfaceValue("foo")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/pkg b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/want/wire_errs.txt new file mode 100644 index 00000000000..d11189ad520 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/want/wire_errs.txt @@ -0,0 +1,3 @@ +example.com/foo/wire.go:x:y: not enough arguments in call to wire.InterfaceValue + have (string) + want (interface{}, interface{}) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/foo.go b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/foo.go new file mode 100644 index 00000000000..7406ee89808 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + foo := injectFoo() + bar := injectBar() + fmt.Println(foo) + fmt.Println(bar) +} + +type Foo int +type Bar int + +func provideFoo() Foo { + return Foo(42) +} +func provideBar() Bar { + return Bar(99) +} diff --git a/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go new file mode 100644 index 00000000000..8c67b41e08d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go @@ -0,0 +1,33 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() Foo { + // This non-call statement makes this an invalid injector. + _ = 42 + panic(wire.Build(provideFoo)) +} + +func injectBar() Bar { + // Two call statements are also invalid. + panic(wire.Build(provideBar)) + panic(wire.Build(provideBar)) +} diff --git a/pkg/build/wire/internal/wire/testdata/InvalidInjector/pkg b/pkg/build/wire/internal/wire/testdata/InvalidInjector/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InvalidInjector/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/InvalidInjector/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/InvalidInjector/want/wire_errs.txt new file mode 100644 index 00000000000..aba63c0e9b0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/InvalidInjector/want/wire_errs.txt @@ -0,0 +1,3 @@ +a call to wire.Build indicates that this function is an injector, but injectors must consist of only the wire.Build call and an optional return + +a call to wire.Build indicates that this function is an injector, but injectors must consist of only the wire.Build call and an optional return \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/foo.go b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/foo.go new file mode 100644 index 00000000000..a633b2d3115 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/foo.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(inject("foo", "bar").A) +} + +type Foo struct { + A string +} + +func provideFoo(a string) *Foo { + return &Foo{A: a} +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go new file mode 100644 index 00000000000..22d939c7cd1 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject(a, b string) *Foo { + // fail: can't have two args of the same type. + panic(wire.Build(provideFoo)) +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/pkg b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/want/wire_errs.txt new file mode 100644 index 00000000000..f23405dcbda --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/want/wire_errs.txt @@ -0,0 +1,5 @@ +example.com/foo/wire.go:x:y: multiple bindings for string +current: +<- argument b to injector function inject (example.com/foo/wire.go:x:y) +previous: +<- argument a to injector function inject (example.com/foo/wire.go:x:y) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/foo.go b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/foo.go new file mode 100644 index 00000000000..3db08b89ff7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/foo.go @@ -0,0 +1,45 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 ( + "io" + "strings" + + "github.com/grafana/grafana/pkg/build/wire" +) + +type context struct{} + +func main() {} + +type Foo string +type Bar io.Reader + +var Set = wire.NewSet(provideFoo) +var SuperSet = wire.NewSet(Set) +var SetWithDuplicateBindings = wire.NewSet(Set, SuperSet) + +func provideFoo() Foo { + return Foo("foo") +} + +func provideFooAgain() Foo { + return Foo("foo foo") +} + +func provideBar() Bar { + return io.Reader(strings.NewReader("hello")) +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go new file mode 100644 index 00000000000..5bc1500b05d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go @@ -0,0 +1,53 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "strings" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject() Foo { + // fail: provideFoo and provideFooAgain both provide Foo. + panic(wire.Build(provideFoo, provideFooAgain)) +} + +func injectFromSet() Foo { + // fail: provideFoo is also provided by Set. + panic(wire.Build(provideFoo, Set)) +} + +func injectFromNestedSet() Foo { + // fail: provideFoo is also provided by SuperSet, via Set. + panic(wire.Build(provideFoo, SuperSet)) +} + +func injectFromSetWithDuplicateBindings() Foo { + // fail: DuplicateBindingsSet has two providers for Foo. + panic(wire.Build(SetWithDuplicateBindings)) +} + +func injectDuplicateValues() Foo { + // fail: provideFoo and wire.Value both provide Foo. + panic(wire.Build(provideFoo, wire.Value(Foo("foo")))) +} + +func injectDuplicateInterface() Bar { + // fail: provideBar and wire.Bind both provide Bar. + panic(wire.Build(provideBar, wire.Bind(new(Bar), new(*strings.Reader)))) +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleBindings/pkg b/pkg/build/wire/internal/wire/testdata/MultipleBindings/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleBindings/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/MultipleBindings/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/MultipleBindings/want/wire_errs.txt new file mode 100644 index 00000000000..99440003504 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleBindings/want/wire_errs.txt @@ -0,0 +1,41 @@ +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo +current: +<- provider "provideFooAgain" (example.com/foo/foo.go:x:y) +previous: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo +current: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +previous: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +<- provider set "Set" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo +current: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +previous: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +<- provider set "Set" (example.com/foo/foo.go:x:y) +<- provider set "SuperSet" (example.com/foo/foo.go:x:y) + +example.com/foo/foo.go:x:y: SetWithDuplicateBindings has multiple bindings for example.com/foo.Foo +current: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +<- provider set "Set" (example.com/foo/foo.go:x:y) +<- provider set "SuperSet" (example.com/foo/foo.go:x:y) +previous: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) +<- provider set "Set" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo +current: +<- wire.Value (example.com/foo/wire.go:x:y) +previous: +<- provider "provideFoo" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Bar +current: +<- wire.Bind (example.com/foo/wire.go:x:y) +previous: +<- provider "provideBar" (example.com/foo/foo.go:x:y) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/foo.go b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/foo.go new file mode 100644 index 00000000000..6a0ea8195bc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/foo.go @@ -0,0 +1,47 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectMissingOutputType()) + fmt.Println(injectMultipleMissingTypes()) + fmt.Println(injectMissingRecursiveType()) +} + +type Foo int +type Bar int +type Baz int + +func provideBaz(foo Foo, bar Bar) Baz { + return 0 +} + +type Zip int +type Zap int +type Zop int + +func provideZip(foo Foo) Zip { + return 0 +} + +func provideZap(zip Zip) Zap { + return 0 +} + +func provideZop(zap Zap) Zop { + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go new file mode 100644 index 00000000000..de8c23907a7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectMissingOutputType() Foo { + // Error: no provider for Foo. + wire.Build() + return Foo(0) +} + +func injectMultipleMissingTypes() Baz { + // Error: provideBaz needs Foo and Bar, both missing. + wire.Build(provideBaz) + return Baz(0) +} + +func injectMissingRecursiveType() Zop { + // Error: + // Zop -> Zap -> Zip -> Foo + // provideZop needs Zap, provideZap needs Zip, provideZip needs Foo, + // which is missing. + wire.Build(provideZop, provideZap, provideZip) + return Zop(0) +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/pkg b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt new file mode 100644 index 00000000000..f46d55c68a6 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/want/wire_errs.txt @@ -0,0 +1,12 @@ +example.com/foo/wire.go:x:y: inject injectMissingOutputType: no provider found for example.com/foo.Foo, output of injector + +example.com/foo/wire.go:x:y: inject injectMultipleMissingTypes: no provider found for example.com/foo.Foo +needed by example.com/foo.Baz in provider "provideBaz" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: inject injectMultipleMissingTypes: no provider found for example.com/foo.Bar +needed by example.com/foo.Baz in provider "provideBaz" (example.com/foo/foo.go:x:y) + +example.com/foo/wire.go:x:y: inject injectMissingRecursiveType: no provider found for example.com/foo.Foo +needed by example.com/foo.Zip in provider "provideZip" (example.com/foo/foo.go:x:y) +needed by example.com/foo.Zap in provider "provideZap" (example.com/foo/foo.go:x:y) +needed by example.com/foo.Zop in provider "provideZop" (example.com/foo/foo.go:x:y) \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/bar/bar.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/bar/bar.go new file mode 100644 index 00000000000..549f8fae440 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/bar/bar.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +import ( + "example.com/foo" +) + +type Config struct { + V int +} + +type Service struct { + Cfg *Config + F *foo.Service +} + +func New(cfg *Config, f *foo.Service) *Service { + return &Service{Cfg: cfg, F: f} +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/baz/baz.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/baz/baz.go new file mode 100644 index 00000000000..eb408527aa5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/baz/baz.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 baz + +import ( + "example.com/bar" +) + +type Config struct { + V int +} + +type Service struct { + Cfg *Config + B *bar.Service +} + +func New(cfg *Config, b *bar.Service) *Service { + return &Service{Cfg: cfg, B: b} +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/foo/foo.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/foo/foo.go new file mode 100644 index 00000000000..9844ccb5499 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 foo + +type Config struct { + V int +} + +type Service struct { + Cfg *Config +} + +func New(cfg *Config) *Service { + return &Service{Cfg: cfg} +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go new file mode 100644 index 00000000000..fb501789ed4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go @@ -0,0 +1,68 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "fmt" + + "example.com/bar" + "example.com/baz" + "example.com/foo" + "github.com/grafana/grafana/pkg/build/wire" +) + +type MainConfig struct { + Foo *foo.Config + Bar *bar.Config + baz *baz.Config +} + +type MainService struct { + Foo *foo.Service + Bar *bar.Service + baz *baz.Service +} + +func (m *MainService) String() string { + return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.baz.Cfg.V) +} + +func newMainService(MainConfig) *MainService { + wire.Build( + wire.Struct(new(MainService), "Foo", "Bar", "baz"), + wire.FieldsOf( + new(MainConfig), + "Foo", + "Bar", + "baz", + ), + foo.New, + bar.New, + baz.New, + ) + return nil +} + +func main() { + cfg := MainConfig{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + baz: &baz.Config{3}, + } + svc := newMainService(cfg) + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/pkg b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/pkg new file mode 100644 index 00000000000..b7badd92db3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/pkg @@ -0,0 +1 @@ +example.com/main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/program_out.txt new file mode 100644 index 00000000000..b85905ec0b9 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/program_out.txt @@ -0,0 +1 @@ +1 2 3 diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go new file mode 100644 index 00000000000..e4068205704 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go @@ -0,0 +1,59 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/bar" + "example.com/baz" + "example.com/foo" + "fmt" +) + +// Injectors from wire.go: + +func newMainService(mainConfig MainConfig) *MainService { + config := mainConfig.Foo + service := foo.New(config) + barConfig := mainConfig.Bar + barService := bar.New(barConfig, service) + bazConfig := mainConfig.baz + bazService := baz.New(bazConfig, barService) + mainService := &MainService{ + Foo: service, + Bar: barService, + baz: bazService, + } + return mainService +} + +// wire.go: + +type MainConfig struct { + Foo *foo.Config + Bar *bar.Config + baz *baz.Config +} + +type MainService struct { + Foo *foo.Service + Bar *bar.Service + baz *baz.Service +} + +func (m *MainService) String() string { + return fmt.Sprintf("%d %d %d", m.Foo.Cfg.V, m.Bar.Cfg.V, m.baz.Cfg.V) +} + +func main() { + cfg := MainConfig{ + Foo: &foo.Config{1}, + Bar: &bar.Config{2}, + baz: &baz.Config{3}, + } + svc := newMainService(cfg) + fmt.Println(svc.String()) +} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/foo.go new file mode 100644 index 00000000000..9952e48977d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 ( + stdcontext "context" + "fmt" + "os" +) + +type context struct{} + +func main() { + c, err := inject(stdcontext.Background(), struct{}{}) + if err != nil { + fmt.Println("ERROR:", err) + os.Exit(1) + } + fmt.Println(c) +} + +func provide(ctx stdcontext.Context) (context, error) { + return context{}, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go new file mode 100644 index 00000000000..78cc79105f0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + stdcontext "context" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject(context stdcontext.Context, err struct{}) (context, error) { + panic(wire.Build(provide)) +} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/pkg b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/program_out.txt new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/program_out.txt @@ -0,0 +1 @@ +{} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go new file mode 100644 index 00000000000..e32329d325e --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go @@ -0,0 +1,21 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + context2 "context" +) + +// Injectors from wire.go: + +func inject(context3 context2.Context, err2 struct{}) (context, error) { + mainContext, err := provide(context3) + if err != nil { + return context{}, err + } + return mainContext, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go new file mode 100644 index 00000000000..0c814f01bef --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go @@ -0,0 +1,58 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +// This file is specifically designed to cause issues with copying the +// AST, particularly with the identifier "context". + +package main + +import ( + stdcontext "context" + "fmt" + "os" + "reflect" + + "github.com/grafana/grafana/pkg/build/wire" +) + +type context struct{} + +func main() { + if _, ok := reflect.TypeOf(context{}).MethodByName("Provide"); !ok { + fmt.Println("ERROR: context.Provide renamed") + os.Exit(1) + } + c, err := inject(stdcontext.Background(), struct{}{}) + if err != nil { + fmt.Println("ERROR:", err) + os.Exit(1) + } + fmt.Println(c) +} + +func Provide(context2 stdcontext.Context) (context, error) { + var context3 = stdcontext.Background() + _ = context2 + _ = context3 + return context{}, nil +} + +func inject(context stdcontext.Context, err struct{}) (context, error) { + panic(wire.Build(Provide)) +} + +func (context) Provide() { +} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/pkg b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/program_out.txt new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/program_out.txt @@ -0,0 +1 @@ +{} diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go new file mode 100644 index 00000000000..c0815204bd1 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go @@ -0,0 +1,51 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + context2 "context" + "fmt" + "os" + "reflect" +) + +// Injectors from foo.go: + +func inject(context3 context2.Context, err2 struct{}) (context, error) { + mainContext, err := Provide(context3) + if err != nil { + return context{}, err + } + return mainContext, nil +} + +// foo.go: + +type context struct{} + +func main() { + if _, ok := reflect.TypeOf(context{}).MethodByName("Provide"); !ok { + fmt.Println("ERROR: context.Provide renamed") + os.Exit(1) + } + c, err := inject(context2.Background(), struct{}{}) + if err != nil { + fmt.Println("ERROR:", err) + os.Exit(1) + } + fmt.Println(c) +} + +func Provide(context2_2 context2.Context) (context, error) { + var context3 = context2.Background() + _ = context2_2 + _ = context3 + return context{}, nil +} + +func (context) Provide() { +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/foo.go new file mode 100644 index 00000000000..e94ff6b91ff --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} + +// provideMessage provides a friendly user greeting. +func provideMessage() string { + return "Hello, World!" +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go new file mode 100644 index 00000000000..0570bb2a3bd --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(provideMessage) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/pkg b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go new file mode 100644 index 00000000000..8b124e562d8 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := provideMessage() + return string2 +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go new file mode 100644 index 00000000000..50e62af7d3f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(wire.Value("Hello, World!")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/pkg b/pkg/build/wire/internal/wire/testdata/NiladicValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go new file mode 100644 index 00000000000..16c0df257c2 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := _wireStringValue + return string2 +} + +var ( + _wireStringValue = "Hello, World!" +) diff --git a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/foo.go new file mode 100644 index 00000000000..996347e291b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/foo.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectFooer().Foo()) +} + +type Fooer interface { + Foo() string +} + +type Bar string + +func (b Bar) Foo() string { + return string(b) +} + +func provideBar() Bar { + return "Hello, World!" +} diff --git a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go new file mode 100644 index 00000000000..f4cd412752d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooer() Fooer { + wire.Build(provideBar) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/pkg b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/want/wire_errs.txt new file mode 100644 index 00000000000..23b30e014f9 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectFooer: no provider found for example.com/foo.Fooer, output of injector \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/foo.go new file mode 100644 index 00000000000..9952e48977d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 ( + stdcontext "context" + "fmt" + "os" +) + +type context struct{} + +func main() { + c, err := inject(stdcontext.Background(), struct{}{}) + if err != nil { + fmt.Println("ERROR:", err) + os.Exit(1) + } + fmt.Println(c) +} + +func provide(ctx stdcontext.Context) (context, error) { + return context{}, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go new file mode 100644 index 00000000000..758cb5fb093 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + stdcontext "context" + + "github.com/grafana/grafana/pkg/build/wire" +) + +// The notable characteristic of this test is that there are no +// parameter names on the inject stub. + +func inject(stdcontext.Context, struct{}) (context, error) { + wire.Build(provide) + return context{}, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/pkg b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/program_out.txt new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/program_out.txt @@ -0,0 +1 @@ +{} diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go new file mode 100644 index 00000000000..b8760db4b81 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go @@ -0,0 +1,21 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + context2 "context" +) + +// Injectors from wire.go: + +func inject(contextContext context2.Context, arg struct{}) (context, error) { + mainContext, err := provide(contextContext) + if err != nil { + return context{}, err + } + return mainContext, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/NoopBuild/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NoopBuild/foo/foo.go new file mode 100644 index 00000000000..d149a4ad860 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoopBuild/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println("Hello, World!") +} diff --git a/pkg/build/wire/internal/wire/testdata/NoopBuild/pkg b/pkg/build/wire/internal/wire/testdata/NoopBuild/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoopBuild/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/NoopBuild/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/NoopBuild/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/NoopBuild/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/NoopBuild/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NoopBuild/want/wire_gen.go new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/foo.go b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/foo.go new file mode 100644 index 00000000000..4ed90e541f5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/foo.go @@ -0,0 +1,62 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 ( + "errors" + "fmt" + "strings" +) + +var ( + cleanedFoo = false + cleanedBar = false +) + +func main() { + _, cleanup, err := injectBaz() + if err == nil { + fmt.Println("") + } else { + fmt.Println(strings.Contains(err.Error(), "bork!")) + } + fmt.Println(cleanedFoo, cleanedBar, cleanup == nil) +} + +type Foo int +type Bar int +type Baz int + +func provideFoo() (*Foo, func()) { + foo := new(Foo) + *foo = 42 + return foo, func() { *foo = 0; cleanedFoo = true } +} + +func provideBar(foo *Foo) (*Bar, func(), error) { + bar := new(Bar) + *bar = 77 + return bar, func() { + if *foo == 0 { + panic("foo cleaned up before bar") + } + *bar = 0 + cleanedBar = true + }, nil +} + +func provideBaz(bar *Bar) (Baz, error) { + return 0, errors.New("bork!") +} diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go new file mode 100644 index 00000000000..5d52e9243c7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectBaz() (Baz, func(), error) { + wire.Build(provideFoo, provideBar, provideBaz) + return 0, nil, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/pkg b/pkg/build/wire/internal/wire/testdata/PartialCleanup/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/program_out.txt new file mode 100644 index 00000000000..649cfc2cf86 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/program_out.txt @@ -0,0 +1,2 @@ +true +true true true diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go new file mode 100644 index 00000000000..6cf2640186d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go @@ -0,0 +1,28 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectBaz() (Baz, func(), error) { + foo, cleanup := provideFoo() + bar, cleanup2, err := provideBar(foo) + if err != nil { + cleanup() + return 0, nil, err + } + baz, err := provideBaz(bar) + if err != nil { + cleanup2() + cleanup() + return 0, nil, err + } + return baz, func() { + cleanup2() + cleanup() + }, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/anon1/anon1.go b/pkg/build/wire/internal/wire/testdata/PkgImport/anon1/anon1.go new file mode 100644 index 00000000000..5ee862ff03f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/anon1/anon1.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 anon1 diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/anon2/anon2.go b/pkg/build/wire/internal/wire/testdata/PkgImport/anon2/anon2.go new file mode 100644 index 00000000000..79fd5e890b4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/anon2/anon2.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 anon2 diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/bar/bar.go b/pkg/build/wire/internal/wire/testdata/PkgImport/bar/bar.go new file mode 100644 index 00000000000..f65629da015 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/bar/bar.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +type Bar int + +func ProvideBar() Bar { + return 1 +} diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/foo/foo.go b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/foo.go new file mode 100644 index 00000000000..689251d435c --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/foo.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type FooBar int + +var Set = wire.NewSet( + provideFoo, + bar.ProvideBar, + provideFooBar) + +func provideFoo() Foo { + return 41 +} + +func provideFooBar(foo Foo, barVal bar.Bar) FooBar { + return FooBar(foo) + FooBar(barVal) +} diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go new file mode 100644 index 00000000000..4f9c9d9a7ba --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + _ "example.com/anon1" // intentionally duplicated + _ "example.com/anon1" // intentionally duplicated + _ "example.com/anon2" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/pkg b/pkg/build/wire/internal/wire/testdata/PkgImport/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/PkgImport/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go new file mode 100644 index 00000000000..5f3e127faff --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go @@ -0,0 +1,25 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "example.com/bar" +) + +import ( + _ "example.com/anon1" + _ "example.com/anon2" +) + +// Injectors from wire.go: + +func injectFooBar() FooBar { + foo := provideFoo() + barBar := bar.ProvideBar() + fooBar := provideFooBar(foo, barBar) + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/foo.go new file mode 100644 index 00000000000..1fcc71620b4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/foo.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFoo()) +} + +type fooer interface { + Do() string +} + +type foo struct{} + +func (f *foo) Do() string { + return "did foo" +} + +func newFoo() *foo { + return &foo{} +} + +var ( + setA = wire.NewSet(newFoo) + // This set is invalid because it has a wire.Bind but no matching provider. + // From the user guide: + // Any set that includes an interface binding must also have a provider in + // the same set that provides the concrete type. + setB = wire.NewSet(wire.Bind(new(fooer), new(*foo))) + setC = wire.NewSet(setA, setB) +) diff --git a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go new file mode 100644 index 00000000000..2e765205481 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() *foo { + wire.Build(setC) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/pkg b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/want/wire_errs.txt new file mode 100644 index 00000000000..656cd94df7f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/foo.go:x:y: wire.Bind of concrete type "*example.com/foo.foo" to interface "example.com/foo.fooer", but setB does not include a provider for "*example.com/foo.foo" \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/foo.go b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/foo.go new file mode 100644 index 00000000000..e94ff6b91ff --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} + +// provideMessage provides a friendly user greeting. +func provideMessage() string { + return "Hello, World!" +} diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go new file mode 100644 index 00000000000..0570bb2a3bd --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(provideMessage) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/pkg b/pkg/build/wire/internal/wire/testdata/RelativePkg/pkg new file mode 100644 index 00000000000..b10f96cf477 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/pkg @@ -0,0 +1 @@ +./foo diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go new file mode 100644 index 00000000000..8b124e562d8 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := provideMessage() + return string2 +} diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/foo.go new file mode 100644 index 00000000000..a541be5204e --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/foo.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + i := injectInterface() + fmt.Println(i) +} + +type Interface int +type Select int + +func provideInterface(s Select) Interface { + return Interface(int(s) + 1) +} + +func provideSelect() Select { + return Select(41) +} diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go new file mode 100644 index 00000000000..c375ee159f7 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +// Wire tries to disambiguate the variable "select" by prepending +// the package name; this package-scoped variable conflicts with that +// and forces a different name. +var mainSelect = 0 + +func injectInterface() Interface { + // interface and select are Go reserved words, so + // Wire should avoid using them as variable names. + panic(wire.Build(provideInterface, provideSelect)) +} diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/pkg b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go new file mode 100644 index 00000000000..7ff31104195 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go @@ -0,0 +1,22 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectInterface() Interface { + select2 := provideSelect() + mainInterface := provideInterface(select2) + return mainInterface +} + +// wire.go: + +// Wire tries to disambiguate the variable "select" by prepending +// the package name; this package-scoped variable conflicts with that +// and forces a different name. +var mainSelect = 0 diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/foo.go new file mode 100644 index 00000000000..6af8eb87c8e --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/foo.go @@ -0,0 +1,25 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectStringer("Hello, World!")) +} + +type MyString string + +func (s MyString) String() string { return string(s) } diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go new file mode 100644 index 00000000000..fb79f9ceb41 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "fmt" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectStringer(s MyString) fmt.Stringer { + wire.Build(wire.Bind(new(fmt.Stringer), new(MyString))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/pkg b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go new file mode 100644 index 00000000000..9d1a331d258 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go @@ -0,0 +1,17 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "fmt" +) + +// Injectors from wire.go: + +func injectStringer(s MyString) fmt.Stringer { + return s +} diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/foo.go new file mode 100644 index 00000000000..e7ded242843 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/foo.go @@ -0,0 +1,41 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 ( + "errors" + "fmt" + "strings" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + foo, err := injectFoo() + fmt.Println(foo) // should be zero, the injector should ignore provideFoo's return value. + if err == nil { + fmt.Println("") + } else { + fmt.Println(strings.Contains(err.Error(), "there is no Foo")) + } +} + +type Foo int + +func provideFoo() (Foo, error) { + return 42, errors.New("there is no Foo") +} + +var Set = wire.NewSet(provideFoo) diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go new file mode 100644 index 00000000000..a729e826273 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() (Foo, error) { + wire.Build(Set) + return 0, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/pkg b/pkg/build/wire/internal/wire/testdata/ReturnError/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ReturnError/want/program_out.txt new file mode 100644 index 00000000000..f9d60c07bb2 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/want/program_out.txt @@ -0,0 +1,2 @@ +0 +true diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go new file mode 100644 index 00000000000..5d9746675db --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go @@ -0,0 +1,17 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFoo() (Foo, error) { + foo, err := provideFoo() + if err != nil { + return 0, err + } + return foo, nil +} diff --git a/pkg/build/wire/internal/wire/testdata/Struct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Struct/foo/foo.go new file mode 100644 index 00000000000..2236d15c483 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Struct/foo/foo.go @@ -0,0 +1,56 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + "sync" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fb := injectFooBar() + pfb := injectPartFooBar() + fmt.Println(fb.Foo, fb.Bar) + fmt.Println(pfb.Foo, pfb.Bar) +} + +type Foo int +type Bar int + +type FooBar struct { + mu sync.Mutex `wire:"-"` + Foo Foo + Bar Bar +} + +func provideFoo() Foo { + return 41 +} + +func provideBar() Bar { + return 1 +} + +var Set = wire.NewSet( + wire.Struct(new(FooBar), "*"), + provideFoo, + provideBar) + +var PartSet = wire.NewSet( + wire.Struct(new(FooBar), "Foo"), + provideFoo, +) diff --git a/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go new file mode 100644 index 00000000000..29ef937ddfc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build(Set) + return FooBar{} +} + +func injectPartFooBar() FooBar { + wire.Build(PartSet) + return FooBar{} +} diff --git a/pkg/build/wire/internal/wire/testdata/Struct/pkg b/pkg/build/wire/internal/wire/testdata/Struct/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Struct/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Struct/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/Struct/want/program_out.txt new file mode 100644 index 00000000000..3ea9a3a5fdc --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Struct/want/program_out.txt @@ -0,0 +1,2 @@ +41 1 +41 0 diff --git a/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go new file mode 100644 index 00000000000..e4bf0c727d1 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go @@ -0,0 +1,27 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() FooBar { + foo := provideFoo() + bar := provideBar() + fooBar := FooBar{ + Foo: foo, + Bar: bar, + } + return fooBar +} + +func injectPartFooBar() FooBar { + foo := provideFoo() + fooBar := FooBar{ + Foo: foo, + } + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/foo.go new file mode 100644 index 00000000000..b308ebadfbb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + fmt.Println(inject(A{"Hello"})) +} + +type A struct { + B string +} diff --git a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go new file mode 100644 index 00000000000..7a84cdb9dcb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func inject(a A) string { + wire.Build(wire.Struct(new(*A), "*")) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/pkg b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/want/wire_errs.txt new file mode 100644 index 00000000000..e1ada68073f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: first argument to Struct must be a pointer to a named struct; found **example.com/foo.A \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/foo/foo.go b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/foo.go new file mode 100644 index 00000000000..dad7ec5cae0 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/foo.go @@ -0,0 +1,50 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fb := injectFooBar() + e := injectEmptyStruct() + fmt.Printf("%d %d %v\n", fb.Foo, fb.Bar, e) +} + +type Foo int +type Bar int + +type FooBar struct { + Foo Foo + Bar Bar +} + +type Empty struct{} + +func provideFoo() Foo { + return 41 +} + +func provideBar() Bar { + return 1 +} + +var Set = wire.NewSet( + wire.Struct(new(FooBar), "*"), + provideFoo, + provideBar) diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go new file mode 100644 index 00000000000..c08f942eb8b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() *FooBar { + wire.Build(Set) + return nil +} + +func injectEmptyStruct() *Empty { + wire.Build(wire.Struct(new(Empty))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/pkg b/pkg/build/wire/internal/wire/testdata/StructPointer/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/StructPointer/want/program_out.txt new file mode 100644 index 00000000000..a0875ea91f5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/want/program_out.txt @@ -0,0 +1 @@ +41 1 &{} diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go new file mode 100644 index 00000000000..e96e8cdd606 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go @@ -0,0 +1,24 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() *FooBar { + foo := provideFoo() + bar := provideBar() + fooBar := &FooBar{ + Foo: foo, + Bar: bar, + } + return fooBar +} + +func injectEmptyStruct() *Empty { + empty := &Empty{} + return empty +} diff --git a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/foo.go b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/foo.go new file mode 100644 index 00000000000..fddb0fe21c3 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/foo.go @@ -0,0 +1,48 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + "sync" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + pfb := injectPartFooBar() + fmt.Println(pfb.Foo) +} + +type Foo int + +type FooBar struct { + mu sync.Mutex `wire:"-"` + Foo Foo +} + +func provideFoo() Foo { + return 42 +} + +func provideMutex() sync.Mutex { + return sync.Mutex{} +} + +var ProhibitSet = wire.NewSet( + wire.Struct(new(FooBar), "mu", "Foo"), + provideMutex, + provideFoo, +) diff --git a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go new file mode 100644 index 00000000000..5e0d5800859 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectPartFooBar() FooBar { + wire.Build(ProhibitSet) + return FooBar{} +} diff --git a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/pkg b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/want/wire_errs.txt new file mode 100644 index 00000000000..2482bc580cf --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/foo.go:x:y: "mu" is prevented from injecting by wire \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/foo.go b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/foo.go new file mode 100644 index 00000000000..1ff42ba5838 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/foo.go @@ -0,0 +1,46 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type Bar int +type FooBar int + +func provideFoo() Foo { + return 40 +} + +func provideBar() Bar { + return 2 +} + +func provideFooBar(foo Foo, bar Bar) FooBar { + return FooBar(foo) + FooBar(bar) +} + +var Set = wire.NewSet( + provideFoo, + provideBar, + provideFooBar) diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go new file mode 100644 index 00000000000..4106dd8d91f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/pkg b/pkg/build/wire/internal/wire/testdata/TwoDeps/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go new file mode 100644 index 00000000000..bfc52d7f244 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go @@ -0,0 +1,16 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() FooBar { + foo := provideFoo() + bar := provideBar() + fooBar := provideFooBar(foo, bar) + return fooBar +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/bar/bar.go b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/bar/bar.go new file mode 100644 index 00000000000..72157426840 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/bar/bar.go @@ -0,0 +1,19 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +var foo struct { + X int +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/foo.go new file mode 100644 index 00000000000..d177addf8c5 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Printf("%v\n", injectedBar()) +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go new file mode 100644 index 00000000000..e0b4618c316 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedBar() string { + // Fails because bar.foo is unexported. + wire.Build(bar.foo.X) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/pkg b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/want/wire_errs.txt new file mode 100644 index 00000000000..ae23f88c391 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: foo not exported by package bar \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/bar/bar.go b/pkg/build/wire/internal/wire/testdata/UnexportedValue/bar/bar.go new file mode 100644 index 00000000000..71d43c92215 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/bar/bar.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 bar + +import "github.com/grafana/grafana/pkg/build/wire" + +var Value = wire.Value(privateMsg) + +var privateMsg = "Hello, World!" diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/foo.go new file mode 100644 index 00000000000..c2f26106547 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/foo.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go new file mode 100644 index 00000000000..a26cbf97b25 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "example.com/bar" + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + // Fails because bar.Value references unexported bar.privateMsg. + wire.Build(bar.Value) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/pkg b/pkg/build/wire/internal/wire/testdata/UnexportedValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/UnexportedValue/want/wire_errs.txt new file mode 100644 index 00000000000..07b5c342168 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectedMessage: value string can't be used: uses unexported identifier privateMsg \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/foo.go b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/foo.go new file mode 100644 index 00000000000..fc22552d75d --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/foo.go @@ -0,0 +1,83 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type Bar int +type Unused int +type UnusedInSet int +type OneOfTwo int +type TwoOfTwo int + +type FooBar struct { + MyFoo *Foo + MyBar Bar + MyUnused Unused +} + +var ( + unusedSet = wire.NewSet(provideUnusedInSet) + partiallyUsedSet = wire.NewSet(provideOneOfTwo, provideTwoOfTwo) +) + +type Fooer interface { + Foo() string +} + +func (f *Foo) Foo() string { + return fmt.Sprintf("Hello World %d", f) +} + +func provideFoo() *Foo { + f := new(Foo) + *f = 1 + return f +} + +func provideBar(foo *Foo, one OneOfTwo) Bar { + return Bar(int(*foo) + int(one)) +} + +func provideUnused() Unused { + return 1 +} + +func provideUnusedInSet() UnusedInSet { + return 1 +} + +func provideOneOfTwo() OneOfTwo { + return 1 +} + +func provideTwoOfTwo() TwoOfTwo { + return 1 +} + +type S struct { + Cfg Config +} + +type Config int diff --git a/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go new file mode 100644 index 00000000000..681b80d10ff --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build( + provideFoo, // needed as input for provideBar + provideBar, // needed for FooBar + partiallyUsedSet, // 1/2 providers in the set are needed + provideUnused, // not needed -> error + wire.Value("unused"), // not needed -> error + unusedSet, // nothing in set is needed -> error + wire.Bind(new(Fooer), new(*Foo)), // binding to Fooer is not needed -> error + wire.FieldsOf(new(S), "Cfg"), // S.Cfg not needed -> error + wire.Struct(new(FooBar), "MyFoo", "MyBar"), // needed for FooBar + ) + return FooBar{} +} diff --git a/pkg/build/wire/internal/wire/testdata/UnusedProviders/pkg b/pkg/build/wire/internal/wire/testdata/UnusedProviders/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnusedProviders/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/UnusedProviders/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/UnusedProviders/want/wire_errs.txt new file mode 100644 index 00000000000..54dd582a2df --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/UnusedProviders/want/wire_errs.txt @@ -0,0 +1,9 @@ +example.com/foo/wire.go:x:y: inject injectFooBar: unused provider set "unusedSet" + +example.com/foo/wire.go:x:y: inject injectFooBar: unused provider "main.provideUnused" + +example.com/foo/wire.go:x:y: inject injectFooBar: unused value of type string + +example.com/foo/wire.go:x:y: inject injectFooBar: unused interface binding to type example.com/foo.Fooer + +example.com/foo/wire.go:x:y: inject injectFooBar: unused field "example.com/foo.S".Cfg \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/foo.go new file mode 100644 index 00000000000..bff5e895f48 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/foo.go @@ -0,0 +1,36 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func main() { + fmt.Println(injectFooBar()) +} + +type Foo int +type FooBar int + +var Set = wire.NewSet( + wire.Value(Foo(41)), + provideFooBar) + +func provideFooBar(foo Foo) FooBar { + return FooBar(foo) + 1 +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go new file mode 100644 index 00000000000..4106dd8d91f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFooBar() FooBar { + wire.Build(Set) + return 0 +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/pkg b/pkg/build/wire/internal/wire/testdata/ValueChain/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ValueChain/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go new file mode 100644 index 00000000000..da2cccb3f06 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go @@ -0,0 +1,19 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFooBar() FooBar { + foo := _wireFooValue + fooBar := provideFooBar(foo) + return fooBar +} + +var ( + _wireFooValue = Foo(41) +) diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/foo.go new file mode 100644 index 00000000000..2c6c88b12b4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/foo.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + fmt.Println(injectedMessage()) +} + +type Foo string diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go new file mode 100644 index 00000000000..a389be1b630 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() Foo { + wire.Build(wire.Value(Foo("Hello, World!"))) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/pkg b/pkg/build/wire/internal/wire/testdata/ValueConversion/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go new file mode 100644 index 00000000000..b663861d920 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() Foo { + foo := _wireFooValue + return foo +} + +var ( + _wireFooValue = Foo("Hello, World!") +) diff --git a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/foo.go new file mode 100644 index 00000000000..11267e7782a --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/foo.go @@ -0,0 +1,37 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +type foo struct { + V int +} + +type bar struct { + V int +} + +func newBar(v int) *bar { + return &bar{V: v} +} + +func main() { + f := &foo{V: 42} + b := injectBar(f) + fmt.Printf("%d\n", b.V) +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go new file mode 100644 index 00000000000..e2ca3f2f3e4 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectBar(f *foo) *bar { + wire.Build( + newBar, + wire.Value(f.V), // fails because f.V is not from package scope + ) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/pkg b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/want/wire_errs.txt new file mode 100644 index 00000000000..ed7a45dcfa9 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: inject injectBar: value int can't be used: f is not declared in package scope \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/foo.go new file mode 100644 index 00000000000..4a412a48214 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/foo.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + "io/ioutil" + "strings" +) + +func main() { + r := injectedReader(strings.NewReader("hello world")) + buf, _ := ioutil.ReadAll(r) + fmt.Println(string(buf)) +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go new file mode 100644 index 00000000000..eb548fa1f00 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "io" + "strings" + + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedReader(r *strings.Reader) io.Reader { + wire.Build(wire.Value(io.Reader(r))) + return nil +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/pkg b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/want/wire_errs.txt b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/want/wire_errs.txt new file mode 100644 index 00000000000..19af8703fac --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/want/wire_errs.txt @@ -0,0 +1 @@ +example.com/foo/wire.go:x:y: argument to Value may not be an interface value (found io.Reader); use InterfaceValue instead \ No newline at end of file diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/foo.go b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/foo.go new file mode 100644 index 00000000000..d9aaa155983 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/foo.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" +) + +func main() { + f := injectFoo() + fmt.Printf("%d\n", f.X) +} + +type Foo struct { + X int +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go new file mode 100644 index 00000000000..51633dd0626 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectFoo() Foo { + wire.Build(wire.Value(Foo{X: 42})) + return Foo{} +} diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/pkg b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/program_out.txt new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/program_out.txt @@ -0,0 +1 @@ +42 diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go new file mode 100644 index 00000000000..f41ed12200f --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectFoo() Foo { + foo := _wireFooValue + return foo +} + +var ( + _wireFooValue = Foo{X: 42} +) diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/foo/foo.go b/pkg/build/wire/internal/wire/testdata/VarValue/foo/foo.go new file mode 100644 index 00000000000..c8db0afddeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/VarValue/foo/foo.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + +func main() { + // Mutating value; value should have been stored at package initialization. + msg = "Hello, World!" + + fmt.Println(injectedMessage()) +} + +var msg string = "Package init" diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go new file mode 100644 index 00000000000..e8a718a2e26 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage() string { + wire.Build(wire.Value(msg)) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/pkg b/pkg/build/wire/internal/wire/testdata/VarValue/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/VarValue/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/VarValue/want/program_out.txt new file mode 100644 index 00000000000..f4a93ef0c58 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/VarValue/want/program_out.txt @@ -0,0 +1 @@ +Package init diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go new file mode 100644 index 00000000000..07e4f01993b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go @@ -0,0 +1,18 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage() string { + string2 := _wireStringValue + return string2 +} + +var ( + _wireStringValue = msg +) diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/foo/foo.go b/pkg/build/wire/internal/wire/testdata/Varargs/foo/foo.go new file mode 100644 index 00000000000..d5a541402ee --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Varargs/foo/foo.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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" + "strings" +) + +func main() { + fmt.Println(injectedMessage("", "Hello,", "World!")) +} + +type title string + +// provideMessage provides a friendly user greeting. +func provideMessage(words ...string) string { + return strings.Join(words, " ") +} diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go new file mode 100644 index 00000000000..33d7acc5f45 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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. + +//+build wireinject + +package main + +import ( + "github.com/grafana/grafana/pkg/build/wire" +) + +func injectedMessage(t title, lines ...string) string { + wire.Build(provideMessage) + return "" +} diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/pkg b/pkg/build/wire/internal/wire/testdata/Varargs/pkg new file mode 100644 index 00000000000..f7a5c8cee31 --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Varargs/pkg @@ -0,0 +1 @@ +example.com/foo diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/want/program_out.txt b/pkg/build/wire/internal/wire/testdata/Varargs/want/program_out.txt new file mode 100644 index 00000000000..8ab686eafeb --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Varargs/want/program_out.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go new file mode 100644 index 00000000000..703a549132b --- /dev/null +++ b/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go @@ -0,0 +1,14 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +// Injectors from wire.go: + +func injectedMessage(t title, lines ...string) string { + string2 := provideMessage(lines...) + return string2 +} diff --git a/pkg/build/wire/internal/wire/wire.go b/pkg/build/wire/internal/wire/wire.go new file mode 100644 index 00000000000..5cedeb1ab3b --- /dev/null +++ b/pkg/build/wire/internal/wire/wire.go @@ -0,0 +1,960 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire provides compile-time dependency injection logic as a +// Go library. +package wire + +import ( + "bytes" + "context" + "errors" + "fmt" + "go/ast" + "go/format" + "go/printer" + "go/token" + "go/types" + "io/ioutil" + "path/filepath" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/packages" +) + +// GenerateResult stores the result for a package from a call to Generate. +type GenerateResult struct { + // PkgPath is the package's PkgPath. + PkgPath string + // OutputPath is the path where the generated output should be written. + // May be empty if there were errors. + OutputPath string + // Content is the gofmt'd source code that was generated. May be nil if + // there were errors during generation. + Content []byte + // Errs is a slice of errors identified during generation. + Errs []error +} + +// Commit writes the generated file to disk. +func (gen GenerateResult) Commit() error { + if len(gen.Content) == 0 { + return nil + } + return ioutil.WriteFile(gen.OutputPath, gen.Content, 0666) +} + +// GenerateOptions holds options for Generate. +type GenerateOptions struct { + // Header will be inserted at the start of each generated file. + Header []byte + PrefixOutputFile string + Tags string +} + +// Generate performs dependency injection for the packages that match the given +// patterns, return a GenerateResult for each package. The package pattern is +// defined by the underlying build system. For the go tool, this is described at +// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns +// +// wd is the working directory and env is the set of environment +// variables to use when loading the package specified by pkgPattern. If +// env is nil or empty, it is interpreted as an empty set of variables. +// In case of duplicate environment variables, the last one in the list +// takes precedence. +// +// Generate may return one or more errors if it failed to load the packages. +func Generate(ctx context.Context, wd string, env []string, patterns []string, opts *GenerateOptions) ([]GenerateResult, []error) { + if opts == nil { + opts = &GenerateOptions{} + } + pkgs, errs := load(ctx, wd, env, opts.Tags, patterns) + if len(errs) > 0 { + return nil, errs + } + generated := make([]GenerateResult, len(pkgs)) + for i, pkg := range pkgs { + generated[i].PkgPath = pkg.PkgPath + outDir, err := detectOutputDir(pkg.GoFiles) + if err != nil { + generated[i].Errs = append(generated[i].Errs, err) + continue + } + generated[i].OutputPath = filepath.Join(outDir, opts.PrefixOutputFile+"wire_gen.go") + g := newGen(pkg) + injectorFiles, errs := generateInjectors(g, pkg) + if len(errs) > 0 { + generated[i].Errs = errs + continue + } + copyNonInjectorDecls(g, injectorFiles, pkg.TypesInfo) + goSrc := g.frame(opts.Tags) + if len(opts.Header) > 0 { + goSrc = append(opts.Header, goSrc...) + } + fmtSrc, err := format.Source(goSrc) + if err != nil { + // This is likely a bug from a poorly generated source file. + // Add an error but also the unformatted source. + generated[i].Errs = append(generated[i].Errs, err) + } else { + goSrc = fmtSrc + } + generated[i].Content = goSrc + } + return generated, nil +} + +func detectOutputDir(paths []string) (string, error) { + if len(paths) == 0 { + return "", errors.New("no files to derive output directory from") + } + dir := filepath.Dir(paths[0]) + for _, p := range paths[1:] { + if dir2 := filepath.Dir(p); dir2 != dir { + return "", fmt.Errorf("found conflicting directories %q and %q", dir, dir2) + } + } + return dir, nil +} + +// generateInjectors generates the injectors for a given package. +func generateInjectors(g *gen, pkg *packages.Package) (injectorFiles []*ast.File, _ []error) { + oc := newObjectCache([]*packages.Package{pkg}) + injectorFiles = make([]*ast.File, 0, len(pkg.Syntax)) + ec := new(errorCollector) + for _, f := range pkg.Syntax { + for _, decl := range f.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + buildCall, err := findInjectorBuild(pkg.TypesInfo, fn) + if err != nil { + ec.add(err) + continue + } + if buildCall == nil { + continue + } + if len(injectorFiles) == 0 || injectorFiles[len(injectorFiles)-1] != f { + // This is the first injector generated for this file. + // Write a file header. + name := filepath.Base(g.pkg.Fset.File(f.Pos()).Name()) + g.p("// Injectors from %s:\n\n", name) + injectorFiles = append(injectorFiles, f) + } + sig := pkg.TypesInfo.ObjectOf(fn.Name).Type().(*types.Signature) + ins, _, err := injectorFuncSignature(sig) + if err != nil { + if w, ok := err.(*wireErr); ok { + ec.add(notePosition(w.position, fmt.Errorf("inject %s: %v", fn.Name.Name, w.error))) + } else { + ec.add(notePosition(g.pkg.Fset.Position(fn.Pos()), fmt.Errorf("inject %s: %v", fn.Name.Name, err))) + } + continue + } + injectorArgs := &InjectorArgs{ + Name: fn.Name.Name, + Tuple: ins, + Pos: fn.Pos(), + } + set, errs := oc.processNewSet(pkg.TypesInfo, pkg.PkgPath, buildCall, injectorArgs, "") + if len(errs) > 0 { + ec.add(notePositionAll(g.pkg.Fset.Position(fn.Pos()), errs)...) + continue + } + if errs := g.inject(fn.Pos(), fn.Name.Name, sig, set, fn.Doc); len(errs) > 0 { + ec.add(errs...) + continue + } + } + + for _, impt := range f.Imports { + if impt.Name != nil && impt.Name.Name == "_" { + g.anonImports[impt.Path.Value] = true + } + } + } + if len(ec.errors) > 0 { + return nil, ec.errors + } + return injectorFiles, nil +} + +// copyNonInjectorDecls copies any non-injector declarations from the +// given files into the generated output. +func copyNonInjectorDecls(g *gen, files []*ast.File, info *types.Info) { + for _, f := range files { + name := filepath.Base(g.pkg.Fset.File(f.Pos()).Name()) + first := true + for _, decl := range f.Decls { + switch decl := decl.(type) { + case *ast.FuncDecl: + // OK to ignore error, as any error cases should already have + // been filtered out. + if buildCall, _ := findInjectorBuild(info, decl); buildCall != nil { + continue + } + case *ast.GenDecl: + if decl.Tok == token.IMPORT { + continue + } + default: + continue + } + if first { + g.p("// %s:\n\n", name) + first = false + } + // TODO(light): Add line number at top of each declaration. + g.writeAST(info, decl) + g.p("\n\n") + } + } +} + +// importInfo holds info about an import. +type importInfo struct { + // name is the identifier that is used in the generated source. + name string + // differs is true if the import is given an identifier that does not + // match the package's identifier. + differs bool +} + +// gen is the file-wide generator state. +type gen struct { + pkg *packages.Package + buf bytes.Buffer + imports map[string]importInfo + anonImports map[string]bool + values map[ast.Expr]string +} + +func newGen(pkg *packages.Package) *gen { + return &gen{ + pkg: pkg, + anonImports: make(map[string]bool), + imports: make(map[string]importInfo), + values: make(map[ast.Expr]string), + } +} + +// frame bakes the built up source body into an unformatted Go source file. +func (g *gen) frame(tags string) []byte { + if g.buf.Len() == 0 { + return nil + } + var buf bytes.Buffer + if len(tags) > 0 { + tags = fmt.Sprintf(" gen -tags \"%s\"", tags) + } + buf.WriteString("// Code generated by Wire. DO NOT EDIT.\n\n") + buf.WriteString("//go:generate go run -mod=mod github.com/google/wire/cmd/wire" + tags + "\n") + buf.WriteString("//+build !wireinject\n\n") + buf.WriteString("package ") + buf.WriteString(g.pkg.Name) + buf.WriteString("\n\n") + if len(g.imports) > 0 { + buf.WriteString("import (\n") + imps := make([]string, 0, len(g.imports)) + for path := range g.imports { + imps = append(imps, path) + } + sort.Strings(imps) + for _, path := range imps { + // Omit the local package identifier if it matches the package name. + info := g.imports[path] + if info.differs { + fmt.Fprintf(&buf, "\t%s %q\n", info.name, path) + } else { + fmt.Fprintf(&buf, "\t%q\n", path) + } + } + buf.WriteString(")\n\n") + } + if len(g.anonImports) > 0 { + buf.WriteString("import (\n") + anonImps := make([]string, 0, len(g.anonImports)) + for path := range g.anonImports { + anonImps = append(anonImps, path) + } + sort.Strings(anonImps) + + for _, path := range anonImps { + fmt.Fprintf(&buf, "\t_ %s\n", path) + } + buf.WriteString(")\n\n") + } + buf.Write(g.buf.Bytes()) + return buf.Bytes() +} + +// inject emits the code for an injector. +func (g *gen) inject(pos token.Pos, name string, sig *types.Signature, set *ProviderSet, doc *ast.CommentGroup) []error { + injectSig, err := funcOutput(sig) + if err != nil { + return []error{notePosition(g.pkg.Fset.Position(pos), + fmt.Errorf("inject %s: %v", name, err))} + } + params := sig.Params() + calls, errs := solve(g.pkg.Fset, injectSig.out, params, set) + if len(errs) > 0 { + return mapErrors(errs, func(e error) error { + if w, ok := e.(*wireErr); ok { + return notePosition(w.position, fmt.Errorf("inject %s: %v", name, w.error)) + } + return notePosition(g.pkg.Fset.Position(pos), fmt.Errorf("inject %s: %v", name, e)) + }) + } + type pendingVar struct { + name string + expr ast.Expr + typeInfo *types.Info + } + var pendingVars []pendingVar + ec := new(errorCollector) + for i := range calls { + c := &calls[i] + if c.hasCleanup && !injectSig.cleanup { + ts := types.TypeString(c.out, nil) + ec.add(notePosition( + g.pkg.Fset.Position(pos), + fmt.Errorf("inject %s: provider for %s returns cleanup but injection does not return cleanup function", name, ts))) + } + if c.hasErr && !injectSig.err { + ts := types.TypeString(c.out, nil) + ec.add(notePosition( + g.pkg.Fset.Position(pos), + fmt.Errorf("inject %s: provider for %s returns error but injection not allowed to fail", name, ts))) + } + if c.kind == valueExpr { + if err := accessibleFrom(c.valueTypeInfo, c.valueExpr, g.pkg.PkgPath); err != nil { + // TODO(light): Display line number of value expression. + ts := types.TypeString(c.out, nil) + ec.add(notePosition( + g.pkg.Fset.Position(pos), + fmt.Errorf("inject %s: value %s can't be used: %v", name, ts, err))) + } + if g.values[c.valueExpr] == "" { + t := c.valueTypeInfo.TypeOf(c.valueExpr) + + name := typeVariableName(t, "", func(name string) string { return "_wire" + export(name) + "Value" }, g.nameInFileScope) + g.values[c.valueExpr] = name + pendingVars = append(pendingVars, pendingVar{ + name: name, + expr: c.valueExpr, + typeInfo: c.valueTypeInfo, + }) + } + } + } + if len(ec.errors) > 0 { + return ec.errors + } + + // Perform one pass to collect all imports, followed by the real pass. + injectPass(name, sig, calls, set, doc, &injectorGen{ + g: g, + errVar: disambiguate("err", g.nameInFileScope), + discard: true, + }) + injectPass(name, sig, calls, set, doc, &injectorGen{ + g: g, + errVar: disambiguate("err", g.nameInFileScope), + discard: false, + }) + if len(pendingVars) > 0 { + g.p("var (\n") + for _, pv := range pendingVars { + g.p("\t%s = ", pv.name) + g.writeAST(pv.typeInfo, pv.expr) + g.p("\n") + } + g.p(")\n\n") + } + return nil +} + +// rewritePkgRefs rewrites any package references in an AST into references for the +// generated package. +func (g *gen) rewritePkgRefs(info *types.Info, node ast.Node) ast.Node { + start, end := node.Pos(), node.End() + node = copyAST(node) + // First, rewrite all package names. This lets us know all the + // potentially colliding identifiers. + node = astutil.Apply(node, func(c *astutil.Cursor) bool { + switch node := c.Node().(type) { + case *ast.Ident: + // This is an unqualified identifier (qualified identifiers are peeled off below). + obj := info.ObjectOf(node) + if obj == nil { + return false + } + if pkg := obj.Pkg(); pkg != nil && obj.Parent() == pkg.Scope() && pkg.Path() != g.pkg.PkgPath { + // An identifier from either a dot import or read from a different package. + newPkgID := g.qualifyImport(pkg.Name(), pkg.Path()) + c.Replace(&ast.SelectorExpr{ + X: ast.NewIdent(newPkgID), + Sel: ast.NewIdent(node.Name), + }) + return false + } + return true + case *ast.SelectorExpr: + pkgIdent, ok := node.X.(*ast.Ident) + if !ok { + return true + } + pkgName, ok := info.ObjectOf(pkgIdent).(*types.PkgName) + if !ok { + return true + } + // This is a qualified identifier. Rewrite and avoid visiting subexpressions. + imported := pkgName.Imported() + newPkgID := g.qualifyImport(imported.Name(), imported.Path()) + c.Replace(&ast.SelectorExpr{ + X: ast.NewIdent(newPkgID), + Sel: ast.NewIdent(node.Sel.Name), + }) + return false + default: + return true + } + }, nil) + // Now that we have all the identifiers, rename any variables declared + // in this scope to not collide. + newNames := make(map[types.Object]string) + inNewNames := func(n string) bool { + for _, other := range newNames { + if other == n { + return true + } + } + return false + } + var scopeStack []*types.Scope + pkgScope := g.pkg.Types.Scope() + node = astutil.Apply(node, func(c *astutil.Cursor) bool { + if scope := info.Scopes[c.Node()]; scope != nil { + scopeStack = append(scopeStack, scope) + } + id, ok := c.Node().(*ast.Ident) + if !ok { + return true + } + obj := info.ObjectOf(id) + if obj == nil { + // We rewrote this identifier earlier, so it does not need + // further rewriting. + return true + } + if n, ok := newNames[obj]; ok { + // We picked a new name for this symbol. Rewrite it. + c.Replace(ast.NewIdent(n)) + return false + } + if par := obj.Parent(); par == nil || par == pkgScope { + // Don't rename methods, field names, or top-level identifiers. + return true + } + + // Rename any symbols defined within rewritePkgRefs's node that conflict + // with any symbols in the generated file. + objName := obj.Name() + if pos := obj.Pos(); pos < start || end <= pos || !(g.nameInFileScope(objName) || inNewNames(objName)) { + return true + } + newName := disambiguate(objName, func(n string) bool { + if g.nameInFileScope(n) || inNewNames(n) { + return true + } + if len(scopeStack) > 0 { + // Avoid picking a name that conflicts with other names in the + // current scope. + _, obj := scopeStack[len(scopeStack)-1].LookupParent(n, token.NoPos) + if obj != nil { + return true + } + } + return false + }) + newNames[obj] = newName + c.Replace(ast.NewIdent(newName)) + return false + }, func(c *astutil.Cursor) bool { + if info.Scopes[c.Node()] != nil { + // Should be top of stack; pop it. + scopeStack = scopeStack[:len(scopeStack)-1] + } + return true + }) + return node +} + +// writeAST prints an AST node into the generated output, rewriting any +// package references it encounters. +func (g *gen) writeAST(info *types.Info, node ast.Node) { + node = g.rewritePkgRefs(info, node) + if err := printer.Fprint(&g.buf, g.pkg.Fset, node); err != nil { + panic(err) + } +} + +func (g *gen) qualifiedID(pkgName, pkgPath, sym string) string { + name := g.qualifyImport(pkgName, pkgPath) + if name == "" { + return sym + } + return name + "." + sym +} + +func (g *gen) qualifyImport(name, path string) string { + if path == g.pkg.PkgPath { + return "" + } + // TODO(light): This is depending on details of the current loader. + const vendorPart = "vendor/" + unvendored := path + if i := strings.LastIndex(path, vendorPart); i != -1 && (i == 0 || path[i-1] == '/') { + unvendored = path[i+len(vendorPart):] + } + if info, ok := g.imports[unvendored]; ok { + return info.name + } + // TODO(light): Use parts of import path to disambiguate. + newName := disambiguate(name, func(n string) bool { + // Don't let an import take the "err" name. That's annoying. + return n == "err" || g.nameInFileScope(n) + }) + g.imports[unvendored] = importInfo{ + name: newName, + differs: newName != name, + } + return newName +} + +func (g *gen) nameInFileScope(name string) bool { + for _, other := range g.imports { + if other.name == name { + return true + } + } + for _, other := range g.values { + if other == name { + return true + } + } + _, obj := g.pkg.Types.Scope().LookupParent(name, token.NoPos) + return obj != nil +} + +func (g *gen) qualifyPkg(pkg *types.Package) string { + return g.qualifyImport(pkg.Name(), pkg.Path()) +} + +func (g *gen) p(format string, args ...interface{}) { + fmt.Fprintf(&g.buf, format, args...) +} + +// injectorGen is the per-injector pass generator state. +type injectorGen struct { + g *gen + + paramNames []string + localNames []string + cleanupNames []string + errVar string + + // discard causes ig.p and ig.writeAST to no-op. Useful to run + // generation for side-effects like filling in g.imports. + discard bool +} + +// injectPass generates an injector given the output from analysis. +// The sig passed in should be verified. +func injectPass(name string, sig *types.Signature, calls []call, set *ProviderSet, doc *ast.CommentGroup, ig *injectorGen) { + params := sig.Params() + injectSig, err := funcOutput(sig) + if err != nil { + // This should be checked by the caller already. + panic(err) + } + if doc != nil { + for _, c := range doc.List { + ig.p("%s\n", c.Text) + } + } + ig.p("func %s(", name) + for i := 0; i < params.Len(); i++ { + if i > 0 { + ig.p(", ") + } + pi := params.At(i) + a := pi.Name() + if a == "" || a == "_" { + a = typeVariableName(pi.Type(), "arg", unexport, ig.nameInInjector) + } else { + a = disambiguate(a, ig.nameInInjector) + } + ig.paramNames = append(ig.paramNames, a) + if sig.Variadic() && i == params.Len()-1 { + // Keep the varargs signature instead of a slice for the last argument if the + // injector is variadic. + ig.p("%s ...%s", ig.paramNames[i], types.TypeString(pi.Type().(*types.Slice).Elem(), ig.g.qualifyPkg)) + } else { + ig.p("%s %s", ig.paramNames[i], types.TypeString(pi.Type(), ig.g.qualifyPkg)) + } + } + outTypeString := types.TypeString(injectSig.out, ig.g.qualifyPkg) + switch { + case injectSig.cleanup && injectSig.err: + ig.p(") (%s, func(), error) {\n", outTypeString) + case injectSig.cleanup: + ig.p(") (%s, func()) {\n", outTypeString) + case injectSig.err: + ig.p(") (%s, error) {\n", outTypeString) + default: + ig.p(") %s {\n", outTypeString) + } + for i := range calls { + c := &calls[i] + lname := typeVariableName(c.out, "v", unexport, ig.nameInInjector) + ig.localNames = append(ig.localNames, lname) + switch c.kind { + case structProvider: + ig.structProviderCall(lname, c) + case funcProviderCall: + ig.funcProviderCall(lname, c, injectSig) + case valueExpr: + ig.valueExpr(lname, c) + case selectorExpr: + ig.fieldExpr(lname, c) + default: + panic("unknown kind") + } + } + if len(calls) == 0 { + ig.p("\treturn %s", ig.paramNames[set.For(injectSig.out).Arg().Index]) + } else { + ig.p("\treturn %s", ig.localNames[len(calls)-1]) + } + if injectSig.cleanup { + ig.p(", func() {\n") + for i := len(ig.cleanupNames) - 1; i >= 0; i-- { + ig.p("\t\t%s()\n", ig.cleanupNames[i]) + } + ig.p("\t}") + } + if injectSig.err { + ig.p(", nil") + } + ig.p("\n}\n\n") +} + +func (ig *injectorGen) funcProviderCall(lname string, c *call, injectSig outputSignature) { + ig.p("\t%s", lname) + prevCleanup := len(ig.cleanupNames) + if c.hasCleanup { + cname := disambiguate("cleanup", ig.nameInInjector) + ig.cleanupNames = append(ig.cleanupNames, cname) + ig.p(", %s", cname) + } + if c.hasErr { + ig.p(", %s", ig.errVar) + } + ig.p(" := ") + ig.p("%s(", ig.g.qualifiedID(c.pkg.Name(), c.pkg.Path(), c.name)) + for i, a := range c.args { + if i > 0 { + ig.p(", ") + } + if a < len(ig.paramNames) { + ig.p("%s", ig.paramNames[a]) + } else { + ig.p("%s", ig.localNames[a-len(ig.paramNames)]) + } + } + if c.varargs { + ig.p("...") + } + ig.p(")\n") + if c.hasErr { + ig.p("\tif %s != nil {\n", ig.errVar) + for i := prevCleanup - 1; i >= 0; i-- { + ig.p("\t\t%s()\n", ig.cleanupNames[i]) + } + ig.p("\t\treturn %s", zeroValue(injectSig.out, ig.g.qualifyPkg)) + if injectSig.cleanup { + ig.p(", nil") + } + // TODO(light): Give information about failing provider. + ig.p(", err\n") + ig.p("\t}\n") + } +} + +func (ig *injectorGen) structProviderCall(lname string, c *call) { + ig.p("\t%s", lname) + ig.p(" := ") + if _, ok := c.out.(*types.Pointer); ok { + ig.p("&") + } + ig.p("%s{\n", ig.g.qualifiedID(c.pkg.Name(), c.pkg.Path(), c.name)) + for i, a := range c.args { + ig.p("\t\t%s: ", c.fieldNames[i]) + if a < len(ig.paramNames) { + ig.p("%s", ig.paramNames[a]) + } else { + ig.p("%s", ig.localNames[a-len(ig.paramNames)]) + } + ig.p(",\n") + } + ig.p("\t}\n") +} + +func (ig *injectorGen) valueExpr(lname string, c *call) { + ig.p("\t%s := %s\n", lname, ig.g.values[c.valueExpr]) +} + +func (ig *injectorGen) fieldExpr(lname string, c *call) { + a := c.args[0] + ig.p("\t%s := ", lname) + if c.ptrToField { + ig.p("&") + } + if a < len(ig.paramNames) { + ig.p("%s.%s\n", ig.paramNames[a], c.name) + } else { + ig.p("%s.%s\n", ig.localNames[a-len(ig.paramNames)], c.name) + } +} + +// nameInInjector reports whether name collides with any other identifier +// in the current injector. +func (ig *injectorGen) nameInInjector(name string) bool { + if name == ig.errVar { + return true + } + for _, a := range ig.paramNames { + if a == name { + return true + } + } + for _, l := range ig.localNames { + if l == name { + return true + } + } + for _, l := range ig.cleanupNames { + if l == name { + return true + } + } + return ig.g.nameInFileScope(name) +} + +func (ig *injectorGen) p(format string, args ...interface{}) { + if ig.discard { + return + } + ig.g.p(format, args...) +} + +// zeroValue returns the shortest expression that evaluates to the zero +// value for the given type. +func zeroValue(t types.Type, qf types.Qualifier) string { + switch u := t.Underlying().(type) { + case *types.Array, *types.Struct: + return types.TypeString(t, qf) + "{}" + case *types.Basic: + info := u.Info() + switch { + case info&types.IsBoolean != 0: + return "false" + case info&(types.IsInteger|types.IsFloat|types.IsComplex) != 0: + return "0" + case info&types.IsString != 0: + return `""` + default: + panic("unreachable") + } + case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice: + return "nil" + default: + panic("unreachable") + } +} + +// typeVariableName invents a disambiguated variable name derived from the type name. +// If no name can be derived from the type, defaultName is used. +// transform is used to transform the derived name(s) (including defaultName); +// commonly used functions include export and unexport. +// collides is used to see if a name is ambiguous. If any one of the derived +// names is unambiguous, it used; otherwise, the first derived name is +// disambiguated using disambiguate(). +func typeVariableName(t types.Type, defaultName string, transform func(string) string, collides func(string) bool) string { + if p, ok := t.(*types.Pointer); ok { + t = p.Elem() + } + var names []string + switch t := t.(type) { + case *types.Basic: + if t.Name() != "" { + names = append(names, t.Name()) + } + case *types.Named: + obj := t.Obj() + if name := obj.Name(); name != "" { + names = append(names, name) + } + // Provide an alternate name prefixed with the package name if possible. + // E.g., in case of collisions, we'll use "fooCfg" instead of "cfg2". + if pkg := obj.Pkg(); pkg != nil && pkg.Name() != "" { + names = append(names, fmt.Sprintf("%s%s", pkg.Name(), strings.Title(obj.Name()))) + } + } + + // If we were unable to derive a name, use defaultName. + if len(names) == 0 { + names = append(names, defaultName) + } + + // Transform the name(s). + for i, name := range names { + names[i] = transform(name) + } + + // See if there's an unambiguous name; if so, use it. + for _, name := range names { + if !token.Lookup(name).IsKeyword() && !collides(name) { + return name + } + } + // Otherwise, disambiguate the first name. + return disambiguate(names[0], collides) +} + +// unexport converts a name that is potentially exported to an unexported name. +func unexport(name string) string { + if name == "" { + return "" + } + r, sz := utf8.DecodeRuneInString(name) + if !unicode.IsUpper(r) { + // foo -> foo + return name + } + r2, sz2 := utf8.DecodeRuneInString(name[sz:]) + if !unicode.IsUpper(r2) { + // Foo -> foo + return string(unicode.ToLower(r)) + name[sz:] + } + // UPPERWord -> upperWord + sbuf := new(strings.Builder) + sbuf.WriteRune(unicode.ToLower(r)) + i := sz + r, sz = r2, sz2 + for unicode.IsUpper(r) && sz > 0 { + r2, sz2 := utf8.DecodeRuneInString(name[i+sz:]) + if sz2 > 0 && unicode.IsLower(r2) { + break + } + i += sz + sbuf.WriteRune(unicode.ToLower(r)) + r, sz = r2, sz2 + } + sbuf.WriteString(name[i:]) + return sbuf.String() +} + +// export converts a name that is potentially unexported to an exported name. +func export(name string) string { + if name == "" { + return "" + } + r, sz := utf8.DecodeRuneInString(name) + if unicode.IsUpper(r) { + // Foo -> Foo + return name + } + // fooBar -> FooBar + sbuf := new(strings.Builder) + sbuf.WriteRune(unicode.ToUpper(r)) + sbuf.WriteString(name[sz:]) + return sbuf.String() +} + +// disambiguate picks a unique name, preferring name if it is already unique. +// It also disambiguates against Go's reserved keywords. +func disambiguate(name string, collides func(string) bool) string { + if !token.Lookup(name).IsKeyword() && !collides(name) { + return name + } + buf := []byte(name) + if len(buf) > 0 && buf[len(buf)-1] >= '0' && buf[len(buf)-1] <= '9' { + buf = append(buf, '_') + } + base := len(buf) + for n := 2; ; n++ { + buf = strconv.AppendInt(buf[:base], int64(n), 10) + sbuf := string(buf) + if !token.Lookup(sbuf).IsKeyword() && !collides(sbuf) { + return sbuf + } + } +} + +// accessibleFrom reports whether node can be copied to wantPkg without +// violating Go visibility rules. +func accessibleFrom(info *types.Info, node ast.Node, wantPkg string) error { + var unexportError error + ast.Inspect(node, func(node ast.Node) bool { + if unexportError != nil { + return false + } + ident, ok := node.(*ast.Ident) + if !ok { + return true + } + obj := info.ObjectOf(ident) + if _, ok := obj.(*types.PkgName); ok { + // Local package names are fine, since we can just reimport them. + return true + } + if pkg := obj.Pkg(); pkg != nil { + if !ast.IsExported(ident.Name) && pkg.Path() != wantPkg { + unexportError = fmt.Errorf("uses unexported identifier %s", obj.Name()) + return false + } + if obj.Parent() != nil && obj.Parent() != pkg.Scope() { + unexportError = fmt.Errorf("%s is not declared in package scope", obj.Name()) + return false + } + } + return true + }) + return unexportError +} + +var ( + errorType = types.Universe.Lookup("error").Type() + cleanupType = types.NewSignature(nil, nil, nil, false) +) diff --git a/pkg/build/wire/internal/wire/wire_test.go b/pkg/build/wire/internal/wire/wire_test.go new file mode 100644 index 00000000000..9b1493327e7 --- /dev/null +++ b/pkg/build/wire/internal/wire/wire_test.go @@ -0,0 +1,559 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire + +import ( + "bytes" + "context" + "flag" + "fmt" + "go/build" + "go/types" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp" +) + +var record = flag.Bool("record", false, "whether to run tests against cloud resources and record the interactions") + +func TestWire(t *testing.T) { + const testRoot = "testdata" + testdataEnts, err := ioutil.ReadDir(testRoot) // ReadDir sorts by name. + if err != nil { + t.Fatal(err) + } + // The marker function package source is needed to have the test cases + // type check. loadTestCase places this file at the well-known import path. + wireGo, err := ioutil.ReadFile(filepath.Join("..", "..", "wire.go")) + if err != nil { + t.Fatal(err) + } + tests := make([]*testCase, 0, len(testdataEnts)) + for _, ent := range testdataEnts { + name := ent.Name() + if !ent.IsDir() || strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") { + continue + } + test, err := loadTestCase(filepath.Join(testRoot, name), wireGo) + if err != nil { + t.Error(err) + continue + } + tests = append(tests, test) + } + + var goToolPath string + if *record { + goToolPath = filepath.Join(build.Default.GOROOT, "bin", "go") + if _, err := os.Stat(goToolPath); err != nil { + t.Fatal("go toolchain not available:", err) + } + } + ctx := context.Background() + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + // Materialize a temporary GOPATH directory. + gopath, err := ioutil.TempDir("", "wire_test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(gopath) + gopath, err = filepath.EvalSymlinks(gopath) + if err != nil { + t.Fatal(err) + } + if err := test.materialize(gopath); err != nil { + t.Fatal(err) + } + wd := filepath.Join(gopath, "src", "example.com") + gens, errs := Generate(ctx, wd, append(os.Environ(), "GOPATH="+gopath), []string{test.pkg}, &GenerateOptions{Header: test.header}) + var gen GenerateResult + if len(gens) > 1 { + t.Fatalf("got %d generated files, want 0 or 1", len(gens)) + } + if len(gens) == 1 { + gen = gens[0] + if len(gen.Errs) > 0 { + errs = append(errs, gen.Errs...) + } + if len(gen.Content) > 0 { + defer t.Logf("wire_gen.go:\n%s", gen.Content) + } + } + if len(errs) > 0 { + gotErrStrings := make([]string, len(errs)) + for i, e := range errs { + t.Log(e.Error()) + gotErrStrings[i] = scrubError(gopath, e.Error()) + } + if !test.wantWireError { + t.Fatal("Did not expect errors. To -record an error, create want/wire_errs.txt.") + } + if *record { + wireErrsFile := filepath.Join(testRoot, test.name, "want", "wire_errs.txt") + if err := ioutil.WriteFile(wireErrsFile, []byte(strings.Join(gotErrStrings, "\n\n")), 0666); err != nil { + t.Fatalf("failed to write wire_errs.txt file: %v", err) + } + } else { + if diff := cmp.Diff(gotErrStrings, test.wantWireErrorStrings); diff != "" { + t.Errorf("Errors didn't match expected errors from wire_errors.txt:\n%s", diff) + } + } + return + } + if test.wantWireError { + t.Fatal("wire succeeded; want error") + } + outPathSane := true + if prefix := gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator); !strings.HasPrefix(gen.OutputPath, prefix) { + outPathSane = false + t.Errorf("suggested output path = %q; want to start with %q", gen.OutputPath, prefix) + } + + if *record { + // Record ==> Build the generated Wire code, + // check that the program's output matches the + // expected output, save wire output on + // success. + if !outPathSane { + return + } + if err := gen.Commit(); err != nil { + t.Fatalf("failed to write wire_gen.go to test GOPATH: %v", err) + } + if err := goBuildCheck(goToolPath, gopath, test); err != nil { + t.Fatalf("go build check failed: %v", err) + } + testdataWireGenPath := filepath.Join(testRoot, test.name, "want", "wire_gen.go") + if err := ioutil.WriteFile(testdataWireGenPath, gen.Content, 0666); err != nil { + t.Fatalf("failed to record wire_gen.go to testdata: %v", err) + } + } else { + // Replay ==> Load golden file and compare to + // generated result. This check is meant to + // detect non-deterministic behavior in the + // Generate function. + if !bytes.Equal(gen.Content, test.wantWireOutput) { + gotS, wantS := string(gen.Content), string(test.wantWireOutput) + diff := cmp.Diff(strings.Split(gotS, "\n"), strings.Split(wantS, "\n")) + t.Fatalf("wire output differs from golden file. If this change is expected, run with -record to update the wire_gen.go file.\n*** got:\n%s\n\n*** want:\n%s\n\n*** diff:\n%s", gotS, wantS, diff) + } + } + }) + } +} + +func goBuildCheck(goToolPath, gopath string, test *testCase) error { + // Run `go build`. + testExePath := filepath.Join(gopath, "bin", "testprog") + buildCmd := []string{"build", "-o", testExePath} + buildCmd = append(buildCmd, test.pkg) + cmd := exec.Command(goToolPath, buildCmd...) + cmd.Dir = filepath.Join(gopath, "src", "example.com") + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + if buildOut, err := cmd.CombinedOutput(); err != nil { + if len(buildOut) > 0 { + return fmt.Errorf("build: %v; output:\n%s", err, buildOut) + } + return fmt.Errorf("build: %v", err) + } + + // Run the resulting program and compare its output to the expected + // output. + out, err := exec.Command(testExePath).Output() + if err != nil { + return fmt.Errorf("run compiled program: %v", err) + } + if !bytes.Equal(out, test.wantProgramOutput) { + gotS, wantS := string(out), string(test.wantProgramOutput) + diff := cmp.Diff(strings.Split(gotS, "\n"), strings.Split(wantS, "\n")) + return fmt.Errorf("compiled program output doesn't match:\n*** got:\n%s\n\n*** want:\n%s\n\n*** diff:\n%s", gotS, wantS, diff) + } + return nil +} + +func TestUnexport(t *testing.T) { + tests := []struct { + name string + want string + }{ + {"", ""}, + {"a", "a"}, + {"ab", "ab"}, + {"A", "a"}, + {"AB", "ab"}, + {"A_", "a_"}, + {"ABc", "aBc"}, + {"ABC", "abc"}, + {"AB_", "ab_"}, + {"foo", "foo"}, + {"Foo", "foo"}, + {"HTTPClient", "httpClient"}, + {"IFace", "iFace"}, + {"SNAKE_CASE", "snake_CASE"}, + {"HTTP", "http"}, + } + for _, test := range tests { + if got := unexport(test.name); got != test.want { + t.Errorf("unexport(%q) = %q; want %q", test.name, got, test.want) + } + } +} + +func TestExport(t *testing.T) { + tests := []struct { + name string + want string + }{ + {"", ""}, + {"a", "A"}, + {"ab", "Ab"}, + {"A", "A"}, + {"AB", "AB"}, + {"A_", "A_"}, + {"ABc", "ABc"}, + {"ABC", "ABC"}, + {"AB_", "AB_"}, + {"foo", "Foo"}, + {"Foo", "Foo"}, + {"HTTPClient", "HTTPClient"}, + {"httpClient", "HttpClient"}, + {"IFace", "IFace"}, + {"iFace", "IFace"}, + {"SNAKE_CASE", "SNAKE_CASE"}, + {"HTTP", "HTTP"}, + } + for _, test := range tests { + if got := export(test.name); got != test.want { + t.Errorf("export(%q) = %q; want %q", test.name, got, test.want) + } + } +} + +func TestTypeVariableName(t *testing.T) { + var ( + boolT = types.Typ[types.Bool] + stringT = types.Typ[types.String] + fooVarT = types.NewNamed(types.NewTypeName(0, nil, "foo", stringT), stringT, nil) + nonameVarT = types.NewNamed(types.NewTypeName(0, nil, "", stringT), stringT, nil) + barVarInFooPkgT = types.NewNamed(types.NewTypeName(0, types.NewPackage("my.example/foo", "foo"), "bar", stringT), stringT, nil) + ) + tests := []struct { + description string + typ types.Type + defaultName string + transformAppend string + collides map[string]bool + want string + }{ + {"basic type", boolT, "", "", map[string]bool{}, "bool"}, + {"basic type with transform", boolT, "", "suffix", map[string]bool{}, "boolsuffix"}, + {"basic type with collision", boolT, "", "", map[string]bool{"bool": true}, "bool2"}, + {"basic type with transform and collision", boolT, "", "suffix", map[string]bool{"boolsuffix": true}, "boolsuffix2"}, + {"a different basic type", stringT, "", "", map[string]bool{}, "string"}, + {"named type", fooVarT, "", "", map[string]bool{}, "foo"}, + {"named type with transform", fooVarT, "", "suffix", map[string]bool{}, "foosuffix"}, + {"named type with collision", fooVarT, "", "", map[string]bool{"foo": true}, "foo2"}, + {"named type with transform and collision", fooVarT, "", "suffix", map[string]bool{"foosuffix": true}, "foosuffix2"}, + {"noname type", nonameVarT, "bar", "", map[string]bool{}, "bar"}, + {"noname type with transform", nonameVarT, "bar", "s", map[string]bool{}, "bars"}, + {"noname type with transform and collision", nonameVarT, "bar", "s", map[string]bool{"bars": true}, "bars2"}, + {"var in pkg type", barVarInFooPkgT, "", "", map[string]bool{}, "bar"}, + {"var in pkg type with collision", barVarInFooPkgT, "", "", map[string]bool{"bar": true}, "fooBar"}, + {"var in pkg type with double collision", barVarInFooPkgT, "", "", map[string]bool{"bar": true, "fooBar": true}, "bar2"}, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%s: typeVariableName(%v, %q, %q, %v)", test.description, test.typ, test.defaultName, test.transformAppend, test.collides), func(t *testing.T) { + got := typeVariableName(test.typ, test.defaultName, func(name string) string { return name + test.transformAppend }, func(name string) bool { return test.collides[name] }) + if !isIdent(got) { + t.Errorf("%q is not an identifier", got) + } + if got != test.want { + t.Errorf("got %q want %q", got, test.want) + } + if test.collides[got] { + t.Errorf("%q collides", got) + } + }) + } +} + +func TestDisambiguate(t *testing.T) { + tests := []struct { + name string + want string + collides map[string]bool + }{ + {"foo", "foo", nil}, + {"foo", "foo2", map[string]bool{"foo": true}}, + {"foo", "foo3", map[string]bool{"foo": true, "foo1": true, "foo2": true}}, + {"foo1", "foo1_2", map[string]bool{"foo": true, "foo1": true, "foo2": true}}, + {"foo\u0661", "foo\u0661", map[string]bool{"foo": true, "foo1": true, "foo2": true}}, + {"foo\u0661", "foo\u06612", map[string]bool{"foo": true, "foo1": true, "foo2": true, "foo\u0661": true}}, + {"select", "select2", nil}, + {"var", "var2", nil}, + } + for _, test := range tests { + t.Run(fmt.Sprintf("disambiguate(%q, %v)", test.name, test.collides), func(t *testing.T) { + got := disambiguate(test.name, func(name string) bool { return test.collides[name] }) + if !isIdent(got) { + t.Errorf("%q is not an identifier", got) + } + if got != test.want { + t.Errorf("got %q want %q", got, test.want) + } + if test.collides[got] { + t.Errorf("%q collides", got) + } + }) + } +} + +func isIdent(s string) bool { + if len(s) == 0 { + return false + } + r, i := utf8.DecodeRuneInString(s) + if !unicode.IsLetter(r) && r != '_' { + return false + } + for i < len(s) { + r, sz := utf8.DecodeRuneInString(s[i:]) + if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '_' { + return false + } + i += sz + } + return true +} + +// scrubError rewrites the given string to remove occurrences of GOPATH/src, +// rewrites OS-specific path separators to slashes, and any line/column +// information to a fixed ":x:y". For example, if the gopath parameter is +// "C:\GOPATH" and running on Windows, the string +// "C:\GOPATH\src\foo\bar.go:15:4" would be rewritten to "foo/bar.go:x:y". +func scrubError(gopath string, s string) string { + sb := new(strings.Builder) + query := gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator) + for { + // Find next occurrence of source root. This indicates the next path to + // scrub. + start := strings.Index(s, query) + if start == -1 { + sb.WriteString(s) + break + } + + // Find end of file name (extension ".go"). + fileStart := start + len(query) + fileEnd := strings.Index(s[fileStart:], ".go") + if fileEnd == -1 { + // If no ".go" occurs to end of string, further searches will fail too. + // Break the loop. + sb.WriteString(s) + break + } + fileEnd += fileStart + 3 // Advance to end of extension. + + // Write out file name and advance scrub position. + file := s[fileStart:fileEnd] + if os.PathSeparator != '/' { + file = strings.Replace(file, string(os.PathSeparator), "/", -1) + } + sb.WriteString(s[:start]) + sb.WriteString(file) + s = s[fileEnd:] + + // Peek past to see if there is line/column info. + linecol, linecolLen := scrubLineColumn(s) + sb.WriteString(linecol) + s = s[linecolLen:] + } + return sb.String() +} + +func scrubLineColumn(s string) (replacement string, n int) { + if !strings.HasPrefix(s, ":") { + return "", 0 + } + // Skip first colon and run of digits. + for n++; len(s) > n && '0' <= s[n] && s[n] <= '9'; { + n++ + } + if n == 1 { + // No digits followed colon. + return "", 0 + } + + // Start on column part. + if !strings.HasPrefix(s[n:], ":") { + return ":x", n + } + lineEnd := n + // Skip second colon and run of digits. + for n++; len(s) > n && '0' <= s[n] && s[n] <= '9'; { + n++ + } + if n == lineEnd+1 { + // No digits followed second colon. + return ":x", lineEnd + } + return ":x:y", n +} + +type testCase struct { + name string + pkg string + header []byte + goFiles map[string][]byte + wantProgramOutput []byte + wantWireOutput []byte + wantWireError bool + wantWireErrorStrings []string +} + +// loadTestCase reads a test case from a directory. +// +// The directory structure is: +// +// root/ +// +// pkg +// file containing the package name containing the inject function +// (must also be package main) +// +// ... +// any Go files found recursively placed under GOPATH/src/... +// +// want/ +// +// wire_errs.txt +// Expected errors from the Wire Generate function, +// missing if no errors expected. +// Distinct errors are separated by a blank line, +// and line numbers and line positions are scrubbed +// (e.g. "$GOPATH/src/foo.go:52:8" --> "foo.go:x:y"). +// +// wire_gen.go +// verified output of wire from a test run with +// -record, missing if wire_errs.txt is present +// +// program_out.txt +// expected output from the final compiled program, +// missing if wire_errs.txt is present +func loadTestCase(root string, wireGoSrc []byte) (*testCase, error) { + name := filepath.Base(root) + pkg, err := ioutil.ReadFile(filepath.Join(root, "pkg")) + if err != nil { + return nil, fmt.Errorf("load test case %s: %v", name, err) + } + header, _ := ioutil.ReadFile(filepath.Join(root, "header")) + var wantProgramOutput []byte + var wantWireOutput []byte + wireErrb, err := ioutil.ReadFile(filepath.Join(root, "want", "wire_errs.txt")) + wantWireError := err == nil + var wantWireErrorStrings []string + if wantWireError { + wantWireErrorStrings = strings.Split(string(wireErrb), "\n\n") + } else { + if !*record { + wantWireOutput, err = ioutil.ReadFile(filepath.Join(root, "want", "wire_gen.go")) + if err != nil { + return nil, fmt.Errorf("load test case %s: %v, if this is a new testcase, run with -record to generate the wire_gen.go file", name, err) + } + } + wantProgramOutput, err = ioutil.ReadFile(filepath.Join(root, "want", "program_out.txt")) + if err != nil { + return nil, fmt.Errorf("load test case %s: %v", name, err) + } + } + goFiles := map[string][]byte{ + "github.com/google/wire/wire.go": wireGoSrc, + "github.com/grafana/grafana/pkg/build/wire/wire.go": wireGoSrc, + } + err = filepath.Walk(root, func(src string, info os.FileInfo, err error) error { + if err != nil { + return err + } + rel, err := filepath.Rel(root, src) + if err != nil { + return err // unlikely + } + if info.Mode().IsDir() && rel == "want" { + // The "want" directory should not be included in goFiles. + return filepath.SkipDir + } + if !info.Mode().IsRegular() || filepath.Ext(src) != ".go" { + return nil + } + data, err := ioutil.ReadFile(src) + if err != nil { + return err + } + goFiles["example.com/"+filepath.ToSlash(rel)] = data + return nil + }) + if err != nil { + return nil, fmt.Errorf("load test case %s: %v", name, err) + } + return &testCase{ + name: name, + pkg: string(bytes.TrimSpace(pkg)), + header: header, + goFiles: goFiles, + wantWireOutput: wantWireOutput, + wantProgramOutput: wantProgramOutput, + wantWireError: wantWireError, + wantWireErrorStrings: wantWireErrorStrings, + }, nil +} + +// materialize creates a new GOPATH at the given directory, which may or +// may not exist. +func (test *testCase) materialize(gopath string) error { + for name, content := range test.goFiles { + dst := filepath.Join(gopath, "src", filepath.FromSlash(name)) + if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { + return fmt.Errorf("materialize GOPATH: %v", err) + } + if err := ioutil.WriteFile(dst, content, 0666); err != nil { + return fmt.Errorf("materialize GOPATH: %v", err) + } + } + + // Add go.mod files to example.com and github.com/google/wire. + const importPath = "example.com" + const depPath = "github.com/grafana/grafana/pkg/build/wire" + depLoc := filepath.Join(gopath, "src", filepath.FromSlash(depPath)) + example := fmt.Sprintf("module %s\n\nrequire %s v0.1.0\nreplace %s => %s\n", importPath, depPath, depPath, depLoc) + gomod := filepath.Join(gopath, "src", filepath.FromSlash(importPath), "go.mod") + if err := ioutil.WriteFile(gomod, []byte(example), 0666); err != nil { + return fmt.Errorf("generate go.mod for %s: %v", gomod, err) + } + if err := ioutil.WriteFile(filepath.Join(depLoc, "go.mod"), []byte("module "+depPath+"\n"), 0666); err != nil { + return fmt.Errorf("generate go.mod for %s: %v", depPath, err) + } + return nil +} diff --git a/pkg/build/wire/wire.go b/pkg/build/wire/wire.go new file mode 100644 index 00000000000..6af91dda6cf --- /dev/null +++ b/pkg/build/wire/wire.go @@ -0,0 +1,196 @@ +// Copyright 2018 The Wire Authors +// +// 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 +// +// https://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 wire contains directives for Wire code generation. +// For an overview of working with Wire, see the user guide at +// https://github.com/google/wire/blob/master/docs/guide.md +// +// The directives in this package are used as input to the Wire code generation +// tool. The entry point of Wire's analysis are injector functions: function +// templates denoted by only containing a call to Build. The arguments to Build +// describes a set of providers and the Wire code generation tool builds a +// directed acylic graph of the providers' output types. The generated code will +// fill in the function template by using the providers from the provider set to +// instantiate any needed types. +package wire + +// ProviderSet is a marker type that collects a group of providers. +type ProviderSet struct{} + +// NewSet creates a new provider set that includes the providers in its +// arguments. Each argument is a function value, a provider set, a call to +// Struct, a call to Bind, a call to Value, a call to InterfaceValue or a call +// to FieldsOf. +// +// Passing a function value to NewSet declares that the function's first +// return value type will be provided by calling the function. The arguments +// to the function will come from the providers for their types. As such, all +// the function's parameters must be of non-identical types. The function may +// optionally return an error as its last return value and a cleanup function +// as the second return value. A cleanup function must be of type func() and is +// guaranteed to be called before the cleanup function of any of the +// provider's inputs. If any provider returns an error, the injector function +// will call all the appropriate cleanup functions and return the error from +// the injector function. +// +// Passing a ProviderSet to NewSet is the same as if the set's contents +// were passed as arguments to NewSet directly. +// +// The behavior of passing the result of a call to other functions in this +// package are described in their respective doc comments. +// +// For compatibility with older versions of Wire, passing a struct value of type +// S to NewSet declares that both S and *S will be provided by creating a new +// value of the appropriate type by filling in each field of S using the +// provider of the field's type. This form is deprecated and will be removed in +// a future version of Wire: new providers sets should use wire.Struct. +func NewSet(...interface{}) ProviderSet { + return ProviderSet{} +} + +// Build is placed in the body of an injector function template to declare the +// providers to use. The Wire code generation tool will fill in an +// implementation of the function. The arguments to Build are interpreted the +// same as NewSet: they determine the provider set presented to Wire's +// dependency graph. Build returns an error message that can be sent to a call +// to panic(). +// +// The parameters of the injector function are used as inputs in the dependency +// graph. +// +// Similar to provider functions passed into NewSet, the first return value is +// the output of the injector function, the optional second return value is a +// cleanup function, and the optional last return value is an error. If any of +// the provider functions in the injector function's provider set return errors +// or cleanup functions, the corresponding return value must be present in the +// injector function template. +// +// Examples: +// +// func injector(ctx context.Context) (*sql.DB, error) { +// wire.Build(otherpkg.FooSet, myProviderFunc) +// return nil, nil +// } +// +// func injector(ctx context.Context) (*sql.DB, error) { +// panic(wire.Build(otherpkg.FooSet, myProviderFunc)) +// } +func Build(...interface{}) string { + return "implementation not generated, run wire" +} + +// A Binding maps an interface to a concrete type. +type Binding struct{} + +// Bind declares that a concrete type should be used to satisfy a dependency on +// the type of iface. iface must be a pointer to an interface type, to must be a +// pointer to a concrete type. +// +// Example: +// +// type Fooer interface { +// Foo() +// } +// +// type MyFoo struct{} +// +// func (MyFoo) Foo() {} +// +// var MySet = wire.NewSet( +// wire.Struct(new(MyFoo)) +// wire.Bind(new(Fooer), new(MyFoo))) +func Bind(iface, to interface{}) Binding { + return Binding{} +} + +// bindToUsePointer is detected by the wire tool to indicate that Bind's second argument should take a pointer. +// See https://github.com/google/wire/issues/120 for details. +const bindToUsePointer = true + +// A ProvidedValue is an expression that is copied to the generated injector. +type ProvidedValue struct{} + +// Value binds an expression to provide the type of the expression. +// The expression may not be an interface value; use InterfaceValue for that. +// +// Example: +// +// var MySet = wire.NewSet(wire.Value([]string(nil))) +func Value(interface{}) ProvidedValue { + return ProvidedValue{} +} + +// InterfaceValue binds an expression to provide a specific interface type. +// The first argument is a pointer to the interface which user wants to provide. +// The second argument is the actual variable value whose type implements the +// interface. +// +// Example: +// +// var MySet = wire.NewSet(wire.InterfaceValue(new(io.Reader), os.Stdin)) +func InterfaceValue(typ interface{}, x interface{}) ProvidedValue { + return ProvidedValue{} +} + +// A StructProvider represents a named struct. +type StructProvider struct{} + +// Struct specifies that the given struct type will be provided by filling in +// the fields in the struct that have the names given. +// +// The first argument must be a pointer to the struct type. For a struct type +// Foo, Wire will use field-filling to provide both Foo and *Foo. The remaining +// arguments are field names to fill in. As a special case, if a single name "*" +// is given, then all of the fields in the struct will be filled in. +// +// For example: +// +// type S struct { +// MyFoo *Foo +// MyBar *Bar +// } +// var Set = wire.NewSet(wire.Struct(new(S), "MyFoo")) -> inject only S.MyFoo +// var Set = wire.NewSet(wire.Struct(new(S), "*")) -> inject all fields +func Struct(structType interface{}, fieldNames ...string) StructProvider { + return StructProvider{} +} + +// StructFields is a collection of the fields from a struct. +type StructFields struct{} + +// FieldsOf declares that the fields named of the given struct type will be used +// to provide the types of those fields. The structType argument must be a +// pointer to the struct or a pointer to a pointer to the struct it wishes to reference. +// +// The following example would provide Foo and Bar using S.MyFoo and S.MyBar respectively: +// +// type S struct { +// MyFoo Foo +// MyBar Bar +// } +// +// func NewStruct() S { /* ... */ } +// var Set = wire.NewSet(wire.FieldsOf(new(S), "MyFoo", "MyBar")) +// +// or +// +// func NewStruct() *S { /* ... */ } +// var Set = wire.NewSet(wire.FieldsOf(new(*S), "MyFoo", "MyBar")) +// +// If the structType argument is a pointer to a pointer to a struct, then FieldsOf +// additionally provides a pointer to each field type (e.g., *Foo and *Bar in the +// example above). +func FieldsOf(structType interface{}, fieldNames ...string) StructFields { + return StructFields{} +} diff --git a/pkg/services/pluginsintegration/pluginsintegration.go b/pkg/services/pluginsintegration/pluginsintegration.go index d00fdaaa1bd..5c67ebf1eab 100644 --- a/pkg/services/pluginsintegration/pluginsintegration.go +++ b/pkg/services/pluginsintegration/pluginsintegration.go @@ -2,6 +2,7 @@ package pluginsintegration import ( "github.com/google/wire" + "github.com/prometheus/client_golang/prometheus" "github.com/grafana/grafana/pkg/infra/tracing"