Sunday Aug 04, 2013

Cross Modular Dead Code Detection

The follow up question from Michael Bishop re dead code detection introduced in this blog entry yesterday is as follows:

So if I add all my source directories to this GUI and search, it will find code that's not used BY any module IN any module? Sounds pretty cool indeed.

I simulated the above scenario as follows. I created this application, i.e., it has two modules:

The two classes highlighted above have this content:

package org.m1;

public class SayGreetingProcessor {

    public void sayHello(String name){
        System.out.println("Hello " + name);
    }
    
    public void sayBye(String name){
        System.out.println("Bye " + name);
    }
    
    public void sayHelloAgain(String name){
        System.out.println("Hello Again " + name);
    }
    
}

Of the three methods above, only one is used, in the other module:

package org.m2;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.m1.SayGreetingProcessor;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;

@ActionID(
        category = "File",
        id = "org.m2.SayHelloAction")
@ActionRegistration(
        displayName = "#CTL_SayHelloAction")
@ActionReference(path = "Menu/File", position = 1300)
@Messages("CTL_SayHelloAction=Say Hello")
public final class SayHelloAction implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        SayGreetingProcessor sgp = new SayGreetingProcessor();
        sgp.sayHello("Tom");
    }
    
    private void doSomethingElse() {
        
    }
    
}

As you can see, the other module also has a dead private method. In addition, the annotations are going to generate a Bundle class with content that will also be potentially dead, hence we should expect the dead code detector to pick those up too. And note that there are filters we could use in the dead code detector to exclude the Bundle class.

When I run the dead code detector on the whole application, the result is very pleasing:

In other words, the two unused public methods have been correctly identified and the used method has been correctly omitted.

The next step is for the GUI to be better integrated into the NetBeans IDE. Ideally the results would appear in the Output window as hyperlinks so the user can jump directly to the dead code. For the moment, I've created an Action and made it available only on the module suite project node, since that's where Michael Bishop is interested in having it, later I'll make it available on other project nodes too, after making sure they work correctly there too:

Ultimately, this should be integrated into the Source Inspector, i.e., in the same way as FindBugs and other inspectors in Source | Inspect.

The Action you see in the screenshot above is defined as follows:

package org.netbeans.dcd.actions;

import java.awt.Dialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import org.netbeans.api.project.Project;
import org.netbeans.dcd.Parameters;
import org.netbeans.dcd.ui.DeadCodeDetectorUI;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle.Messages;

@ActionID(
        category = "Project",
        id = "org.netbeans.dcd.actions.DetectDeadCodeAction")
@ActionRegistration(
        displayName = "#CTL_DetectDeadCodeAction")
@ActionReference(
        path = "Projects/org-netbeans-modules-apisupport-project-suite/Actions",
        position = 1000)
@Messages("CTL_DetectDeadCodeAction=Detect Dead Code")
public final class DetectDeadCodeAction implements ActionListener {

    private final Project context;

    public DetectDeadCodeAction(Project context) {
        this.context = context;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
        File pkg = FileUtil.toFile(context.getProjectDirectory());
        Parameters p = new Parameters(
                Arrays.asList(pkg), 
                true, 
                false, 
                false, 
                false, 
                null, 
                null);
        DeadCodeDetectorUI dcdui = new DeadCodeDetectorUI(p);
        DialogDescriptor dd = new DialogDescriptor(dcdui, "Dead Code Detector");
        Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
        dialog.setVisible(true);
    }

}

The Parameters class above initializes the GUI, e.g., provides the list of directories to search and sets whether public or private members should be handled.

Emeric Vernat, the author of the detector, has indicated he's fine making it available as a NetBeans plugin, so here it is:

http://plugins.netbeans.org/plugin/50005/?show=true

Want to help with this plugin? Then reproduce the scenario above and leave a comment to state whether you get the same results as me. Only once you've successfully reproduced the above scenario should you try to apply it to your own. Otherwise I'll not be able to accurately reproduce any problems that might arise on your end.

Note: I've noticed that the detector only returns results after the whole project has been built. And it needs to be built again prior to any subsequent detection run, i.e., if after running the detector you would like to run it again but have made changes in the code in the meantime, you need to rebuild the project prior to running the detector. That means that the detector makes use of .class files, not .java files, and hence if your Java source files haven't been compiled into Java class files, within the folder that the detector is analyzing, you will not get results.

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« August 2013 »
SunMonTueWedThuFriSat
    
1
24
       
Today