execute.go
func processOutput(dbg debugFunction, state *linterState, out []byte) {
	re := state.regex
	all := re.FindAllSubmatchIndex(out, -1)
	dbg("%s hits %d: %s", state.Name, len(all), state.Pattern)

	cwd, err := os.Getwd()
	if err != nil {
		warning("failed to get working directory %s", err)
	}

	// Create a local copy of vars so they can be modified by the linter output
	vars := state.vars.Copy()

	for _, indices := range all {
		group := [][]byte{}
		for i := 0; i < len(indices); i += 2 {
			var fragment []byte
			if indices[i] != -1 {
				fragment = out[indices[i]:indices[i+1]]
			}
			group = append(group, fragment)
		}

		issue, err := NewIssue(state.Linter.Name, config.formatTemplate)
		kingpin.FatalIfError(err, "Invalid output format")

		for i, name := range re.SubexpNames() {
			if group[i] == nil {
				continue
			}
			part := string(group[i])
			if name != "" {
				vars[name] = part
			}
			switch name {
			case "path":
				issue.Path, err = newIssuePathFromAbsPath(cwd, part)
				if err != nil {
					warning("failed to make %s a relative path: %s", part, err)
				}
			case "line":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "line matched invalid integer")
				issue.Line = int(n)

			case "col":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "col matched invalid integer")
				issue.Col = int(n)

			case "message":
				issue.Message = part

			case "":
			}
		}
		// TODO: set messageOveride and severity on the Linter instead of reading
		// them directly from the static config
		if m, ok := config.MessageOverride[state.Name]; ok {
			issue.Message = vars.Replace(m)
		}
		if sev, ok := config.Severity[state.Name]; ok {
			issue.Severity = Severity(sev)
		}
		if state.exclude != nil && state.exclude.MatchString(issue.String()) {
			continue
		}
		if state.include != nil && !state.include.MatchString(issue.String()) {
			continue
		}
		state.issues <- issue
	}
}
func processOutput(dbg debugFunction, state *linterState, out []byte) {
	re := state.regex
	all := re.FindAllSubmatchIndex(out, -1)
	dbg("%s hits %d: %s", state.Name, len(all), state.Pattern)

	cwd, err := os.Getwd()
	if err != nil {
		warning("failed to get working directory %s", err)
	}

	// Create a local copy of vars so they can be modified by the linter output
	vars := state.vars.Copy()

	for _, indices := range all {
		group := [][]byte{}
		for i := 0; i < len(indices); i += 2 {
			var fragment []byte
			if indices[i] != -1 {
				fragment = out[indices[i]:indices[i+1]]
			}
			group = append(group, fragment)
		}

		issue, err := NewIssue(state.Linter.Name, config.formatTemplate)
		kingpin.FatalIfError(err, "Invalid output format")

		for i, name := range re.SubexpNames() {
			if group[i] == nil {
				continue
			}
			part := string(group[i])
			if name != "" {
				vars[name] = part
			}
			switch name {
			case "path":
				issue.Path, err = newIssuePathFromAbsPath(cwd, part)
				if err != nil {
					warning("failed to make %s a relative path: %s", part, err)
				}
			case "line":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "line matched invalid integer")
				issue.Line = int(n)

			case "col":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "col matched invalid integer")
				issue.Col = int(n)

			case "message":
				issue.Message = part

			case "":
			}
		}
		// TODO: set messageOveride and severity on the Linter instead of reading
		// them directly from the static config
		if m, ok := config.MessageOverride[state.Name]; ok {
			issue.Message = vars.Replace(m)
		}
		if sev, ok := config.Severity[state.Name]; ok {
			issue.Severity = Severity(sev)
		}
		if state.exclude != nil && state.exclude.MatchString(issue.String()) {
			continue
		}
		if state.include != nil && !state.include.MatchString(issue.String()) {
			continue
		}
		state.issues <- issue
	}
}
// nolint: gocyclo