export fn x() i32 {
var a: i32 = 1; // okay
a = 2;
return a;
}
This does not: export fn x() i32 {
var a: i32 = 1; // unused variable
var a2: i32 = 2;
return a2;
}
> Not that this lint actually achieves that, or even prevents real errors.I have to agree. If there's one thing I've learned over the years, it's that you can't prevent bad code by enforcing lint errors.
I completely disagree, I regularly catch mistakes/bugs in my own code due to enforced linting errors (both locally and in CI), and just in the past few months I can recall numerous instances where CI linting caught bugs in teammates' code too.
There are both false positives and false negatives with linting, but that doesn't make it useless at all.
And that's why this issue is kind of hard. It's why I end up block scoping a lot of error code in Go, which is ugly but generally safer.
It is.
> That `a` is reassigned doesn't make it unused. It is, after all, a variable.
But that's exactly why it's bad, "unused variable" is not a super useful signal in the first place, and if you assert that everything must be used then you need to protect against "unused stores" instead.
SSA tells us there is essentially no difference between assigning and reassigning.
A lot of simple cases can be handled with linting rules, BUT that, IMO requires a few additional things. Namely, being expression based, rather than statement based, and enforcing something like a hindley milner type system.
The missing error checks are annoying, but if you have appropriate editor config it is hard to miss them: https://cdn.billmill.org/static/newsyctmp/warning.png
Basically writing go without `staticcheck`[1] is not recommended. If you do have it set up, it's pretty easy to avoid simple errors like that. I do wish the compiler checked it for you.
Don’t get me wrong I like a good unused code warning.
What frustrates me is that Go’s is dumb / unreliable, and it will stop you from working entirely until you’ve complied with this whim, which has a fraction of a percent chance of identifying a real bug.
> Basically writing go without `staticcheck`[1] is not recommended.
So why have these things as mandatory compiler errors?
I guess they could have levels and allow them in debug mode or with special flag or something?
Why do they care?
> I guess they could have levels and allow them in debug mode or with special flag or something?
Well yes, that's how normal compilers do things.
Of course if you’re the only developer working on your own codebase, you’d wonder why they care but you’re also not the main group they were targeting.
So someone’s anal retentive was made into a langage mandate rather than anything actually useful?
> Given enough time and developers, things like unused variables will start to seep in and make the codebase dirtier.
Why are dead variables any dirtier than dead stores? Or unchecked errors?
If you mean stores to fresh local variables, I assume that should also count in a proper/ideal implementation of this.
If the idea is for code to be literally WYSISWYG, then your compiler will no longer eliminate variables or code for you, so you have to do it.
In principle, a dead variable in final code is also almost certainly a mistake: either by not including the variable in the computation or for declaring the variable at all. Maybe a little annoying while you're figuring something out, but makes sense in the context of polished code.
I mean stores for which there is no load.
> I assume that should also count in a proper/ideal implementation of this.
It certainly does in a proper implementation, but not in zig. Or go.
> If the idea is for code to be literally WYSISWYG, then your compiler will no longer eliminate variables or code for you, so you have to do it.
That sounds stupid.
> a dead variable in final code is also almost certainly a mistake
See “final code” is the issue at hand. The problem with go is that it specifically does not do that. It has a half-assed implementation which it forces upon you all the time.
> Maybe a little annoying while you're figuring something out
It’s a bloody nuisance when you new figuring things out or in the middle or refactoring something.
> but makes sense in the context of polished code.
There’s a ton more which makes sense “in the context of polished code” which go can’t be arsed to do.
Checking that errors are not implicitly ignored for starters.
Stores for which there are no load can still be observable in concurrent scenarios. That's why I specified stores to locals with no load because those are not observable.
> That sounds stupid.
Sounds totally reasonable to me in scenarios where you need the output of the compiler to be very predictable.
Not that this lint actually achieves that, or even prevents real errors. Go has the same, and it's so simplistic as to only be annoying. For instance not sure whether this fails in Zig but Go will allow this:
Error of second call is never checked, but go has no issue with that, because it only tracks definitions per use. also works fine, despite probably sending complete nonsense to Bar, for the same reason.But then it's an absolute pain in the ass every time you're fucking around and stop using a debug import or whatever.