mirror of
https://github.com/github/codeql.git
synced 2026-05-04 05:05:12 +02:00
Migrate Java code to separate QL repo.
This commit is contained in:
30
java/ql/src/DeadCode/DeadClass.java
Normal file
30
java/ql/src/DeadCode/DeadClass.java
Normal file
@@ -0,0 +1,30 @@
|
||||
public static void main(String[] args) {
|
||||
String firstName = /*...*/; String lastName = /*...*/;
|
||||
// Construct a customer
|
||||
Customer customer = new Customer();
|
||||
// Set important properties (but not the address)
|
||||
customer.setName(firstName, lastName);
|
||||
// Save the customer
|
||||
customer.save();
|
||||
}
|
||||
|
||||
public class Customer {
|
||||
private Address address;
|
||||
// ...
|
||||
|
||||
// This setter and getter are unused, and so may be deleted.
|
||||
public void addAddress(String line1, String line2, String line3) {
|
||||
address = new Address(line1, line2, line3);
|
||||
}
|
||||
public Address getAddress() { return address; }
|
||||
}
|
||||
|
||||
/*
|
||||
* This class is only constructed from dead code, and may be deleted.
|
||||
*/
|
||||
public class Address {
|
||||
// ...
|
||||
public Address(String line1, String line2, String line3) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
99
java/ql/src/DeadCode/DeadClass.qhelp
Normal file
99
java/ql/src/DeadCode/DeadClass.qhelp
Normal file
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Classes that are never used at runtime are redundant and should be removed.
|
||||
</p>
|
||||
<include src="DeadCodeSummary.qhelp"/>
|
||||
<p>
|
||||
Classes are considered dead if at runtime:
|
||||
</p>
|
||||
<ul>
|
||||
<li>No methods declared in the class, or a sub-type, are called.</li>
|
||||
<li>No fields declared in the class, or a sub-type, are read.</li>
|
||||
<li>The class is never constructed.</li>
|
||||
</ul>
|
||||
<p>Any class which is not dead is considered to be "live". Nested classes are considered and listed
|
||||
separately, as a live nested class within a dead outer class can be moved to a separate file,
|
||||
allowing the outer class to be deleted.
|
||||
</p>
|
||||
<p>
|
||||
A special exception is made for "namespace classes". A namespace class is used only to group static
|
||||
fields, methods and nested classes - it is never instantiated, has no public constructor and has no
|
||||
instance methods. If a class is considered to be a namespace class, then it is live if at least one
|
||||
of the static members of that class is live - including static nested classes.
|
||||
</p>
|
||||
<include src="DeadCodeDetails.qhelp"/>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Before making any changes, confirm that the class is not required by verifying that the only
|
||||
dependencies on the class are from other dead classes and methods. This confirmation is necessary
|
||||
because there may be project-specific frameworks or techniques which can introduce hidden
|
||||
dependencies. If this project is for a library, then consider whether the class is part of the
|
||||
external API, and may be used in external projects that are not included in the snapshot.
|
||||
</p>
|
||||
<p>
|
||||
After confirming that the class is not required, remove the class. You will also need to remove any
|
||||
references to this class, which may, in turn, require removing other unused classes, methods
|
||||
and fields (see Example 1).
|
||||
</p>
|
||||
<p>
|
||||
Nested classes within this type should be moved, either to a new top-level type, or to another
|
||||
type, unless they are also marked as dead, in which case they can also be removed. Alternatively,
|
||||
if there are some live nested classes within the dead class, the class can be retained by
|
||||
converting all live nested classes to static members, and removing all instance methods and fields,
|
||||
and all dead static members (see Example 2).
|
||||
</p>
|
||||
<include src="DeadCodeExtraEntryPoints.qhelp"/>
|
||||
</recommendation>
|
||||
<section title="Example 1">
|
||||
<p>
|
||||
In the following example, we have a number of classes, and an "entry point" in the form of a main
|
||||
method.
|
||||
</p>
|
||||
<sample src="DeadClass.java" />
|
||||
<p>
|
||||
The class <code>Customer</code> is constructed in the main method, and is therefore live. The
|
||||
class <code>Address</code> is constructed in <code>setAddress</code>, so we might think that it
|
||||
would also be live. However, <code>setAddress</code> is never called by the main method, so,
|
||||
assuming that this is the entire program, an <code>Address</code> is never constructed at runtime.
|
||||
Therefore, the <code>Address</code> class is dead and can be removed without changing the meaning
|
||||
of this program. To delete the <code>Address</code> class we will also need to delete the
|
||||
<code>setAddress</code> and <code>getAddress</code> methods, and the <code>address</code> field,
|
||||
otherwise the program will not compile.
|
||||
</p>
|
||||
</section>
|
||||
<section title="Example 2">
|
||||
<p>
|
||||
In the next example, we have a <code>CustomerActions</code> class containing <code>Action</code>s
|
||||
that affect customers. For example, this could be a Java Swing application, and the
|
||||
<code>Action</code>s could be actions that are available in the user interface.
|
||||
</p>
|
||||
<sample src="NamespaceClass.java" />
|
||||
<p>
|
||||
The <code>CustomerActions</code> class has a constructor and an instance method, which are never
|
||||
called. Instead, actions are instantiated directly. Although this makes the nested
|
||||
<code>Action</code> classes live, live nested classes do not make the outer class live. Therefore,
|
||||
the <code>CustomerActions</code> class is marked as dead.
|
||||
</p>
|
||||
<p>
|
||||
There are two ways to resolve the dead <code>CustomerActions</code> class:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Move each nested static action that is used by the program to a new file, or nest it within
|
||||
a different class, then delete the dead <code>CustomerActions</code> class.</li>
|
||||
<li>Convert the <code>CustomerActions</code> class to a <em>namespace class</em>. First convert the
|
||||
constructor to a <em>suppressed constructor</em> by making it private, preventing the class from
|
||||
being instantiated, then remove the instance method <code>createAddCustomerAction</code>.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Taking the second approach, this is the final result.
|
||||
</p>
|
||||
<sample src="NamespaceClass2.java" />
|
||||
</section>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
35
java/ql/src/DeadCode/DeadClass.ql
Normal file
35
java/ql/src/DeadCode/DeadClass.ql
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @name Dead class
|
||||
* @description Dead classes add unnecessary complexity.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/dead-class
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadClass c, Element origin, string reason
|
||||
where
|
||||
if exists(DeadRoot root | root = c.getADeadRoot() | not root = c.getACallable()) then (
|
||||
// Report a list of the dead roots.
|
||||
origin = c.getADeadRoot() and
|
||||
not origin = c.getACallable() and
|
||||
// There are uses of this class from outside the class.
|
||||
reason = " is only used from dead code originating at $@."
|
||||
) else (
|
||||
// There are no dead roots outside this class.
|
||||
origin = c and
|
||||
if c.isUnusedOutsideClass() then
|
||||
// Never accessed outside this class, so it's entirely unused.
|
||||
reason = " is entirely unused."
|
||||
else
|
||||
/*
|
||||
* There are no dead roots outside the class, but the class has a possible liveness cause
|
||||
* external to the class, so it must be accessed from at least one dead-code cycle.
|
||||
*/
|
||||
reason = " is only used from or in a dead-code cycle."
|
||||
)
|
||||
select c, "The class " + c.getName() + reason, origin, origin.getName()
|
||||
24
java/ql/src/DeadCode/DeadCodeDetails.qhelp
Normal file
24
java/ql/src/DeadCode/DeadCodeDetails.qhelp
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<fragment>
|
||||
<p>
|
||||
A class, method, or field may be dead even if it has dependencies from other parts of the program,
|
||||
if those dependencies are from code that is also considered to be dead. We can also consider this
|
||||
from the opposite side - an element is live, if and only if there is an entry point - such as a
|
||||
<code>main</code> method - that eventually calls the method, reads the field or constructs the class.
|
||||
</p>
|
||||
<p>
|
||||
When identifying dead code, we make an assumption that the snapshot of the project includes all
|
||||
possible callers of the code. If the project is a library project, this may not be the case, and
|
||||
code may be flagged as dead when it is only used by other projects not included in the snapshot.
|
||||
</p>
|
||||
<p>
|
||||
You can customize the results by defining additional "entry points" or by identifying fields that
|
||||
are accessed using reflection. You may also wish to "whitelist" classes, methods or fields that
|
||||
should be excluded from the results. Please refer to the Semmle documentation for more information.
|
||||
</p>
|
||||
</fragment>
|
||||
</qhelp>
|
||||
15
java/ql/src/DeadCode/DeadCodeExtraEntryPoints.qhelp
Normal file
15
java/ql/src/DeadCode/DeadCodeExtraEntryPoints.qhelp
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<fragment>
|
||||
<p>
|
||||
If you observe a large number of false positives, you may need to add extra entry points to
|
||||
identify hidden dependencies caused by the use of a particular framework or technique, or to
|
||||
identify library project entry points. Please refer to the Semmle documentation for more
|
||||
information on how to do this.
|
||||
</p>
|
||||
</fragment>
|
||||
|
||||
</qhelp>
|
||||
9
java/ql/src/DeadCode/DeadCodeReferences.qhelp
Normal file
9
java/ql/src/DeadCode/DeadCodeReferences.qhelp
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<references>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Redundant_code">Redundant code</a>.</li>
|
||||
<li>CERT Java Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/java/MSC56-J.+Detect+and+remove+superfluous+code+and+values">MSC56-J. Detect and remove superfluous code and values</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
17
java/ql/src/DeadCode/DeadCodeSummary.qhelp
Normal file
17
java/ql/src/DeadCode/DeadCodeSummary.qhelp
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<fragment>
|
||||
<p>
|
||||
Redundant, or "dead", code imposes a burden on those reading or maintaining the software project.
|
||||
It can make it harder to understand the structure of the code, as well as increasing the complexity
|
||||
of adding new features or fixing bugs. It can also affect compilation and build times for the
|
||||
project, as dead code will still be compiled and built even if it is never used. In some cases it
|
||||
may also affect runtime performance - for example, fields that are written to but never read from,
|
||||
where the value written to the field is expensive to compute. Removing dead code should not
|
||||
change the meaning of the program.
|
||||
</p>
|
||||
</fragment>
|
||||
</qhelp>
|
||||
23
java/ql/src/DeadCode/DeadEnumConstant.java
Normal file
23
java/ql/src/DeadCode/DeadEnumConstant.java
Normal file
@@ -0,0 +1,23 @@
|
||||
public enum Result {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public Result runOperation(String value) {
|
||||
if (value == 1) {
|
||||
return SUCCESS;
|
||||
} else {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Result operationResult = runOperation(args[0]);
|
||||
if (operationResult == Result.ERROR) {
|
||||
exit(1);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
55
java/ql/src/DeadCode/DeadEnumConstant.qhelp
Normal file
55
java/ql/src/DeadCode/DeadEnumConstant.qhelp
Normal file
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Enum constants that are never used at runtime are redundant and should be removed.
|
||||
</p>
|
||||
<include src="DeadCodeSummary.qhelp"/>
|
||||
<p>
|
||||
An enum constant is considered dead if at runtime it is never used, or only used in comparisons. Any
|
||||
enum constant which is not dead is considered to be "live".
|
||||
</p>
|
||||
<p>
|
||||
An enum constant that is only used in a comparison is considered dead because the comparison will
|
||||
always produce the same result. This is because no variable holds the value of the enum constant,
|
||||
so the comparison of any variable against the constant will always return the same result.
|
||||
</p>
|
||||
<include src="DeadCodeDetails.qhelp"/>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Before making any changes, confirm that the enum constant is not required by verifying that the
|
||||
enum constant is never used. This confirmation is necessary because there may be project-specific
|
||||
frameworks or techniques which can introduce hidden dependencies. If this project is for a library,
|
||||
then consider whether the enum constant is part of the external API, and may be used in external
|
||||
projects that are not included in the snapshot.
|
||||
</p>
|
||||
<p>
|
||||
After confirming that the enum constant is not required, remove the enum constant. You will also
|
||||
need to remove any references to this enum constant, which may, in turn, require removing other dead
|
||||
code.
|
||||
</p>
|
||||
<include src="DeadCodeExtraEntryPoints.qhelp"/>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
In the following example, we have an enum class called <code>Result</code>, intended to report the
|
||||
result of some operation:
|
||||
</p>
|
||||
<sample src="DeadEnumConstant.java" />
|
||||
<p>
|
||||
The method <code>runOperation</code> performs some operation, and returns a <code>Result</code>
|
||||
depending on whether the operation succeeded. However, it only returns either <code>SUCCESS</code>
|
||||
or <code>FAILURE</code>, and never <code>ERROR</code>. The <code>main</code> method calls
|
||||
<code>runOperation</code>, and checks whether the returned result is the <code>ERROR</code>.
|
||||
However, this check will always return the same result - <code>false</code>. This is because the
|
||||
<code>operationResult</code> can never hold <code>ERROR</code>, because <code>ERROR</code> is never
|
||||
stored or returned anywhere in the program. Therefore, <code>ERROR</code> is dead and can be removed,
|
||||
along with the comparison check, and the <code>exit(1);</code>.
|
||||
</p>
|
||||
</example>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
18
java/ql/src/DeadCode/DeadEnumConstant.ql
Normal file
18
java/ql/src/DeadCode/DeadEnumConstant.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Dead enum constant
|
||||
* @description Dead enum constants add unnecessary complexity.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/dead-enum-constant
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from UnusedEnumConstant e
|
||||
where not e.whitelisted()
|
||||
select e, e.getName() + " is unused -- its value is never obtained."
|
||||
7
java/ql/src/DeadCode/DeadField.java
Normal file
7
java/ql/src/DeadCode/DeadField.java
Normal file
@@ -0,0 +1,7 @@
|
||||
public class FieldOnlyRead {
|
||||
private int deadField;
|
||||
|
||||
private int getDeadField() {
|
||||
return deadField;
|
||||
}
|
||||
}
|
||||
68
java/ql/src/DeadCode/DeadField.qhelp
Normal file
68
java/ql/src/DeadCode/DeadField.qhelp
Normal file
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Fields that are never read at runtime are unnecessary and should be removed.
|
||||
</p>
|
||||
<include src="DeadCodeSummary.qhelp"/>
|
||||
<p>
|
||||
Fields are considered dead if at runtime they are never read directly or indirectly, for example
|
||||
through a framework or a use of reflection. Any field which is not dead is considered to be "live".
|
||||
</p>
|
||||
<p>
|
||||
Fields are considered to be dead if they are only written to, and never read.
|
||||
</p>
|
||||
<include src="DeadCodeDetails.qhelp"/>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Before making any changes, confirm that the field is not required by verifying that the field is
|
||||
only read from dead methods. This confirmation is necessary because there may be project-specific
|
||||
frameworks or techniques which can introduce hidden dependencies. If this project is for a library,
|
||||
then consider whether the field is part of the external API, and may be used in external projects
|
||||
that are not included in the snapshot.
|
||||
</p>
|
||||
<p>
|
||||
After confirming that the field is not required, remove the field. You will also need to remove any
|
||||
references to this field, which may, in turn, require removing other unused classes, methods
|
||||
and fields.
|
||||
</p>
|
||||
<include src="DeadCodeExtraEntryPoints.qhelp"/>
|
||||
</recommendation>
|
||||
<section title="Example 1">
|
||||
<p>
|
||||
In the following example, we have a class containing a single field called <code>deadField</code>:
|
||||
</p>
|
||||
<sample src="DeadField.java" />
|
||||
<p>
|
||||
The field is only read from the method <code>getDeadField</code>. However, <code>getDeadField</code>
|
||||
is never called, so the field is never read at runtime. The field is therefore marked as dead.
|
||||
</p>
|
||||
</section>
|
||||
<section title="Example 2">
|
||||
<p>
|
||||
In this example, we have another class containing a single field called <code>writtenToField</code>:
|
||||
</p>
|
||||
<sample src="DeadFieldWrittenTo.java" />
|
||||
<p>
|
||||
The field is written to in the method <code>runThing</code>, which is live because it is called by
|
||||
the <code>main</code> method. However, the field is never read at runtime, only written to. The
|
||||
field is therefore marked as dead.
|
||||
</p>
|
||||
</section>
|
||||
<section title="Example 3">
|
||||
<p>
|
||||
In this example, we have a class representing something that can be serialized to and from XML:
|
||||
</p>
|
||||
<sample src="DeadFieldSerialized.java" />
|
||||
<p>
|
||||
The field <code>field</code> is written and read by the serialization framework in order to store
|
||||
the contents of the object in an XML file, or to construct an instance of the object from an XML
|
||||
file. The field is therefore considered to be read at runtime, which makes the field live.
|
||||
</p>
|
||||
</section>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
31
java/ql/src/DeadCode/DeadField.ql
Normal file
31
java/ql/src/DeadCode/DeadField.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Dead field
|
||||
* @description Fields that are never read are likely unnecessary.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/dead-field
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadField f, Element origin, string reason
|
||||
where
|
||||
not f.isInDeadScope() and
|
||||
if exists(FieldRead read | read = f.getAnAccess()) then (
|
||||
if exists(DeadRoot root | root = getADeadRoot(f.getAnAccess().(FieldRead).getEnclosingCallable())) then (
|
||||
origin = getADeadRoot(f.getAnAccess().(FieldRead).getEnclosingCallable()) and
|
||||
reason = " is only read from dead code originating at $@."
|
||||
) else (
|
||||
origin = f and
|
||||
reason = " is only read from a dead-code cycle."
|
||||
)
|
||||
) else (
|
||||
origin = f and
|
||||
reason = " is entirely unread."
|
||||
)
|
||||
select f, "The field " + f.getName() + reason, origin, origin.getName()
|
||||
9
java/ql/src/DeadCode/DeadFieldSerialized.java
Normal file
9
java/ql/src/DeadCode/DeadFieldSerialized.java
Normal file
@@ -0,0 +1,9 @@
|
||||
@XmlRootElement
|
||||
public class SerializableClass {
|
||||
@XmlAttribute
|
||||
private String field;
|
||||
|
||||
public void setField(String field) {
|
||||
this.field = field;
|
||||
}
|
||||
}
|
||||
12
java/ql/src/DeadCode/DeadFieldWrittenTo.java
Normal file
12
java/ql/src/DeadCode/DeadFieldWrittenTo.java
Normal file
@@ -0,0 +1,12 @@
|
||||
public class FieldOnlyRead {
|
||||
private int writtenToField;
|
||||
|
||||
public void runThing() {
|
||||
writtenToField = 2;
|
||||
callOtherThing();
|
||||
}
|
||||
|
||||
public static main(String[] args) {
|
||||
runThing();
|
||||
}
|
||||
}
|
||||
23
java/ql/src/DeadCode/DeadMethod.java
Normal file
23
java/ql/src/DeadCode/DeadMethod.java
Normal file
@@ -0,0 +1,23 @@
|
||||
public static void main(String[] args) {
|
||||
// Only call the live method
|
||||
liveMethod();
|
||||
}
|
||||
|
||||
/** This method is live because it is called by main(..) */
|
||||
public static void liveMethod() {
|
||||
otherLiveMethod()
|
||||
}
|
||||
|
||||
/** This method is live because it is called by a live method */
|
||||
public static void otherLiveMethod() {
|
||||
}
|
||||
|
||||
|
||||
/** This method is dead because it is never called */
|
||||
public static void deadMethod() {
|
||||
otherDeadMethod();
|
||||
}
|
||||
|
||||
/** This method is dead because it is only called by dead methods */
|
||||
public static void otherDeadMethod() {
|
||||
}
|
||||
81
java/ql/src/DeadCode/DeadMethod.qhelp
Normal file
81
java/ql/src/DeadCode/DeadMethod.qhelp
Normal file
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Methods that are never called at runtime are redundant and should be removed.
|
||||
</p>
|
||||
<include src="DeadCodeSummary.qhelp"/>
|
||||
<p>
|
||||
Methods are considered dead if at runtime they are never called, either directly, by a method call,
|
||||
or indirectly, through a framework or use of reflection. Any method which is not dead is considered
|
||||
to be "live".
|
||||
</p>
|
||||
<p>
|
||||
The results can include methods, constructors and initializers. Initializers come in two forms,
|
||||
instance initializers and static initializers. For each class there will be at most one dead
|
||||
initializer of each type, representing all the initialization of that type in the class.
|
||||
</p>
|
||||
<include src="DeadCodeDetails.qhelp"/>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Before making any changes, confirm that the method is not required by verifying that the only
|
||||
dependencies on the method are from other dead methods. This confirmation is necessary because
|
||||
there may be project-specific frameworks or techniques which can introduce hidden dependencies.
|
||||
If this project is for a library, then consider whether the method is part of the external API,
|
||||
and may be used in external projects that are not included in the snapshot.
|
||||
</p>
|
||||
<p>
|
||||
After confirming that the method is not required, remove the method. You will also need to remove
|
||||
any references to this method, which may, in turn, require removing other unused classes, methods
|
||||
and fields (see Example 1).
|
||||
</p>
|
||||
<p>
|
||||
If the result is a static initializer, then all <code>static { ... }</code> blocks and
|
||||
initializers on static fields are dead within that class. In addition, the lack of static
|
||||
initialization implies that all static methods and fields are also dead and can be removed. These
|
||||
methods and fields will also be reported separately. In contrast, static nested classes may still
|
||||
be live, because constructing or accessing the nested static class does not trigger static
|
||||
initialization of the outer class.
|
||||
</p>
|
||||
<p>
|
||||
If the result is an instance initializer, then all instance initializer <code>{ ... }</code> blocks
|
||||
and initializers on instance fields are dead. In addition, the lack of instance initialization
|
||||
implies that the class is never constructed, which means that all instance methods and fields are
|
||||
also dead and can be removed. These methods and fields will also be reported separately.
|
||||
</p>
|
||||
<include src="DeadCodeExtraEntryPoints.qhelp"/>
|
||||
</recommendation>
|
||||
<section title="Example 1">
|
||||
<p>
|
||||
In the following example, we have a number of methods, and an "entry point" in the form of a main
|
||||
method.
|
||||
</p>
|
||||
<sample src="DeadMethod.java" />
|
||||
<p>
|
||||
The method <code>liveMethod</code> is called from the main method, and is therefore considered live.
|
||||
<code>liveMethod</code> calls <code>otherLiveMethod</code>, which also makes that live.
|
||||
</p>
|
||||
<p>
|
||||
In contrast, <code>deadMethod</code> is never called, and does not represent an entry point, so is
|
||||
marked as dead. Likewise, <code>otherDeadMethod</code> is only called from the
|
||||
<code>deadMethod</code>, so is also marked as dead.
|
||||
</p>
|
||||
</section>
|
||||
<section title="Example 2">
|
||||
<p>
|
||||
In this example, we have a test class containing a number of methods.
|
||||
</p>
|
||||
<sample src="DeadMethodTest.java" />
|
||||
<p>
|
||||
In this case, no methods are called directly. However, the annotations on the methods indicate that
|
||||
this is a test class - specifically, JUnit - and that the methods will be called by the test
|
||||
framework when running the tests. <code>testCustomer</code> and <code>setUp</code> are therefore
|
||||
considered to be "live".
|
||||
</p>
|
||||
</section>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
33
java/ql/src/DeadCode/DeadMethod.ql
Normal file
33
java/ql/src/DeadCode/DeadMethod.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Dead method
|
||||
* @description Dead methods add unnecessary complexity.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/dead-function
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
import java
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadMethod c, Callable origin, string reason
|
||||
where
|
||||
not c.isInDeadScope() and
|
||||
if exists(DeadRoot deadRoot | deadRoot = getADeadRoot(c) | deadRoot.getSourceDeclaration() != c) then (
|
||||
// We've found a dead root that is not this callable (or an instantiation thereof).
|
||||
origin = getADeadRoot(c).getSourceDeclaration() and
|
||||
reason = " is only used from dead code originating at $@."
|
||||
) else (
|
||||
origin = c and
|
||||
if exists(Callable cause | cause = possibleLivenessCause(c) and not cause instanceof DeadRoot |
|
||||
cause.getSourceDeclaration() = c implies possibleLivenessCause(cause).getSourceDeclaration() != c)
|
||||
then
|
||||
// There are no dead roots that are not this callable (or an instantiation thereof), and at least one
|
||||
// liveness cause (ignoring trivial cycles between a parameterized callable and its source declaration).
|
||||
reason = " is only used from, or in, a dead-code cycle."
|
||||
else
|
||||
reason = " is entirely unused."
|
||||
)
|
||||
select c, "The method " + c.getName() + reason, origin, origin.getName()
|
||||
12
java/ql/src/DeadCode/DeadMethodTest.java
Normal file
12
java/ql/src/DeadCode/DeadMethodTest.java
Normal file
@@ -0,0 +1,12 @@
|
||||
public class TestClass {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// ...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomer() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
17
java/ql/src/DeadCode/FLinesOfDeadCode.qhelp
Normal file
17
java/ql/src/DeadCode/FLinesOfDeadCode.qhelp
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<include src="DeadCodeSummary.qhelp"/>
|
||||
<include src="DeadCodeDetails.qhelp"/>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Any code that is marked as dead should be reviewed and, if it is genuinely not used, deleted. You
|
||||
can see which classes, methods and fields contribute to this metric using the rules for Dead Code
|
||||
analysis.
|
||||
</p>
|
||||
</recommendation>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
50
java/ql/src/DeadCode/FLinesOfDeadCode.ql
Normal file
50
java/ql/src/DeadCode/FLinesOfDeadCode.ql
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @name Lines of dead code in files
|
||||
* @description The number of lines of dead code in a file.
|
||||
* @kind treemap
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg sum max
|
||||
* @id java/lines-of-dead-code
|
||||
* @tags maintainability
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
import java
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n =
|
||||
// Lines of code contributed by dead classes.
|
||||
sum(DeadClass deadClass | deadClass.getFile() = f |
|
||||
deadClass.getNumberOfLinesOfCode() -
|
||||
/*
|
||||
* Remove inner and local classes, as they are reported as separate dead classes. Do not
|
||||
* remove anonymous classes, because they aren't reported separately.
|
||||
*/
|
||||
sum(NestedClass innerClass | innerClass.getEnclosingType() = deadClass and not innerClass.isAnonymous() |
|
||||
innerClass.getNumberOfLinesOfCode()
|
||||
)
|
||||
) +
|
||||
// Lines of code contributed by dead methods, not in dead classes.
|
||||
sum(DeadMethod deadMethod | deadMethod.getFile() = f and not deadMethod.isInDeadScope() |
|
||||
deadMethod.getNumberOfLinesOfCode() -
|
||||
/*
|
||||
* Remove local classes defined in the dead method - they are reported separately as a dead
|
||||
* class. We keep anonymous class counts, because anonymous classes are not reported
|
||||
* separately.
|
||||
*/
|
||||
sum(LocalClass localClass | localClass.getLocalClassDeclStmt().getEnclosingCallable() = deadMethod |
|
||||
localClass.getNumberOfLinesOfCode()
|
||||
)
|
||||
) +
|
||||
// Lines of code contributed by dead fields, not in dead classes.
|
||||
sum(DeadField deadField | deadField.getFile() = f and not deadField.isInDeadScope() |
|
||||
deadField.getNumberOfLinesOfCode()
|
||||
) +
|
||||
// Lines of code contributed by unused enum constants.
|
||||
sum(UnusedEnumConstant deadEnumConstant | deadEnumConstant.getFile() = f |
|
||||
deadEnumConstant.getNumberOfLinesOfCode()
|
||||
)
|
||||
select f, n
|
||||
order by n desc
|
||||
25
java/ql/src/DeadCode/NamespaceClass.java
Normal file
25
java/ql/src/DeadCode/NamespaceClass.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This class is dead because it is never constructed, and the instance methods are not
|
||||
* called.
|
||||
*/
|
||||
public class CustomerActions {
|
||||
public CustomerActions() {
|
||||
}
|
||||
|
||||
// This method is never called,
|
||||
public Action createAddCustomerAction () {
|
||||
return new AddCustomerAction();
|
||||
}
|
||||
|
||||
// These two are used directly
|
||||
public static class AddCustomerAction extends Action { /* ... */ }
|
||||
public static class RemoveCustomerAction extends Action { /* ... */ }
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Construct the actions directly
|
||||
Action action = new CustomerActions.AddCustomerAction();
|
||||
action.run();
|
||||
Action action = new CustomerActions.RemoveCustomerAction();
|
||||
action.run();
|
||||
}
|
||||
19
java/ql/src/DeadCode/NamespaceClass2.java
Normal file
19
java/ql/src/DeadCode/NamespaceClass2.java
Normal file
@@ -0,0 +1,19 @@
|
||||
// This class is now live - it is used as a namespace class
|
||||
public class CustomerActions {
|
||||
/*
|
||||
* This constructor is suppressing construction of this class, so is not considered
|
||||
* dead.
|
||||
*/
|
||||
private CustomerActions() { }
|
||||
// These two are used directly
|
||||
public static class AddCustomerAction extends Action { /* ... */ }
|
||||
public static class RemoveCustomerAction extends Action { /* ... */ }
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Construct the actions directly
|
||||
Action action = new CustomerActions.AddCustomerAction();
|
||||
action.run();
|
||||
Action action = new CustomerActions.RemoveCustomerAction();
|
||||
action.run();
|
||||
}
|
||||
3
java/ql/src/DeadCode/UselessParameter.java
Normal file
3
java/ql/src/DeadCode/UselessParameter.java
Normal file
@@ -0,0 +1,3 @@
|
||||
public void isAbsolutePath(String path, String name) {
|
||||
return path.startsWith("/") || path.startsWith("\\");
|
||||
}
|
||||
39
java/ql/src/DeadCode/UselessParameter.qhelp
Normal file
39
java/ql/src/DeadCode/UselessParameter.qhelp
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Parameters that are never read in the body of the method, and are not required due to overriding,
|
||||
are useless and can be removed. Useless parameters unnecessarily complicate the interface for that
|
||||
method, and cause a maintenance and development burden.
|
||||
</p>
|
||||
<p>
|
||||
Methods with useless parameters indicate that either the method can be simplified by removing the
|
||||
parameter, or that the method is not using a value it should be using. Parameters of methods that
|
||||
override other methods will not be marked as useless, because they are required. Similarly,
|
||||
parameters of methods that are overridden by other methods are not marked as useless if they are
|
||||
used by one of the overriding methods.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
The method should be inspected to determine whether the parameter should be used within the body.
|
||||
If the method is overridden, also consider whether any override methods should be using the
|
||||
parameter. If the parameter is not required, it should be removed.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
In the following example, we have a method for determining whether a <code>String</code> path
|
||||
is an absolute path:
|
||||
</p>
|
||||
<sample src="UselessParameter.java" />
|
||||
<p>
|
||||
The method uses the parameter <code>path</code> to determine the return value. However, the
|
||||
parameter <code>name</code> is not used within the body of the method. The parameter will be marked
|
||||
as useless, and can be removed from the program.
|
||||
</p>
|
||||
</example>
|
||||
<include src="DeadCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
16
java/ql/src/DeadCode/UselessParameter.ql
Normal file
16
java/ql/src/DeadCode/UselessParameter.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Useless parameter
|
||||
* @description Parameters that are not used add unnecessary complexity to an interface.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id java/unused-parameter
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from RootdefCallable c
|
||||
where not c.whitelisted()
|
||||
select c.unusedParameter() as p, "The parameter " + p + " is unused."
|
||||
Reference in New Issue
Block a user