-
Notifications
You must be signed in to change notification settings - Fork 89
Writing A Rule
Lint rules are AST visiting rules, because the AST gives us the ingredients we need to form good auto-fixing strategies. This will become apparent momentarily. In this example, we are using the optional GradleModelAware
feature to imbue our visitor with knowledge of the evaluated Gradle Project
object. The combination of Groovy AST and the evaluated model allows us to write powerful rules.
class FixJerseyBundleRule extends GradleLintRule {
String description = 'remove fix-jersey-bundle plugin when it has no effect'
@Override
void visitApplyPlugin(MethodCallExpression call, String plugin) {
if(plugin == 'nebula.fix-jersey-bundle') {
def foundJerseyBundle = false
def deps = project.configurations*.resolvedConfiguration*.firstLevelModuleDependencies.flatten()
while(!deps.isEmpty() && !foundJerseyBundle) {
foundJerseyBundle = deps.any { it.name == 'jersey-bundle' }
deps = deps*.children.flatten()
}
if(!foundJerseyBundle)
addBuildLintViolation('since there is no dependency on jersey-bundle this plugin has no effect', call)
.delete(call) // Note: we could keep chaining additional fixes here if there was more to do
}
}
}
We use the AST to look for the specific piece of code where nebula.fix-jersey-bundle
was applied. We could determine through the Gradle model that the plugin had been applied, but not how or where this had been accomplished. Then we transition to using the Gradle model to determine if jersey-bundle
is in our transitive dependencies. We could not have determined this with the AST alone! Also, since linting runs not only after project configuration but in fact LAST in the task execution order, we can comfortably use Gradle bits like resolvedConfiguration
without fear of introducing side effects (like resolving a configuration early that will later be touched by another plugin).
As shown above, addBuildLintViolation
is used to indicate to the lint plugin that this block of code applying nebula.fix-jersey-bundle
violates the rule, and the delete
fix hint tells fixGradleLint
that it can safely delete this code snippet.
Finally, notice how we overrode the visitApplyPlugin
method. GradleLintRule
implements the GradleAstVisitor
interface which adds several convenience hooks for Gradle specific constructs to the rich set of hooks already provided by CodeNarc's AbstractAstVisitor
, including:
visitApplyPlugin(MethodCallExpression call, String plugin)
visitExtensionProperty(ExpressionStatement expression, String extension, String prop, String value)
visitExtensionProperty(ExpressionStatement expression, String extension, String prop)
visitGradleDependency(MethodCallExpression call, String conf, GradleDependency dep)
visitConfigurationExclude(MethodCallExpression call, String conf, GradleDependency exclude)
visitDependencies()
There are several lint violation fixes currently available. For AST-based fixes, you would typically want to use insertAfter
, insertBefore
, replaceWith
, or delete
. For fixes that cannot specify AST nodes, such as text files, replaceAll
, deleteLines
, deleteFile
and createFile
are also available.
Lastly, provide a properties file that ties the Rule
implementation to a short name that is used to apply the rule through the gradleLint.rules
extension property. For our example, add a properties file: META-INF/lint-rules/fix-jersey-bundle.properties'. Any jar that provides properties files in
META-INF/lint-rules` and is on the buildscript classpath effectively contributes usable rules to the plugin.
Our properties file contains a single line:
implementation-class=org.example.gradle.lint.FixJerseyBundleRule