From c47b5fd4b8f9b7c08774e631dae60ca51c23e7c9 Mon Sep 17 00:00:00 2001 From: roel0 Date: Mon, 19 Mar 2018 21:55:59 +0100 Subject: [PATCH 1/8] Automatically determine build flags by parsing 'make -n' output #1167 --- ale_linters/c/clang.vim | 11 +++++-- ale_linters/c/gcc.vim | 11 +++++-- autoload/ale/c.vim | 63 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 7680305..25d9371 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -3,20 +3,27 @@ call ale#Set('c_clang_executable', 'clang') call ale#Set('c_clang_options', '-std=c11 -Wall') +call ale#Set('c_gcc_parse_makefile', 0) function! ale_linters#c#clang#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_clang_executable') endfunction function! ale_linters#c#clang#GetCommand(buffer) abort - let l:paths = ale#c#FindLocalHeaderPaths(a:buffer) +let l:cflags = [] + if g:ale_c_parse_makefile + let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') + endif + if empty(l:cflags) + let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) + endif " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) \ . ' -S -x c -fsyntax-only ' \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . ale#c#IncludeOptions(l:paths) + \ . l:cflags . ' ' \ . ale#Var(a:buffer, 'c_clang_options') . ' -' endfunction diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 4b241e3..3ad243a 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -3,20 +3,27 @@ call ale#Set('c_gcc_executable', 'gcc') call ale#Set('c_gcc_options', '-std=c11 -Wall') +call ale#Set('c_parse_makefile', 0) function! ale_linters#c#gcc#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_gcc_executable') endfunction function! ale_linters#c#gcc#GetCommand(buffer) abort - let l:paths = ale#c#FindLocalHeaderPaths(a:buffer) + let l:cflags = [] + if g:ale_c_parse_makefile + let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') + endif + if empty(l:cflags) + let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) + endif " -iquote with the directory the file is in makes #include work for " headers in the same directory. return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) \ . ' -S -x c -fsyntax-only ' \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . ale#c#IncludeOptions(l:paths) + \ . l:cflags . ' ' \ . ale#Var(a:buffer, 'c_gcc_options') . ' -' endfunction diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index f6ad7de..4bf8ad4 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -22,6 +22,69 @@ function! ale#c#FindProjectRoot(buffer) abort return '' endfunction +function! ale#c#ParseCFlags(project_root, stdout_make) abort + let l:cflags_list = [] + let l:cflags = split(a:stdout_make, " ") + let l:shell_option = 0 + let l:macro_option = 0 + let l:previous_option = '' + for l:option in l:cflags + " Check if cflag contained spaces + if l:shell_option || stridx(l:option, "=`") >= 0 + " Cflag contained shell command with spaces (ex. -D='date +%s') + let l:shell_option = 1 + let l:previous_option .= l:option . ' ' + if l:option[-1: -1] != "`" + continue + endif + let l:shell_option = 0 + elseif l:macro_option || stridx(l:option, "$((") > 0 + " Cflag contained macro with spaces (ex -Da=$(( 4 * 20 ))) + let l:macro_option = 1 + let l:previous_option .= l:option . ' ' + if stridx(l:option, "))") < 0 + continue + endif + let l:macro_option = 0 + endif + if l:previous_option != '' + let l:option = l:previous_option + let l:previous_option = '' + endif + " Fix relative paths if needed + if stridx(l:option, "-I") >= 0 + if stridx(l:option, "-I/") < 0 + let l:option = '-I' . a:project_root . s:sep . l:option[2:] + endif + endif + " Parse the cflag + if stridx(l:option, "-I") >= 0 || + \ stridx(l:option, "-D") >= 0 + if index(l:cflags_list, l:option) < 0 + call add(l:cflags_list, l:option) + endif + endif + endfor + return l:cflags_list +endfunction + +function! ale#c#ParseMakefile(buffer) abort + let l:project_root = ale#c#FindProjectRoot(a:buffer) + let l:project_cflags = [] + + if !empty(l:project_root) + if !empty(globpath(l:project_root, 'Makefile', 0)) + let stdout_make = system('cd '. l:project_root . ' && make -n') + for l:object in split(l:stdout_make, '\n') + if stridx(l:object, expand("%t")) + return ale#c#ParseCFlags(l:project_root, l:object) + endif + endfor + endif + endif + return [] +endfunction + " Given a buffer number, search for a project root, and output a List " of directories to include based on some heuristics. " From 3fb7efa2c6d249ccc5b69036dbeda690c7459515 Mon Sep 17 00:00:00 2001 From: roel0 Date: Tue, 20 Mar 2018 11:56:46 +0100 Subject: [PATCH 2/8] Added some unit tests and fixed some linting errors for automatic makefile parsing in C #1167 --- ale_linters/c/clang.vim | 10 +-- ale_linters/c/gcc.vim | 8 ++- autoload/ale/c.vim | 32 ++++----- test/test_c_parse_makefile.vader | 72 +++++++++++++++++++ .../test_c_projects/makefile_project/Makefile | 3 + .../makefile_project/subdir/file.c | 0 6 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 test/test_c_parse_makefile.vader create mode 100644 test/test_c_projects/makefile_project/subdir/file.c diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 25d9371..74279b7 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -3,7 +3,7 @@ call ale#Set('c_clang_executable', 'clang') call ale#Set('c_clang_options', '-std=c11 -Wall') -call ale#Set('c_gcc_parse_makefile', 0) +call ale#Set('c_clang_parse_makefile', 0) function! ale_linters#c#clang#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_clang_executable') @@ -11,11 +11,13 @@ endfunction function! ale_linters#c#clang#GetCommand(buffer) abort let l:cflags = [] - if g:ale_c_parse_makefile - let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') + if g:ale_c_clang_parse_makefile + let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') . ' ' endif if empty(l:cflags) let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) + else + let l:cflags .= ' ' endif " -iquote with the directory the file is in makes #include work for @@ -23,7 +25,7 @@ let l:cflags = [] return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) \ . ' -S -x c -fsyntax-only ' \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags . ' ' + \ . l:cflags \ . ale#Var(a:buffer, 'c_clang_options') . ' -' endfunction diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 3ad243a..3199fe8 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -3,7 +3,7 @@ call ale#Set('c_gcc_executable', 'gcc') call ale#Set('c_gcc_options', '-std=c11 -Wall') -call ale#Set('c_parse_makefile', 0) +call ale#Set('c_gcc_parse_makefile', 0) function! ale_linters#c#gcc#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_gcc_executable') @@ -11,11 +11,13 @@ endfunction function! ale_linters#c#gcc#GetCommand(buffer) abort let l:cflags = [] - if g:ale_c_parse_makefile + if g:ale_c_gcc_parse_makefile let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') endif if empty(l:cflags) let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) + else + let l:cflags .= ' ' endif " -iquote with the directory the file is in makes #include work for @@ -23,7 +25,7 @@ function! ale_linters#c#gcc#GetCommand(buffer) abort return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) \ . ' -S -x c -fsyntax-only ' \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags . ' ' + \ . l:cflags \ . ale#Var(a:buffer, 'c_gcc_options') . ' -' endfunction diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 4bf8ad4..7dc532b 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -24,42 +24,42 @@ endfunction function! ale#c#ParseCFlags(project_root, stdout_make) abort let l:cflags_list = [] - let l:cflags = split(a:stdout_make, " ") + let l:cflags = split(a:stdout_make) let l:shell_option = 0 let l:macro_option = 0 let l:previous_option = '' for l:option in l:cflags " Check if cflag contained spaces - if l:shell_option || stridx(l:option, "=`") >= 0 + if l:shell_option || stridx(l:option, '=`') >= 0 " Cflag contained shell command with spaces (ex. -D='date +%s') let l:shell_option = 1 let l:previous_option .= l:option . ' ' - if l:option[-1: -1] != "`" + if l:option[-1: -1] isnot? '`' continue endif let l:shell_option = 0 - elseif l:macro_option || stridx(l:option, "$((") > 0 + elseif l:macro_option || stridx(l:option, '$((') > 0 " Cflag contained macro with spaces (ex -Da=$(( 4 * 20 ))) let l:macro_option = 1 let l:previous_option .= l:option . ' ' - if stridx(l:option, "))") < 0 + if stridx(l:option, '))') < 0 continue endif let l:macro_option = 0 endif - if l:previous_option != '' + if l:previous_option isnot? '' let l:option = l:previous_option let l:previous_option = '' endif " Fix relative paths if needed - if stridx(l:option, "-I") >= 0 - if stridx(l:option, "-I/") < 0 + if stridx(l:option, '-I') >= 0 + if stridx(l:option, '-I' . s:sep) < 0 let l:option = '-I' . a:project_root . s:sep . l:option[2:] endif endif " Parse the cflag - if stridx(l:option, "-I") >= 0 || - \ stridx(l:option, "-D") >= 0 + if stridx(l:option, '-I') >= 0 || + \ stridx(l:option, '-D') >= 0 if index(l:cflags_list, l:option) < 0 call add(l:cflags_list, l:option) endif @@ -74,12 +74,12 @@ function! ale#c#ParseMakefile(buffer) abort if !empty(l:project_root) if !empty(globpath(l:project_root, 'Makefile', 0)) - let stdout_make = system('cd '. l:project_root . ' && make -n') - for l:object in split(l:stdout_make, '\n') - if stridx(l:object, expand("%t")) - return ale#c#ParseCFlags(l:project_root, l:object) - endif - endfor + let l:stdout_make = system('cd '. l:project_root . ' && make -n') + for l:object in split(l:stdout_make, '\n') + if stridx(l:object, expand('#' . a:buffer . '...')) + return ale#c#ParseCFlags(l:project_root, l:object) + endif + endfor endif endif return [] diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader new file mode 100644 index 0000000..fac04bb --- /dev/null +++ b/test/test_c_parse_makefile.vader @@ -0,0 +1,72 @@ +Before: + Save g:ale_c_gcc_options + Save g:ale_c_gcc_parse_makefile + Save g:ale_c_clang_options + Save g:ale_c_clang_parse_makefile + Save g:ale_cpp_gcc_options + Save g:ale_cpp_clang_options + + call ale#test#SetDirectory('/testplugin/test') + + let g:ale_c_gcc_parse_makefile=1 + let g:ale_c_clang_parse_makefile=1 + let g:ale_c_gcc_options = '' + let g:ale_c_clang_options = '' + let g:ale_cpp_gcc_options = '' + let g:ale_cpp_clang_options = '' + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +" Run this only once for this series of tests. The cleanup Execute step +" will run at the bottom of this file. +" +" We need to move .git/HEAD away so we don't match it, as we need to test +" functions which look for .git/HEAD. +Execute(Move .git/HEAD to a temp dir): + let g:temp_head_filename = tempname() + let g:head_filename = findfile('.git/HEAD', ';') + + if !empty(g:head_filename) + call writefile(readfile(g:head_filename, 'b'), g:temp_head_filename, 'b') + call delete(g:head_filename) + endif + +Execute(The C GCC handler should include directories specified in the include path for projects with a Makefile): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('gcc') + \ . ' -S -x c -fsyntax-only ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir/'))[1:-3] . ' ' + \ . ' -' + \ , ale_linters#c#gcc#GetCommand(bufnr('')) + +Execute(The C++ Clang handler should include directories specified in the include path for projects with a Makefile): + runtime! ale_linters/c/clang.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ale#Escape('clang') + \ . ' -S -x c -fsyntax-only ' + \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) + \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir'))[1:-2] . ' ' + \ . ' -' + \ , ale_linters#c#clang#GetCommand(bufnr('')) + + +Execute(Move .git/HEAD back): + if !empty(g:head_filename) + call writefile(readfile(g:temp_head_filename, 'b'), g:head_filename, 'b') + call delete(g:temp_head_filename) + endif + + unlet! g:temp_head_filename + unlet! g:head_filename diff --git a/test/test_c_projects/makefile_project/Makefile b/test/test_c_projects/makefile_project/Makefile index e69de29..8b49a94 100644 --- a/test/test_c_projects/makefile_project/Makefile +++ b/test/test_c_projects/makefile_project/Makefile @@ -0,0 +1,3 @@ +file.o : subdir/file.c + cc -c subdir/file.c -Isubdir + diff --git a/test/test_c_projects/makefile_project/subdir/file.c b/test/test_c_projects/makefile_project/subdir/file.c new file mode 100644 index 0000000..e69de29 From 38953c46266706541569f6f89c19ef8af59d36f5 Mon Sep 17 00:00:00 2001 From: roel0 Date: Tue, 20 Mar 2018 12:37:53 +0100 Subject: [PATCH 3/8] Clang parser shoud fallback on old method if parsing fails #1167 --- Dockerfile | 1 + ale_linters/c/clang.vim | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index eba9a1f..babb597 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ RUN install_vim -tag v8.0.0027 -build \ ENV PACKAGES="\ bash \ git \ + make \ python \ py-pip \ " diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 74279b7..366cf2d 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -12,7 +12,7 @@ endfunction function! ale_linters#c#clang#GetCommand(buffer) abort let l:cflags = [] if g:ale_c_clang_parse_makefile - let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') . ' ' + let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') endif if empty(l:cflags) let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) From 18d0aeb1a0cca2b749c3d2232f853fcaddcdb56b Mon Sep 17 00:00:00 2001 From: roel0 Date: Tue, 20 Mar 2018 21:49:31 +0100 Subject: [PATCH 4/8] * Shell commands should by called async with the help of a command chain * The makefile parser unit test should only test the cflag parser itself #1167 --- Dockerfile | 1 - ale_linters/c/clang.vim | 14 +++-- ale_linters/c/gcc.vim | 12 ++-- autoload/ale/c.vim | 56 ++++++++++++------- .../test_c_clang_command_callbacks.vader | 4 +- .../test_c_gcc_command_callbacks.vader | 4 +- test/test_c_import_paths.vader | 16 +++--- test/test_c_parse_makefile.vader | 48 +++++++++------- 8 files changed, 92 insertions(+), 63 deletions(-) diff --git a/Dockerfile b/Dockerfile index babb597..eba9a1f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ RUN install_vim -tag v8.0.0027 -build \ ENV PACKAGES="\ bash \ git \ - make \ python \ py-pip \ " diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 366cf2d..01e9247 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -3,16 +3,15 @@ call ale#Set('c_clang_executable', 'clang') call ale#Set('c_clang_options', '-std=c11 -Wall') -call ale#Set('c_clang_parse_makefile', 0) function! ale_linters#c#clang#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_clang_executable') endfunction -function! ale_linters#c#clang#GetCommand(buffer) abort -let l:cflags = [] - if g:ale_c_clang_parse_makefile - let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') +function! ale_linters#c#clang#GetCommand(buffer, output) abort + let l:cflags = [] + if !empty(a:output) + let l:cflags = join(ale#c#ParseMakefile(a:buffer, join(a:output, '\n')), ' ') endif if empty(l:cflags) let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) @@ -33,6 +32,9 @@ call ale#linter#Define('c', { \ 'name': 'clang', \ 'output_stream': 'stderr', \ 'executable_callback': 'ale_linters#c#clang#GetExecutable', -\ 'command_callback': 'ale_linters#c#clang#GetCommand', +\ 'command_chain': [ +\ {'callback': 'ale#c#ParseMakefile', 'output_stream': 'stdout'}, +\ {'callback': 'ale_linters#c#clang#GetCommand'} +\ ], \ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \}) diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 3199fe8..155c5dd 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -3,16 +3,15 @@ call ale#Set('c_gcc_executable', 'gcc') call ale#Set('c_gcc_options', '-std=c11 -Wall') -call ale#Set('c_gcc_parse_makefile', 0) function! ale_linters#c#gcc#GetExecutable(buffer) abort return ale#Var(a:buffer, 'c_gcc_executable') endfunction -function! ale_linters#c#gcc#GetCommand(buffer) abort +function! ale_linters#c#gcc#GetCommand(buffer, output) abort let l:cflags = [] - if g:ale_c_gcc_parse_makefile - let l:cflags = join(ale#c#ParseMakefile(a:buffer), ' ') + if !empty(a:output) + let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') endif if empty(l:cflags) let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) @@ -33,6 +32,9 @@ call ale#linter#Define('c', { \ 'name': 'gcc', \ 'output_stream': 'stderr', \ 'executable_callback': 'ale_linters#c#gcc#GetExecutable', -\ 'command_callback': 'ale_linters#c#gcc#GetCommand', +\ 'command_chain': [ +\ {'callback': 'ale#c#ParseMakefile', 'output_stream': 'stdout'}, +\ {'callback': 'ale_linters#c#gcc#GetCommand'} +\ ], \ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \}) diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 7dc532b..314bb9a 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -1,6 +1,7 @@ " Author: gagbo , w0rp " Description: Functions for integrating with C-family linters. +call ale#Set('c_parse_makefile', 0) let s:sep = has('win32') ? '\' : '/' function! ale#c#FindProjectRoot(buffer) abort @@ -22,27 +23,30 @@ function! ale#c#FindProjectRoot(buffer) abort return '' endfunction -function! ale#c#ParseCFlags(project_root, stdout_make) abort - let l:cflags_list = [] - let l:cflags = split(a:stdout_make) +function! ale#c#ParseCFlagsToList(buffer, cflags) abort + let l:project_root = ale#c#FindProjectRoot(a:buffer) + let l:previous_option = '' let l:shell_option = 0 let l:macro_option = 0 - let l:previous_option = '' - for l:option in l:cflags + let l:cflags_list = [] + + for l:option in a:cflags " Check if cflag contained spaces if l:shell_option || stridx(l:option, '=`') >= 0 " Cflag contained shell command with spaces (ex. -D='date +%s') let l:shell_option = 1 - let l:previous_option .= l:option . ' ' + let l:previous_option .= l:option if l:option[-1: -1] isnot? '`' + let l:previous_option .= ' ' continue endif let l:shell_option = 0 elseif l:macro_option || stridx(l:option, '$((') > 0 " Cflag contained macro with spaces (ex -Da=$(( 4 * 20 ))) let l:macro_option = 1 - let l:previous_option .= l:option . ' ' + let l:previous_option .= l:option if stridx(l:option, '))') < 0 + let l:previous_option .= ' ' continue endif let l:macro_option = 0 @@ -54,7 +58,7 @@ function! ale#c#ParseCFlags(project_root, stdout_make) abort " Fix relative paths if needed if stridx(l:option, '-I') >= 0 if stridx(l:option, '-I' . s:sep) < 0 - let l:option = '-I' . a:project_root . s:sep . l:option[2:] + let l:option = '-I' . l:project_root . s:sep . l:option[2:] endif endif " Parse the cflag @@ -68,21 +72,33 @@ function! ale#c#ParseCFlags(project_root, stdout_make) abort return l:cflags_list endfunction -function! ale#c#ParseMakefile(buffer) abort - let l:project_root = ale#c#FindProjectRoot(a:buffer) - let l:project_cflags = [] +function! ale#c#ParseCFlags(buffer, stdout_make) abort + if g:ale_c_parse_makefile + for l:cflags in split(a:stdout_make, '\n') + if stridx(l:cflags, expand('#' . a:buffer . '...')) + let l:cflags = split(l:cflags) + break + endif + endfor + if !empty(l:cflags) + return ale#c#ParseCFlagsToList(a:buffer, l:cflags) + endif + endif + retur [] +endfunction - if !empty(l:project_root) - if !empty(globpath(l:project_root, 'Makefile', 0)) - let l:stdout_make = system('cd '. l:project_root . ' && make -n') - for l:object in split(l:stdout_make, '\n') - if stridx(l:object, expand('#' . a:buffer . '...')) - return ale#c#ParseCFlags(l:project_root, l:object) - endif - endfor +function! ale#c#ParseMakefile(buffer) abort + if g:ale_c_parse_makefile + let l:project_root = ale#c#FindProjectRoot(a:buffer) + let l:project_cflags = [] + + if !empty(l:project_root) + if !empty(globpath(l:project_root, 'Makefile', 0)) + return 'cd '. l:project_root . ' && make -n' + endif endif endif - return [] + return '' endfunction " Given a buffer number, search for a project root, and output a List diff --git a/test/command_callback/test_c_clang_command_callbacks.vader b/test/command_callback/test_c_clang_command_callbacks.vader index d6fc8ca..2f6d4dd 100644 --- a/test/command_callback/test_c_clang_command_callbacks.vader +++ b/test/command_callback/test_c_clang_command_callbacks.vader @@ -30,10 +30,10 @@ Execute(The executable should be configurable): Execute(The executable should be used in the command): AssertEqual \ ale#Escape('clang') . b:command_tail, - \ ale_linters#c#clang#GetCommand(bufnr('')) + \ ale_linters#c#clang#GetCommand(bufnr(''), []) let b:ale_c_clang_executable = 'foobar' AssertEqual \ ale#Escape('foobar') . b:command_tail, - \ ale_linters#c#clang#GetCommand(bufnr('')) + \ ale_linters#c#clang#GetCommand(bufnr(''), []) diff --git a/test/command_callback/test_c_gcc_command_callbacks.vader b/test/command_callback/test_c_gcc_command_callbacks.vader index 8038f41..3557576 100644 --- a/test/command_callback/test_c_gcc_command_callbacks.vader +++ b/test/command_callback/test_c_gcc_command_callbacks.vader @@ -30,10 +30,10 @@ Execute(The executable should be configurable): Execute(The executable should be used in the command): AssertEqual \ ale#Escape('gcc') . b:command_tail, - \ ale_linters#c#gcc#GetCommand(bufnr('')) + \ ale_linters#c#gcc#GetCommand(bufnr(''), []) let b:ale_c_gcc_executable = 'foobar' AssertEqual \ ale#Escape('foobar') . b:command_tail, - \ ale_linters#c#gcc#GetCommand(bufnr('')) + \ ale_linters#c#gcc#GetCommand(bufnr(''), []) diff --git a/test/test_c_import_paths.vader b/test/test_c_import_paths.vader index 6080779..a2ffe54 100644 --- a/test/test_c_import_paths.vader +++ b/test/test_c_import_paths.vader @@ -42,7 +42,7 @@ Execute(The C GCC handler should include 'include' directories for projects with \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' - \ , ale_linters#c#gcc#GetCommand(bufnr('')) + \ , ale_linters#c#gcc#GetCommand(bufnr(''), []) Execute(The C GCC handler should include 'include' directories for projects with a configure file): runtime! ale_linters/c/gcc.vim @@ -55,7 +55,7 @@ Execute(The C GCC handler should include 'include' directories for projects with \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/configure_project/include')) . ' ' \ . ' -' - \ , ale_linters#c#gcc#GetCommand(bufnr('')) + \ , ale_linters#c#gcc#GetCommand(bufnr(''), []) Execute(The C GCC handler should include root directories for projects with .h files in them): runtime! ale_linters/c/gcc.vim @@ -68,7 +68,7 @@ Execute(The C GCC handler should include root directories for projects with .h f \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' - \ , ale_linters#c#gcc#GetCommand(bufnr('')) + \ , ale_linters#c#gcc#GetCommand(bufnr(''), []) Execute(The C GCC handler should include root directories for projects with .hpp files in them): runtime! ale_linters/c/gcc.vim @@ -81,7 +81,7 @@ Execute(The C GCC handler should include root directories for projects with .hpp \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' - \ , ale_linters#c#gcc#GetCommand(bufnr('')) + \ , ale_linters#c#gcc#GetCommand(bufnr(''), []) Execute(The C Clang handler should include 'include' directories for projects with a Makefile): runtime! ale_linters/c/clang.vim @@ -94,7 +94,7 @@ Execute(The C Clang handler should include 'include' directories for projects wi \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include')) . ' ' \ . ' -' - \ , ale_linters#c#clang#GetCommand(bufnr('')) + \ , ale_linters#c#clang#GetCommand(bufnr(''), []) Execute(The C Clang handler should include 'include' directories for projects with a configure file): runtime! ale_linters/c/clang.vim @@ -107,7 +107,7 @@ Execute(The C Clang handler should include 'include' directories for projects wi \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' - \ , ale_linters#c#clang#GetCommand(bufnr('')) + \ , ale_linters#c#clang#GetCommand(bufnr(''), []) Execute(The C Clang handler should include root directories for projects with .h files in them): runtime! ale_linters/c/clang.vim @@ -120,7 +120,7 @@ Execute(The C Clang handler should include root directories for projects with .h \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/h_file_project')) . ' ' \ . ' -' - \ , ale_linters#c#clang#GetCommand(bufnr('')) + \ , ale_linters#c#clang#GetCommand(bufnr(''), []) Execute(The C Clang handler should include root directories for projects with .hpp files in them): runtime! ale_linters/c/clang.vim @@ -133,7 +133,7 @@ Execute(The C Clang handler should include root directories for projects with .h \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project/subdir')) . ' ' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/hpp_file_project')) . ' ' \ . ' -' - \ , ale_linters#c#clang#GetCommand(bufnr('')) + \ , ale_linters#c#clang#GetCommand(bufnr(''), []) Execute(The C++ GCC handler should include 'include' directories for projects with a Makefile): runtime! ale_linters/cpp/gcc.vim diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader index fac04bb..1fb67c9 100644 --- a/test/test_c_parse_makefile.vader +++ b/test/test_c_parse_makefile.vader @@ -1,15 +1,13 @@ Before: + Save g:ale_c_parse_makefile Save g:ale_c_gcc_options - Save g:ale_c_gcc_parse_makefile Save g:ale_c_clang_options - Save g:ale_c_clang_parse_makefile Save g:ale_cpp_gcc_options Save g:ale_cpp_clang_options call ale#test#SetDirectory('/testplugin/test') - let g:ale_c_gcc_parse_makefile=1 - let g:ale_c_clang_parse_makefile=1 + let g:ale_c_parse_makefile=1 let g:ale_c_gcc_options = '' let g:ale_c_clang_options = '' let g:ale_cpp_gcc_options = '' @@ -35,32 +33,44 @@ Execute(Move .git/HEAD to a temp dir): call delete(g:head_filename) endif -Execute(The C GCC handler should include directories specified in the include path for projects with a Makefile): +Execute(The CFlags parser should be able to parse include directives): runtime! ale_linters/c/gcc.vim call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ale#Escape('gcc') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) - \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir/'))[1:-3] . ' ' - \ . ' -' - \ , ale_linters#c#gcc#GetCommand(bufnr('')) + \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir'] + \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -c file.c') -Execute(The C++ Clang handler should include directories specified in the include path for projects with a Makefile): - runtime! ale_linters/c/clang.vim +Execute(The CFlags parser should be able to parse macro directives): + runtime! ale_linters/c/gcc.vim call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ale#Escape('clang') - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir')) - \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/subdir'))[1:-2] . ' ' - \ . ' -' - \ , ale_linters#c#clang#GetCommand(bufnr('')) + \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ '-DTEST=1'] + \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=1 -c file.c') +Execute(The CFlags parser should be able to parse macro directives with spaces): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ '-DTEST=$(( 2 * 4 ))'] + \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c') + +Execute(The CFlags parser should be able to parse shell directives with spaces): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=`date +%s` -c file.c') Execute(Move .git/HEAD back): if !empty(g:head_filename) From 7593e2037741fa264aa4029529180cc152c802b6 Mon Sep 17 00:00:00 2001 From: roel0 Date: Wed, 21 Mar 2018 07:37:32 +0100 Subject: [PATCH 5/8] Fix failing unit tests for windows --- autoload/ale/c.vim | 2 +- test/test_c_parse_makefile.vader | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 314bb9a..6d5d94d 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -1,4 +1,4 @@ -" Author: gagbo , w0rp +" Author: gagbo , w0rp , roel0 " Description: Functions for integrating with C-family linters. call ale#Set('c_parse_makefile', 0) diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader index 1fb67c9..a5d4591 100644 --- a/test/test_c_parse_makefile.vader +++ b/test/test_c_parse_makefile.vader @@ -39,7 +39,7 @@ Execute(The CFlags parser should be able to parse include directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir'] + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -c file.c') Execute(The CFlags parser should be able to parse macro directives): @@ -48,7 +48,7 @@ Execute(The CFlags parser should be able to parse macro directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), \ '-DTEST=1'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=1 -c file.c') @@ -58,7 +58,7 @@ Execute(The CFlags parser should be able to parse macro directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), \ '-DTEST=$(( 2 * 4 ))'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c') @@ -68,7 +68,7 @@ Execute(The CFlags parser should be able to parse shell directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I/testplugin/test/test_c_projects/makefile_project/subdir', + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=`date +%s` -c file.c') From 69237a7e57a67e37e8dcd0c3d97b4a6ffda5929a Mon Sep 17 00:00:00 2001 From: roel0 Date: Wed, 21 Mar 2018 20:44:35 +0100 Subject: [PATCH 6/8] Added additional unit tests + adapted review comments #1167 --- ale_linters/c/clang.vim | 12 +--- ale_linters/c/gcc.vim | 12 +--- autoload/ale/c.vim | 62 +++++++++++------ test/test_c_parse_makefile.vader | 69 +++++++++++++------ .../test_c_projects/makefile_project/Makefile | 3 - 5 files changed, 92 insertions(+), 66 deletions(-) diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim index 01e9247..ddec4fc 100644 --- a/ale_linters/c/clang.vim +++ b/ale_linters/c/clang.vim @@ -9,15 +9,7 @@ function! ale_linters#c#clang#GetExecutable(buffer) abort endfunction function! ale_linters#c#clang#GetCommand(buffer, output) abort - let l:cflags = [] - if !empty(a:output) - let l:cflags = join(ale#c#ParseMakefile(a:buffer, join(a:output, '\n')), ' ') - endif - if empty(l:cflags) - let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) - else - let l:cflags .= ' ' - endif + let l:cflags = ale#c#GetCFlags(a:buffer, a:output) " -iquote with the directory the file is in makes #include work for " headers in the same directory. @@ -33,7 +25,7 @@ call ale#linter#Define('c', { \ 'output_stream': 'stderr', \ 'executable_callback': 'ale_linters#c#clang#GetExecutable', \ 'command_chain': [ -\ {'callback': 'ale#c#ParseMakefile', 'output_stream': 'stdout'}, +\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#clang#GetCommand'} \ ], \ 'callback': 'ale#handlers#gcc#HandleGCCFormat', diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim index 155c5dd..9856395 100644 --- a/ale_linters/c/gcc.vim +++ b/ale_linters/c/gcc.vim @@ -9,15 +9,7 @@ function! ale_linters#c#gcc#GetExecutable(buffer) abort endfunction function! ale_linters#c#gcc#GetCommand(buffer, output) abort - let l:cflags = [] - if !empty(a:output) - let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') - endif - if empty(l:cflags) - let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) - else - let l:cflags .= ' ' - endif + let l:cflags = ale#c#GetCFlags(a:buffer, a:output) " -iquote with the directory the file is in makes #include work for " headers in the same directory. @@ -33,7 +25,7 @@ call ale#linter#Define('c', { \ 'output_stream': 'stderr', \ 'executable_callback': 'ale_linters#c#gcc#GetExecutable', \ 'command_chain': [ -\ {'callback': 'ale#c#ParseMakefile', 'output_stream': 'stdout'}, +\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#gcc#GetCommand'} \ ], \ 'callback': 'ale#handlers#gcc#HandleGCCFormat', diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 6d5d94d..54e553b 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -23,14 +23,14 @@ function! ale#c#FindProjectRoot(buffer) abort return '' endfunction -function! ale#c#ParseCFlagsToList(buffer, cflags) abort - let l:project_root = ale#c#FindProjectRoot(a:buffer) +function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort let l:previous_option = '' let l:shell_option = 0 let l:macro_option = 0 let l:cflags_list = [] for l:option in a:cflags + " Check if cflag contained spaces if l:shell_option || stridx(l:option, '=`') >= 0 " Cflag contained shell command with spaces (ex. -D='date +%s') @@ -51,16 +51,19 @@ function! ale#c#ParseCFlagsToList(buffer, cflags) abort endif let l:macro_option = 0 endif + if l:previous_option isnot? '' let l:option = l:previous_option let l:previous_option = '' endif + " Fix relative paths if needed if stridx(l:option, '-I') >= 0 if stridx(l:option, '-I' . s:sep) < 0 - let l:option = '-I' . l:project_root . s:sep . l:option[2:] + let l:option = '-I' . a:path_prefix . s:sep . l:option[2:] endif endif + " Parse the cflag if stridx(l:option, '-I') >= 0 || \ stridx(l:option, '-D') >= 0 @@ -69,35 +72,50 @@ function! ale#c#ParseCFlagsToList(buffer, cflags) abort endif endif endfor + return l:cflags_list endfunction function! ale#c#ParseCFlags(buffer, stdout_make) abort - if g:ale_c_parse_makefile - for l:cflags in split(a:stdout_make, '\n') - if stridx(l:cflags, expand('#' . a:buffer . '...')) - let l:cflags = split(l:cflags) - break - endif - endfor - if !empty(l:cflags) - return ale#c#ParseCFlagsToList(a:buffer, l:cflags) - endif + if !g:ale_c_parse_makefile + return [] endif - retur [] + + let l:buffer_filename = expand('#' . a:buffer . '...') + + for l:cflags in split(a:stdout_make, '\n') + if stridx(l:cflags, l:buffer_filename) + let l:cflags = split(l:cflags) + break + endif + endfor + + let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags) endfunction -function! ale#c#ParseMakefile(buffer) abort - if g:ale_c_parse_makefile - let l:project_root = ale#c#FindProjectRoot(a:buffer) - let l:project_cflags = [] +function! ale#c#GetCFlags(buffer, output) abort + let l:cflags = ' ' - if !empty(l:project_root) - if !empty(globpath(l:project_root, 'Makefile', 0)) - return 'cd '. l:project_root . ' && make -n' - endif + if g:ale_c_parse_makefile && !empty(a:output) + let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' ' + endif + + if l:cflags is# ' ' + let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)) + endif + + return l:cflags +endfunction + +function! ale#c#GetMakeCommand(buffer) abort + if g:ale_c_parse_makefile + let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + if !empty(l:makefile_path) + return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' endif endif + return '' endfunction diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader index a5d4591..0323ac8 100644 --- a/test/test_c_parse_makefile.vader +++ b/test/test_c_parse_makefile.vader @@ -19,20 +19,6 @@ After: call ale#test#RestoreDirectory() call ale#linter#Reset() -" Run this only once for this series of tests. The cleanup Execute step -" will run at the bottom of this file. -" -" We need to move .git/HEAD away so we don't match it, as we need to test -" functions which look for .git/HEAD. -Execute(Move .git/HEAD to a temp dir): - let g:temp_head_filename = tempname() - let g:head_filename = findfile('.git/HEAD', ';') - - if !empty(g:head_filename) - call writefile(readfile(g:head_filename, 'b'), g:temp_head_filename, 'b') - call delete(g:head_filename) - endif - Execute(The CFlags parser should be able to parse include directives): runtime! ale_linters/c/gcc.vim @@ -72,11 +58,52 @@ Execute(The CFlags parser should be able to parse shell directives with spaces): \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=`date +%s` -c file.c') -Execute(Move .git/HEAD back): - if !empty(g:head_filename) - call writefile(readfile(g:temp_head_filename, 'b'), g:head_filename, 'b') - call delete(g:temp_head_filename) - endif +Execute(The CFlagsToList parser should be able to parse multiple cflags): + runtime! ale_linters/c/gcc.vim - unlet! g:temp_head_filename - unlet! g:head_filename + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Isubdir -DTEST=`date +%s` -c file.c')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #2): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Isubdir -Ikernel/include -DTEST=`date +%s` -c file.c')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #3): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Isubdir -Ikernel/include -DTEST=`date +%s` -c file.c')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #4): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . + \ '-Ikernel/include -DTEST=`date +%s` -c file.c')) diff --git a/test/test_c_projects/makefile_project/Makefile b/test/test_c_projects/makefile_project/Makefile index 8b49a94..e69de29 100644 --- a/test/test_c_projects/makefile_project/Makefile +++ b/test/test_c_projects/makefile_project/Makefile @@ -1,3 +0,0 @@ -file.o : subdir/file.c - cc -c subdir/file.c -Isubdir - From cf62ef7b070c08bc6858aa88f0ff45be56b7c9b7 Mon Sep 17 00:00:00 2001 From: roel0 Date: Wed, 21 Mar 2018 20:56:29 +0100 Subject: [PATCH 7/8] Fixed windows compatibility unit tests #1167 --- test/test_c_parse_makefile.vader | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader index 0323ac8..f1988ec 100644 --- a/test/test_c_parse_makefile.vader +++ b/test/test_c_parse_makefile.vader @@ -79,7 +79,9 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #2): \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Isubdir -Ikernel/include -DTEST=`date +%s` -c file.c')) + \ split('gcc -Isubdir ' . + \ '-I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c')) Execute(The CFlagsToList parser should be able to parse multiple cflags #3): runtime! ale_linters/c/gcc.vim @@ -92,7 +94,9 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #3): \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Dgoal=9 -Isubdir -Ikernel/include -DTEST=`date +%s` -c file.c')) + \ split('gcc -Dgoal=9 -Isubdir ' . + \ '-I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c')) Execute(The CFlagsToList parser should be able to parse multiple cflags #4): runtime! ale_linters/c/gcc.vim @@ -106,4 +110,5 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #4): \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . - \ '-Ikernel/include -DTEST=`date +%s` -c file.c')) + \ '-I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c')) From dfb3e194d7a05b747c77d312a72e5149595bbcef Mon Sep 17 00:00:00 2001 From: roel0 Date: Tue, 27 Mar 2018 10:18:24 +0200 Subject: [PATCH 8/8] Extended unit tests + simplified parsing algoritme #1167 --- autoload/ale/c.vim | 58 +++++++----------- test/test_c_parse_makefile.vader | 100 ++++++++++++++++++++++++++----- 2 files changed, 107 insertions(+), 51 deletions(-) diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 54e553b..5ab10f0 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -24,44 +24,30 @@ function! ale#c#FindProjectRoot(buffer) abort endfunction function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort - let l:previous_option = '' - let l:shell_option = 0 - let l:macro_option = 0 let l:cflags_list = [] + let l:previous_options = [] for l:option in a:cflags - - " Check if cflag contained spaces - if l:shell_option || stridx(l:option, '=`') >= 0 - " Cflag contained shell command with spaces (ex. -D='date +%s') - let l:shell_option = 1 - let l:previous_option .= l:option - if l:option[-1: -1] isnot? '`' - let l:previous_option .= ' ' - continue - endif - let l:shell_option = 0 - elseif l:macro_option || stridx(l:option, '$((') > 0 - " Cflag contained macro with spaces (ex -Da=$(( 4 * 20 ))) - let l:macro_option = 1 - let l:previous_option .= l:option - if stridx(l:option, '))') < 0 - let l:previous_option .= ' ' - continue - endif - let l:macro_option = 0 + call add(l:previous_options, l:option) + " Check if cflag contained a '-' and should not have been splitted + let l:option_list = split(l:option, '\zs') + if l:option_list[-1] isnot# ' ' + continue endif - if l:previous_option isnot? '' - let l:option = l:previous_option - let l:previous_option = '' - endif + let l:option = join(l:previous_options, '-') + let l:previous_options = [] + + let l:option = '-' . substitute(l:option, '^\s*\(.\{-}\)\s*$', '\1', '') " Fix relative paths if needed - if stridx(l:option, '-I') >= 0 - if stridx(l:option, '-I' . s:sep) < 0 - let l:option = '-I' . a:path_prefix . s:sep . l:option[2:] - endif + if stridx(l:option, '-I') >= 0 && + \ stridx(l:option, '-I' . s:sep) < 0 + let l:rel_path = join(split(l:option, '\zs')[2:], '') + let l:rel_path = substitute(l:rel_path, '"', '', 'g') + let l:rel_path = substitute(l:rel_path, '''', '', 'g') + let l:option = ale#Escape('-I' . a:path_prefix . + \ s:sep . l:rel_path) endif " Parse the cflag @@ -81,11 +67,11 @@ function! ale#c#ParseCFlags(buffer, stdout_make) abort return [] endif - let l:buffer_filename = expand('#' . a:buffer . '...') - - for l:cflags in split(a:stdout_make, '\n') - if stridx(l:cflags, l:buffer_filename) - let l:cflags = split(l:cflags) + let l:buffer_filename = expand('#' . a:buffer . ':t') + let l:cflags = [] + for l:lines in split(a:stdout_make, '\\n') + if stridx(l:lines, l:buffer_filename) >= 0 + let l:cflags = split(l:lines, '-') break endif endfor diff --git a/test/test_c_parse_makefile.vader b/test/test_c_parse_makefile.vader index f1988ec..7c2fc21 100644 --- a/test/test_c_parse_makefile.vader +++ b/test/test_c_parse_makefile.vader @@ -25,7 +25,7 @@ Execute(The CFlags parser should be able to parse include directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')] + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -c file.c') Execute(The CFlags parser should be able to parse macro directives): @@ -34,7 +34,7 @@ Execute(The CFlags parser should be able to parse macro directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ '-DTEST=1'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=1 -c file.c') @@ -44,7 +44,7 @@ Execute(The CFlags parser should be able to parse macro directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ '-DTEST=$(( 2 * 4 ))'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c') @@ -54,7 +54,7 @@ Execute(The CFlags parser should be able to parse shell directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlags(bufnr(''), 'gcc -Isubdir -DTEST=`date +%s` -c file.c') @@ -64,10 +64,10 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ split('gcc -Isubdir -DTEST=`date +%s` -c file.c')) + \ split('gcc -Isubdir -DTEST=`date +%s` -c file.c', '-')) Execute(The CFlagsToList parser should be able to parse multiple cflags #2): runtime! ale_linters/c/gcc.vim @@ -75,13 +75,13 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #2): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ ['-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ [ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ split('gcc -Isubdir ' . \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c')) + \ ' -DTEST=`date +%s` -c file.c', '-')) Execute(The CFlagsToList parser should be able to parse multiple cflags #3): runtime! ale_linters/c/gcc.vim @@ -90,13 +90,13 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #3): AssertEqual \ ['-Dgoal=9', - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ split('gcc -Dgoal=9 -Isubdir ' . \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c')) + \ ' -DTEST=`date +%s` -c file.c', '-')) Execute(The CFlagsToList parser should be able to parse multiple cflags #4): runtime! ale_linters/c/gcc.vim @@ -105,10 +105,80 @@ Execute(The CFlagsToList parser should be able to parse multiple cflags #4): AssertEqual \ ['-Dgoal=9', - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), \ '-DTEST=`date +%s`'] \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . \ '-I'. ale#path#Simplify('kernel/include') . - \ ' -DTEST=`date +%s` -c file.c')) + \ ' -DTEST=`date +%s` -c file.c', '-')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #5): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . + \ '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c', '-')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #6): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . + \ '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c', '-')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #7): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . + \ '-I''dir with spaces''' . ' -Idir-with-dash' . + \ ' -I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c', '-')) + +Execute(The CFlagsToList parser should be able to parse multiple cflags #8): + runtime! ale_linters/c/gcc.vim + + call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') + + AssertEqual + \ ['-Dgoal=9', + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), + \ '-Dmacro-with-dash', + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')), + \ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ '-DTEST=`date +%s`'] + \ , ale#c#ParseCFlagsToList(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ split('gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' . + \ '-Dmacro-with-dash ' . + \ '-I''dir with spaces''' . ' -Idir-with-dash' . + \ ' -I'. ale#path#Simplify('kernel/include') . + \ ' -DTEST=`date +%s` -c file.c', '-'))