惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I Dynamic imports solve all the problems, right? 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Using Stripe with Angular (Deprecated) Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Even Faster AngularJS Data Structures Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Asynchronous calls with ES6 generators Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman The magic of $resource (or simply a client-side Active Record) AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
Fast, extensible, configurable, and beautiful linter for Go
2018-05-28 · via Minko Gechev's blog

Edit · May 28, 2018 · 8 minutes read · Follow @mgechev Go Static analysis Linting Tooling

About a year ago I decided to polish my Go skills. Although the language is pretty small compared to most others that I use on a daily basis, it still has some useful syntax constructs that I didn’t use enough. What a better way to brush up your skills in a programming language other than building tools with it…for analyzing programs written in it?

Revive demo

Introducing Revive

When I started using Go, a few years ago, I noticed how opinionated the ecosystem was. First, the syntax of the language is very minimalistic. Such property reduces the expressiveness but also makes the code much easier to read, compared to languages such as Perl and Ruby.

Gopher

Second, the “tabs vs. spaces” debate is not relevant in the Go world. We all use tabs. That’s it. Do I prefer tabs over spaces? It doesn’t matter - gofmt already picked tabs for me. I also noticed that there’s golint. It’s a tool which enforces semantical & syntactical practices. There are few things in golint different from most linters, I’ve used in the past:

  • It’s not extensible. You can’t include/exclude rules, and the project is not wide open for new rules.
  • It doesn’t let us disable rules for specific files or a range of lines in a file.
  • In golint failures have a level of confidence. Sometimes failures are false negatives.

The Go team is keeping the tool opinionated and minimalistic. It follows the Go philosophy, and I respect that. The closed scope of the project has few interesting implications:

Logo of Revive

That’s why I decided to make my toy linter project public and share it with the community. Revive implements all the rules which golint has and the failures have the same concept of “confidence” that golint introduced. In fact, invoking revive with no flags has the same behavior as golint, with the difference that it runs faster. Revive builds on top of golint by:

  • Allowing us to enable or disable rules using a configuration file.
  • Allowing us to configure the linting rules with a TOML file.
  • Providing functionality for disabling a specific rule or the entire linter for a file or a range of lines.
    • golint allows this only for generated files.
  • Providing multiple formatters which let us customize the output.
  • Allowing us to customize the return code for the entire linter or based on the failure of only some rules.
  • Everyone can extend it easily with custom rules or formatters.
  • Revive provides more rules compared to golint.
  • Revive runs faster. It runs the rules over each file in a separate goroutine.

I love the opinionated culture in the Go community. I believe that this is the right direction which lets us focus on the important things instead of losing time in discussions on trivial matters. That’s why with revive, we can make even more syntax-related arguments irrelevant by defining more opinionated rules and providing stricter presets.

Later, I’ll explain how we can quickly develop more rules for revive but before that, let me share a few instructions on how you can use the linter!

Usage

Install revive with:

go get github.com/mgechev/revive

The command above adds the revive binary under $GOPATH/bin.

Using the tool with no flags has the same behavior as golint. The magic happens when we add the -formatter flag:

Friendly formatter

From the image above, we can see that we got 31 warnings for the "exported" rule. This rule is port of a built-in rule from golint which enforces practices for exported symbols (find the full set of rules here).

If we prefer to ignore these warnings for the entire project, we can use a config file in TOML format:

Edit config

What if we want to disable a specific rule for only part of the file? In such case, we can use the following technique:

package models

//revive:disable

type Expression struct {
    Value      string
    IsStar     bool
    IsVariadic bool
    IsWriter   bool
    Underlying string
}
//revive:enable

The annotations above disables all revive rules for the entire struct. In case we prefer to disable only the exported rule, we should use:

package models

//revive:disable:exported

type Expression struct {
    Value      string
    IsStar     bool
    IsVariadic bool
    IsWriter   bool
    Underlying string
}
//revive:enable:exported

Keep in mind that comments which are meant for the linter, should not start with a whitespace. For more details see revive: unidiomatic // revive: syntax.

Finally, if we want to ignore all files from a directory, we can use the -exclude flag:

revive -exclude tests/... ./...

The command above will lint all files from the current directory, excluding all files in tests, recursively. If we want to exclude more than one directory use:

revive -exclude tests/... -exclude utils/... ./...

Configurability

In an image in the last section, we saw that by using a config file in TOML format, we could configure the execution of revive. Here’s an example config file:

# Ignores files with "GENERATED" header, similar to golint
ignoreGeneratedHeader = true

# Sets the default severity to "warning"
severity = "warning"

# Sets the default failure confidence. The semantics behind this property
# is that revive ignores all failures with a confidence level below 0.8.
confidence = 0.8

# Sets the error code for failures with severity "error"
errorCode = 0

# Sets the error code for failures with severity "warning"
warningCode = 0

# Configuration of the `cyclomatic` rule. Here we specify that
# the rule should fail if it detects code with higher complexity than 10.
[rule.cyclomatic]
  arguments = [10]

# Sets the severity of the `package-comments` rule to "error".
[rule.package-comments]
  severity = "error"

Let’s quickly go over the individual properties.

  • ignoreGeneratedHeader - golint ignores files which have header GENERATED. To disable this behavior, set the flag to false.
  • severity - revive has two types of severity - warning and error. This way we can distinguish between critical failures with high confidence level and such with lower.
  • confidence - similarly to golint, revive lets us assign a confidence level to rules which may return false negatives. The confidence property lets us filter failures below given confidence level.
  • errorCode and warningCode - these two properties let us have different return codes for the different severities. We may want your errors to fail the CI but our warnings not to.

The remaining lines are related to rule configuration. Each rule can be configured by setting its arguments and its severity. For example, the rule for cyclomatic complexity above, fails if in our codebase there’s a construction which exceeds the cyclomatic complexity of 10.

Gopher

Extensibility and Contributions

Initially, I wanted to let developers use be able to create external plugins which later could be referenced by the configuration file and loaded dynamically. Unfortunately, -buildmode=plugin has very limited support with known issues.

Ignoring this limitation, there are other two easy ways to add new rules and run them against your code:

  • Contribute to the project - revive is open for external contributions. If a rule makes sense and passes its unit tests, it’s more than welcome to become part of revive! On top of that, creating a new rule is just a matter of implementing this simple interface:
type Rule interface {
    Name() string
    Apply(*File, Arguments) []Failure
}
  • Fork the project and push the rules there. If you think your rules won’t apply to others (although the chances are that they will), you can fork revive and not push them upstream. Just make sure that you sync your code with upstream once in a while to get all the new features and bug fixes from there!

Keep in mind that to create a rule you don’t have to be familiar with the entire codebase. All rules are well encapsulated into visitors with simple interface. A sample implementation of arguments-limit could be found here.

What about formatters?

Well, creating a new formatter is as simple as creating a new rule. Just implement the following interface:

type Formatter interface {
    Format(<-chan Failure, RulesConfig) (string, error)
    Name() string
}

Here you can find a sample implementation of a JSON formatter.

Performance

I run some basic benchmarks to compare the performance of golint and revive. Here’s what I found out after running both linters against kubernetes:

time golint be/…
real    0m25.389s
user    0m29.221s
sys     0m3.065s
time revive be/…
real    0m6.524s
user    0m22.882s
sys     0m1.114s

Since revive lints the individual files in separate goroutines, it outperforms golint about 4 times.

Conclusion

Revive is a simple, fast, configurable, extensible, flexible, and beautiful linter for Go. It runs the linting rules on top of each file in a separate goroutine which improves the performance significantly. Revive lets us configure the individual rules and disable them for the entire project, individual files, or range of lines within a file. Last but not least, revive lets us use a set of built-in formatters which output the failures in a digestible, accessible, and easy to consume format.

The project is continuously evolving and open for new contributions in the form of new rules, formatters, or bug fixes! If you want to create an even stricter linting preset, cut the coding style discussions in your team to the minimum, and focus on the essential things, revive may help.

Drop mic