Repository: ycm-core/ycmd

Issue:
ycmd insists I'm not using exceptions
I'm using ycmd through emacs, with flycheck integration. In the error list, I continually get errors "cannot use throw with exceptions disabled", "cannot use try with exceptions disabled". I'm using a compilation database, plus system paths as my flags. I certainly am not specify -fnoexceptions. I even tried to fight this error by explicitly adding -fexceptions. When I show the debug info for ycmd, it looks like this:

```
ycmd debug information for buffer read_vwap_publication.x.cpp in c++-mode:

Server has Clang support compiled in: True
Clang version: TPM built 20160117 clang version 3.6.2 (tags/RELEASE_362/final) (based on LLVM 3.6.2)
Flags for <redacted> loaded from <redacted>.ycm_extra_conf.py:
['-x', 'c', '-<redacted>', '-m64', '-pthread', '-fno-lto', '-g', '-std=c++11', '-Wdeprecated',
'-Woverloaded-virtual', '-stdlib=libc++', '-D__CPP0X', '-Werror=overloaded-virtual', '-Wno-gnu',
'-fcolor-diagnostics', '-Wno-inconsistent-missing-override', '-Wpointer-arith', '-Wreturn-type',

(
many many includes
)

(gcc 5.3.0 includes, ended up present twice)
(clang3.6.2 includes)

'-isystem', '/usr/include', '-Wall', '-Wextra', '-fexceptions', '-std=c++11', '-isystem', <some_long_path>/vim-YouCompleteMe/third_party/ycmd/ycmd/../clang_includes']

Server running at: 127.0.0.1:56135
```

Any ideas why ycmd would insist that I have asked for no exceptions, even though I have not asked for it anywhere?



Discussion:
Could you try with the latest pre-built Clang binaries (`./build.py --clang-completer` will do it automatically for you) and report back?

You have '-x c'. C didn't support exceptions last time I checked. You need to tell clang you are compiling c++ 

> On 6 Jul 2016, at 00:54, quicknir notifications@github.com wrote:
> 
> I'm using ycmd through emacs, with flycheck integration. In the error list, I continually get errors "cannot use throw with exceptions disabled", "cannot use try with exceptions disabled". I'm using a compilation database, plus system paths as my flags. I certainly am not specify -fnoexceptions. I even tried to fight this error by explicitly adding -fexceptions. When I show the debug info for ycmd, it looks like this:
> 
> ycmd debug information for buffer read_vwap_publication.x.cpp in c++-mode:
> 
> Server has Clang support compiled in: True
> Clang version: TPM built 20160117 clang version 3.6.2 (tags/RELEASE_362/final) (based on LLVM 3.6.2)
> Flags for <redacted> loaded from <redacted>.ycm_extra_conf.py:
> ['-x', 'c', '-<redacted>', '-m64', '-pthread', '-fno-lto', '-g', '-std=c++11', '-Wdeprecated', '-Woverloaded-virtual', '-stdlib=libc++', '-D__CPP0X', '-Werror=overloaded-virtual', '-Wno-gnu', '-fcolor-diagnostics', '-Wno-inconsistent-missing-override', '-Wpointer-arith', '-Wreturn-type', (
> 
> many many includes
> 
> )
> 
>  '/spare/local/nir/venv-mosaic/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0', '-isystem', '/spare/local/nir/venv-mosaic/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/x86_64-unknown-linux-gnu', '-isystem', '/spare/local/nir/venv-mosaic/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/backward', '-isystem', '/usr/local/include', '-isystem', '/spare/local/nir/venv-mosaic/llvm/3.6.2/bin/../lib/clang/3.6.2/include', '-isystem', '/usr/include', '-Wall', '-Wextra', '-fexceptions', '-std=c++11', '-isystem', '/spare/local/nir/venv-mosaic/vim-YouCompleteMe/1.20160128/share/vim/bundle/vim-YouCompleteMe/third_party/ycmd/ycmd/../clang_includes']
> 
> Server running at: 127.0.0.1:56135
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub, or mute the thread.

@micbou Are you talking about upgrading the version of clang that ycmd calls to, or the version of clang that ycmd is built against? Neither of these are very easy to upgrade in my particular case; do you have a concrete reason to think that will improve things?

@puremourning C doesn't support about a million things that C++ does, last time I checked, yet I'm not getting any of those other errors. Seriously, snarkiness aside, I don't know why exactly that `-x c` is there, but I really don't think it's compiling in C mode since I don't get any other errors. What's strange is that the `-x c` is not in my compile_commands.json, so I don't know where it's coming from. It's worth understanding this too, even if the language is ultimately C++ maybe this somehow changes some default.

Ah then we are guessing c from the compiler invocation. (There is some heuristic in there.) If you change the invocation to clang++ in compile commands does it work?

> On 6 Jul 2016, at 15:06, quicknir notifications@github.com wrote:
> 
> @micbou Are you talking about upgrading the version of clang that ycmd calls to, or the version of clang that ycmd is built against? Neither of these are very easy to upgrade in my particular case; do you have a concrete reason to think that will improve things?
> 
> @puremourning C doesn't support about a million things that C++ does, last time I checked, yet I'm not getting any of those other errors. Seriously, snarkiness aside, I don't know why exactly that -x c is there, but I really don't think it's compiling in C mode since I don't get any other errors. What's strange is that the -x c is not in my compile_commands.json, so I don't know where it's coming from. It's worth understanding this too, even if the language is ultimately C++ maybe this somehow changes some default.
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.

Regarding clang version. We only support the version of clang downloaded by build.py. You seem to have quite an older version (maybe you are using --system-libclang?)

We are referring to the version ycmd liked against and run against. 

> On 6 Jul 2016, at 15:06, quicknir notifications@github.com wrote:
> 
> @micbou Are you talking about upgrading the version of clang that ycmd calls to, or the version of clang that ycmd is built against? Neither of these are very easy to upgrade in my particular case; do you have a concrete reason to think that will improve things?
> 
> @puremourning C doesn't support about a million things that C++ does, last time I checked, yet I'm not getting any of those other errors. Seriously, snarkiness aside, I don't know why exactly that -x c is there, but I really don't think it's compiling in C mode since I don't get any other errors. What's strange is that the -x c is not in my compile_commands.json, so I don't know where it's coming from. It's worth understanding this too, even if the language is ultimately C++ maybe this somehow changes some default.
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.

@puremourning very interestingly, changing the `c` to `c++` fixes it! I have no real explanation for the overall behavior. I tested this by just manually changing the ycmd code to replace flags[1] with "c++". Also, as you predict, changing the compile_commands.json invocation from clang to clang++ fixes it as well.

It seems though that in this case the heuristic is not really accurate, in the sense that I am actually building my project from this exact compile_commands.json, so it seems like invoking `clang` on a `.cpp` file does in fact launch in C++ mode.

Is simply replacing flags[1] a safe bet? can anything else ever be there? I'd rather avoid tweaking my compile_commands.json but I can do that  as well if you think that my first workaround is better. In the longer term might be better to improve this heuristic (if it hasn't already been fixed in a more recent version).

@puremourning btw my clang version is 3.6.1, I cannot easily upgrade the version of clang for various reasons.

You should do this in your `.ycm_extra_conf.py` file and the heuristic is fine: the correct compiler for C++ sources is `clang++`, not `clang`. Something is wrong with the generation of your `compile_commands.json` file. Anyway, the issue seems to be resolved so I am closing it.

@micbou Nothing is wrong with my compile_commands.json, and the heuristic is wrong. The only difference between clang and clang++ is which standard library they link against: http://stackoverflow.com/questions/20047218/what-is-the-difference-clang-clang-std-c11. But here, I am not linking: all of the commands in compile_commands.json are with the -c flags, as they are building specific translation units. And regardless of heuristics for clang, I am compiling with -std=c++11 as a flag. `clang -c -std=c++11 ...` is a perfectly correct way to build C++ object files.

In summary, regardless of clang/clang++, if -c is present (which it probably nearly always will be for using ycmd in larger projects), then -std=.... should always overrule everything else. That is the correct heuristic.

Edit: actually, possibly clang is not deciding on C++ because of the -std, but rather because of the .cpp extension. So maybe that should be the heuristic.

So, can the heuristic be fixed, please?

Looking for the value of the `-std` flag then falling back to the name of the compiler if there is no `-std` flag would probably be a better heuristic to determine the `-x` flag. Reopening.

@micbou I read a little more about this, info on this is actually a bit contradictory, but it seems like it's the extension that drives the heuristic, the -std flag will only be applied if appropriate. The best quote I've found on this topic is from a clang dev post:

> Of course you can simply use "clang" to compile C++ code, too. You just have to pass "-x c++" and 
> "-lc++" (for libc++) or "-lstdc++" (for libstdc++) to it ("-x c++" is optional if the source file 
> is recognized (using its extension) as C++ code. 

http://clang-developers.42468.n3.nabble.com/Difference-between-clang-and-clang-td3001279.html

The rabbit hole gets deeper. Regardless of which of the two techniques I use to fix this, flycheck seems to get completely disabled as a result. That is no matter what I type, no errors are triggered, even though auto completion works. So I'm actually not sure if the fix had anything to do with anything. Because again, I was never able to see errors for anything other than exceptions, even though my code has tons of templates and other things.

So now I'm just very confused.

Okay, I discovered the issue. It seems like when C++ mode was added, some flags that were being ignored before were activated. In particular, -Wextra was generating an enormous amount of errors from my header files. It seems like ycmd places a limit on how many errors it will send back, so the net effect was to completely prevent any errors from being flagged in the source file I was working.

So I guess: a) is there a way to increase the maximum number of errors sent back? b) does ycmd in newer versions stop sending back errors that occur in other files, or is there a setting that I can use to effect this now?

I'm not aware of any artificial limit on the number of diags returned _by ycmd_. However, if clang can't parse your file well enough (e.g. it hits too many internally), it _might_ not produce any.  We know that it can be very slow if it can't generate a PCH due to errors.

Can you find the result of (the emacs equivalent of) `:YcmDiags` ? In Vim this command returns the full list of diagnostics. If not, can you run clang driver manually from the command line with (i think it is...) `-fsyntax-only`

I'm sorry we don't support the emacs client here, so I can't comment on what the client itself might do with a large number of errors. 

Alternatively, if you can use wireshark to snoop the http traffic between your client and the ycmd server, we can look at the json. Beware that there is of course an infosec issue with sharing that data (it includes your source code).

Edit: one option also, if the headers causing trouble are system headers is to check this out : http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-in-system-headers 

> it seems like it's the extension that drives the heuristic, the -std flag will only be applied if appropriate.

In our case, we can't completely rely on the extension because of headers: it is not uncommon to use the `.h` extension for C++ headers. If we deduce the language mode from the extension, it could lead to the situation where some files are compiled in C++ while others in C and we clearly don't want that. So I still think that looking at the `-std` flag is a good addition to the current behavior.

> a) is there a way to increase the maximum number of errors sent back?

There is the `max_diagnostics_to_display` option (its default value is set to 30). I am not sure but it looks like the only way to change its value on the emacs client is to modify it in the [emacs.el](https://github.com/abingham/emacs-ycmd/blob/master/ycmd.el#L1574) file.

@puremourning I'm not sure what ycmdiags does, but I found a log file in emacs. It shows the exact json being sent back and forth. So I can clearly see that many many errors get sent back from the server, it's just that they were all in header files, and then it's stop sending errors. As soon as I e.g. comment out the header files it finds errors in the current file (a huge pile since there's no headers :-) ). I'm aware of the systems headers flags, this is actually not in third party code. A use case for the problem I'm experiencing might be someone writing new code in a code base and trying to use more warnings than the existing code uses as part of its build system. This was basically my situation, I solved this problem by basically just removing a single flag that isn't that useful anyway that we don't compile with (unused function parameters).

I'm actually lucky in that the codebase I work on compiles with Wall and Wextra with a couple exceptions. This issue would be a lot more problematic on a crappier codebase.

@micbou headers are generally not translation unit targets anyhow though. The approach in ycmd as I understand it is to try to use a heuristic to find a .cpp/.c/.cc etc file that corresponds to the .h file and then use the flags from there. So one could get the extension as part of the heuristic. Anyhow it is just a thought, I think using `-std` is a good improvement as well.

I've managed to work around this max diagnostics issue as I wrote above. It might be an improvement to consider though, to simply filter out diagnostics that don't occur in the current file. I'm perhaps missing something, but from my usage of ycmd they don't seem to be used for anything (they don't show up in flycheck in any way). This is preferable to simply raising the parameter you mentioned. If someone is indeed trying to be more conscientious with warnings then the codebase they are #including, one can very easily get hundreds of warnings from header files. One might have to raise max diagnostics so high that performance becomes an issue, and there's no real correct number other than just not having a limit at all. At least having an option to disable header file warning diagnostics would be useful at least for some people, and it seems to me naively, not too bad to implement.

btw if it is helpful, now that the problem is tracked down, I can close this and open two separate issues: one where I summarize the discussion on heuristic language detection, and one where (if you think the idea has merit) I suggest having an option to disable header file diagnostics. The titles would be more suggestive and it would perhaps facilitate further discussion re implementation.

Leaving this here for future reference: libclang has an API to determine if a Location (such as that associated with a diagnostic) is from the "main file" related to a TU: [`clang_Location_IsFromMainFile`](http://clang.llvm.org/doxygen/group__CINDEX__LOCATIONS.html#gacb4ca7b858d66f0205797ae84cc4e8f2)

In theory we could suppress any errors for which that is not true. Personally, I would resist this strongly, as I want to know if there are errors in my headers, flags or anywhere else. We also typically attempt to avoid configurable or contentious features... :/

>  from my usage of ycmd they don't seem to be used for anything (they don't show up in flycheck in any way)

FWIW: I think that's an issue related to your client. The Vim client certainly does display them.


Resolving patch:
diff --git a/ycmd/completers/cpp/flags.py b/ycmd/completers/cpp/flags.py
--- a/ycmd/completers/cpp/flags.py
+++ b/ycmd/completers/cpp/flags.py
@@ -176,7 +176,7 @@ def _CallExtraConfFlagsForFile( module, filename, client_data ):
 
 
 def PrepareFlagsForClang( flags, filename, add_extra_clang_flags = True ):
-  flags = _CompilerToLanguageFlag( flags )
+  flags = _AddLanguageFlagWhenAppropriate( flags )
   flags = _RemoveXclangFlags( flags )
   flags = _RemoveUnusedFlags( flags, filename )
   if add_extra_clang_flags:
@@ -240,22 +240,24 @@ def _RemoveFlagsPrecedingCompiler( flags ):
   return flags[ :-1 ]
 
 
-def _CompilerToLanguageFlag( flags ):
-  """When flags come from the compile_commands.json file, the flag preceding
-  the first flag starting with a dash is usually the path to the compiler that
-  should be invoked.  We want to replace it with a corresponding language flag.
-  E.g., -x c for gcc and -x c++ for g++."""
+def _AddLanguageFlagWhenAppropriate( flags ):
+  """When flags come from the compile_commands.json file, the flag preceding the
+  first flag starting with a dash is usually the path to the compiler that
+  should be invoked. Since LibClang does not deduce the language from the
+  compiler name, we explicitely set the language to C++ if the compiler is a C++
+  one (g++, clang++, etc.). Otherwise, we let LibClang guess the language from
+  the file extension. This handles the case where the .h extension is used for
+  C++ headers."""
 
   flags = _RemoveFlagsPrecedingCompiler( flags )
 
-  # First flag is now the compiler path or a flag starting with a dash
-  if flags[ 0 ].startswith( '-' ):
-    return flags
-
-  language = ( 'c++' if CPP_COMPILER_REGEX.search( flags[ 0 ] ) else
-               'c' )
+  # First flag is now the compiler path or a flag starting with a dash.
+  first_flag = flags[ 0 ]
 
-  return flags[ :1 ] + [ '-x', language ] + flags[ 1: ]
+  if ( not first_flag.startswith( '-' ) and
+       CPP_COMPILER_REGEX.search( first_flag ) ):
+    return [ first_flag, '-x', 'c++' ] + flags[ 1: ]
+  return flags
 
 
 def _RemoveUnusedFlags( flags, filename ):


Test patch:
diff --git a/ycmd/tests/clang/flags_test.py b/ycmd/tests/clang/flags_test.py
--- a/ycmd/tests/clang/flags_test.py
+++ b/ycmd/tests/clang/flags_test.py
@@ -300,12 +300,12 @@ def RemoveXclangFlags_test():
        flags._RemoveXclangFlags( expected + to_remove + expected ) )
 
 
-def CompilerToLanguageFlag_Passthrough_test():
+def AddLanguageFlagWhenAppropriate_Passthrough_test():
   eq_( [ '-foo', '-bar' ],
-       flags._CompilerToLanguageFlag( [ '-foo', '-bar' ] ) )
+       flags._AddLanguageFlagWhenAppropriate( [ '-foo', '-bar' ] ) )
 
 
-def _ReplaceCompilerTester( compiler, language ):
+def _AddLanguageFlagWhenAppropriateTester( compiler, language_flag = [] ):
   to_removes = [
     [],
     [ '/usr/bin/ccache' ],
@@ -314,19 +314,20 @@ def _ReplaceCompilerTester( compiler, language ):
   expected = [ '-foo', '-bar' ]
 
   for to_remove in to_removes:
-    eq_( [ compiler, '-x', language ] + expected,
-         flags._CompilerToLanguageFlag( to_remove + [ compiler ] + expected ) )
+    eq_( [ compiler ] + language_flag + expected,
+         flags._AddLanguageFlagWhenAppropriate( to_remove + [ compiler ] +
+                                                expected ) )
 
 
-def CompilerToLanguageFlag_ReplaceCCompiler_test():
+def AddLanguageFlagWhenAppropriate_CCompiler_test():
   compilers = [ 'cc', 'gcc', 'clang', '/usr/bin/cc',
                 '/some/other/path', 'some_command' ]
 
   for compiler in compilers:
-    yield _ReplaceCompilerTester, compiler, 'c'
+    yield _AddLanguageFlagWhenAppropriateTester, compiler
 
 
-def CompilerToLanguageFlag_ReplaceCppCompiler_test():
+def AddLanguageFlagWhenAppropriate_CppCompiler_test():
   compilers = [ 'c++', 'g++', 'clang++', '/usr/bin/c++',
                 '/some/other/path++', 'some_command++',
                 'c++-5', 'g++-5.1', 'clang++-3.7.3', '/usr/bin/c++-5',
@@ -335,7 +336,7 @@ def CompilerToLanguageFlag_ReplaceCppCompiler_test():
                 '/some/other/path++-4.9.31', 'some_command++-5.10' ]
 
   for compiler in compilers:
-    yield _ReplaceCompilerTester, compiler, 'c++'
+    yield _AddLanguageFlagWhenAppropriateTester, compiler, [ '-x', 'c++' ]
 
 
 def ExtraClangFlags_test():