Skip to content

Commit

Permalink
Add a sonar formatter.
Browse files Browse the repository at this point in the history
  • Loading branch information
xpol committed Apr 22, 2019
1 parent 7360cfb commit af92e80
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 3 deletions.
123 changes: 123 additions & 0 deletions spec/cli_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,129 @@ not ok 8 spec/samples/python_code.lua:1:6: (E011) expected '=' near '__future__'
]], get_output "bad_file spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter JUnit --no-config")
end)

it("has built-in JUnit formatter", function()
assert.equal([[{
"issues": [
{
"engineId": "luacheck",
"ruleId": "FATAL",
"severity": "BLOCKER",
"type": "BUG",
"effortMinutes": 10,
"primaryLocation": {
"message": "I/O error",
"filePath": "bad_file",
"textRange": {
"startLine": 1
}
}
},
{
"engineId": "luacheck",
"ruleId": "211",
"severity": "MAJOR",
"type": "CODE_SMELL",
"effortMinutes": 10,
"primaryLocation": {
"message": "unused function 'helper'",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 3,
"startColumn": 15
}
}
},
{
"engineId": "luacheck",
"ruleId": "212",
"severity": "MAJOR",
"type": "CODE_SMELL",
"effortMinutes": 10,
"primaryLocation": {
"message": "unused variable length argument",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 3,
"startColumn": 22
}
}
},
{
"engineId": "luacheck",
"ruleId": "111",
"severity": "MAJOR",
"type": "CODE_SMELL",
"effortMinutes": 10,
"primaryLocation": {
"message": "setting non-standard global variable 'embrace'",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 7,
"startColumn": 9
}
}
},
{
"engineId": "luacheck",
"ruleId": "412",
"severity": "MAJOR",
"type": "CODE_SMELL",
"effortMinutes": 10,
"primaryLocation": {
"message": "variable 'opt' was previously defined as an argument on line 7",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 8,
"startColumn": 9
}
},
"secondaryLocations": [
{
"message": "opt",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 7,
"startColumn": 17,
"endColumn": 19
}
}
]
},
{
"engineId": "luacheck",
"ruleId": "113",
"severity": "MAJOR",
"type": "CODE_SMELL",
"effortMinutes": 10,
"primaryLocation": {
"message": "accessing undefined variable 'hepler'",
"filePath": "spec/samples/bad_code.lua",
"textRange": {
"startLine": 9,
"startColumn": 10
}
}
},
{
"engineId": "luacheck",
"ruleId": "011",
"severity": "BLOCKER",
"type": "BUG",
"effortMinutes": 10,
"primaryLocation": {
"message": "expected '=' near '__future__'",
"filePath": "spec/samples/python_code.lua",
"textRange": {
"startLine": 1,
"startColumn": 5
}
}
}
]
}
]], get_output "bad_file spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter sonar --no-config")
end)

it("has built-in Visual Studio aware formatter", function()
assert.equal([[
luacheck : fatal error F1: couldn't check bad_file: couldn't read: No such file or directory
Expand Down
112 changes: 109 additions & 3 deletions src/luacheck/format.lua
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,15 @@ local function fatal_type(file_report)
return capitalize(file_report.fatal) .. " error"
end

local function is_error(event)
return event.code:sub(1, 1) == "0"
end

local function count_warnings_errors(events)
local warnings, errors = 0, 0

for _, event in ipairs(events) do
if event.code:sub(1, 1) == "0" then
if is_error(event) then
errors = errors + 1
else
warnings = warnings + 1
Expand Down Expand Up @@ -134,7 +138,7 @@ local function format_location(file, location, opts)
end

local function event_code(event)
return (event.code:sub(1, 1) == "0" and "E" or "W")..event.code
return (is_error(event) and "E" or "W")..event.code
end

local function format_event(file_name, event, opts)
Expand Down Expand Up @@ -268,6 +272,108 @@ function format.builtin_formatters.JUnit(report, file_names)
return table.concat(buf, "\n")
end

local sonar_error = {
severity = "BLOCKER",
type = "BUG",
}
local sonar_warning = {
severity = "MAJOR",
type = "CODE_SMELL",
}

local function add_json_line(buf, text, comma)
local final = comma and text..',' or text
table.insert(buf, final)
end

local function append_sonar_issue(buf, file_name, event, is_last_event)
local category = is_error(event) and sonar_error or sonar_warning
local message = format_message(event, false)
local has_secondary_locations = event.prev_line ~= nil

add_json_line(buf, (' {'))
add_json_line(buf, (' "engineId": "luacheck",'))
add_json_line(buf, (' "ruleId": "%s",'):format(event.code))
add_json_line(buf, (' "severity": "%s",'):format(category.severity))
add_json_line(buf, (' "type": "%s",'):format(category.type))
add_json_line(buf, (' "effortMinutes": %d,'):format(10))
add_json_line(buf, (' "primaryLocation": {'))
add_json_line(buf, (' "message": "%s",'):format(message))
add_json_line(buf, (' "filePath": "%s",'):format(file_name))
add_json_line(buf, (' "textRange": {'))
add_json_line(buf, (' "startLine": %d,'):format(event.line))
local has_end_column = event.endColumn and event.endColumn > event.column
add_json_line(buf, (' "startColumn": %d'):format(event.column - 1), has_end_column)
if has_end_column then
add_json_line(buf, (' "endColumn": %d'):format(event.endColumn - 1))
end
add_json_line(buf, (' }'))
add_json_line(buf, (' }'), has_secondary_locations)

if has_secondary_locations then
add_json_line(buf, (' "secondaryLocations": ['))
add_json_line(buf, (' {'))
add_json_line(buf, (' "message": "%s",'):format(event.name))
add_json_line(buf, (' "filePath": "%s",'):format(file_name))
add_json_line(buf, (' "textRange": {'))
local has_prev_column = event.prev_column ~= nil
add_json_line(buf, (' "startLine": %d'):format(event.prev_line), has_prev_column)
if has_prev_column then
local has_end_column = event.prev_end_column and event.prev_end_column > event.prev_column
add_json_line(buf, (' "startColumn": %d'):format(event.prev_column - 1), has_end_column)
if has_end_column then
add_json_line(buf, (' "endColumn": %d'):format(event.prev_end_column - 1))
end
end
add_json_line(buf, (' }'))
add_json_line(buf, (' }'))
add_json_line(buf, (' ]'))
end

add_json_line(buf, (' }'), not is_last_event)
end

local function append_snoar_fatal(buf, file_name, file_report, is_last_file)
add_json_line(buf, (' {'))
add_json_line(buf, (' "engineId": "luacheck",'))
add_json_line(buf, (' "ruleId": "FATAL",'))
add_json_line(buf, (' "severity": "BLOCKER",'))
add_json_line(buf, (' "type": "BUG",'))
add_json_line(buf, (' "effortMinutes": %d,'):format(10))
add_json_line(buf, (' "primaryLocation": {'))
add_json_line(buf, (' "message": "%s",'):format(fatal_type(file_report)))
add_json_line(buf, (' "filePath": "%s",'):format(file_name))
add_json_line(buf, (' "textRange": {'))
add_json_line(buf, (' "startLine": 1'))
add_json_line(buf, (' }'))
add_json_line(buf, (' }'))
add_json_line(buf, (' }'), not is_last_file)
end

function format.builtin_formatters.sonar(report, file_names)
local buf = {}
add_json_line(buf, '{')
add_json_line(buf, ' "issues": [')

for file_i, file_report in ipairs(report) do
local file_name = file_names[file_i]
local is_last_file = file_i == #file_names
if file_report.fatal then
append_snoar_fatal(buf, file_name, file_report, is_last_file)
else
for event_i, event in ipairs(file_report) do
local is_last_event = event_i == #file_report
append_sonar_issue(buf, file_name, event, is_last_file and is_last_event)
end
end
end

add_json_line(buf, ' ]')
add_json_line(buf, '}')

return table.concat(buf, "\n")
end

local fatal_error_codes = {
["I/O"] = "F1",
["syntax"] = "F2",
Expand All @@ -287,7 +393,7 @@ function format.builtin_formatters.visual_studio(report, file_names)
for _, event in ipairs(file_report) do
-- Older documentation on the format suggests that it could support column range.
-- Newer docs don't mention it. Don't use it for now.
local event_type = event.code:sub(1, 1) == "0" and "error" or "warning"
local event_type = is_error(event) and "error" or "warning"
local message = format_message(event)
table.insert(buf, ("%s(%d,%d) : %s %s: %s"):format(
file_names[i], event.line, event.column, event_type, event_code(event), message))
Expand Down

0 comments on commit af92e80

Please sign in to comment.