From 541656cc203fbb4008df98044c047301cae98019 Mon Sep 17 00:00:00 2001
From: Denis Isaev <denis@golangci.com>
Date: Mon, 11 Jun 2018 11:17:59 +0300
Subject: [PATCH] Write JSON output more compactly and output object, not array

---
 pkg/golinters/dupl.go |  2 +-
 pkg/golinters/gas.go  |  3 ++-
 pkg/printers/json.go  | 14 ++++++++++++--
 pkg/result/issue.go   | 13 ++++++++++---
 4 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/pkg/golinters/dupl.go b/pkg/golinters/dupl.go
index 0ede2d50..dcabdbe9 100644
--- a/pkg/golinters/dupl.go
+++ b/pkg/golinters/dupl.go
@@ -41,7 +41,7 @@ func (d Dupl) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue,
 				Filename: i.From.Filename(),
 				Line:     i.From.LineStart(),
 			},
-			LineRange: result.Range{
+			LineRange: &result.Range{
 				From: i.From.LineStart(),
 				To:   i.From.LineEnd(),
 			},
diff --git a/pkg/golinters/gas.go b/pkg/golinters/gas.go
index 75d9a727..7c37a639 100644
--- a/pkg/golinters/gas.go
+++ b/pkg/golinters/gas.go
@@ -41,9 +41,10 @@ func (lint Gas) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issu
 	res := make([]result.Issue, 0, len(issues))
 	for _, i := range issues {
 		text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
-		var r result.Range
+		var r *result.Range
 		line, err := strconv.Atoi(i.Line)
 		if err != nil {
+			r = &result.Range{}
 			if n, rerr := fmt.Sscanf(i.Line, "%d-%d", &r.From, &r.To); rerr != nil || n != 2 {
 				logutils.HiddenWarnf("Can't convert gas line number %q of %v to int: %s", i.Line, i, err)
 				continue
diff --git a/pkg/printers/json.go b/pkg/printers/json.go
index 45ab98a9..c3ec923d 100644
--- a/pkg/printers/json.go
+++ b/pkg/printers/json.go
@@ -14,15 +14,25 @@ func NewJSON() *JSON {
 	return &JSON{}
 }
 
+type JSONResult struct {
+	Issues []result.Issue
+}
+
 func (JSON) Print(ctx context.Context, issues <-chan result.Issue) (bool, error) {
-	var allIssues []result.Issue
+	allIssues := []result.Issue{}
 	for i := range issues {
 		allIssues = append(allIssues, i)
 	}
-	outputJSON, err := json.Marshal(allIssues)
+
+	res := JSONResult{
+		Issues: allIssues,
+	}
+
+	outputJSON, err := json.Marshal(res)
 	if err != nil {
 		return false, err
 	}
+
 	fmt.Fprint(StdOut, string(outputJSON))
 	return len(allIssues) != 0, nil
 }
diff --git a/pkg/result/issue.go b/pkg/result/issue.go
index 6fce3c13..4daa4e17 100644
--- a/pkg/result/issue.go
+++ b/pkg/result/issue.go
@@ -11,8 +11,8 @@ type Issue struct {
 	Text       string
 
 	Pos       token.Position
-	LineRange Range
-	HunkPos   int
+	LineRange *Range `json:",omitempty"`
+	HunkPos   int    `json:",omitempty"`
 }
 
 func (i Issue) FilePath() string {
@@ -24,6 +24,13 @@ func (i Issue) Line() int {
 }
 
 func (i Issue) GetLineRange() Range {
+	if i.LineRange == nil {
+		return Range{
+			From: i.Line(),
+			To:   i.Line(),
+		}
+	}
+
 	if i.LineRange.From == 0 {
 		return Range{
 			From: i.Line(),
@@ -31,5 +38,5 @@ func (i Issue) GetLineRange() Range {
 		}
 	}
 
-	return i.LineRange
+	return *i.LineRange
 }