Skip to content

๐Ÿ›ก Linter for Go that checks static call arguments against the function guards (aka contracts).

License

Notifications You must be signed in to change notification settings

orsinium-labs/arguard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

40 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ›ก arguard

Linter for Go that checks static call arguments against the function guards (aka contracts).

๐Ÿ› ๏ธ Example

Let's say, you have the following function:

func div(n, d float64) float64 {
  if d == 0 {
    panic("denominator must not be zero")
  }
  return n / d
}

And then you call it like this:

div(userInput, 0.)

Even if we don't know userInput, we can see that this function call will panic in runtime because the second argument is always zero.

The linter finds and reports such places using safe partial code execution and black magic.

๐Ÿ“ฆ Installation

go install github.com/orsinium-labs/arguard@latest

๐Ÿ“• Usage

arguard ./...

Available flags:

  • -contracts.follow-imports: set this flag to false to not extract contracts from the imported modules. In other words, contract (guard) violations will be reported only if the function with the contract and the function call are located in the same analyzed package. Useful for better performance.
  • -contracts.report-contracts: emit a message for every detected contract. Useful for debugging to see if a contract was detected by the linter or not.
  • -arguard.report-errors: set this flag to show failures during contract execution. By default, if arguard fails to execute a contract, it just moves on without reporting anything. Useful for debugging to see why a contract error wasn't reported.

๐Ÿค” QnA

  1. ๐Ÿ’ซ How does it work? There are two analyzers inside. The first one detects safe-to-execute contracts (guards) in the code. The second one detects calls to functions with known contracts, extracts statically known arguments, and executes contracts that can be executed using yaegi.
  2. ๐Ÿ“„ What is a guard (contract)? An if condition at the beginning of the function (only other contracts can go before it) with a safe-to-execute check and the body only returning an error or calling panic.
  3. ๐Ÿ’ช How reliable are results? If it reports an error, there is, most likely, an error. If it doesn't report an error, there still might be an error. It's a linter, not a formal verifier.
  4. โš–๏ธ How stable is the project? Static analysis in Go can be messy, especially when we also do partial code execution. The linter might fail, be wrong, or be not as smart as it potentially can be. Still, it's a static analyzer, not a production dependency, so it should be safe to use it on any project in any environment. Keep in mind, though, that there is still a partial code execution, so you probably shouldn't run it on untrusted code, just to be safe.
  5. ๐Ÿ”จ Would there be breaking changes? The project follows SemVer. However, every release, even a patch one, can start reporting new violations in your code. So, in a sense, every release can be breaking.
  6. ๐Ÿงฉ Is there a golangci-lint integration? Not yet but eventually will be. It's easy to integrate any analysis-powered linter with golangci-lint, and arguard is analysis-powered. Stay tuned.
  7. โœ๏ธ Is there an IDE integration? Not yet. When we have golangci-lint integration, IDE integrations will come for free.
  8. ๐Ÿง  Is this a new idea? The project implements one of the things that deal, my Python library for Design by Contract can do. Deal itself is built upon the wisdom of generations, see this timeline.
  9. ๐Ÿ“š Is there a Go API so I can use it as a library? Yes. The analysis package provides a native way to compose analyzers, and that's how you can use the project. See main.go for an example of how to create and compose multiple analysis.Analyzer instances.
  10. ๐Ÿง‘โ€๐Ÿ”ง๏ธ Is it actively maintained? The project, in the best traditions of UNIX-way, has a very small and clearly defined scope. I might return to it from time to time and bring new interesting ideas that I had during sleepless nights, but there is nothing to maintain daily. If it works today, it won't break tomorrow, thanks to the short list of dependencies and the Go 1 compatibility promise.
  11. ๐Ÿž What if I found a bug? Fork the project, fix the bug, write some tests, and open a Pull Request. I usually merge and release any contributions within a day.