Is anyone working on a zig equivalent to clang’s static analyzer? I’m looking for something that detects leaks, overflows, out of bounds, etc at compile time.
Searching turned up a few linters, and some linters claiming to be static analyzers, but no tools that analyze to the degree of the clang static analyzer.
I think it’s an interesting issue, I’m by no mean knowledgeable enough to say how hard it would be to implement a static analyzer for Zig, but given that the parser is accessible, and that there are a couple of incoming features that might help, Implement a server in the compiler that serves information about the compilation #615. I think as a starting point it could be implemented as a live AST that’s being analyzed in parrallel, by different sub “programs” each for a given check if that make sense, a bit like how optimization passes are implemented in LLVM (Tiny modules doing scoped transformations on IR), I don’t know if the AST is the right level for an analyzer, it would probably be better to use an IR for that, but I’m sure Zig type system offers many opportunity to provide better static analysis. One issue that might be hard to tackle is how to deal with Lazy evaluation and, dead code elimination with comptime of unused branches/functions and stuff.
My thinking here is that Zig can actually pull-off global analysis, as its compilation model is very conductive to it. So you should be able to know concrete type backing the arena here, at the call-site.
The compiler doesn’t have a notion of “heap allocation”, nor does it know what an allocator is, as that’s just a user space struct.
For a static analyzer to do this analysis, we would have to tell it what functions allocate memory, and which functions pair with it to free the memory. We could do this with “magical comments”, that is, a special string inside comments that the analyzer would look for, something like //allocator_function, and something similar for freeing, and there would have to be a way to match which allocators pair with each free. Hardcoding the analyzer to look for std.mem.Allocator I dont’t think is great. Someone could write their own allocator interface, and we would expect the analyzer to work with it. More generally, if we’re going through all this trouble, we might as well make it work with any kind of resource, like files, as it would expand the use cases for free. Ultimately, it’s all about matching resource acquisition with release.
There was a proposal for a pair-matching mechanism in function declarations. I can’t find it now, but it was canceled.
I think the best way to achieve this is not an external tool, but instead comptime validation. Each struct knows what it means to acquire a resource and to free it, and it could check it with its own logic, that suits it better. The problem with that, is that the checks would need to keep state during compilation. So, for example, whenever we call alloc, we increment a counter, and whenever we call free, we decrement. If it’s 0, we know that acquisition and release are balanced. Doing this at runtime is trivial, but at comptime, I don’t know how to achieve this. We would need a comptime var that crosses multiple scopes.