Hacker News new | past | comments | ask | show | jobs | submit login

To me fanalyzer is one of GCC killer features over clang. It makes programming C much easier by explaining errors. The error messages also began to feel similar to Rust in terms of being developer friendly.



I know Rust (esp on HN) is very hyped for its memory safety and nice abstractions, but I really wonder how much Rust owes its popularity to its error messages.

I would say the #1 reason I stop learning a technology is because of frustrating or unclear errors.

EDIT: Getting a bit of topic, but I meant more because I love C and would love it more with rust level error messages.


Clang already had decent error messages by the time rust stabilized. There's simply not much you can do at runtime to explain a segfault.


Not when you called templated functions and were greeted with compile-time template stack traces. Or you called overloaded functions and were presented with 50 alternatives you might have meant. The language is inherently unfriendly to user-friendly error messages.


I agree, and I'd go a step further:

In my opinion, the complexity of the interactions between C++'s {preprocessor, overload resolution, template resolution, operator overloading, and implicit casting} can make it really hard to know the meaning of a code snippet you're looking at.

If people use these features only in a very limited, disciplined manner it can be okay.

But on projects where they don't, by golly it's a mess.

(I suppose it's possible to write a horrible mess in any language, so maybe it's unfair for me to pick on C++.)


[flagged]


I’m talking about C++. You wrote that Clang already had friendly error messages. While they were less unfriendly than GCC, calling them friendly is a stretch.

Rust having traits instead of templates is a big ergonomic improvement in that area.


Funnily enough, trait bounds are still a big pain in the neck to provide good diagnostics for because of the amount of things that need to be tracked that are cross cutting across stages of the compiler that under normal operation don't need to talk to each other. They got better in 2018, as async/await put them even more front and center and focused some attention on them, and a lot of work for keeping additional metadata around was added since then (search the codebase for enum ObligationCauseCode if you're curious) to improve them. Now with the new "next" trait solver they have a chance to get even better.

It still easier than providing good diagnostics for template errors though :) (althought I'm convinced that if addressing those errors was high priority, common cases of template instantiations could be modeled internally in the same way as traits purely for diagnostics and materially improve the situation — I understand why it hasn't happened, it is hard and not obviously important).


ASan seems to do quite a lot.


That's what makes me wary of modifying my NixOS config. A single typo and you get an error dump comparable to C++03 templates.


That's definitely the most painful part of iterating on Nix code for me, even in simple configs. You eventually develop an intuition for common problems and rely more on that than on deciphering the stack traces, but that's really not ideal.


Actually, thats a reason why I never even touched Nix. Besides, being functional and all the hype, but the syntax and naming of the language feels ad-hoc enough for me to never have caught on...


It's what got me pissed of enough with xmonad to discard it.


... but you do get an error. That's a lot better what you typically get with C or C++. Assuming it's valid systax, of course.

This is a veering off topic, but I do agree that Nix-the-language has a lot of issues.

(You might suggest Guix, but I don't want to faff about with non-supported repositories for table stakes like firmware and such. Maybe Nickel will eventually provide a more pleasant and principled way to define Nix configurations?)


My favourite Nix error message is

    infinite recursion encountered, at undefined position


I tried some kind of BBC micro at a computer museum, and found out that if you had an error anywhere in your BASIC program, it would just print "error". No line number, no hint at what the problem was.


I could understand some kind of ancient system not having the detail or knowledge to explain what happened in particular, but this is something that still happens in a lot of Microsoft software in particular.

Outlook has a consistent tendency to give you errors like "Couldn't get your mail for some reason", or Windows saying "Hey networking isn't working". No "connection timed out" or "couldn't get an IP address" or "DNS lookup failed" or any other error message that is possible to diagnose. Even the Windows network troubleshooting wizard (the "let us try to diagnose why things aren't working for you" process) would consistently give me "yeah man idk" results, when the error is that I'm not getting an address from DHCP and should be extremely easy to diagnose.

I get that in a lot of cases, problems cut across lots of errors or areas of responsibility, and getting some other team making some other library to expose their internals to your application might be difficult in an environment like Microsoft, but it's just inexplicable that so much software, even these days, resorts to "nope can't do it" and bail out.


Haha, reminds me of some Scheme interpreter that would just say something like 'missing paren' at position 0 or EOF depending on where the imbalance was :)

... but, yeah... I'm pretty sure there could be some hints as to whereabouts that infinite recursion was detected.


Arguably Rust got good error messages by learning from Elm: https://elm-lang.org/news/compiler-errors-for-humans


Elm is acknowledged as being the initial inspiration for focusing on diagnostics early on, but Rust got good error messages through elbow grease and focused attention over a long period of time.

People getting used to good errors and demanding more, is part of the virtuous circle that keeps them high quality.

Making good looking diagnostics requires UX work, but making good diagnostics requires a flexible compiler architecture and a lot of effort, nothing more, nothing less.


Rust's eye towards errors predates Elm entirely.



> I would say the #1 reason I stop learning a technology is because of frustrating or unclear errors.

Overly verbose error messages that obscure more than illuminate are chief complaint against C++.

Honestly, they can just sap all the energy out of a project.


"You violated a template rule. Here's a novel on everything that's broken as a result"

It's why the Constraint system was important for C++.


Yeah Rust is popular because it's a practical language with a nice type system, decent escape hatches, and good tooling. The borrow checker attracts some, but it could have easily been done in a way with terrible usability.


> The borrow checker attracts some, but it could have easily been done in a way with terrible usability.

Why would anyone use the resulting language over C? What you're describing is C with a slightly friendlier compiler.


I have never heard C as being described to have a good type system.


To this day, many C programmers believe that strong typing just means pounding extra hard on the keyboard.

Peter van der Linden, "Expert C Programming"


"Strongly typed, weakly checked". Which is a funny way to say "Not strongly typed" or perhaps more generously "The compilers aren't very good and neither are the programmers but other than that..." (and yes I write that as a long time C programmer)

But hey, C does have types:

First it has several different integers with silly names like "long" and "short".

Then it has the integers again but wearing a Groucho mask and with twice as many zeroes, "float" and "double".

Then an integer that's probably one byte, unless it isn't, in which case it is anyway, and which doesn't know whether it's signed or not, "char".

Then a very small integer that takes up too much space ("_Bool" aka bool)

Finally though, it does have types which definitely aren't integers, unfortunately they participates in integer arithmetic anyway and many C programmers believe they're integers, but the compiler doesn't so that's... well it's a disaster, I speak of course of the pointers.


You could try to argue this is the only source of rust's popularity.... or you could admit that the borrow checker is in fact a reason why folks use Rust over C.


The hard problem with C is that it's hard to tell if what the programmer wrote is an error. Hence warnings... which can be very hit or miss, or absurd overkill in some cases.

(Signed overflow being a prime example where you really either just need to define what happens or accept that your compiler is basically never going to warn you about a possible signed overflow -- which is UB. The compromise here by Rust is to allow one to pick between some implementation defined behaviors. That seems pretty sensible.)


For signed overflow I use -fsanitize=signed-integer-overflow .


Good. I wonder how many people do and also if their compilers support it. (One would hope so, of course. I assume clang and GCC do.)

... but the question is really what you ship to production.

Btw, possible signed overflow was just an example of things people do not want warnings for. OOB is far more dangerous, obviously... and the cost for sanitizer in that case is HUGE... and it doesn't actually catch all cases AFAIUI.


For OOB you can enable bound checking in the C++ standard library. That's relatively cheap. Of course it won't help with C raw pointers and C array.


For production one could use -fsanitize-undefined-trap-on-error that turns it into traps. I would not describe the cost of -fsanitize-undefined=bounds has huge. The cost of Asan is huge.


Clang has a similar tool, the Clang Static Analyzer: https://clang-analyzer.llvm.org/


I've found it to have quite poor defaults for its analysis (things like suggesting "use annex k strcpy_s instead of strcpy"). fanalyzer is still by far the easiest to configure.


And had it for much much longer than GCC.


I have had the exact opposite experience: clang constantly gives me much better error messages than GCC, implementations of some warnings or errors catch more cases, and clang-tidy is able to do much better static analysis.


"Copilot explain this error" has made this whole discussion irrelevant for me.


An issue is immediacy: problems are better the earlier they are pointed out (why online errors are better than compile errorswl, which are better than CI errors, which are runtime errors). Having to copy paste an error adds a layer of indirection that gets in the way of the flow.

Another is reproducibility and accuracy: LLMs have a tendency to confidently state things that are wrong, and to say different things to different people, the compiler has the advantage of being deterministic and generally have better understanding of what's going on to produce correct suggestions (although we still have cases of incorrect assumptions producing invalid suggestions, I believe we have a good track record there).

If those tools help you, more power to you, but I fear their use by inexperienced rustaceans being misled (an expert can identify when the bot is wrong, a novice might just end up questioning their sanity).

Side note: the more I write the more I realize that the same concerns I have with LLMs also apply to the compiler in some way and am trying to bridge that cognitive dissonance. I'm guessing that the reproducibility argument, ensuring the same good error triggers for everyone that makes the same mistake and the lack of human curation, are the thing that makes me uneasy about LLMs for teaching languages.


FYI, in VS Code, you highlight the error in the terminal, right click and select "copilot explain this." One less layer of indirection. In C++, I ultimately only end up using it for 10% of the errors, but because it's the type of error with a terrible message, copilot sees through it and puts it in plain English.

I was so impressed with gpt-4's ability to diagnose and correct errors that i made this app to catch python runtime errors, and automatically make gpt-4 code inject the correction: https://github.com/matthewkolbe/OpenAIError


Certainly for the only new diagnostic I wrote for Rust, I expect an LLM's hallucinations are likely to have undesirable consequences. When you write 'X' where we need a u8, my diagnostic says you can write b'X' which is likely what you meant, but the diagnostic deliberately won't do this if you wrote '€' or '£' or numerous other symbols that aren't ASCII - because b'€' is an error too, so we didn't help you if we advised you to write that, you need to figure out what you actually meant. I would expect some LLMs to suggest b'€' there anyway.


This reminds me one of the reasons I hated C++ so much. 1000+ lines of error messages about template instantiation, instead of 'error: missing semicolon'.


In our programming class in high school we were using Borland C++; I had a classmate call me over to ask about an error they were getting from the compiler.

> "Missing semicolon on line 32"

I looked at it, looked at them, and said "You're missing a semicolon on line 32". They looked at line 32 and, hey! look at that! Forgot a semicolon at the end. Added it and their program worked fine.

Even the best error messages can't help some people.


I'm quite surprised to hear this. What do you get from GCC's analyser that Clang's static analyser doesn't already report?

I tried to use GCC's analyser several times, but I couldn't find any good front ends to it that make the output readable. Clang has multiple (reasonably good HTML output, CodeChecker, Xcode integration, etc.). How do you read the output?

Furthermore, I find that GCC produces many more false positives than Clang.


While I wish GCC would implement integrations and/or a language server, I usually do C programming in the terminal (with entr to trigger automatic rebuild on save).

I do find some false positives, but I haven't had many of them to be a deal breaker for me. Aside from what I mentioned about the errors being descriptive, I do like the defaults and that it's part of the compilation process.

for example, possible malloc null warning is on by default (which i don't think is on clang).


I'm quite surprised that clang doesn't have static analysis! That doesn't seem right, but I don't program much in C anymore.


It does. However it catches some different things*




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: