BarcelonaJUG2016: walkmod: how to run and design code transformations
Transcript of BarcelonaJUG2016: walkmod: how to run and design code transformations
walkmodhttp://www.walkmod.com-‐ twitter: @walkmod
Raquel Pau ([email protected])
Let’s start working with clean code!
1
A bit of me
• Technical lead at Walkmod
• Software engineer at Sparsity-‐Technologies
• JavaCodeGeekswriter
• Research publications about UML/OCL
2
First Law of Software Quality
error = (more code)2E = mc2
Moral: some errors always come back
3
Ok, we need some help…
4
Really?
5
Developers principle
DRY6
Walkmod is for..
• Automatizing the resolution of problems detected by code quality tools
• Automatizing the practice of code conventions in development teams (e.g create CRUD services)
• Automatizing global code changes (e.gmigrations)
7
and it is...
• Open source (LGPL v3)– Discover what is happening with your code!– It is the result of contributions by people with the same needs.
• Editor/ide independent
– Command line interface– Maven/Gradle plugin
8
challenges
• Automatic source code fixes must be applied without re-‐formatting
• False-‐positives are not allowed
• Plugins support for custom code conventions
• Automatic software updates.
9
LET’S PLAYAPPLYING WALKMOD IN GOOGLE/GUAVA
10
commands (1)
• walkmod plugins
• walkmod inspect ${pluginid}
• walkmod add ${pluginid:transformationid}
• walkmod apply / check
11
commands (2)
• walkmod set-‐reader -‐d ${path} ${reader}
• walkmod set-‐writer -‐d ${path} ${writer}
• walkmod add-‐excludes ${file}
• walkmod add-‐includes ${file}
12
google/guava results
501 java files in 113 sec ~ 4,4 files/sec
13
HOW IT WORKSFROM BOTTOM TO TOP
14
Execution Workflow
1. Parsing process 2. Semantic analysis 3. Run code transformations4. Write the applied changes
15
parsing process
CompilationUnit cu = ASTManager.parse("public class Foo { "+
"public boolean test(int[] x[]) { "+"return false;"+
" }"+" } ");
TypeDeclaration td = cu.getTypes().get(0);MethodDeclaration md = (MethodDeclaration) td.getMembers().get(0);
16
semantic analysis:type resolution
• Resolves the java.lang.Class of every single expression
“hello”.substring(1).indexOf(“o”) + 3
• Resolves the java.lang.Method of every single MethodCallExpression node.
c. getEmployees().get(0)
string string int int
int
com.foo.Company#getEmployees() java.util.List#get(int)17
semantic analysis:definitions and references
class MyClass {private int my_field;
}
class MyClass {private int myField;
}
All my_field references need to be updated!
this.my_field = my_field; this.myField = my_field;
18
Semantic analysis:API
• SymbolDataAware#getSymbolData()
• SymbolDefinition#getUsages()• SymbolReference#getSymbolDefinition()
• ScopeAwareInterface for SymbolDefinition, SymbolReference & BlockStmt– #getVariableDefinitions()– #getTypeDefinitions()– #getMethodDefinitions()
• Refactorizable#rename(String name) Interface for VariableDeclaration & Parameter.Applies safe updates to all the references to that variable/parameter.
19
semantic analysis:External symbol references
RefactorConfigurationController#getMethodRefactorRules
class MyClass {public int bar_(){}
}
class MyClass {public int bar(){}
}
{Foo:bar_() => Foo:bar()}
20
semantic analysis benefits
• Ensures non false-‐positives J
• Powerful transformations:– Remove dead code– Naming conventions / migrations– Class hierarchy queries
21
code transformations
@RequiresSemanticAnalysyspublic class HelloVisitor extendsVoidVisitorAdapter<VisitorContext> {...@Overridepublic void visit(MethodDeclaration md, VisitorContext ctx){//TODOsuper.visit(md, ctx);
}
@Overridepublic void visit(FieldDeclaration fd, VisitorContext ctx){//TODOfd.getType().accept(this, ctx);
}
}
22
Semantic rules testing
public class UseCollectionIsEmptyTest extends SemanticTest {
@Testpublic void testEqualsToZero() throws Exception {
CompilationUnit cu = compile("import java.util.List; “+” public class Foo { “+”public boolean testIsEmpty(List list){“+
” return list.size() == 0; }}");
UseCollectionIsEmpty visitor = new UseCollectionIsEmpty();cu.accept(visitor, null);
MethodDeclaration md = (MethodDeclaration) cu.getTypes().get(0).getMembers().get(0);
BlockStmt block = md.getBody();ReturnStmt returnStmt = (ReturnStmt) block.getStmts().get(0);
Assert.assertTrue(returnStmt.getExpr() instanceof MethodCallExpr);}
}
23
writing the applied changes
• eclipse-formatter (default): files are written using an eclipse formatter configuration.
• javalang:string-writer: files are written applying the minimum change set.
> walkmod set-writer ${writer}
24
CREATING A CODE CONVENTIONTO FIX A SONAR RULE VIOLATION
25
General working procedure
1. Fork walkmod/walkmod-sonar-plugin
2. Git checkout master3. Create a new visitor per rule4. Create a test5. Define it in the walkmod-sonar-plugin.xml
6. Create pull request7. We deploy the changes under a new version
to the maven repository
26
Let’s go to implement this rule
27
Local integration tests
1. Go to the walkmod-‐sonar-‐plugin directory2. Execute mvn install3. Replace jars:
1. Open the following directory:${HOME}/.ivy2/cache/org.walkmod/walkmod-‐sonar-‐plugin/jars
2. Replace the jar that has the same name than walkmod-‐sonar-‐plugin/target/walkmod-‐sonar-‐plugin-‐${version}.jar
4. Run walkmod apply -‐-‐offline
28
ROADMAPTO IMPROVE THE USER EXPERIENCE
29
our roadmap is
• More fine incremental execution
• Report the changes introduced by each transformation. Why they appear?
• Support more sonar/checkstyle/pmd rules ;)
• Full integration with Intellij/eclipse/netbeans
30
and...walkmodhub
• SaaS to run walkmod after:– Every push or pull request to Github– Incremental execution– Sends a pull request with the required changes
• Get your clean code sticker!
31