The Rascal Metaprogramming Language

admin2 admin2
3 Min Read

Finding the most complex methods in a set of Java files

We use Cyclomatic Complexity to measure the complexity of a method.
The first module implements a Cyclomatic Complexity calculation using
Concrete Syntax pattern matching.

module CalculateCC

import lang::java::syntax::Java15;

int cyclomaticComplexity(MethodDec m) {
  result = 1;
  visit (m) {
    case (Stm)`do  while ();`: result += 1;
    case (Stm)`while () `: result += 1;
    case (Stm)`if () `: result +=1;
    case (Stm)`if ()  else `: result +=1;
    case (Stm)`for (<{Expr ","}* _>; ; <{Expr ","}*_>) ` : result += 1;
    case (Stm)`for ( ;  ; <{Expr ","}* _>) `: result += 1;
    case (Stm)`for ( : ) ` : result += 1;
    case (Stm)`switch ( ) `: result += 1;
    case (SwitchLabel)`case  :` : result += 1;
    case (CatchClause)`catch () ` : result += 1;
  }
  return result;
}

Then we iterate over all the files in a directory and it’s sub directories
and select the top 10 most complex methods

module FindComplexFiles

import List;
import Exception;
import ParseTree;
import util::FileSystem;
import lang::java::syntax::Disambiguate;
import lang::java::syntax::Java15;

import CalculateCC;

lrel[int cc, loc method] findComplexFiles(loc project, int limit = 10) {
  result = [*maxCC(f) | /file(f) <- crawl(project), f.extension == "java"];	
  result = sort(result, bool (<int a, loc _>, <int b, loc _>) { return a < b; });
  return head(reverse(result), limit);
}

set[MethodDec] allMethods(loc file) 
  = {m | /MethodDec m := parse(#start[CompilationUnit], file)};

lrel[int cc, loc method] maxCC(loc file) 
  = [ | m <- allMethods(file)];

Running findComplex on the JHotdraw project returns the
following top 10 complex methods

rascal>findComplexFiles(|project://jhotdraw751/|)
lrel[int cc,loc method]: [
  <83,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/io/StreamPosTokenizer.java|(17762,15910,<499,4>,<946,5>)>,
  <42,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/draw/liner/SlantedLiner.java|(1396,6337,<53,4>,<208,5>)>,
  <42,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/geom/BezierPath.java|(14900,4669,<486,4>,<634,5>)>,
  <31,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/draw/DefaultDrawingViewTransferHandler.java|(2659,12674,<75,4>,<297,5>)>,
  <30,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/draw/liner/ElbowLiner.java|(1422,5658,<52,4>,<186,5>)>,
  <28,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/color/HSLPhysiologicColorSpace.java|(1234,3094,<41,4>,<149,5>)>,
  <28,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/xml/JavaPrimitivesDOMFactory.java|(6035,3378,<175,4>,<263,5>)>,
  <25,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/net/n3/nanoxml/StdXMLParser.java|(11928,7203,<460,3>,<657,4>)>,
  <25,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/xml/JavaPrimitivesDOMFactory.java|(9424,2954,<266,4>,<335,5>)>,
  <24,|project://jhotdraw751/JHotDraw%207.5.1/Source/jhotdraw7/src/main/java/org/jhotdraw/draw/liner/CurvedLiner.java|(1413,5993,<53,4>,<191,5>)>
]

|project...| are source locations, which is a native concept in
Rascal.
Source locations represent a unique identifier for files (URI) along with a
specific region in a file.

Inside the Rascal IDE you can click on any of these source locations, and
an editor will open and show the file and the relevant range

This is a very basic source-to-source Java transformation which changes the style of the code without changing its semantics. The code uses concrete syntax to make sure no syntax errors are introduced and to make sure the rules always match something that really exists in Java.

module Idiomatic

import lang::java::syntax::Java15;
import IO;
import ParseTree;

CompilationUnit idiomatic(CompilationUnit unit) = innermost visit(unit) {
   case (Stm) `if (!)  else ` => 
        (Stm) `if ()   else `
        
   case (Stm) `if () ` => 
        (Stm) `if () {  }` 
     when (Stm) `` !:= a
        
   case (Stm) `if ()  else ` => 
        (Stm) `if () {  } else {  }` 
     when (Stm) `` !:= a
                 
   case (Stm) `if () { return true; } else { return false; }` =>
        (Stm) `return ;`
};

The following code is a small test program written to demonstrate the effect of the idiomatic function. Test functions are integrated in Rascal and will generate random input for parameters and integrate with the IDE to produce test reports.

test bool example() {
  code = (CompilationUnit) `class MyClass { int m() { if (!x) println("x"); else println("y");  if (x) return true; else return false; } }`;
  return idiomatic(code)
         ==  
         (CompilationUnit) `class MyClass    { int m() { if (x) { println("y"); } else { println("x"); }  return x; } }` ;
}

Read More

Share This Article
Leave a Comment

Leave a Reply