From 16d0c52d24e8948b8bd8030e3fd112e0b6361c06 Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 30 Jul 2018 20:09:43 +0100 Subject: [PATCH] Indicate that a C compiler failed due to problems in a header file --- ale_linters/c/clang.vim | 2 +- ale_linters/c/gcc.vim | 2 +- ale_linters/cpp/clang.vim | 2 +- ale_linters/cpp/gcc.vim | 2 +- ale_linters/objc/clang.vim | 2 +- ale_linters/objcpp/clang.vim | 2 +- autoload/ale/handlers/gcc.vim | 77 ++++++++++++++++++++++++--- test/handler/test_clang_handler.vader | 15 +++++- test/handler/test_gcc_handler.vader | 65 +++++++++++++++++----- 9 files changed, 141 insertions(+), 28 deletions(-) diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 48ea76dd..9d49fabb 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -28,5 +28,5 @@ call ale#linter#Define('c', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#clang#GetCommand'} \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 63fe87a6..e2dff65d 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -28,5 +28,5 @@ call ale#linter#Define('c', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#gcc#GetCommand'} \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/ale_linters/cpp/clang.vim b/ale_linters/cpp/clang.vim index 4f3c5b6f..72793a71 100644 --- a/ale_linters/cpp/clang.vim +++ b/ale_linters/cpp/clang.vim @@ -28,5 +28,5 @@ call ale#linter#Define('cpp', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#cpp#clang#GetCommand'}, \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim index 242e389e..17f5acf5 100644 --- a/ale_linters/cpp/gcc.vim +++ b/ale_linters/cpp/gcc.vim @@ -29,5 +29,5 @@ call ale#linter#Define('cpp', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#cpp#gcc#GetCommand'}, \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/ale_linters/objc/clang.vim b/ale_linters/objc/clang.vim index f4725a0e..4e80ac5c 100644 --- a/ale_linters/objc/clang.vim +++ b/ale_linters/objc/clang.vim @@ -19,5 +19,5 @@ call ale#linter#Define('objc', { \ 'output_stream': 'stderr', \ 'executable': 'clang', \ 'command_callback': 'ale_linters#objc#clang#GetCommand', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/ale_linters/objcpp/clang.vim b/ale_linters/objcpp/clang.vim index 0e9cefe9..d1474f17 100644 --- a/ale_linters/objcpp/clang.vim +++ b/ale_linters/objcpp/clang.vim @@ -19,5 +19,5 @@ call ale#linter#Define('objcpp', { \ 'output_stream': 'stderr', \ 'executable': 'clang++', \ 'command_callback': 'ale_linters#objcpp#clang#GetCommand', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim index 4b53652a..b8bac77f 100644 --- a/autoload/ale/handlers/gcc.vim +++ b/autoload/ale/handlers/gcc.vim @@ -5,6 +5,13 @@ scriptencoding utf-8 let s:pragma_error = '#pragma once in main file' +" Look for lines like the following. +" +" :8:5: warning: conversion lacks type at end of format [-Wformat=] +" :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) +" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] +let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' + function! s:IsHeaderFile(filename) abort return a:filename =~? '\v\.(h|hpp)$' endfunction @@ -18,16 +25,63 @@ function! s:RemoveUnicodeQuotes(text) abort return l:text endfunction +" Report problems inside of header files just for gcc and clang +function! s:ParseProblemsInHeaders(buffer, lines) abort + let l:output = [] + let l:include_item = {} + + for l:line in a:lines[: -2] + let l:include_match = matchlist(l:line, '\v^In file included from') + + if !empty(l:include_item) + let l:pattern_match = matchlist(l:line, s:pattern) + + if !empty(l:pattern_match) && l:pattern_match[1] is# '' + if has_key(l:include_item, 'lnum') + call add(l:output, l:include_item) + endif + + let l:include_item = {} + + continue + endif + + let l:include_item.detail .= "\n" . l:line + endif + + if !empty(l:include_match) + if empty(l:include_item) + let l:include_item = { + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': l:line, + \} + endif + endif + + if !empty(l:include_item) + let l:stdin_match = matchlist(l:line, '\vfrom \:(\d+):(\d*):?$') + + if !empty(l:stdin_match) + let l:include_item.lnum = str2nr(l:stdin_match[1]) + + if str2nr(l:stdin_match[2]) + let l:include_item.col = str2nr(l:stdin_match[2]) + endif + endif + endif + endfor + + if !empty(l:include_item) && has_key(l:include_item, 'lnum') + call add(l:output, l:include_item) + endif + + return l:output +endfunction + function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort - " Look for lines like the following. - " - " :8:5: warning: conversion lacks type at end of format [-Wformat=] - " :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) - " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] - let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' let l:output = [] - for l:match in ale#util#GetMatches(a:lines, l:pattern) + for l:match in ale#util#GetMatches(a:lines, s:pattern) " Filter out the pragma errors if s:IsHeaderFile(bufname(bufnr(''))) \&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error @@ -67,3 +121,12 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort return l:output endfunction + +" Handle problems with the GCC format, but report problems inside of headers. +function! ale#handlers#gcc#HandleGCCFormatWithIncludes(buffer, lines) abort + let l:output = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines) + + call extend(l:output, s:ParseProblemsInHeaders(a:buffer, a:lines)) + + return l:output +endfunction diff --git a/test/handler/test_clang_handler.vader b/test/handler/test_clang_handler.vader index 278737a8..cc8eabd0 100644 --- a/test/handler/test_clang_handler.vader +++ b/test/handler/test_clang_handler.vader @@ -8,9 +8,20 @@ Execute(clang errors from included files should be parsed correctly): \ 'type': 'E', \ 'text': 'expected identifier or ''(''', \ }, + \ { + \ 'lnum': 3, + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': join([ + \ 'In file included from :3:', + \ 'In file included from ./a.h:1:', + \ './b.h:1:1: error: expected identifier or ''(''', + \ '{{{', + \ '^', + \ ], "\n"), + \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ - \ 'In file included from test.c:3:', + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ + \ 'In file included from :3:', \ 'In file included from ./a.h:1:', \ './b.h:1:1: error: expected identifier or ''(''', \ '{{{', diff --git a/test/handler/test_gcc_handler.vader b/test/handler/test_gcc_handler.vader index 678d3f42..43b38769 100644 --- a/test/handler/test_gcc_handler.vader +++ b/test/handler/test_gcc_handler.vader @@ -1,7 +1,7 @@ Execute(The GCC handler should ignore other lines of output): AssertEqual \ [], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ 'foo', \ 'bar', \ 'baz', @@ -17,12 +17,24 @@ Execute(GCC errors from included files should be parsed correctly): \ 'type': 'E', \ 'text': 'expected identifier or ''('' before ''{'' token', \ }, + \ { + \ 'lnum': 3, + \ 'col': 2, + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': join([ + \ 'In file included from :3:2:', + \ 'broken.h:1:1: error: expected identifier or ''('' before ''{'' token', + \ ' {{{', + \ ' ^', + \ ], "\n"), + \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ - \ 'In file included from :3:0:', + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ + \ 'In file included from :3:2:', \ 'broken.h:1:1: error: expected identifier or ''('' before ''{'' token', \ ' {{{', \ ' ^', + \ 'compilation terminated.', \ ]) AssertEqual @@ -34,13 +46,25 @@ Execute(GCC errors from included files should be parsed correctly): \ 'type': 'E', \ 'text': 'expected identifier or ''('' before ''{'' token', \ }, + \ { + \ 'lnum': 5, + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': join([ + \ 'In file included from a.h:1:0,', + \ ' from :5:', + \ 'b.h:1:1: error: expected identifier or ''('' before ''{'' token', + \ ' {{{', + \ ' ^', + \ ], "\n"), + \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ 'In file included from a.h:1:0,', - \ ' from test.c:3:', + \ ' from :5:', \ 'b.h:1:1: error: expected identifier or ''('' before ''{'' token', \ ' {{{', \ ' ^', + \ 'compilation terminated.', \ ]) AssertEqual @@ -59,16 +83,31 @@ Execute(GCC errors from included files should be parsed correctly): \ 'type': 'E', \ 'text': 'unknown type name ''other_bad_type''', \ }, + \ { + \ 'lnum': 3, + \ 'text': 'Error found in header. See :ALEDetail', + \ 'detail': join([ + \ 'In file included from a.h:1:0,', + \ ' from :3:', + \ 'b.h:1:1: error: unknown type name ‘bad_type’', + \ ' bad_type x;', + \ ' ^', + \ 'b.h:2:1: error: unknown type name ‘other_bad_type’', + \ ' other_bad_type y;', + \ ' ^', + \ ], "\n"), + \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ 'In file included from a.h:1:0,', - \ ' from test.c:3:', + \ ' from :3:', \ 'b.h:1:1: error: unknown type name ‘bad_type’', \ ' bad_type x;', \ ' ^', \ 'b.h:2:1: error: unknown type name ‘other_bad_type’', \ ' other_bad_type y;', \ ' ^', + \ 'compilation terminated.', \ ]) Execute(The GCC handler shouldn't complain about #pragma once for headers): @@ -76,7 +115,7 @@ Execute(The GCC handler shouldn't complain about #pragma once for headers): AssertEqual \ [], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ ':1:1: warning: #pragma once in main file [enabled by default]', \ ]) @@ -84,7 +123,7 @@ Execute(The GCC handler shouldn't complain about #pragma once for headers): AssertEqual \ [], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ ':1:1: warning: #pragma once in main file [enabled by default]', \ ]) @@ -119,7 +158,7 @@ Execute(The GCC handler should handle syntax errors): \ 'text': 'expected '';'' before ''o''' \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ ':6:12: error: invalid suffix "p" on integer constant', \ ':17:5: error: invalid suffix "n" on integer constant', \ ':4: error: variable or field ''foo'' declared void', @@ -130,7 +169,7 @@ Execute(The GCC handler should handle syntax errors): Execute(The GCC handler should handle notes with no previous message): AssertEqual \ [], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ ':1:1: note: x', \ ':1:1: note: x', \ ]) @@ -145,7 +184,7 @@ Execute(The GCC handler should interpret - as being the current file): \ 'text': 'Some error', \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ '-:6:12: error: Some error', \ ]) @@ -159,6 +198,6 @@ Execute(The GCC handler should handle fatal error messages due to missing files) \ 'text': 'foo.h: No such file or directory' \ }, \ ], - \ ale#handlers#gcc#HandleGCCFormat(347, [ + \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [ \ ':3:12: fatal error: foo.h: No such file or directory', \ ])