Add support for parsing compile_commands.json files for C compilers

This commit is contained in:
w0rp 2018-07-29 19:24:19 +01:00
parent a7b8cb4fe3
commit ac4bac8ea4
No known key found for this signature in database
GPG key ID: 0FC1ECAA8C81CD83
19 changed files with 345 additions and 239 deletions

View file

@ -14,10 +14,10 @@ function! ale_linters#c#clang#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer))
\ . ' -S -x c -fsyntax-only ' \ . ' -S -x c -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'c_clang_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'c_clang_options')) . ' -'
endfunction endfunction
call ale#linter#Define('c', { call ale#linter#Define('c', {

View file

@ -35,7 +35,7 @@ function! s:GetBuildDirectory(buffer) abort
return l:build_dir return l:build_dir
endif endif
return ale#c#FindCompileCommands(a:buffer) return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endfunction endfunction
function! ale_linters#c#clangtidy#GetCommand(buffer) abort function! ale_linters#c#clangtidy#GetCommand(buffer) abort

View file

@ -14,10 +14,10 @@ function! ale_linters#c#gcc#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer))
\ . ' -S -x c -fsyntax-only ' \ . ' -S -x c -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'c_gcc_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -'
endfunction endfunction
call ale#linter#Define('c', { call ale#linter#Define('c', {

View file

@ -14,10 +14,10 @@ function! ale_linters#cpp#clang#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer)) return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer))
\ . ' -S -x c++ -fsyntax-only ' \ . ' -S -x c++ -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'cpp_clang_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'cpp_clang_options')) . ' -'
endfunction endfunction
call ale#linter#Define('cpp', { call ale#linter#Define('cpp', {

View file

@ -16,7 +16,7 @@ function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort
let l:build_dir = ale#Var(a:buffer, 'c_build_dir') let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
if empty(l:build_dir) if empty(l:build_dir)
let l:build_dir = ale#c#FindCompileCommands(a:buffer) let l:build_dir = ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endif endif
" The extra arguments in the command are used to prevent .plist files from " The extra arguments in the command are used to prevent .plist files from

View file

@ -29,7 +29,7 @@ function! s:GetBuildDirectory(buffer) abort
return l:build_dir return l:build_dir
endif endif
return ale#c#FindCompileCommands(a:buffer) return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endfunction endfunction
function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort

View file

@ -14,10 +14,10 @@ function! ale_linters#cpp#gcc#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer))
\ . ' -S -x c++ -fsyntax-only ' \ . ' -S -x c++ -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'cpp_gcc_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -'
endfunction endfunction
call ale#linter#Define('cpp', { call ale#linter#Define('cpp', {

View file

@ -2,6 +2,7 @@
" Description: Functions for integrating with C-family linters. " Description: Functions for integrating with C-family linters.
call ale#Set('c_parse_makefile', 0) call ale#Set('c_parse_makefile', 0)
call ale#Set('c_parse_compile_commands', 0)
let s:sep = has('win32') ? '\' : '/' let s:sep = has('win32') ? '\' : '/'
" Set just so tests can override it. " Set just so tests can override it.
@ -26,11 +27,11 @@ function! ale#c#FindProjectRoot(buffer) abort
return '' return ''
endfunction endfunction
function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
let l:cflags_list = [] let l:cflags_list = []
let l:previous_options = [] let l:previous_options = []
for l:option in a:cflags for l:option in split(a:cflag_line, '-')
call add(l:previous_options, l:option) call add(l:previous_options, l:option)
" Check if cflag contained a '-' and should not have been splitted " Check if cflag contained a '-' and should not have been splitted
let l:option_list = split(l:option, '\zs') let l:option_list = split(l:option, '\zs')
@ -62,32 +63,122 @@ function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort
endif endif
endfor endfor
return l:cflags_list return join(l:cflags_list, ' ')
endfunction endfunction
function! ale#c#ParseCFlags(buffer, stdout_make) abort function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort
if !g:ale_c_parse_makefile if !g:ale_c_parse_makefile
return [] return ''
endif endif
let l:buffer_filename = expand('#' . a:buffer . ':t') let l:buffer_filename = expand('#' . a:buffer . ':t')
let l:cflags = [] let l:cflag_line = ''
for l:lines in split(a:stdout_make, '\\n')
if stridx(l:lines, l:buffer_filename) >= 0 " Find a line matching this buffer's filename in the make output.
let l:cflags = split(l:lines, '-') for l:line in a:make_output
if stridx(l:line, l:buffer_filename) >= 0
let l:cflag_line = l:line
break break
endif endif
endfor endfor
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags) let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h')
return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line)
endfunction
" Given a buffer number, find the build subdirectory with compile commands
" The subdirectory is returned without the trailing /
function! ale#c#FindCompileCommands(buffer) abort
" Look above the current source file to find compile_commands.json
let l:json_file = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
if !empty(l:json_file)
return l:json_file
endif
" Search in build directories if we can't find it in the project.
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
let l:c_build_dir = l:path . s:sep . l:dirname
let l:json_file = l:c_build_dir . s:sep . 'compile_commands.json'
if filereadable(l:json_file)
return l:json_file
endif
endfor
endfor
return ''
endfunction
" Cache compile_commands.json data in a Dictionary, so we don't need to read
" the same files over and over again. The key in the dictionary will include
" the last modified time of the file.
if !exists('s:compile_commands_cache')
let s:compile_commands_cache = {}
endif
function! s:GetListFromCompileCommandsFile(compile_commands_file) abort
if empty(a:compile_commands_file)
return []
endif
let l:time = getftime(a:compile_commands_file)
if l:time < 0
return []
endif
let l:key = a:compile_commands_file . ':' . l:time
if has_key(s:compile_commands_cache, l:key)
return s:compile_commands_cache[l:key]
endif
let l:data = []
silent! let l:data = json_decode(join(readfile(a:compile_commands_file), ''))
if !empty(l:data)
let s:compile_commands_cache[l:key] = l:data
return l:data
endif
return []
endfunction
function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort
for l:item in a:json_list
if bufnr(l:item.file) is a:buffer
return ale#c#ParseCFlags(a:dir, l:item.command)
endif
endfor
return ''
endfunction
function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort
let l:dir = ale#path#Dirname(a:compile_commands_file)
let l:json_list = s:GetListFromCompileCommandsFile(a:compile_commands_file)
return ale#c#ParseCompileCommandsFlags(a:buffer, l:dir, l:json_list)
endfunction endfunction
function! ale#c#GetCFlags(buffer, output) abort function! ale#c#GetCFlags(buffer, output) abort
let l:cflags = ' ' let l:cflags = ' '
if g:ale_c_parse_makefile && !empty(a:output) if ale#Var(a:buffer, 'c_parse_makefile') && !empty(a:output)
let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' ' let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output)
endif
if ale#Var(a:buffer, 'c_parse_compile_commands')
let l:json_file = ale#c#FindCompileCommands(a:buffer)
if !empty(l:json_file)
let l:cflags = ale#c#FlagsFromCompileCommands(a:buffer, l:json_file)
endif
endif endif
if l:cflags is# ' ' if l:cflags is# ' '
@ -98,8 +189,9 @@ function! ale#c#GetCFlags(buffer, output) abort
endfunction endfunction
function! ale#c#GetMakeCommand(buffer) abort function! ale#c#GetMakeCommand(buffer) abort
if g:ale_c_parse_makefile if ale#Var(a:buffer, 'c_parse_makefile')
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
if !empty(l:makefile_path) if !empty(l:makefile_path)
return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n'
endif endif
@ -161,19 +253,3 @@ let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
\ 'build', \ 'build',
\ 'bin', \ 'bin',
\]) \])
" Given a buffer number, find the build subdirectory with compile commands
" The subdirectory is returned without the trailing /
function! ale#c#FindCompileCommands(buffer) abort
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
let l:c_build_dir = l:path . s:sep . l:dirname
if filereadable(l:c_build_dir . '/compile_commands.json')
return l:c_build_dir
endif
endfor
endfor
return ''
endfunction

View file

@ -105,6 +105,21 @@ function! ale#path#GetAbsPath(base_directory, filename) abort
return ale#path#Simplify(a:base_directory . l:sep . a:filename) return ale#path#Simplify(a:base_directory . l:sep . a:filename)
endfunction endfunction
" Given a path, return the directory name for that path, with no trailing
" slashes. If the argument is empty(), return an empty string.
function! ale#path#Dirname(path) abort
if empty(a:path)
return ''
endif
" For /foo/bar/ we need :h:h to get /foo
if a:path[-1:] is# '/'
return fnamemodify(a:path, ':h:h')
endif
return fnamemodify(a:path, ':h')
endfunction
" Given a buffer number and a relative or absolute path, return 1 if the " Given a buffer number and a relative or absolute path, return 1 if the
" two paths represent the same file on disk. " two paths represent the same file on disk.
function! ale#path#IsBufferPath(buffer, complex_filename) abort function! ale#path#IsBufferPath(buffer, complex_filename) abort

View file

@ -25,13 +25,28 @@ g:ale_c_build_dir *g:ale_c_build_dir*
Type: |String| Type: |String|
Default: `''` Default: `''`
A path to the directory containing the `compile_commands.json` file to use For programs that can read `compile_commands.json` files, this option can be
with c-family linters. Usually setting this option to a non-empty string set to the directory containing the file for the project. ALE will try to
will override the |g:ale_c_build_dir_names| option to impose a compilation determine the location of `compile_commands.json` automatically, but if your
database (it can be useful if multiple builds are in multiple build file exists in some other directory, you can set this option so ALE will
subdirectories in the project tree). know where it is.
This feature is also most useful for the clang tools linters, wrapped
around LibTooling (namely clang-tidy here) This directory will be searched instead of |g:ale_c_build_dir_names|.
g:ale_c_parse_compile_commands *g:ale_c_parse_compile_commands*
*b:ale_c_parse_compile_commands*
Type: |Number|
Default: `0`
If set to `1`, ALE will parse `compile_commands.json` files to automatically
determine flags for C or C++ compilers. ALE will first search for the
nearest `compile_commands.json` file, and then look for
`compile_commands.json` files in the directories for
|g:ale_c_build_dir_names|.
If |g:ale_c_parse_makefile| or |b:ale_c_parse_makefile| is set to `1`, the
output of `make -n` will be preferred over `compile_commands.json` files.
g:ale_c_parse_makefile *g:ale_c_parse_makefile* g:ale_c_parse_makefile *g:ale_c_parse_makefile*

View file

@ -10,6 +10,7 @@ The following C options also apply to some C++ linters too.
* |g:ale_c_build_dir_names| * |g:ale_c_build_dir_names|
* |g:ale_c_build_dir| * |g:ale_c_build_dir|
* |g:ale_c_parse_makefile| * |g:ale_c_parse_makefile|
* |g:ale_c_parse_compile_commands|
=============================================================================== ===============================================================================

View file

@ -1,4 +1,7 @@
Before: Before:
Save g:ale_c_parse_makefile
let g:ale_c_parse_makefile = 0
call ale#assert#SetUpLinterTest('c', 'clang') call ale#assert#SetUpLinterTest('c', 'clang')
let b:command_tail = ' -S -x c -fsyntax-only -iquote' let b:command_tail = ' -S -x c -fsyntax-only -iquote'
\ . ' ' . ale#Escape(getcwd()) \ . ' ' . ale#Escape(getcwd())

View file

@ -1,4 +1,7 @@
Before: Before:
Save g:ale_c_parse_makefile
let g:ale_c_parse_makefile = 0
call ale#assert#SetUpLinterTest('c', 'gcc') call ale#assert#SetUpLinterTest('c', 'gcc')
let b:command_tail = ' -S -x c -fsyntax-only -iquote' let b:command_tail = ' -S -x c -fsyntax-only -iquote'

View file

@ -1,4 +1,7 @@
Before: Before:
Save g:ale_c_parse_makefile
let g:ale_c_parse_makefile = 0
call ale#assert#SetUpLinterTest('cpp', 'clang') call ale#assert#SetUpLinterTest('cpp', 'clang')
let b:command_tail = ' -S -x c++ -fsyntax-only -iquote' let b:command_tail = ' -S -x c++ -fsyntax-only -iquote'
\ . ' ' . ale#Escape(getcwd()) \ . ' ' . ale#Escape(getcwd())

View file

@ -1,4 +1,7 @@
Before: Before:
Save g:ale_c_parse_makefile
let g:ale_c_parse_makefile = 0
call ale#assert#SetUpLinterTest('cpp', 'gcc') call ale#assert#SetUpLinterTest('cpp', 'gcc')
let b:command_tail = ' -S -x c++ -fsyntax-only -iquote' let b:command_tail = ' -S -x c++ -fsyntax-only -iquote'
\ . ' ' . ale#Escape(getcwd()) \ . ' ' . ale#Escape(getcwd())

View file

@ -0,0 +1,161 @@
Before:
Save g:ale_c_parse_makefile
call ale#test#SetDirectory('/testplugin/test')
let g:ale_c_parse_makefile = 1
After:
Restore
call ale#test#RestoreDirectory()
Execute(The CFlags parser should be able to parse include directives):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
\ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')),
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c'])
Execute(The CFlags parser should be able to parse macro directives):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
\ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
\ . ' -DTEST=1',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=1 -c file.c'])
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
\ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
\ . ' -DTEST=$(( 2 * 4 ))',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c'])
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
\ ale#Escape('-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=`date +%s` -c file.c'])
Execute(ParseCFlags should be able to parse flags with relative paths):
AssertEqual
\ 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#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Isubdir '
\ . '-I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should be able to parse -Dgoal):
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/kernel/include'))
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Isubdir '
\ . '-I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should ignore -T and other arguments):
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/kernel/include'))
\ . ' -DTEST=`date +%s`',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should handle paths with spaces in double quotes):
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#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should handle paths with spaces in single quotes):
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#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
Execute(ParseCFlags should handle paths with minuses):
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#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ '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(ParseCFlags should handle -D with minuses):
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#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ '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'
\ )
Execute(FlagsFromCompileCommands should tolerate empty values):
AssertEqual '', ale#c#FlagsFromCompileCommands(bufnr(''), '')
Execute(ParseCompileCommandsFlags should tolerate empty values):
AssertEqual '', ale#c#ParseCompileCommandsFlags(bufnr(''), '', [])
Execute(ParseCompileCommandsFlags should parse some basic flags):
noautocmd execute 'file! ' . fnameescape('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')
AssertEqual
\ '-I/usr/include/xmms2',
\ ale#c#ParseCompileCommandsFlags(bufnr(''), '/foo/bar/xmms2-mpris', [
\ {
\ 'directory': '/foo/bar/xmms2-mpris',
\ 'command': '/usr/bin/cc -I/usr/include/xmms2 -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o -c /foo/bar/xmms2-mpris/src/xmms2-mpris.c',
\ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c'
\ },
\ ])

View file

@ -2,6 +2,7 @@ Before:
" Make sure the c.vim file is loaded first. " Make sure the c.vim file is loaded first.
call ale#c#FindProjectRoot(bufnr('')) call ale#c#FindProjectRoot(bufnr(''))
Save g:ale_c_parse_makefile
Save g:ale_c_gcc_options Save g:ale_c_gcc_options
Save g:ale_c_clang_options Save g:ale_c_clang_options
Save g:ale_cpp_gcc_options Save g:ale_cpp_gcc_options
@ -19,6 +20,7 @@ Before:
call ale#test#SetDirectory('/testplugin/test') call ale#test#SetDirectory('/testplugin/test')
let g:ale_c_parse_makefile = 0
let g:ale_c_gcc_options = '' let g:ale_c_gcc_options = ''
let g:ale_c_clang_options = '' let g:ale_c_clang_options = ''
let g:ale_cpp_gcc_options = '' let g:ale_cpp_gcc_options = ''

View file

@ -1,184 +0,0 @@
Before:
Save g:ale_c_parse_makefile
Save g:ale_c_gcc_options
Save g:ale_c_clang_options
Save g:ale_cpp_gcc_options
Save g:ale_cpp_clang_options
call ale#test#SetDirectory('/testplugin/test')
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 = ''
let g:ale_cpp_clang_options = ''
After:
Restore
call ale#test#RestoreDirectory()
call ale#linter#Reset()
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('-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):
runtime! ale_linters/c/gcc.vim
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
\ [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')
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
\ [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')
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
\ [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')
Execute(The CFlagsToList parser should be able to parse multiple cflags):
runtime! ale_linters/c/gcc.vim
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
AssertEqual
\ [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', '-'))
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
\ [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', '-'))
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',
\ 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', '-'))
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',
\ 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', '-'))
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', '-'))

View file

@ -0,0 +1,8 @@
Execute(ale#path#Dirname should return empty strings should be returned for empty values):
AssertEqual '', ale#path#Dirname('')
AssertEqual '', ale#path#Dirname(0)
AssertEqual '', ale#path#Dirname(v:null)
Execute(ale#path#Dirname should return the dirname of paths):
AssertEqual '/foo', ale#path#Dirname('/foo/bar')
AssertEqual '/foo', ale#path#Dirname('/foo/bar/')