When the custom_lints package did not exist, one had to painfully connect to the dart analyser: a terrible and tedious DX. Fortunately, that’s not the case anymore.
The easiest way to get start is to follow the README of custom_lints on pub.dev. You should have your first lint showing in less than 10 minutes !
Tips to work with the custom_lints package:
While the custom_lints package allows you to get started, it only provides you with a blank canvas. If you try to do anything, you will soon find that you have to learn to use paint, brushes and more - all things which are not included in the package. Let’s see how to start walking this new world of great possibilities through an example:
> Let’s create a lint which warns the user not to assign List but to use IList instead.
IList is as immutable equivalent to List from the fast_immutable_collections package. This package is awesome and will remove all the untraceable errors that classic Lists lets slip by.
Note: The best way to learn the concept which follow is practice. I would advise you setup custom_lint and follow along. Here is a repo from which to start (where custom_lint is already setup). Every problem is separated in digestible steps with first the explanation of what we are trying to do and then the solution. The best way to read this article is to try each step before reading the solution.
In this first part, we will try to place the lint under every variable to which a List is assigned. Here is an example:
The first step to creating a lint is to parse the code. In each file, the code is represented by two main trees:
The Element tree represents things that are declared with a name (e.g. classes, variables, methods, …) while the AST tree represents the structure of the code. Here is a concrete example:
What is important to remembers is that the AST tree is more complete but harder to parse. When you can, stick with the Element tree but if you really need, switch to AST.
In our case, we will need to have the Expression in order to be able to wrap it with IList (see next chapter). Therefore we will choose the AST tree.
In order to parse those trees, the easiest way is to use Visitors, which are classes with built-in method to detect what they are visiting. In particular, the RecursiveAstVisitor (or RecursiveElementVisitor) are particularly useful. In our case, we want to override the visitVariableDeclaration method of RecursiveAstVisitor:
You can then use this Visitor to parse the current file inside the custom_lints getLints method:
The getLints method is generative. If this does not sound familiar, you might want to check out the video on this topic from the Flutter team
So far so good, we have gathered all the variable declaration in the file!
Next step is to filter only the VariableDeclarations that are associated to list. To do so we need to:
Finally, we can start building our Lint. The Lint class is fairly easy to use, the only challenge is to find specify the location of our lint. What you need to remember is that once you have any Element, you will be able to use its nameOffset and nameLength:
Here you go, now if we start the project in which our lint is imported we should see is appear where we want:
Now that you have successfully communicated the developers what not to do, let’s show them what to do with quick fixes.
Quick fixes are suggestions integrated in the editor which allows you to fix a lint with one click:
To implement quick fixes, you have to use the getAnalysisErrorFixes parameter of the Lint we just created:
The two things we have to customize are:
For some reason, change is not exported from the analyzer plugin so add an import to be able to export it:
To indicate the change you want to perform, use the changeBuilder.addDartFileEdit method (which is async: don’t forget to await it!). This method takes two parameters:
fileEditBuilder is quite simple to use. The only challenge, as for the lint, it to know the position of what you want to replace. In our example we want to wrap the expression used to define our variable: This won’t be perfect and could be improved but will work well in most cases. The expression can be obtained from VariableDeclaration which we got in the first part:
The last thing you might want to specify is a message letting the user know what the quick fix does:
Go back to the project where you imported the lint and you should see and be able to apply the quick fix:
Tada! We have created our first lint!