Download - Sonar rules in action with walkmod
Sonar rules in ac-on with Walkmod
Raquel Pau [email protected]
INTRODUCTION FROM THE BOTTOM TO THE TOP
The abstract syntax tree
rpau/javalang
rpau/javalang-compiler
Visitor pattern
node.accept (visitor, ctx);
Exercise 1
• Fork & Clone walkmod/walkmod-sonar-plugin
• Run: git checkout tutorial • Open from src/test/java: – org.walkmod.sonar.tutorial.Exercise1
• Run the JUNIT test from Eclipse • Create the HelloVisitor in the same package that modifies the class name “Foo” per “Hello”
Execution Workflow
How to create Plugins
• Create a Maven project – Ar-factId: walkmod-‐xxxx-‐plugin – Dependencies: javalang-compiler & walkmod-core
• Plugin descriptor: – META-‐INF/walkmod/walkmod-‐xxxx-‐plugin.xml – Spring configura-on file
• Deployment to Maven central
SONAR RULES GITHUB.COM/WALKMOD/WALKMOD-SONAR-PLUGIN
Syntactic rules
Only require a valid AST
Collapsible "if" statements should be merged [sonar:CollapsibleIfStatements]
if (file != null) { if (file.isFile() || file.isDirectory()){ /* ... */ }}
if (file != null && (file.isFile() || file.isDirectory())) { /* ... */ }
Other syntactic rules
• String literals should not be duplicated • Useless parentheses around expressions should be removed
to prevent any misunderstanding [sonar:RemoveUselessParentheses].
• Strings literals should be placed on the left side when
checking for equality [sonar:StringCheckOnLeV]
Semantic rules
Require type or symbol resolu-on.
• Collection#isEmpty() should be used to test for emptiness
if (myCollection.size() == 0) { /* ... */}
if (myCollection.isEmpty()) { /* ... */}
How to resolve if size() is a java.util.Collection#size() ?
Example: UseCollectionIsEmpty
…MethodCallExpr mce = (MethodCallExpr) methodExpr;MethodSymbolData msd = mce.getSymbolData();
if (msd != null) {if (mce.getName().equals("size") && ("0".equals(((IntegerLiteralExpr) numberExpr).getValue()))) {
if(Collection.class.isAssignableFrom( msd.getMethod().getDeclaringClass())) {
Expression newExpr = new MethodCallExpr(mce.getScope(), "isEmpty"); if (n.getOperator().equals(BinaryExpr.Operator.notEquals)) {
newExpr = new UnaryExpr(newExpr, UnaryExpr.Operator.not); }
n.getParentNode().replaceChildNode(n, newExpr); }}
[sonar:UseCollec-onIsEmpty]
Symbol definitions and references
Field names should comply with a naming convention
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;
@RequiresSemanticAnalysis
@RequiresSemanticAnalysispublic class UseCollectionIsEmpty extends VoidVisitorAdapter<VisitorContext> {….}
Semantic API
• SymbolDataAware#getSymbolData()
• SymbolDefinition#getUsages()• SymbolReference#getSymbolDefinition()
• ScopeAwareInterface for SymbolDefini-on, SymbolReference & BlockStmt – #getVariableDefinitions()– #getTypeDefinitions()– #getMethodDefinitions()
• Refactorizable#rename() Interface for VariableDeclara-on & Parameter. Applies safe updates to all the references to that variable/parameter. [sonar:LocalVarsShouldComplyWithNamingConvention]
• SemanticTest
Semantic rules testing public class UseCollectionIsEmptyTest extends SemanticTest {
@Test public 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); }}
exercise 2
• Open org.walkmod.sonar.tutorial.Exercise2
Try to implement the rule #9 Useless imports should be removed
Other semantic rules
• Local variable and method parameter names should comply with a naming convention
[sonar:LocalVarsShouldComplyWithNamingConvention]
• Useless imports should be removed [sonar:RemoveUselessImports]
• Redundant casts should not be used [sonar:RedundantCastsShouldNotBeUsed]
• String literals should not be duplicated
• Local variables should not shadow class fields
External symbol references
• PROBLEM: Other files could contain references to the desired node to modify
• It is necessary to design a two step process. – First step: To compute the required refactorings.
Produces refactoring configuraCon – Second step: To apply the computed refactorings.
Executes walkmod-‐refactoring-‐plugin
External refactoring API
It is necessary to create a new refactoring chain dynamically
RefactorConfigurationController#getMethodRefactorRules
Returns the map of current refactoring rules and
creates a refactoring chain if it is missing. {Foo:bar(java.lang.String s, int c) => Foo:bar(c)}
Sonar Rules examples
• Unused method parameters should be removed [sonar:RemoveUnusedMethodParameters] • Method names should comply with a naming convention • Class variable fields should not have public accessibility
General working procedure
1. Fork walkmod/walkmod-sonar-plugin
2. Git checkout master 3. Create a new visitor per rule 4. Create a test 5. Define it in the walkmod-sonar-plugin.xml
6. Create pull request 7. We deploy the changes under a new version
to the maven repository
Local integration tests
1. Go to the walkmod-‐sonar-‐plugin directory 2. Execute mvn install 3. 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