Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ale-cpp] Linting in headers don't work. Add option to run clang tools in source file. #782

Closed
phcerdan opened this issue Jul 19, 2017 · 44 comments

Comments

@phcerdan
Copy link

Clang tools, like clang-tidy, clang-check, don't show errors when run in headers, even with a compilation_database (the compilation database doesn't include a compile command for headers)

Skipping /path/to/header.h. Compile command not found.

This is not an ALE problem, but it would be great to add options for a workaround. One option would to run the tool instead of pointing to the current header, pointing to the source file (which is compiled) including the header file.

so:

clang-tidy -p /path/folder_compilation_database header.h
 Skipping /path/to/header.h. Compile command not found.
clang-tidy -p /path/folder_compilation_database source_with_header.cpp
... It works, show errors in header ...
@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

We could consider creating temporary .c files for checking header files which include the header files. There are two problems with this.

  1. We can't pass any additional arguments to clang tools when using compilation databases.
  2. We don't know what a valid path to the header is. Is it foo.h? include/foo.h?

We could write the temporary files with a .cpp extension instead. That might work. I don't know if either linters only run against the files on disk. If they do, then that's not an option.

It sounds like this problem should really be fixed in clang-tidy and clang-check.

@phcerdan
Copy link
Author

It is a long standing problem, even cmake, which generates the compilation_database knows nothing about headers. There is this clangd (Language protocol server by clang) that might solve most of these problems. But still on development.

One non-automated solution would be to let the user choose the file included in the compilation database which uses the header. YouCompleteMe does some sophisticated thing to guess headers, but still it doesn't work when the class in the header uses templates ( a lot of them). Pointing to a source file will solve this, because the template is instantiated there.

It is not an easy problem, and not the job of Ale to solve it, but it would be super nice to have something, with the flexibility of your plugin.

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

Could we just stop setting the -p options for clang-tidy and clang-check if the file extension is .h or .hpp? There are no compile commands for headers anyway. Maybe there is some kind parsing that could be done like in #725 for getting the include paths from the compile_commands.json file.

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

This is all yet another reason to want modules to become part of the C++ standard.

@phcerdan
Copy link
Author

phcerdan commented Jul 20, 2017

Sounds good, and the list for headers extensions should be flexible (there are .hxx, .txx, .tcc, ...).

#725 sounds reasonable, but still, has a problem, how do you choose the source file to get the relevant arguments for the current header? And even with the right ones, what happen with non-instantiated templates? Isn't the checker going to ignore them in that case?

Relevant conversation in YCM when this arose: ycm-core/YouCompleteMe#174.
They faced this, and opted for a python .ycm_conf_file, where the user can set its own compilation flags, or deal with particular files (independent of the compilation database, but also can use it). @Valloric commented there that he changed the suffix in header files to cpp, and then handle that to libclang, or the actual logic of YCM to get completions. But note that YCM/libclang still doesn't autocomplete in headers with templates.

I, as an ALE user, would be happy to setlocal a variable in the .h buffer, pointing to the relevant source (included in the compilation database to get the flags) that actually use that header. If the project is long/complex enough, maybe even creating a vimL auto-command doing this mapping automatically instead of manually.

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

You could use g:ale_pattern_options to define options for the linters based on patterns for the filenames.

@phcerdan
Copy link
Author

Can I change the target file where run the checker for a file different than the current?

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

I don't understand your last message.

You can change any buffer-local option with g:ale_pattern_options. Check out :help g:ale_pattern_options.

@phcerdan
Copy link
Author

Testing it with no luck:

let g:ale_pattern_options = {
      \   '\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_options': '/path/src.cpp',
      \   },
      \}

ALEInfo:


 Current Filetype: cpp
Available Linters: ['clang', 'clangcheck', 'clangtidy', 'cppcheck', 'cpplint', 'g++']
  Enabled Linters: ['clangtidy']
 Linter Variables:

let g:ale_cpp_clangtidy_checks = ['*']
let g:ale_cpp_clangtidy_executable = 'clang-tidy'
let g:ale_cpp_clangtidy_options = ''
let b:ale_cpp_clangtidy_options = '/path/src.cpp'
 Global Variables:

let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%s'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'always'
let g:ale_linter_aliases = {}
let g:ale_linters = {'cpp': ['clangtidy']}
let b:ale_linters = {'cpp': ['clangtidy']}
let g:ale_open_list = 0
let g:ale_set_highlights = 1
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
let g:ale_set_signs = 1
let g:ale_sign_column_always = 0
let g:ale_sign_error = '>>'
let g:ale_sign_offset = 1000000
let g:ale_sign_warning = '--'
let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK']
let g:ale_warn_about_trailing_whitespace = 1
  Command History:

(finished - exit code 0) ['/bin/zsh', '-c', '''clang-tidy'' -checks=''*'' ''/path/include/header.h'' -p ''/path/project/build''']

<<<NO OUTPUT RETURNED>>>

Being editing header.h, and having a compile_commands.json in /path/project/build I would like a command like:

['/bin/zsh', '-c', '''clang-tidy'' -checks=''*'' ''/path/src.cpp'' -p ''/path/project/build''']

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

The guy who implemented the build directory options made it so any additional options are ignored if the build directory is detected. This is because you can't set additional options if you're using a compile_commands.json file.

We might consider adding another option to disable using compile_commands.json, so you can set options manually. This could be the default for header files.

w0rp added a commit that referenced this issue Jul 20, 2017
@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

I pushed a commit now which makes it so the -p build directory option is never set if the filename ends with .h or .hpp. It should also set the options you define now.

@w0rp
Copy link
Member

w0rp commented Jul 20, 2017

Passing a .cpp filename as an option won't really do much, but you can pass other compiler arguments, which will be set after --.

rsrchboy added a commit to rsrchboy/ale that referenced this issue Jul 20, 2017
* upstream/master:
  dense-analysis#782 - Do not set the build directory for clang-tidy for header files, which does not work
  Update the installation notes for the standard package management sysytem so helptag generation will actually work
  Fix dense-analysis#786 - Only set --no-local-style for yapf if a configuration file is detected
phcerdan added a commit to phcerdan/ale that referenced this issue Jul 21, 2017
Provide extra logic to handle header files in cpp.
Only using clangtidy, but same workaround will work in other clang checkers.

First, provide a list of headers suffixes:
By default:
let g:cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
And then, use a variable specific for headers:
let b:cpp_clangtidy_header_sourcefile = ''

This variable should point to a source file (a .cpp) which uses the
current header.
This source file could be set by the user when editing the buffer, with
a simple:
let b:cpp_clangtidy_header_sourcefile

Or set a g:ale_pattern_options in a project vimL file.
let g:ale_pattern_options = {
      \   'my_favourite_header\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_header_sourcefile': '/path/test_header.cpp',
      \   },
      \}

Related: dense-analysis#782
phcerdan added a commit to phcerdan/ale that referenced this issue Jul 21, 2017
Provide extra logic to handle header files in cpp.
Only using clangtidy, but same workaround will work in other clang checkers.

First, provide a list of headers suffixes:
By default:
let g:cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
And then, use a variable specific for headers:
let b:cpp_clangtidy_header_sourcefile = ''

This variable should point to a source file (a .cpp) which uses the
current header.
This source file could be set by the user when editing the buffer, with
a simple:
let b:cpp_clangtidy_header_sourcefile

Or set a g:ale_pattern_options in a project vimL file.
let g:ale_pattern_options = {
      \   'my_favourite_header\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_header_sourcefile': '/path/test_header.cpp',
      \   },
      \}

Related: dense-analysis#782
@phcerdan
Copy link
Author

phcerdan commented Jul 21, 2017

I have created a PR with an option header_sourcefile to let the user choose a sourcefile when in a header.
It works! #790

I have tested with:
vim header.h -c "let b:ale_cpp_clangtidy_header_sourcefile='source.cpp'"

And ALEInfo:

 Current Filetype: cpp
Available Linters: ['clang', 'clangcheck', 'clangtidy', 'cppcheck', 'cpplint', 'g++']
  Enabled Linters: ['clangtidy']
 Linter Variables:

let g:ale_cpp_clangtidy_checks = ['*']
let g:ale_cpp_clangtidy_executable = 'clang-tidy'
let g:ale_cpp_clangtidy_header_sourcefile = ''
let b:ale_cpp_clangtidy_header_sourcefile = 'source.cpp'
let g:ale_cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
let g:ale_cpp_clangtidy_options = ''
 Global Variables:

let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%s'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'always'
let g:ale_linter_aliases = {}
let g:ale_linters = {'cpp': ['clangtidy']}
let g:ale_open_list = 0
let g:ale_set_highlights = 1
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
let g:ale_set_signs = 1
let g:ale_sign_column_always = 0
let g:ale_sign_error = '>>'
let g:ale_sign_offset = 1000000
let g:ale_sign_warning = '--'
let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK']
let g:ale_warn_about_trailing_whitespace = 1
  Command History:

(started) ['/bin/zsh', '-c', '''clang-tidy'' -checks=''*'' ''source.cpp'' -p ''/path/build''']

@w0rp
Copy link
Member

w0rp commented Jul 21, 2017

I closed that pull request. I don't like the solution, I think it's much too complicated, and that's a recipe for creating a series of further issues.

I think we should either not check header files with clang-tidy at all, or maybe check the directory the header is in, something like that. Something which is along the lines of something clang-tidy really supports. Having to configure a filename to check for each header you want to check is too much work, and it's going to lead to weird issues when we start implementing showing errors from other files in quickfix, etc.

@phcerdan
Copy link
Author

I understand, it is hack. clang-tools works with cpp files. I will use it though, I work most of the time in headers with templates. And thanks for the review, first time with vimL.

In terms of checking for the directory of the headers, usually all the headers are in an include directory, where no .cpp files are, so nothing will change in that case.

@w0rp
Copy link
Member

w0rp commented Jul 21, 2017

I wonder what tools are best to use for this. If you're writing a lot of generic code, you're probably looking at headers almost entirely.

If you ever need to, you can also add your own custom linter in ale_linters in your .vim directory. See :help ale-linter-loading-behaviour and :help ale#linter#Define(). That might be helpful if you need something heavily specialised.

@gagbo
Copy link
Contributor

gagbo commented Sep 15, 2017

I'm not sure clang-tools will be able to deal with this issue.

I looked a little bit into Rip-Rip/clang_complete plugin because the doc speaks about the header issue, and apparently, the workaround is to use actual clang to run a dry compilation and passing the -header flag :

My main problem with this investigation is that I can't find information on either -header or even -fsyntax-only when I print the help from the clang in my path, so I can't be sure that it's the workaround here.

@sasq64
Copy link

sasq64 commented Oct 30, 2017

There is the concept of "companion files" which are files with the same basename, one cpp and one h.

A simple option would be to add a flag that when turned on does

clang-tidy myfile.cpp -header-filter=myfile.h

If myfile.cpp exist when opening myfile.h

@sasq64
Copy link

sasq64 commented Oct 30, 2017

A workaround right now would be to create a clang-tidy wrapper that just does this check and then runs

clang-tidy -p build myfile.cpp -line-filter='[{"name":"myfile.h"}]' -header-filter=myfile.h

@sasq64
Copy link

sasq64 commented Oct 30, 2017

Python is not my language but I took a swing at making a simple wrapper. Works for simple cases:
https://gist.github.com/sasq64/b1bc795a5a8b971e36a664a3427ba805

...except ALE removes the compilation database from the options. Is there a flag to turn that "feature" off ?

@w0rp
Copy link
Member

w0rp commented Oct 30, 2017

I don't know what you're talking about, so I don't know. I know that clang-tidy stops detecting compile_commands.json files if you pass other options with --.

Just about all code in this repository must be VimL only.

@sasq64
Copy link

sasq64 commented Oct 30, 2017

I meant that you did a change so that "-p " is not added when running clang-tidy on a header;
87616c5

@gagbo
Copy link
Contributor

gagbo commented Oct 31, 2017

I'm not sure I understand either. The help from clang-tidy seem to tell that -header-filter's purpose is to keep only the warnings from given header files while checking a cpp file :

  -header-filter=<string>      - 
                                 Regular expression matching the names of the
                                 headers to output diagnostics from. Diagnostics
                                 from the main file of each translation unit are
                                 always displayed.
                                 Can be used together with -line-filter.
                                 This option overrides the 'HeaderFilter' option
                                 in .clang-tidy file, if any.
  -line-filter=<string>        - 
                                 List of files with line ranges to filter the
                                 warnings. Can be used together with
                                 -header-filter. The format of the list is a
                                 JSON array of objects:
                                   [
                                     {"name":"file1.cpp","lines":[[1,3],[5,7]]},
                                     {"name":"file2.h"}
                                   ]

Therefore, we still have the same issue (How can we actually run the clang-tools checking header files with the correct include and compilation flags from a compilation database). Also, clang-check does not have any options like this. And the workaround you propose ( clang-tidy -p build myfile.cpp -line-filter='[{"name":"myfile.h"}]' -header-filter=myfile.h) looks like it's the same solution as the original post : lint the 'associated' cpp file and filter the output to only have errors related to header. The whole discussion has been made as to why it's not seen as the best solution here (then again, you can disagree)

Do you have a MWE to show how we can make it work ? Like 1 cpp, 1 header, 1 compilation database so that clang-tidy test.h shows relevant errors (ie. not "can't find "). I'm quite interested if you make it work.

I think our best shot is to parse the compilation database and try to guess relevant folder from here #725 but the PR does not advance much these days (time is a rare resource)

@purpley
Copy link

purpley commented Apr 17, 2018

So I've noticed that if you run the linter on a cpp file and then later on the header, for a moment, the correct errors will show up in the header, before being replaced by garbage.

As a temporary solution it seems like having header files do the callbacks when you call :ALELint without actually trying to lint would at least give the correct errors/warnings. It's not a real solution, but since linting headers doesnt work at all right now, and i'm guessing (maybe?) it wouldnt be too hard to implement, it might be worth doing.

@gagbo
Copy link
Contributor

gagbo commented Apr 17, 2018

When you 'lint' cpp files, you call a compiler so you can check the translation unit it's associated with. This program will find some errors and warnings, linked to lines written in the headers they include, so you get hits for these lines.

It seems that what's happening on what you describe is that you see the warnings associated to the implementation file you just linted, and they just disappear after some time because an event triggered :ALELint again.

The current issue with header files (if you want to find "errors" and "warnings" that are not just whitespace changes), is that we need a translation unit to try to "compile" the code and see if warnings and issues arise. I don't know if it is that easy. Usually, to get the hits inside a header file, you need to try to compile the files including them. So there seem to be only 2 solutions in my opinion :

  • Call makeprg asynchronously to try to compile the whole project and collect header errors
  • Find out somehow which implementation files include the current header buffer, and compile/check these asynchronously to collect the header errors.

Both solutions do not look that easy to implement efficiently.

EDIT : I commented without really checking the whole thread because I thought my answer would be short, I hope I don't repeat myself too much

@purpley
Copy link

purpley commented Apr 18, 2018

yeah, which is why as a temporary measure you let :ALELint in header files only call the callbacks and skip trying to lint, so if you've run the linter in the implementation file, you get warnings and errors in the associated header.

it's not a real solution, because it requires linting the implementation file manually, but it lets you lint a header.

@phcerdan
Copy link
Author

phcerdan commented Apr 18, 2018

Until the compile_commands.json doesn't include headers this task is complicated.
There is a utility: https://github.com/Sarcasm/compdb that aims to do exactly that, creating compile_commands.json for headers from an existing json, that might be worth exploring, but I haven't tested it properly.

I tried a workaround that I personally use, but I understand it is not the best solution: phcerdan@5d9f6f9

There you set in the local buffer of your header:
let b:cpp_clangtidy_header_sourcefile=/path/source_file.cpp

And ALE will compile the source file when your header is modified, that will lint your current header.

@w0rp
Copy link
Member

w0rp commented Apr 18, 2018

I think the solution for clang-tidy and clang-check is to do something similar to what you did. Look for a file like foo.c or foo.cpp for a header named something like foo.h or foo.hpp, and run the tools against the source files instead. For headers where no translation-unit can be found, we can just not run the commands.

@phcerdan
Copy link
Author

phcerdan commented Apr 18, 2018

That automatic lookup is something YouCompleteMe uses as well, and it is good.
Pointing to a user selected source file has the benefits that also work for template headers, which have no .cpp source file. User can point to a test.cpp using the template class to get the lint.

@w0rp
Copy link
Member

w0rp commented Apr 18, 2018

Okay, sounds like a good idea. I'll give it a go when I can.

@phcerdan
Copy link
Author

The problem with the look up is that cpp doesn't impose a convention in structure as rust (for example) does.
Majority of people put headers in include, cpp files in src and tests... somewhere, in a paralell test or tests folder, or somewhere else. But the naming for the test is not standarized. I tend to use test_headername.cpp, but ... random choice.

@w0rp
Copy link
Member

w0rp commented Apr 20, 2018

Yes, that's the big problem with C++. There are no standards for these things. If the standards existed, the this problem would have already been solved in some way. I think the best you can do is try to look for what the majority of people use, and make most people happy.

@w0rp w0rp added the C/C++ label Apr 23, 2018
@cdbrkfxrpt
Copy link

cdbrkfxrpt commented Jun 27, 2018

Thank you so much for ale, it's really great!
I would really love for clang-tidy to be supported better, i.e. for it to work on headers. Sadly, I am super tight on time currently and can't really contribute anything, but if the status on this changes, that would be cool.
Specifically, I'm getting this error:

  Command History:
(executable check - success) clang++
(started) ['/bin/zsh', '-c', '''clang++'' -S -x c++ -fsyntax-only -iquote ''/home/flrn/workbench/eich18/software/vimeTK/include'' -std=c++14 -Wall -Wextra -pedantic -I./include -I./thirdparty/docopt.cpp -I./thirdparty/yaml-cpp/include -I./thirdparty/progress_bar/include -I./thirdparty/spdlog/include - < ''/tmp/vAV126U/1/core.h''']
(executable check - success) clang-tidy
(started) ['/bin/zsh', '-c', '''clang-tidy'' -checks=''boost-*,bugprone-*,cert-*,cppcoreguidelines-*,clang-analyzer-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*'' ''/home/flrn/workbench/eich18/software/vimeTK/include/core.h''']
(finished - exit code 0) ['/bin/zsh', '-c', '''clang++'' -S -x c++ -fsyntax-only -iquote ''/home/flrn/workbench/eich18/software/vimeTK/include'' -std=c++14 -Wall -Wextra -pedantic -I./include -I./thirdparty/docopt.cpp -I./thirdparty/yaml-cpp/include -I./thirdparty/progress_bar/include -I./thirdparty/spdlog/include - < ''/tmp/vAV126U/2/core.h''']
<<<NO OUTPUT RETURNED>>>
(finished - exit code 1) ['/bin/zsh', '-c', '''clang-tidy'' -checks=''boost-*,bugprone-*,cert-*,cppcoreguidelines-*,clang-analyzer-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*'' ''/path/to/project/vimeTK/include/core.h''']
<<<OUTPUT STARTS>>>
/home/flrn/workbench/eich18/software/vimeTK/include/core.h:20:10: error: 'functional' file not found [clang-diagnostic-error]
#include <functional>
         ^
<<<OUTPUT ENDS>>>

if and only if I turn on clang-tidy. I want to use clang and clang-tidy, thus:

let g:ale_cpp_clang_executable = 'clang++'
let g:ale_cpp_clang_options = '-std=c++14 -Wall -Wextra -pedantic -I./include -I./thirdparty/docopt.cpp -I./thirdparty/yaml-cpp/include -I./thirdparty/progress_bar/include -I./thirdparty/spdlog/include'
let g:ale_cpp_clangtidy_checks = ['boost-*', 'bugprone-*', 'cert-*', 'cppcoreguidelines-*', 'clang-analyzer-*', 'hicpp-*', 'misc-*', 'modernize-*', 'performance-*', 'portability-*', 'readability-*']
let g:ale_cpp_clangtidy_executable = 'clang-tidy'
let g:ale_cpp_clangtidy_options = ''

clang-tidy should get what it needs vom compile-commands.json.
Any comment on why this is and possibly how to solve it is greatly appreciated.

@w0rp
Copy link
Member

w0rp commented Jun 28, 2018

Open an issue for that. clang-tidy should be getting options from compile_commands.json, as you say.

@gagbo
Copy link
Contributor

gagbo commented Jun 28, 2018

Using the compile_commands files for header files is not as easy as it can be, since they contain only compilation commands (so only matching implementation files, not header files)

@phcerdan exposed the quickest reasonable solution in an earlier comment, guessing the good implementation files to lint in order to get the warnings in a selected header file is no easy issue. This is hard because a header file can be included potentially in the whole project so you'd need to actually compile everything to be sure to catch all clang-tidy errors in the header.

At this point I really do think it is more sensible to either use the fix mentioned above, or maybe wait and hope that LSP can provide ways of generating this file list like https://github.com/Sarcasm/compdb is apparently aiming to do, but without having to add another dependency on the user's side.

@cdbrkfxrpt
Copy link

cdbrkfxrpt commented Jun 28, 2018

I don't really understand that reasoning. clang-tidy will happily show errors from headers based on what is included from source files - in header only template libraries I run it on the unit test sources and get errors back for my headers.

However, I can't get ale to use my compilation database at all right now, not even for the clang checker, so I can't really provide meaningful input as to whether or not clang-tidy works the way I want it to. Probably I'm doing something wrong; am still looking for what that might be and in the meantime just living with passing the options manually.

@w0rp w0rp mentioned this issue Aug 8, 2018
@w0rp
Copy link
Member

w0rp commented Aug 8, 2018

What might work for this is using -header-filter='.*'.

phcerdan added a commit to phcerdan/ale that referenced this issue Aug 21, 2018
Provide extra logic to handle header files in cpp.
Only using clangtidy, but same workaround will work in other clang checkers.

First, provide a list of headers suffixes:
By default:
let g:cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
And then, use a variable specific for headers:
let b:cpp_clangtidy_header_sourcefile = ''

This variable should point to a source file (a .cpp) which uses the
current header.
This source file could be set by the user when editing the buffer, with
a simple:
let b:cpp_clangtidy_header_sourcefile

Or set a g:ale_pattern_options in a project vimL file.
let g:ale_pattern_options = {
      \   'my_favourite_header\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_header_sourcefile': '/path/test_header.cpp',
      \   },
      \}

Related: dense-analysis#782
@phcerdan
Copy link
Author

phcerdan commented Aug 21, 2018

As a note, I have rebased my workaround on top of current master.
Still works good, setting let b:ale_cpp_clangtidy_header_sourcefile='main.cpp' in a header, you get ale (clangtidy) working of that header. Also works if the header uses templates that have been instantiated in main.cpp.

Maybe instead of modifying clangtidy, this could be called clangtidy-with-headers or similar.
Also the name of the option is incredible long, an alias could be handy. It works for me, maybe it works for others. Here is the diff:

phcerdan@12224d1

Minimal example to test it: https://gist.github.com/phcerdan/ee005812a62753214124783ee4e14256

phcerdan added a commit to phcerdan/ale that referenced this issue Oct 3, 2018
Provide extra logic to handle header files in cpp.
Only using clangtidy, but same workaround will work in other clang checkers.

First, provide a list of headers suffixes:
By default:
let g:cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
And then, use a variable specific for headers:
let b:cpp_clangtidy_header_sourcefile = ''

This variable should point to a source file (a .cpp) which uses the
current header.
This source file could be set by the user when editing the buffer, with
a simple:
let b:cpp_clangtidy_header_sourcefile

Or set a g:ale_pattern_options in a project vimL file.
let g:ale_pattern_options = {
      \   'my_favourite_header\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_header_sourcefile': '/path/test_header.cpp',
      \   },
      \}

Related: dense-analysis#782
phcerdan added a commit to phcerdan/ale that referenced this issue Oct 3, 2018
Provide extra logic to handle header files in cpp.
Only using clangtidy, but same workaround will work in other clang checkers.

First, provide a list of headers suffixes:
By default:
let g:cpp_clangtidy_header_suffixes = ['h', 'hpp', 'hxx', 'tcc']
And then, use a variable specific for headers:
let b:cpp_clangtidy_header_sourcefile = ''

This variable should point to a source file (a .cpp) which uses the
current header.
This source file could be set by the user when editing the buffer, with
a simple:
let b:cpp_clangtidy_header_sourcefile

Or set a g:ale_pattern_options in a project vimL file.
let g:ale_pattern_options = {
      \   'my_favourite_header\.h$': {
      \       'ale_linters': {'cpp': ['clangtidy']},
      \       'ale_cpp_clangtidy_header_sourcefile': '/path/test_header.cpp',
      \   },
      \}

Related: dense-analysis#782
@smancill
Copy link

PR #2272 seems to have fixed linting header files with clang/gcc linters when compilation database is used.

@phcerdan
Copy link
Author

phcerdan commented Feb 12, 2019

PR #2272 seems to have fixed linting header files with clang/gcc linters when compilation database is used.

It fixed a bug, but not this "issue".

@w0rp
Copy link
Member

w0rp commented Apr 15, 2019

In a project I was working on, compile_commands.json didn't include entries for headers where there are entries for the .c files that go with them. Now ALE will fall back on using arguments for source files with a similar name to a header file.

@Ram-Z
Copy link

Ram-Z commented Dec 31, 2019

I don't see the behaviour described in the last patch. It seems that clang-tidy is called without any -p option on header files.

 Current Filetype: cpp
Available Linters: ['ccls', 'clang', 'clangcheck', 'clangd', 'clangtidy', 'clazy', 'cppcheck', 'cpplint', 'cquery', 'flawfinder', 'gcc']
   Linter Aliases:
'gcc' -> ['g++']
  Enabled Linters: ['ccls', 'clang', 'clangcheck', 'clangd', 'clangtidy', 'clazy', 'cppcheck', 'cpplint', 'cquery', 'flawfinder', 'gcc']
 Suggested Fixers: 
  'clang-format' - Fix C/C++ and cuda files with clang-format.
  'clangtidy' - Fix C/C++ and ObjectiveC files with clang-tidy.
  'remove_trailing_lines' - Remove all blank lines at the end of a file.
  'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.
  'uncrustify' - Fix C, C++, C#, ObjectiveC, ObjectiveC++, D, Java, Pawn, and VALA files with uncrustify.
 Linter Variables:

let g:ale_cpp_ccls_executable = 'ccls'
let g:ale_cpp_ccls_init_options = {}
let g:ale_cpp_clang_executable = 'clang++'
let g:ale_cpp_clang_options = '-std=c++14 -Wall'
let g:ale_cpp_clangcheck_executable = 'clang-check'
let g:ale_cpp_clangcheck_options = ''
let g:ale_cpp_clangd_executable = 'clangd'
let g:ale_cpp_clangd_options = ''
let g:ale_cpp_clangtidy_checks = []
let g:ale_cpp_clangtidy_executable = 'clang-tidy'
let g:ale_cpp_clangtidy_extra_options = ''
let g:ale_cpp_clangtidy_options = ''
let g:ale_cpp_clazy_checks = ['level1']
let g:ale_cpp_clazy_executable = 'clazy-standalone'
let g:ale_cpp_clazy_options = ''
let g:ale_cpp_cppcheck_executable = 'cppcheck'
let g:ale_cpp_cppcheck_options = '--enable=style'
let g:ale_cpp_cpplint_executable = 'cpplint'
let g:ale_cpp_cpplint_options = ''
let g:ale_cpp_cquery_cache_directory = '/home/ramsi/.cache/cquery'
let g:ale_cpp_cquery_executable = 'cquery'
let g:ale_cpp_flawfinder_executable = 'flawfinder'
let g:ale_cpp_flawfinder_minlevel = 1
let g:ale_cpp_flawfinder_options = ''
let g:ale_cpp_gcc_executable = 'gcc'
let g:ale_cpp_gcc_options = '-std=c++14 -Wall'
 Global Variables:

let g:ale_cache_executable_check_failures = v:null
let g:ale_change_sign_column_color = 0
let g:ale_command_wrapper = ''
let g:ale_completion_delay = v:null
let g:ale_completion_enabled = 0
let g:ale_completion_max_suggestions = v:null
let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%code: %%s'
let g:ale_echo_msg_info_str = 'Info'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_history_enabled = 1
let g:ale_history_log_output = 1
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_filetype_changed = 1
let g:ale_lint_on_insert_leave = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'normal'
let g:ale_linter_aliases = {}
let g:ale_linters = {}
let g:ale_linters_explicit = 0
let g:ale_list_vertical = 0
let g:ale_list_window_size = 10
let g:ale_loclist_msg_format = '%code: %%s'
let g:ale_lsp_root = {}
let g:ale_max_buffer_history_size = 20
let g:ale_max_signs = -1
let g:ale_maximum_file_size = v:null
let g:ale_open_list = 0
let g:ale_pattern_options = v:null
let g:ale_pattern_options_enabled = v:null
let g:ale_set_balloons = 0
let g:ale_set_highlights = 1
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
let g:ale_set_signs = 1
let g:ale_sign_column_always = 0
let g:ale_sign_error = '>>'
let g:ale_sign_info = '--'
let g:ale_sign_offset = 1000000
let g:ale_sign_style_error = '>>'
let g:ale_sign_style_warning = '--'
let g:ale_sign_warning = '--'
let g:ale_sign_highlight_linenrs = 0
let g:ale_statusline_format = v:null
let g:ale_type_map = {}
let g:ale_use_global_executables = v:null
let g:ale_virtualtext_cursor = 0
let g:ale_warn_about_trailing_blank_lines = 1
let g:ale_warn_about_trailing_whitespace = 1
  Command History:
<snip>
(executable check - success) clang-tidy
(finished - exit code 1) ['/bin/zsh', '-c', '''clang-tidy'' ''/home/ramsi/src/celeraone/src/hashmap.hpp''']

<<<OUTPUT STARTS>>>
/home/ramsi/src/celeraone/src/hashmap.hpp:5:1: warning: #includes are not sorted properly [llvm-include-order]
#include <tuple>
^        ~~~~~~~
         <memory>
<snip>

Seems like the previous behaviour of ignoring build directories when in a header file is still present. The following patch will fix it.

diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index 9b42870..8202afe 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -9,13 +9,6 @@ let s:sep = has('win32') ? '\' : '/'
 let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
 
 function! ale#c#GetBuildDirectory(buffer) abort
-    " Don't include build directory for header files, as compile_commands.json
-    " files don't consider headers to be translation units, and provide no
-    " commands for compiling header files.
-    if expand('#' . a:buffer) =~# '\v\.(h|hpp)$'
-        return ''
-    endif
-
     let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
 
     " c_build_dir has the priority if defined

@Ram-Z
Copy link

Ram-Z commented Dec 31, 2019

There is already a MR open for this. #2919

@w0rp w0rp removed the C/C++ label Feb 22, 2020
@w0rp
Copy link
Member

w0rp commented Aug 9, 2020

Almost all C/C++ issues with flags should now be discussed in #3276, so a comprehensive solution can be found for most people, for automatically detecting flags.

@w0rp w0rp closed this as completed Aug 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants