From c7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 4 Jul 2020 11:12:55 +0200 Subject: go.mod: vendor golangci-lint --- vendor/github.com/golangci/revgrep/.gitignore | 1 + vendor/github.com/golangci/revgrep/.travis.yml | 7 + vendor/github.com/golangci/revgrep/LICENSE | 201 +++++++++++++ vendor/github.com/golangci/revgrep/README.md | 58 ++++ vendor/github.com/golangci/revgrep/revgrep.go | 395 +++++++++++++++++++++++++ 5 files changed, 662 insertions(+) create mode 100644 vendor/github.com/golangci/revgrep/.gitignore create mode 100644 vendor/github.com/golangci/revgrep/.travis.yml create mode 100644 vendor/github.com/golangci/revgrep/LICENSE create mode 100644 vendor/github.com/golangci/revgrep/README.md create mode 100644 vendor/github.com/golangci/revgrep/revgrep.go (limited to 'vendor/github.com/golangci/revgrep') diff --git a/vendor/github.com/golangci/revgrep/.gitignore b/vendor/github.com/golangci/revgrep/.gitignore new file mode 100644 index 000000000..0540fe2ca --- /dev/null +++ b/vendor/github.com/golangci/revgrep/.gitignore @@ -0,0 +1 @@ +testdata/git diff --git a/vendor/github.com/golangci/revgrep/.travis.yml b/vendor/github.com/golangci/revgrep/.travis.yml new file mode 100644 index 000000000..d16d8b5bd --- /dev/null +++ b/vendor/github.com/golangci/revgrep/.travis.yml @@ -0,0 +1,7 @@ +language: go +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -service=travis-ci + diff --git a/vendor/github.com/golangci/revgrep/LICENSE b/vendor/github.com/golangci/revgrep/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/golangci/revgrep/LICENSE @@ -0,0 +1,201 @@ + 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/vendor/github.com/golangci/revgrep/README.md b/vendor/github.com/golangci/revgrep/README.md new file mode 100644 index 000000000..31faefee9 --- /dev/null +++ b/vendor/github.com/golangci/revgrep/README.md @@ -0,0 +1,58 @@ +# Overview + +[![Build Status](https://travis-ci.org/bradleyfalzon/revgrep.svg?branch=master)](https://travis-ci.org/bradleyfalzon/revgrep) [![Coverage +Status](https://coveralls.io/repos/github/bradleyfalzon/revgrep/badge.svg?branch=master)](https://coveralls.io/github/bradleyfalzon/revgrep?branch=master) [![GoDoc](https://godoc.org/github.com/bradleyfalzon/revgrep?status.svg)](https://godoc.org/github.com/bradleyfalzon/revgrep) + +`revgrep` is a CLI tool used to filter static analysis tools to only lines changed based on a commit reference. + +# Install + +```bash +go get -u github.com/bradleyfalzon/revgrep/... +``` + +# Usage + +In the scenario below, a change was made causing a warning in `go vet` on line 5, but `go vet` will show all warnings. +Using `revgrep`, you can show only warnings for lines of code that have been changed (in this case, hiding line 6). + +```bash +[user@host dir (master)]$ go vet +main.go:5: missing argument for Sprintf("%s"): format reads arg 1, have only 0 args +main.go:6: missing argument for Sprintf("%s"): format reads arg 1, have only 0 args +[user@host dir (master)]$ go vet |& revgrep +main.go:5: missing argument for Sprintf("%s"): format reads arg 1, have only 0 args +``` + +`|&` is shown above as many static analysis programs write to `stderr`, not `stdout`, `|&` combines both `stderr` and +`stdout`. It could also be achieved with `go vet 2>&1 | revgrep`. + +`revgrep` CLI tool will return an exit status of 1 if any issues match, else it will return 0. Consider using +`${PIPESTATUS[0]}` for the exit status of the `go vet` command in the above example. + +``` +Usage: revgrep [options] [from-rev] [to-rev] + +from-rev filters issues to lines changed since (and including) this revision + to-rev filters issues to lines changed since (and including) this revision, requires + + If no revisions are given, and there are unstaged changes or untracked files, only those changes are shown + If no revisions are given, and there are no unstaged changes or untracked files, only changes in HEAD~ are shown + If from-rev is given and to-rev is not, only changes between from-rev and HEAD are shown. + + -d Show debug output + -regexp string + Regexp to match path, line number, optional column number, and message +``` + +# Other Examples + +Issues between branches: +```bash +[user@host dir (feature/branch)]$ go vet |& revgrep master +``` + +Issues since last push: +```bash +[user@host dir (master)]$ go vet |& revgrep origin/master +``` diff --git a/vendor/github.com/golangci/revgrep/revgrep.go b/vendor/github.com/golangci/revgrep/revgrep.go new file mode 100644 index 000000000..1a56dcf4e --- /dev/null +++ b/vendor/github.com/golangci/revgrep/revgrep.go @@ -0,0 +1,395 @@ +package revgrep + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +// Checker provides APIs to filter static analysis tools to specific commits, +// such as showing only issues since last commit. +type Checker struct { + // Patch file (unified) to read to detect lines being changed, if nil revgrep + // will attempt to detect the VCS and generate an appropriate patch. Auto + // detection will search for uncommitted changes first, if none found, will + // generate a patch from last committed change. File paths within patches + // must be relative to current working directory. + Patch io.Reader + // NewFiles is a list of file names (with absolute paths) where the entire + // contents of the file is new. + NewFiles []string + // Debug sets the debug writer for additional output. + Debug io.Writer + // RevisionFrom check revision starting at, leave blank for auto detection + // ignored if patch is set. + RevisionFrom string + // RevisionTo checks revision finishing at, leave blank for auto detection + // ignored if patch is set. + RevisionTo string + // Regexp to match path, line number, optional column number, and message. + Regexp string + // AbsPath is used to make an absolute path of an issue's filename to be + // relative in order to match patch file. If not set, current working + // directory is used. + AbsPath string + + // Calculated changes for next calls to IsNewIssue + changes map[string][]pos +} + +// Issue contains metadata about an issue found. +type Issue struct { + // File is the name of the file as it appeared from the patch. + File string + // LineNo is the line number of the file. + LineNo int + // ColNo is the column number or 0 if none could be parsed. + ColNo int + // HunkPos is position from file's first @@, for new files this will be the + // line number. + // + // See also: https://developer.github.com/v3/pulls/comments/#create-a-comment + HunkPos int + // Issue text as it appeared from the tool. + Issue string + // Message is the issue without file name, line number and column number. + Message string +} + +func (c *Checker) preparePatch() error { + // Check if patch is supplied, if not, retrieve from VCS + if c.Patch == nil { + var err error + c.Patch, c.NewFiles, err = GitPatch(c.RevisionFrom, c.RevisionTo) + if err != nil { + return fmt.Errorf("could not read git repo: %s", err) + } + if c.Patch == nil { + return errors.New("no version control repository found") + } + } + + return nil +} + +type InputIssue interface { + FilePath() string + Line() int +} + +type simpleInputIssue struct { + filePath string + lineNumber int +} + +func (i simpleInputIssue) FilePath() string { + return i.filePath +} + +func (i simpleInputIssue) Line() int { + return i.lineNumber +} + +func (c *Checker) Prepare() error { + returnErr := c.preparePatch() + c.changes = c.linesChanged() + return returnErr +} + +func (c Checker) IsNewIssue(i InputIssue) (hunkPos int, isNew bool) { + fchanges, ok := c.changes[i.FilePath()] + if !ok { // file wasn't changed + return 0, false + } + + var ( + fpos pos + changed bool + ) + // found file, see if lines matched + for _, pos := range fchanges { + if pos.lineNo == int(i.Line()) { + fpos = pos + changed = true + break + } + } + + if changed || fchanges == nil { + // either file changed or it's a new file + hunkPos := fpos.lineNo + if changed { // existing file changed + hunkPos = fpos.hunkPos + } + + return hunkPos, true + } + + return 0, false +} + +// Check scans reader and writes any lines to writer that have been added in +// Checker.Patch. +// +// Returns issues written to writer when no error occurs. +// +// If no VCS could be found or other VCS errors occur, all issues are written +// to writer and an error is returned. +// +// File paths in reader must be relative to current working directory or +// absolute. +func (c Checker) Check(reader io.Reader, writer io.Writer) (issues []Issue, err error) { + returnErr := c.Prepare() + writeAll := returnErr != nil + + // file.go:lineNo:colNo:message + // colNo is optional, strip spaces before message + lineRE := regexp.MustCompile(`(.*?\.go):([0-9]+):([0-9]+)?:?\s*(.*)`) + if c.Regexp != "" { + lineRE, err = regexp.Compile(c.Regexp) + if err != nil { + return nil, fmt.Errorf("could not parse regexp: %v", err) + } + } + + // TODO consider lazy loading this, if there's nothing in stdin, no point + // checking for recent changes + c.debugf("lines changed: %+v", c.changes) + + absPath := c.AbsPath + if absPath == "" { + absPath, err = os.Getwd() + if err != nil { + returnErr = fmt.Errorf("could not get current working directory: %s", err) + } + } + + // Scan each line in reader and only write those lines if lines changed + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := lineRE.FindSubmatch(scanner.Bytes()) + if line == nil { + c.debugf("cannot parse file+line number: %s", scanner.Text()) + continue + } + + if writeAll { + fmt.Fprintln(writer, scanner.Text()) + continue + } + + // Make absolute path names relative + path := string(line[1]) + if rel, err := filepath.Rel(absPath, path); err == nil { + c.debugf("rewrote path from %q to %q (absPath: %q)", path, rel, absPath) + path = rel + } + + // Parse line number + lno, err := strconv.ParseUint(string(line[2]), 10, 64) + if err != nil { + c.debugf("cannot parse line number: %q", scanner.Text()) + continue + } + + // Parse optional column number + var cno uint64 + if len(line[3]) > 0 { + cno, err = strconv.ParseUint(string(line[3]), 10, 64) + if err != nil { + c.debugf("cannot parse column number: %q", scanner.Text()) + // Ignore this error and continue + } + } + + // Extract message + msg := string(line[4]) + + c.debugf("path: %q, lineNo: %v, colNo: %v, msg: %q", path, lno, cno, msg) + i := simpleInputIssue{ + filePath: path, + lineNumber: int(lno), + } + hunkPos, changed := c.IsNewIssue(i) + if changed { + issue := Issue{ + File: path, + LineNo: int(lno), + ColNo: int(cno), + HunkPos: hunkPos, + Issue: scanner.Text(), + Message: msg, + } + issues = append(issues, issue) + fmt.Fprintln(writer, scanner.Text()) + } else { + c.debugf("unchanged: %s", scanner.Text()) + } + } + if err := scanner.Err(); err != nil { + returnErr = fmt.Errorf("error reading standard input: %s", err) + } + return issues, returnErr +} + +func (c Checker) debugf(format string, s ...interface{}) { + if c.Debug != nil { + fmt.Fprint(c.Debug, "DEBUG: ") + fmt.Fprintf(c.Debug, format+"\n", s...) + } +} + +type pos struct { + lineNo int // line number + hunkPos int // position relative to first @@ in file +} + +// linesChanges returns a map of file names to line numbers being changed. +// If key is nil, the file has been recently added, else it contains a slice +// of positions that have been added. +func (c Checker) linesChanged() map[string][]pos { + type state struct { + file string + lineNo int // current line number within chunk + hunkPos int // current line count since first @@ in file + changes []pos // position of changes + } + + var ( + s state + changes = make(map[string][]pos) + ) + + for _, file := range c.NewFiles { + changes[file] = nil + } + + if c.Patch == nil { + return changes + } + + scanner := bufio.NewScanner(c.Patch) + for scanner.Scan() { + line := scanner.Text() // TODO scanner.Bytes() + c.debugf(line) + s.lineNo++ + s.hunkPos++ + switch { + case strings.HasPrefix(line, "+++ ") && len(line) > 4: + if s.changes != nil { + // record the last state + changes[s.file] = s.changes + } + // 6 removes "+++ b/" + s = state{file: line[6:], hunkPos: -1, changes: []pos{}} + case strings.HasPrefix(line, "@@ "): + // @@ -1 +2,4 @@ + // chdr ^^^^^^^^^^^^^ + // ahdr ^^^^ + // cstart ^ + chdr := strings.Split(line, " ") + ahdr := strings.Split(chdr[2], ",") + // [1:] to remove leading plus + cstart, err := strconv.ParseUint(ahdr[0][1:], 10, 64) + if err != nil { + panic(err) + } + s.lineNo = int(cstart) - 1 // -1 as cstart is the next line number + case strings.HasPrefix(line, "-"): + s.lineNo-- + case strings.HasPrefix(line, "+"): + s.changes = append(s.changes, pos{lineNo: s.lineNo, hunkPos: s.hunkPos}) + } + + } + if err := scanner.Err(); err != nil { + fmt.Fprintln(os.Stderr, "reading standard input:", err) + } + // record the last state + changes[s.file] = s.changes + + return changes +} + +// GitPatch returns a patch from a git repository, if no git repository was +// was found and no errors occurred, nil is returned, else an error is returned +// revisionFrom and revisionTo defines the git diff parameters, if left blank +// and there are unstaged changes or untracked files, only those will be returned +// else only check changes since HEAD~. If revisionFrom is set but revisionTo +// is not, untracked files will be included, to exclude untracked files set +// revisionTo to HEAD~. It's incorrect to specify revisionTo without a +// revisionFrom. +func GitPatch(revisionFrom, revisionTo string) (io.Reader, []string, error) { + var patch bytes.Buffer + + // check if git repo exists + if err := exec.Command("git", "status").Run(); err != nil { + // don't return an error, we assume the error is not repo exists + return nil, nil, nil + } + + // make a patch for untracked files + var newFiles []string + ls, err := exec.Command("git", "ls-files", "-o").CombinedOutput() + if err != nil { + return nil, nil, fmt.Errorf("error executing git ls-files: %s", err) + } + for _, file := range bytes.Split(ls, []byte{'\n'}) { + if len(file) == 0 || bytes.HasSuffix(file, []byte{'/'}) { + // ls-files was sometimes showing directories when they were ignored + // I couldn't create a test case for this as I couldn't reproduce correctly + // for the moment, just exclude files with trailing / + continue + } + newFiles = append(newFiles, string(file)) + } + + if revisionFrom != "" { + cmd := exec.Command("git", "diff", "--relative", revisionFrom) + if revisionTo != "" { + cmd.Args = append(cmd.Args, revisionTo) + } + cmd.Stdout = &patch + if err := cmd.Run(); err != nil { + return nil, nil, fmt.Errorf("error executing git diff %q %q: %s", revisionFrom, revisionTo, err) + } + + if revisionTo == "" { + return &patch, newFiles, nil + } + return &patch, nil, nil + } + + // make a patch for unstaged changes + // use --no-prefix to remove b/ given: +++ b/main.go + cmd := exec.Command("git", "diff", "--relative") + cmd.Stdout = &patch + if err := cmd.Run(); err != nil { + return nil, nil, fmt.Errorf("error executing git diff: %s", err) + } + unstaged := patch.Len() > 0 + + // If there's unstaged changes OR untracked changes (or both), then this is + // a suitable patch + if unstaged || newFiles != nil { + return &patch, newFiles, nil + } + + // check for changes in recent commit + + cmd = exec.Command("git", "diff", "--relative", "HEAD~") + cmd.Stdout = &patch + if err := cmd.Run(); err != nil { + return nil, nil, fmt.Errorf("error executing git diff HEAD~: %s", err) + } + + return &patch, nil, nil +} -- cgit mrf-deployment