mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Migrate Java code to separate QL repo.
This commit is contained in:
12
java/ql/src/.project
Normal file
12
java/ql/src/.project
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>semmlecode-queries</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.semmle.plugin.qdt.core.qlnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
5
java/ql/src/.qlpath
Normal file
5
java/ql/src/.qlpath
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<ns:qlpath xmlns:ns="https://semmle.com/schemas/qlpath">
|
||||||
|
<dbscheme>/semmlecode-queries/config/semmlecode.dbscheme</dbscheme>
|
||||||
|
<defaultImports><defaultImport>java</defaultImport></defaultImports>
|
||||||
|
</ns:qlpath>
|
||||||
7
java/ql/src/.settings/org.eclipse.jdt.core.prefs
Normal file
7
java/ql/src/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#Tue Nov 04 11:42:37 GMT 2008
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.5
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.5
|
||||||
8
java/ql/src/.vs/VSWorkspaceSettings.json
Normal file
8
java/ql/src/.vs/VSWorkspaceSettings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"ql.projects" : {
|
||||||
|
"." : {
|
||||||
|
"dbScheme" : "config/semmlecode.dbscheme",
|
||||||
|
"libraryPath" : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
class Rectangle
|
||||||
|
{
|
||||||
|
private int w = 10, h = 10;
|
||||||
|
public int getArea() {
|
||||||
|
return w * h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Triangle extends Rectangle
|
||||||
|
{
|
||||||
|
@Override // Annotation of an overriding method
|
||||||
|
public int getArea() {
|
||||||
|
return super.getArea() / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Java enables you to annotate methods that are intended to override a method in a superclass.
|
||||||
|
Compilers are required to generate an error if such an annotated method does not override a method
|
||||||
|
in a superclass, which provides increased protection from potential defects. An annotated method also
|
||||||
|
improves code readability.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Add an <code>@Override</code> annotation to a method that is intended to override a method in a
|
||||||
|
superclass.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>In the following example, <code>Triangle.getArea</code> overrides <code>Rectangle.getArea</code>,
|
||||||
|
so it is annotated with <code>@Override</code>.</p>
|
||||||
|
|
||||||
|
<sample src="MissingOverrideAnnotation.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Item 36.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fpreferences%2Fjava%2Fcompiler%2Fref-preferences-errors-warnings.htm">Java Compiler Errors/Warnings Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Platform, Standard Edition 6, API Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Override.html">Annotation Type Override</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The Java Tutorials:
|
||||||
|
<a href="http://docs.oracle.com/javase/tutorial/java/annotations/predefined.html">Predefined Annotation Types</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Override annotation
|
||||||
|
* @description A method that overrides a method in a superclass but does not have an 'Override'
|
||||||
|
* annotation cannot take advantage of compiler checks, and makes code less readable.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision high
|
||||||
|
* @id java/missing-override-annotation
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class OverridingMethod extends Method {
|
||||||
|
OverridingMethod() {
|
||||||
|
exists(Method m | this.overrides(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isOverrideAnnotated() {
|
||||||
|
this.getAnAnnotation() instanceof OverrideAnnotation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from OverridingMethod m, Method overridden
|
||||||
|
where
|
||||||
|
m.fromSource() and
|
||||||
|
m.overrides(overridden) and
|
||||||
|
not m.isOverrideAnnotated() and
|
||||||
|
not exists(FunctionalExpr mref | mref.asMethod() = m)
|
||||||
|
select m, "This method overrides $@; it is advisable to add an Override annotation.",
|
||||||
|
overridden, overridden.getDeclaringType() + "." + overridden.getName()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A field of immutable type that is not declared <code>final</code>, but is assigned to only in a
|
||||||
|
constructor or static initializer of its declaring type, may lead to defects and makes code less
|
||||||
|
readable. This is because other parts of the code may be based on the assumption that the field has
|
||||||
|
a constant value, and a later modification, which includes an assignment to the field, may
|
||||||
|
invalidate this assumption.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>If a field of immutable type is assigned to only during class or instance initialization,
|
||||||
|
you should usually declare it <code>final</code>. This forces the compiler to verify that the field
|
||||||
|
value cannot be changed subsequently, which can help to avoid defects and increase code readability.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4">4.12.4 final Variables</a>,
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2">8.3.1.2 final Fields</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
52
java/ql/src/Advisory/Declarations/NonFinalImmutableField.ql
Normal file
52
java/ql/src/Advisory/Declarations/NonFinalImmutableField.ql
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-final immutable field
|
||||||
|
* @description A field of immutable type that is assigned to only in a constructor or static
|
||||||
|
* initializer of its declaring type, but is not declared 'final', may lead to defects
|
||||||
|
* and makes code less readable.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/non-final-immutable-field
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class Initialization extends Callable {
|
||||||
|
Initialization() {
|
||||||
|
this instanceof Constructor or
|
||||||
|
this instanceof InitializerMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A binary or unary assignment. */
|
||||||
|
class AnyAssignment extends Expr {
|
||||||
|
AnyAssignment() {
|
||||||
|
this instanceof Assignment or
|
||||||
|
this instanceof UnaryAssignExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The expression modified by this assignment. */
|
||||||
|
Expr getDest() {
|
||||||
|
this.(Assignment).getDest() = result or
|
||||||
|
this.(UnaryAssignExpr).getExpr() = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImmutableField extends Field {
|
||||||
|
ImmutableField() {
|
||||||
|
this.fromSource() and
|
||||||
|
not this instanceof EnumConstant and
|
||||||
|
this.getType() instanceof ImmutableType and
|
||||||
|
// The field is only assigned to in a constructor or static initializer of the type it is declared in.
|
||||||
|
forall(FieldAccess fw, AnyAssignment ae |
|
||||||
|
fw.getField().getSourceDeclaration() = this and
|
||||||
|
fw = ae.getDest()
|
||||||
|
| ae.getEnclosingCallable().getDeclaringType() = this.getDeclaringType() and
|
||||||
|
ae.getEnclosingCallable() instanceof Initialization
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from ImmutableField f
|
||||||
|
where not f.isFinal()
|
||||||
|
select f, "This immutable field is not declared final but is only assigned to during initialization."
|
||||||
38
java/ql/src/Advisory/Declarations/NonPrivateField.qhelp
Normal file
38
java/ql/src/Advisory/Declarations/NonPrivateField.qhelp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A non-final or non-static field that is not declared <code>private</code>,
|
||||||
|
but is not accessed outside of its declaring type, may decrease code maintainability. This is because
|
||||||
|
a field that is accessible from outside the class that it is declared in tends to restrict the class
|
||||||
|
to a particular implementation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>In the spirit of encapsulation, it is generally advisable to choose the
|
||||||
|
most restrictive access modifier (<code>private</code>) for a field, unless
|
||||||
|
there is a good reason to increase its visibility.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 13.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The Java Tutorials:
|
||||||
|
<a href="http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html">Controlling Access to Members of a Class</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
28
java/ql/src/Advisory/Declarations/NonPrivateField.ql
Normal file
28
java/ql/src/Advisory/Declarations/NonPrivateField.ql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-private field
|
||||||
|
* @description A non-constant field that is not declared 'private',
|
||||||
|
* but is not accessed outside of its declaring type, may decrease code maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/non-private-field
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import semmle.code.java.JDKAnnotations
|
||||||
|
|
||||||
|
class NonConstantSourceField extends Field {
|
||||||
|
NonConstantSourceField() {
|
||||||
|
this.fromSource() and
|
||||||
|
not (this.isFinal() and this.isStatic())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from NonConstantSourceField f
|
||||||
|
where
|
||||||
|
not f.isPrivate() and
|
||||||
|
not exists(VarAccess va | va.getVariable() = f |
|
||||||
|
va.getEnclosingCallable().getDeclaringType() != f.getDeclaringType()
|
||||||
|
) and
|
||||||
|
not f.getAnAnnotation() instanceof ReflectiveAccessAnnotation
|
||||||
|
select f, "This non-private field is not accessed outside of its declaring type."
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A method (or constructor) can be marked as deprecated using either the <code>@Deprecated</code>
|
||||||
|
annotation or the <code>@deprecated</code> Javadoc tag. Using a method that has been
|
||||||
|
marked as deprecated is bad practice, typically for one or more of the following reasons:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>The method is dangerous.</li>
|
||||||
|
<li>There is a better alternative method.</li>
|
||||||
|
<li>Methods that are marked as deprecated are often removed from future versions of an API. So using
|
||||||
|
a deprecated method may cause extra maintenance effort when the API is upgraded.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>Avoid using a method that has been marked as deprecated. Follow any guidance that
|
||||||
|
is provided with the <code>@deprecated</code> Javadoc tag, which should explain how to replace the
|
||||||
|
call to the deprecated method.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fpreferences%2Fjava%2Fcompiler%2Fref-preferences-errors-warnings.htm">Java Compiler Errors/Warnings Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Platform, Standard Edition 6, API Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Deprecated.html">Annotation Type Deprecated</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://docs.oracle.com/javase/6/docs/technotes/guides/javadoc/deprecation/deprecation.html">How and When To Deprecate APIs</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @name Deprecated method or constructor invocation
|
||||||
|
* @description Using a method or constructor that has been marked as deprecated may be dangerous or
|
||||||
|
* fail to take advantage of a better method or constructor.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision high
|
||||||
|
* @id java/deprecated-call
|
||||||
|
* @tags maintainability
|
||||||
|
* non-attributable
|
||||||
|
* external/cwe/cwe-477
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
private
|
||||||
|
predicate isDeprecatedCallable(Callable c) {
|
||||||
|
c.getAnAnnotation() instanceof DeprecatedAnnotation or
|
||||||
|
exists(c.getDoc().getJavadoc().getATag("@deprecated"))
|
||||||
|
}
|
||||||
|
|
||||||
|
from Call ca, Callable c
|
||||||
|
where
|
||||||
|
ca.getCallee() = c and
|
||||||
|
isDeprecatedCallable(c) and
|
||||||
|
// Exclude deprecated calls from within deprecated code.
|
||||||
|
not isDeprecatedCallable(ca.getCaller())
|
||||||
|
select ca, "Invoking $@ should be avoided because it has been deprecated.",
|
||||||
|
c, c.getDeclaringType() + "." + c.getName()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Javadoc for method.
|
||||||
|
*
|
||||||
|
* @throws Exception if a problem occurs.
|
||||||
|
*/
|
||||||
|
public void noThrow() {
|
||||||
|
System.out.println("This method does not throw.");
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A Javadoc <code>@throws</code> or <code>@exception</code> tag that references an exception
|
||||||
|
that cannot be thrown is misleading.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Ensure that you only include the <code>@throws</code> or <code>@exception</code> tags in Javadoc
|
||||||
|
when an exception can be thrown.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a method with Javadoc that claims it can throw
|
||||||
|
<code>Exception</code>. Since <code>Exception</code> is a checked exception and the method
|
||||||
|
does not declare that it may throw an exception, the Javadoc is wrong and should be updated.</p>
|
||||||
|
|
||||||
|
<sample src="ImpossibleJavadocThrows.java" />
|
||||||
|
|
||||||
|
<p>In the following example the Javadoc has been corrected by removing the <code>@throws</code>
|
||||||
|
tag.</p>
|
||||||
|
|
||||||
|
<sample src="ImpossibleJavadocThrowsFix.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#throwstag">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @name Javadoc has impossible 'throws' tag
|
||||||
|
* @description Javadoc that incorrectly claims a method or constructor can throw an exception
|
||||||
|
* is misleading.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision high
|
||||||
|
* @id java/inconsistent-javadoc-throws
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
RefType getTaggedType(ThrowsTag tag) {
|
||||||
|
result.hasName(tag.getExceptionName()) and
|
||||||
|
exists(ImportType i | i.getFile() = tag.getFile() | i.getImportedType() = result)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate canThrow(Callable callable, RefType exception) {
|
||||||
|
exists(string uncheckedException |
|
||||||
|
uncheckedException = "RuntimeException" or uncheckedException = "Error"
|
||||||
|
|
|
||||||
|
exception.getASupertype*().hasQualifiedName("java.lang", uncheckedException)
|
||||||
|
) or
|
||||||
|
callable.getAnException().getType().getASubtype*() = exception
|
||||||
|
}
|
||||||
|
|
||||||
|
from ThrowsTag throwsTag, RefType thrownType, Callable docMethod
|
||||||
|
where
|
||||||
|
getTaggedType(throwsTag) = thrownType and
|
||||||
|
docMethod.getDoc().getJavadoc().getAChild*() = throwsTag and
|
||||||
|
not canThrow(docMethod, thrownType)
|
||||||
|
select throwsTag,
|
||||||
|
"Javadoc for " + docMethod + " claims to throw " + thrownType.getName() + " but this is impossible."
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Javadoc for method.
|
||||||
|
*/
|
||||||
|
public void noThrow() {
|
||||||
|
System.out.println("This method does not throw.");
|
||||||
|
}
|
||||||
121
java/ql/src/Advisory/Documentation/JavadocCommon.qll
Normal file
121
java/ql/src/Advisory/Documentation/JavadocCommon.qll
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import java
|
||||||
|
|
||||||
|
/** Holds if the given `Javadoc` contains a minimum of a few characters of text. */
|
||||||
|
private
|
||||||
|
predicate acceptableDocText(Javadoc j) {
|
||||||
|
// Require minimum combined length of all non-tag elements.
|
||||||
|
sum(JavadocElement e, int toSum |
|
||||||
|
e = j.getAChild() and
|
||||||
|
not e = j.getATag(_) and
|
||||||
|
toSum = e.toString().length()
|
||||||
|
|
|
||||||
|
toSum
|
||||||
|
) >= 5
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if the given `JavadocTag` contains a minimum of a few characters of text. */
|
||||||
|
private
|
||||||
|
predicate acceptableTag(JavadocTag t) {
|
||||||
|
sum(JavadocElement e, int toSum |
|
||||||
|
e = t.getAChild() and
|
||||||
|
toSum = e.toString().length()
|
||||||
|
|
|
||||||
|
toSum
|
||||||
|
) >= 5
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A public `RefType`. */
|
||||||
|
class DocuRefType extends RefType {
|
||||||
|
DocuRefType() {
|
||||||
|
this.fromSource() and
|
||||||
|
this.isPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate hasAcceptableDocText() {
|
||||||
|
acceptableDocText(this.getDoc().getJavadoc())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A public (non-getter, non-setter) `Callable` that does not override another method. */
|
||||||
|
class DocuCallable extends Callable {
|
||||||
|
DocuCallable() {
|
||||||
|
this.fromSource() and
|
||||||
|
this.isPublic() and
|
||||||
|
// Ignore overriding methods (only require Javadoc on the root method).
|
||||||
|
not exists(Method root | this.(Method).overrides(root)) and
|
||||||
|
// Ignore getters and setters.
|
||||||
|
not this instanceof SetterMethod and
|
||||||
|
not this instanceof GetterMethod and
|
||||||
|
// Ignore synthetic/implicit constructors.
|
||||||
|
not this.getLocation() = this.getDeclaringType().getLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate hasAcceptableDocText() {
|
||||||
|
acceptableDocText(this.getDoc().getJavadoc())
|
||||||
|
}
|
||||||
|
|
||||||
|
string toMethodOrConstructorString() {
|
||||||
|
(this instanceof Method and result = "method") or
|
||||||
|
(this instanceof Constructor and result = "constructor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A `Parameter` belonging to a `DocuCallable` that has some `Javadoc`. */
|
||||||
|
class DocuParam extends Parameter {
|
||||||
|
DocuParam() {
|
||||||
|
this.fromSource() and
|
||||||
|
this.getCallable() instanceof DocuCallable and
|
||||||
|
// Only consider callables with Javadoc.
|
||||||
|
exists(this.getCallable().getDoc().getJavadoc())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if this parameter has a non-trivial `@param` tag. */
|
||||||
|
predicate hasAcceptableParamTag() {
|
||||||
|
exists(ParamTag t |
|
||||||
|
t = this.getCallable().getDoc().getJavadoc().getATag("@param") and
|
||||||
|
t.getParamName() = this.getName() and
|
||||||
|
acceptableTag(t)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A `DocuCallable` that is a method with `Javadoc` whose return type is not `void`. */
|
||||||
|
class DocuReturn extends DocuCallable {
|
||||||
|
DocuReturn() {
|
||||||
|
this instanceof Method and
|
||||||
|
not this.getReturnType().hasName("void") and
|
||||||
|
// Only consider methods with Javadoc.
|
||||||
|
exists(this.getDoc().getJavadoc())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if this callable's `Javadoc` has a non-trivial `@return` tag. */
|
||||||
|
predicate hasAcceptableReturnTag() {
|
||||||
|
acceptableTag(this.getDoc().getJavadoc().getATag("@return"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A `DocuCallable` that has `Javadoc` and throws at least one exception. */
|
||||||
|
class DocuThrows extends DocuCallable {
|
||||||
|
DocuThrows() {
|
||||||
|
this.fromSource() and
|
||||||
|
// Only consider callables that throw at least one exception.
|
||||||
|
exists(this.getAnException()) and
|
||||||
|
// Only consider callables with Javadoc.
|
||||||
|
exists(this.getDoc().getJavadoc())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this callable has a non-trivial Javadoc `@throws` or `@exception` tag
|
||||||
|
* documenting the given `Exception e`.
|
||||||
|
*/
|
||||||
|
predicate hasAcceptableThrowsTag(Exception e) {
|
||||||
|
exists(Javadoc j |
|
||||||
|
j = this.getDoc().getJavadoc() and
|
||||||
|
exists(JavadocTag t |
|
||||||
|
(t = j.getATag("@throws") or t = j.getATag("@exception")) and
|
||||||
|
t.getChild(0).toString() = e.getName() and
|
||||||
|
acceptableTag(t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Extracts the user's name from the input arguments.
|
||||||
|
*
|
||||||
|
* Precondition: 'args' should contain at least one element, the user's name.
|
||||||
|
*
|
||||||
|
* @param args the command-line arguments.
|
||||||
|
* @return the user's name (the first command-line argument).
|
||||||
|
* @throws NoNameException if 'args' contains no element.
|
||||||
|
*/
|
||||||
|
public static String getName(String[] args) throws NoNameException {
|
||||||
|
if(args.length == 0) {
|
||||||
|
throw new NoNameException();
|
||||||
|
} else {
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A public method or constructor that does not have a Javadoc comment makes an API more
|
||||||
|
difficult to understand and maintain.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Public methods and constructors should be documented to make an API usable.
|
||||||
|
For the purpose of code maintainability, it is also advisable to document non-public
|
||||||
|
methods and constructors.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Javadoc comment should describe <em>what</em> the method or constructor does
|
||||||
|
rather than <em>how</em>, to allow for any potential implementation change that is
|
||||||
|
invisible to users of an API. It should include the following:</p>
|
||||||
|
<ul>
|
||||||
|
<li>A description of any preconditions or postconditions</li>
|
||||||
|
<li>Javadoc tag elements that describe any parameters, return value, and thrown exceptions</li>
|
||||||
|
<li>Any other important aspects such as side-effects and thread safety</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Documentation for users of an API should be written using the standard Javadoc format.
|
||||||
|
This can be accessed conveniently by users of an API from within standard IDEs,
|
||||||
|
and can be transformed automatically into HTML format.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a good Javadoc comment, which clearly explains what the method does,
|
||||||
|
its parameter, return value, and thrown exception.</p>
|
||||||
|
|
||||||
|
<sample src="MissingJavadocMethods.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Item 44.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-142372.html">Requirements for Writing Java API Specifications</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
16
java/ql/src/Advisory/Documentation/MissingJavadocMethods.ql
Normal file
16
java/ql/src/Advisory/Documentation/MissingJavadocMethods.ql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Javadoc for public method or constructor
|
||||||
|
* @description A public method or constructor that does not have a Javadoc comment affects
|
||||||
|
* maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/undocumented-function
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JavadocCommon
|
||||||
|
|
||||||
|
from DocuCallable c
|
||||||
|
where not c.hasAcceptableDocText()
|
||||||
|
select c, "This " + c.toMethodOrConstructorString() + " does not have a non-trivial Javadoc comment."
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A public method or constructor that does not have a Javadoc tag for each parameter
|
||||||
|
makes an API more difficult to understand and maintain.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>The Javadoc comment for a method or constructor should include a Javadoc tag element that
|
||||||
|
describes each parameter.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a good Javadoc comment, which clearly explains the method's
|
||||||
|
parameter.</p>
|
||||||
|
|
||||||
|
<sample src="MissingJavadocMethods.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Item 44.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-142372.html">Requirements for Writing Java API Specifications</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Javadoc for parameter
|
||||||
|
* @description A public method or constructor that does not have a Javadoc tag for each parameter
|
||||||
|
* affects maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/undocumented-parameter
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JavadocCommon
|
||||||
|
|
||||||
|
from DocuParam p
|
||||||
|
where not p.hasAcceptableParamTag()
|
||||||
|
select p, "This parameter does not have a non-trivial Javadoc tag."
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A public method that does not have a Javadoc tag for its return value
|
||||||
|
makes an API more difficult to understand and maintain.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>The Javadoc comment for a method should include a Javadoc tag element that
|
||||||
|
describes the return value.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a good Javadoc comment, which clearly explains the method's
|
||||||
|
return value.</p>
|
||||||
|
|
||||||
|
<sample src="MissingJavadocMethods.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Item 44.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-142372.html">Requirements for Writing Java API Specifications</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Javadoc for method return value
|
||||||
|
* @description A public method that does not have a Javadoc tag for its return
|
||||||
|
* value affects maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/undocumented-return-value
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JavadocCommon
|
||||||
|
|
||||||
|
from DocuReturn c
|
||||||
|
where not c.hasAcceptableReturnTag()
|
||||||
|
select c, "This method's return value does not have a non-trivial Javadoc tag."
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A public method or constructor that throws an exception but
|
||||||
|
does not have a Javadoc tag for the exception makes an API more difficult to understand and maintain.
|
||||||
|
This includes checked exceptions in <code>throws</code> clauses and unchecked exceptions that are
|
||||||
|
explicitly thrown in <code>throw</code> statements.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>The Javadoc comment for a method or constructor should include a Javadoc tag element that
|
||||||
|
describes each thrown exception.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a good Javadoc comment, which clearly explains the method's
|
||||||
|
thrown exception.</p>
|
||||||
|
|
||||||
|
<sample src="MissingJavadocMethods.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Items 44 and 62.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-142372.html">Requirements for Writing Java API Specifications</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
22
java/ql/src/Advisory/Documentation/MissingJavadocThrows.ql
Normal file
22
java/ql/src/Advisory/Documentation/MissingJavadocThrows.ql
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Javadoc for thrown exception
|
||||||
|
* @description A public method or constructor that throws an exception but does not have a
|
||||||
|
* Javadoc tag for the exception affects maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/undocumented-exception
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JavadocCommon
|
||||||
|
|
||||||
|
from DocuThrows c, RefType t
|
||||||
|
where
|
||||||
|
exists(Exception e |
|
||||||
|
c.getAnException() = e and
|
||||||
|
e.getType() = t and
|
||||||
|
not c.hasAcceptableThrowsTag(e)
|
||||||
|
)
|
||||||
|
select c, "This " + c.toMethodOrConstructorString() + " throws $@ but does not have a corresponding Javadoc tag.",
|
||||||
|
t, t.getName()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* The Stack class represents a last-in-first-out stack of objects.
|
||||||
|
*
|
||||||
|
* @author Joseph Bergin
|
||||||
|
* @version 1.0, May 2000
|
||||||
|
* Note that this version is not thread safe.
|
||||||
|
*/
|
||||||
|
public class Stack {
|
||||||
|
// ...
|
||||||
54
java/ql/src/Advisory/Documentation/MissingJavadocTypes.qhelp
Normal file
54
java/ql/src/Advisory/Documentation/MissingJavadocTypes.qhelp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A public class or interface that does not have a Javadoc comment makes an API more
|
||||||
|
difficult to understand and maintain.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Public classes and interfaces should be documented to make an API usable.
|
||||||
|
For the purpose of code maintainability, it is also advisable to document non-public
|
||||||
|
classes and interfaces.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Documentation for users of an API should be written using the standard Javadoc format.
|
||||||
|
This can be accessed conveniently by users of an API from within standard IDEs,
|
||||||
|
and can be transformed automatically into HTML format.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows a good Javadoc comment, which clearly explains what the class does,
|
||||||
|
its author, and version.</p>
|
||||||
|
<sample src="MissingJavadocTypes.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>, Item 44.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-142372.html">Requirements for Writing Java API Specifications</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
16
java/ql/src/Advisory/Documentation/MissingJavadocTypes.ql
Normal file
16
java/ql/src/Advisory/Documentation/MissingJavadocTypes.ql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing Javadoc for public type
|
||||||
|
* @description A public class or interface that does not have a Javadoc comment affects
|
||||||
|
* maintainability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/undocumented-type
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JavadocCommon
|
||||||
|
|
||||||
|
from DocuRefType t
|
||||||
|
where not t.hasAcceptableDocText()
|
||||||
|
select t, "This type does not have a non-trivial Javadoc comment."
|
||||||
52
java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java
Normal file
52
java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* BAD: The following param tag is empty.
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
*/
|
||||||
|
public void emptyParamTag(int p){ ... }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BAD: The following param tag has a misspelled value.
|
||||||
|
*
|
||||||
|
* @param prameter The parameter's value.
|
||||||
|
*/
|
||||||
|
public void typo(int parameter){ ... }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BAD: The following param tag appears to be outdated
|
||||||
|
* since the method does not take any parameters.
|
||||||
|
*
|
||||||
|
* @param sign The number's sign.
|
||||||
|
*/
|
||||||
|
public void outdated(){ ... }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BAD: The following param tag uses html within the tag value.
|
||||||
|
*
|
||||||
|
* @param <code>ordinate</code> The value of the y coordinate.
|
||||||
|
*/
|
||||||
|
public void html(int ordinate){ ... }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BAD: Invalid syntax for type parameter.
|
||||||
|
*
|
||||||
|
* @param T The type of the parameter.
|
||||||
|
* @param parameter The parameter value.
|
||||||
|
*/
|
||||||
|
public <T> void parameterized(T parameter){ ... }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GOOD: A proper Javadoc comment.
|
||||||
|
*
|
||||||
|
* This method calculates the absolute value of a given number.
|
||||||
|
*
|
||||||
|
* @param <T> The number's type.
|
||||||
|
* @param x The number to calculate the absolute value of.
|
||||||
|
* @return The absolute value of <code>x</code>.
|
||||||
|
*/
|
||||||
|
public <T extends Number> T abs(T x){ ... }
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Javadoc comments for public methods and constructors should use the <code>@param</code> tag to describe the available
|
||||||
|
parameters. If the comment includes any empty, incorrect or outdated parameter names then this will make
|
||||||
|
the documentation more difficult to read.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>The Javadoc comment for a method or constructor should always use non-empty <code>@param</code> values that match actual parameter or type parameter names.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>The following example shows good and bad Javadoc comments that use the <code>@param</code> tag.</p>
|
||||||
|
|
||||||
|
<sample src="SpuriousJavadocParam.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.jdt.doc.user/reference/preferences/java/compiler/ref-preferences-javadoc.htm">Java Compiler Javadoc Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#@param">How to Write Doc Comments for the Javadoc Tool</a>,
|
||||||
|
<a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#param">The Java API Documentation Generator</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
33
java/ql/src/Advisory/Documentation/SpuriousJavadocParam.ql
Normal file
33
java/ql/src/Advisory/Documentation/SpuriousJavadocParam.ql
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @name Spurious Javadoc @param tags
|
||||||
|
* @description Javadoc @param tags that do not match any parameters in the method or constructor are confusing.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision very-high
|
||||||
|
* @id java/unknown-javadoc-parameter
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
from Callable callable, ParamTag paramTag, string what, string msg
|
||||||
|
where
|
||||||
|
callable.(Documentable).getJavadoc().getAChild() = paramTag and
|
||||||
|
( if callable instanceof Constructor
|
||||||
|
then what = "constructor"
|
||||||
|
else what = "method"
|
||||||
|
) and
|
||||||
|
if exists(paramTag.getParamName()) then (
|
||||||
|
// The tag's value is neither matched by a callable parameter name ...
|
||||||
|
not callable.getAParameter().getName() = paramTag.getParamName() and
|
||||||
|
// ... nor by a type parameter name.
|
||||||
|
not exists(TypeVariable tv | tv.getGenericCallable() = callable |
|
||||||
|
"<" + tv.getName() + ">" = paramTag.getParamName()
|
||||||
|
) and
|
||||||
|
msg = "@param tag \"" + paramTag.getParamName()
|
||||||
|
+ "\" does not match any actual parameter of " + what + " \""
|
||||||
|
+ callable.getName() + "()\"."
|
||||||
|
) else
|
||||||
|
// The tag has no value at all.
|
||||||
|
msg = "This @param tag does not have a value."
|
||||||
|
select paramTag, msg
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<include src="AvoidCloneableInterface.qhelp" /></qhelp>
|
||||||
23
java/ql/src/Advisory/Java Objects/AvoidCloneMethodAccess.ql
Normal file
23
java/ql/src/Advisory/Java Objects/AvoidCloneMethodAccess.ql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @name Use of clone() method
|
||||||
|
* @description Calling a method that overrides 'Object.clone' is bad practice. Copying an object
|
||||||
|
* using the 'Cloneable interface' and 'Object.clone' is error-prone.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/use-of-clone-method
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class ObjectCloneMethod extends Method {
|
||||||
|
ObjectCloneMethod() {
|
||||||
|
this.getDeclaringType() instanceof TypeObject and
|
||||||
|
this.getName() = "clone" and
|
||||||
|
this.hasNoParameters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from MethodAccess ma, ObjectCloneMethod clone
|
||||||
|
where ma.getMethod().overrides(clone)
|
||||||
|
select ma, "Invoking a method that overrides clone() should be avoided."
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<include src="AvoidCloneableInterface.qhelp" /></qhelp>
|
||||||
25
java/ql/src/Advisory/Java Objects/AvoidCloneOverride.ql
Normal file
25
java/ql/src/Advisory/Java Objects/AvoidCloneOverride.ql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @name Overriding definition of clone()
|
||||||
|
* @description Overriding 'Object.clone' is bad practice. Copying an object using the 'Cloneable
|
||||||
|
* interface' and 'Object.clone' is error-prone.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/override-of-clone-method
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class ObjectCloneMethod extends Method {
|
||||||
|
ObjectCloneMethod() {
|
||||||
|
this.getDeclaringType() instanceof TypeObject and
|
||||||
|
this.getName() = "clone" and
|
||||||
|
this.hasNoParameters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from Method m, ObjectCloneMethod clone
|
||||||
|
where
|
||||||
|
m.fromSource() and
|
||||||
|
m.overrides(clone)
|
||||||
|
select m, "Overriding the Object.clone() method should be avoided."
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
public final class Galaxy {
|
||||||
|
|
||||||
|
// This is the original constructor.
|
||||||
|
public Galaxy (double aMass, String aName) {
|
||||||
|
fMass = aMass;
|
||||||
|
fName = aName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the copy constructor.
|
||||||
|
public Galaxy(Galaxy aGalaxy) {
|
||||||
|
this(aGalaxy.getMass(), aGalaxy.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Copying an object using the <code>Cloneable</code> interface and the <code>Object.clone</code> method
|
||||||
|
is error-prone. This is because the <code>Cloneable</code> interface and the <code>clone</code>
|
||||||
|
method are unusual:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>The <code>Cloneable</code> interface has no methods. Its only use is to trigger different
|
||||||
|
behavior of <code>Object.clone</code>.</li>
|
||||||
|
<li><code>Object.clone</code> is protected.</li>
|
||||||
|
<li><code>Object.clone</code> creates a shallow copy without calling a constructor.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>The first two points mean that a programmer must do two things to get a useful implementation of
|
||||||
|
<code>clone</code>: first, make the class implement <code>Cloneable</code> to change the behavior of
|
||||||
|
<code>Object.clone</code> so that it makes a copy instead of throwing a <code>CloneNotSupportedException</code>;
|
||||||
|
second, override <code>clone</code> to make it public, to allow it to be called. Another
|
||||||
|
consequence of <code>Cloneable</code> not having any methods is that it does not say anything about
|
||||||
|
an object that implements it, which means that you cannot perform a polymorphic clone operation.</p>
|
||||||
|
|
||||||
|
<p>The third point, <code>Object.clone</code> creating a shallow copy, is the most serious one. A
|
||||||
|
shallow copy shares internal state with the original object. This includes private
|
||||||
|
fields that the programmer might not be aware of. A change to the internal state of the original
|
||||||
|
object could affect the copy, and conversely the opposite is true, which could easily lead to
|
||||||
|
unexpected behavior.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Define either a dedicated copy method or a copy constructor (with a parameter whose type is the same
|
||||||
|
as the type that declares the constructor). In most cases, this is at least as good as
|
||||||
|
using the <code>Cloneable</code> interface and the <code>Object.clone</code> method, without the
|
||||||
|
subtlety involved in implementing and using <code>clone</code> correctly.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>In the following example, class <code>Galaxy</code> includes a copy constructor. Its parameter is
|
||||||
|
of type <code>Galaxy</code>.</p>
|
||||||
|
|
||||||
|
<sample src="AvoidCloneableInterface.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 11.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Platform, Standard Edition 6, API Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Cloneable.html">Interface Cloneable</a>,
|
||||||
|
<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29">Object.clone</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
17
java/ql/src/Advisory/Java Objects/AvoidCloneableInterface.ql
Normal file
17
java/ql/src/Advisory/Java Objects/AvoidCloneableInterface.ql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @name Use of Cloneable interface
|
||||||
|
* @description Using the 'Cloneable' interface is bad practice. Copying an object using the
|
||||||
|
* 'Cloneable interface' and 'Object.clone' is error-prone.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/use-of-cloneable-interface
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from RefType t
|
||||||
|
where
|
||||||
|
t.fromSource() and
|
||||||
|
t.getASupertype() instanceof TypeCloneable
|
||||||
|
select t, "This type implements or extends Cloneable, which should be avoided."
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Overriding the <code>Object.finalize</code> method is
|
||||||
|
not a reliable way to terminate use of resources.
|
||||||
|
In particular, there are no guarantees regarding the timeliness of
|
||||||
|
finalizer execution.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Provide explicit termination methods, which should be
|
||||||
|
called by users of an API.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 7.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.6">12.6. Finalization of Class Instances</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
24
java/ql/src/Advisory/Java Objects/AvoidFinalizeOverride.ql
Normal file
24
java/ql/src/Advisory/Java Objects/AvoidFinalizeOverride.ql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @name Overriding definition of finalize()
|
||||||
|
* @description Overriding 'Object.finalize' is not a reliable way to terminate use of resources.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/override-of-finalize-method
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class ObjectFinalizeMethod extends Method {
|
||||||
|
ObjectFinalizeMethod() {
|
||||||
|
this.getDeclaringType() instanceof TypeObject and
|
||||||
|
this.getName() = "finalize" and
|
||||||
|
this.hasNoParameters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from Method m, ObjectFinalizeMethod finalize
|
||||||
|
where
|
||||||
|
m.fromSource() and
|
||||||
|
m.overrides(finalize)
|
||||||
|
select m, "Overriding the Object.finalize() method should be avoided."
|
||||||
8
java/ql/src/Advisory/Naming/NamingConventionsCommon.qll
Normal file
8
java/ql/src/Advisory/Naming/NamingConventionsCommon.qll
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import java
|
||||||
|
|
||||||
|
class ConstantField extends Field {
|
||||||
|
ConstantField() {
|
||||||
|
this.isStatic() and
|
||||||
|
this.isFinal()
|
||||||
|
}
|
||||||
|
}
|
||||||
40
java/ql/src/Advisory/Naming/NamingConventionsConstants.qhelp
Normal file
40
java/ql/src/Advisory/Naming/NamingConventionsConstants.qhelp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A static, final field name that contains lowercase letters does not follow standard
|
||||||
|
naming conventions, which decreases code readability. For example, <code>Min_Width</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Use uppercase letters throughout a static, final field name, and use underscores to
|
||||||
|
separate words within the field name. For example, <code>MIN_WIDTH</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 56.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1">6.1. Declarations</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367">9 - Naming Conventions</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
19
java/ql/src/Advisory/Naming/NamingConventionsConstants.ql
Normal file
19
java/ql/src/Advisory/Naming/NamingConventionsConstants.ql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @name Misnamed static final field
|
||||||
|
* @description A static, final field name that contains lowercase letters decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/misnamed-constant
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import NamingConventionsCommon
|
||||||
|
|
||||||
|
from ConstantField f
|
||||||
|
where
|
||||||
|
f.fromSource() and
|
||||||
|
not f.getName() = "serialVersionUID" and
|
||||||
|
f.getType() instanceof ImmutableType and
|
||||||
|
not f.getName().toUpperCase() = f.getName()
|
||||||
|
select f, "Static final fields should not contain lowercase letters."
|
||||||
40
java/ql/src/Advisory/Naming/NamingConventionsMethods.qhelp
Normal file
40
java/ql/src/Advisory/Naming/NamingConventionsMethods.qhelp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A method name that begins with an uppercase letter does not follow standard
|
||||||
|
naming conventions, which decreases code readability. For example, <code>Getbackground</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Begin the method name with a lowercase letter and use camel case: capitalize the first letter of each word within
|
||||||
|
the method name. For example, <code>getBackground</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 56.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1">6.1. Declarations</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367">9 - Naming Conventions</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
16
java/ql/src/Advisory/Naming/NamingConventionsMethods.ql
Normal file
16
java/ql/src/Advisory/Naming/NamingConventionsMethods.ql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @name Misnamed method
|
||||||
|
* @description A method name that begins with an uppercase letter decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/misnamed-function
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from Method m
|
||||||
|
where
|
||||||
|
m.fromSource() and
|
||||||
|
not m.getName().substring(0, 1).toLowerCase() = m.getName().substring(0, 1)
|
||||||
|
select m, "Method names should start in lowercase."
|
||||||
39
java/ql/src/Advisory/Naming/NamingConventionsPackages.qhelp
Normal file
39
java/ql/src/Advisory/Naming/NamingConventionsPackages.qhelp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A package name that contains uppercase letters does not follow standard
|
||||||
|
naming conventions, which decreases code readability. For example, <code>Com.Sun.Eng</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Use lowercase letters throughout a package name. For example, <code>com.sun.eng</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 56.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1">6.1. Declarations</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367">9 - Naming Conventions</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
17
java/ql/src/Advisory/Naming/NamingConventionsPackages.ql
Normal file
17
java/ql/src/Advisory/Naming/NamingConventionsPackages.ql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @name Misnamed package
|
||||||
|
* @description A package name that contains uppercase letters decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/misnamed-package
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from RefType t, Package p
|
||||||
|
where
|
||||||
|
p = t.getPackage() and
|
||||||
|
t.fromSource() and
|
||||||
|
not p.getName().toLowerCase() = p.getName()
|
||||||
|
select t, "This type belongs to the package " + p.getName() + ", which should not include uppercase letters."
|
||||||
40
java/ql/src/Advisory/Naming/NamingConventionsRefTypes.qhelp
Normal file
40
java/ql/src/Advisory/Naming/NamingConventionsRefTypes.qhelp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A class or interface name that begins with a lowercase letter does not follow standard
|
||||||
|
naming conventions, which decreases code readability. For example, <code>hotelbooking</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Begin the class name with an uppercase letter and use camel case: capitalize the first letter of each word within
|
||||||
|
the class name. For example, <code>HotelBooking</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 56.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1">6.1. Declarations</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367">9 - Naming Conventions</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
17
java/ql/src/Advisory/Naming/NamingConventionsRefTypes.ql
Normal file
17
java/ql/src/Advisory/Naming/NamingConventionsRefTypes.ql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @name Misnamed class or interface
|
||||||
|
* @description A class or interface name that begins with a lowercase letter decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/misnamed-type
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from RefType t
|
||||||
|
where
|
||||||
|
t.fromSource() and
|
||||||
|
not t instanceof AnonymousClass and
|
||||||
|
not t.getName().substring(0, 1).toUpperCase() = t.getName().substring(0, 1)
|
||||||
|
select t, "Class and interface names should start in uppercase."
|
||||||
41
java/ql/src/Advisory/Naming/NamingConventionsVariables.qhelp
Normal file
41
java/ql/src/Advisory/Naming/NamingConventionsVariables.qhelp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A variable name that begins with an uppercase letter does not follow standard
|
||||||
|
naming conventions, which decreases code readability. For example, <code>Numberofguests</code>.
|
||||||
|
This applies to local variables, parameters, and non-constant fields.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Begin the variable name with a lowercase letter and use camel case: capitalize the first letter of each word within
|
||||||
|
the variable name. For example, <code>numberOfGuests</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 56.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java Language Specification:
|
||||||
|
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.1">6.1. Declarations</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367">9 - Naming Conventions</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
18
java/ql/src/Advisory/Naming/NamingConventionsVariables.ql
Normal file
18
java/ql/src/Advisory/Naming/NamingConventionsVariables.ql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @name Misnamed variable
|
||||||
|
* @description A variable name that begins with an uppercase letter decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/misnamed-variable
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import NamingConventionsCommon
|
||||||
|
|
||||||
|
from Variable v
|
||||||
|
where
|
||||||
|
v.fromSource() and
|
||||||
|
not v instanceof ConstantField and
|
||||||
|
v.getName().substring(0, 1).toLowerCase() != v.getName().substring(0, 1)
|
||||||
|
select v, "Variable names should start in lowercase."
|
||||||
16
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.java
Normal file
16
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
int menuChoice;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
switch (menuChoice) {
|
||||||
|
case 1:
|
||||||
|
System.out.println("You chose number 1.");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
System.out.println("You chose number 2.");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
System.out.println("You chose number 3.");
|
||||||
|
break;
|
||||||
|
// BAD: No 'default' case
|
||||||
|
}
|
||||||
47
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.qhelp
Normal file
47
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.qhelp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A <code>switch</code> statement without a <code>default</code> case may allow execution to 'fall
|
||||||
|
through' silently, if no cases are matched.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In a <code>switch</code> statement that is based on a variable of a non-enumerated type, include a
|
||||||
|
<code>default</code> case to prevent execution from falling through silently when no cases are matched.
|
||||||
|
If the <code>default</code> case is intended to be unreachable code, it is advisable that it throws a
|
||||||
|
<code>RuntimeException</code> to alert the user of an internal error.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>In the following example, the <code>switch</code> statement outputs the menu choice that the user
|
||||||
|
has made. However, if the user does not choose 1, 2, or 3, execution falls through silently.</p>
|
||||||
|
|
||||||
|
<sample src="MissingDefaultInSwitch.java" />
|
||||||
|
|
||||||
|
<p>In the following modified example, the <code>switch</code> statement includes a
|
||||||
|
<code>default</code> case, to allow for the user making an invalid menu choice.</p>
|
||||||
|
|
||||||
|
<sample src="MissingDefaultInSwitchGood.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-142311.html#468">7.8 switch Statements</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
19
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.ql
Normal file
19
java/ql/src/Advisory/Statements/MissingDefaultInSwitch.ql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing default case in switch
|
||||||
|
* @description A 'switch' statement that is based on a non-enumerated type and that does not have a
|
||||||
|
* 'default' case may allow execution to 'fall through' silently.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/missing-default-in-switch
|
||||||
|
* @tags reliability
|
||||||
|
* external/cwe/cwe-478
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
from SwitchStmt switch
|
||||||
|
where
|
||||||
|
not switch.getExpr().getType() instanceof EnumType and
|
||||||
|
not exists(switch.getDefaultCase())
|
||||||
|
select switch, "Switch statement does not have a default case."
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
int menuChoice;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
switch (menuChoice) {
|
||||||
|
case 1:
|
||||||
|
System.out.println("You chose number 1.");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
System.out.println("You chose number 2.");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
System.out.println("You chose number 3.");
|
||||||
|
break;
|
||||||
|
default: // GOOD: 'default' case for invalid choices
|
||||||
|
System.out.println("Sorry, you made an invalid choice.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
28
java/ql/src/Advisory/Statements/OneStatementPerLine.qhelp
Normal file
28
java/ql/src/Advisory/Statements/OneStatementPerLine.qhelp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>Code where each statement is defined on a separate line is much easier for programmers to read than code where multiple statements are defined on the same line.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>Separate statements by a newline character.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-142311.html#431">7.1 Simple Statements</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
44
java/ql/src/Advisory/Statements/OneStatementPerLine.ql
Normal file
44
java/ql/src/Advisory/Statements/OneStatementPerLine.ql
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* @name Multiple statements on line
|
||||||
|
* @description More than one statement per line decreases readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/multiple-statements-on-same-line
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
predicate lineDefinesEnum(File f, int line) {
|
||||||
|
exists(Location l |
|
||||||
|
exists(EnumType e | e.getLocation() = l) or
|
||||||
|
exists(EnumConstant e | e.getLocation() = l) |
|
||||||
|
f = l.getFile() and line = l.getStartLine()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate oneLineStatement(Stmt s, File f, int line, int col) {
|
||||||
|
exists(Location l | s.getLocation() = l |
|
||||||
|
f = l.getFile() and
|
||||||
|
line = l.getStartLine() and
|
||||||
|
line = l.getEndLine() and
|
||||||
|
col = l.getStartColumn()
|
||||||
|
) and
|
||||||
|
// Exclude blocks: `{break;}` is not really a violation.
|
||||||
|
not s instanceof Block and
|
||||||
|
// Exclude implicit super constructor invocations.
|
||||||
|
not s instanceof SuperConstructorInvocationStmt and
|
||||||
|
// Java enums are desugared to a whole bunch of generated statements.
|
||||||
|
not lineDefinesEnum(f, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
from Stmt s, Stmt s2
|
||||||
|
where
|
||||||
|
exists(File f, int line, int col, int col2 |
|
||||||
|
oneLineStatement(s, f, line, col) and
|
||||||
|
oneLineStatement(s2, f, line, col2) and
|
||||||
|
col < col2 and
|
||||||
|
// Don't report multiple results if more than 2 statements are on a single line.
|
||||||
|
col = min(int otherCol | oneLineStatement(_, f, line, otherCol))
|
||||||
|
)
|
||||||
|
select s, "This statement is followed by another on the same line."
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
int score;
|
||||||
|
char grade;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
if (score >= 90) {
|
||||||
|
grade = 'A';
|
||||||
|
} else if (score >= 80) {
|
||||||
|
grade = 'B';
|
||||||
|
} else if (score >= 70) {
|
||||||
|
grade = 'C';
|
||||||
|
} else if (score >= 60) {
|
||||||
|
grade = 'D';
|
||||||
|
// BAD: No terminating 'else' clause
|
||||||
|
}
|
||||||
|
System.out.println("Grade = " + grade);
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
An <code>if-else-if</code> statement without a terminating <code>else</code>
|
||||||
|
clause may allow execution to 'fall through' silently, if none of the <code>if</code> clauses are
|
||||||
|
matched.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Include a terminating <code>else</code> clause to <code>if-else-if</code> statements
|
||||||
|
to prevent execution from falling through silently. If the terminating <code>else</code> clause is
|
||||||
|
intended to be unreachable code, it is advisable that it throws a <code>RuntimeException</code> to
|
||||||
|
alert the user of an internal error.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
|
||||||
|
<p>In the following example, the <code>if</code> statement outputs the grade that is achieved depending on the
|
||||||
|
test score. However, if the score is less than 60, execution falls through silently.</p>
|
||||||
|
|
||||||
|
<sample src="TerminateIfElseIfWithElse.java" />
|
||||||
|
|
||||||
|
<p>In the following modified example, the <code>if</code> statement includes a terminating
|
||||||
|
<code>else</code> clause, to allow for scores that are less than 60.</p>
|
||||||
|
|
||||||
|
<sample src="TerminateIfElseIfWithElseGood.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Java SE Documentation:
|
||||||
|
<a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-142311.html#449">7.4 if, if-else, if else-if else Statements</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
18
java/ql/src/Advisory/Statements/TerminateIfElseIfWithElse.ql
Normal file
18
java/ql/src/Advisory/Statements/TerminateIfElseIfWithElse.ql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-terminated if-else-if chain
|
||||||
|
* @description An 'if-else-if' statement without a terminating 'else' clause may allow execution to
|
||||||
|
* 'fall through' silently.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/non-terminated-if-else-if-chain
|
||||||
|
* @tags reliability
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
from IfStmt prev, IfStmt last
|
||||||
|
where
|
||||||
|
not exists(last.getElse()) and
|
||||||
|
prev.getElse() = last
|
||||||
|
select last, "If-else-if statement does not have a terminating else statement."
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
int score;
|
||||||
|
char grade;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
if (score >= 90) {
|
||||||
|
grade = 'A';
|
||||||
|
} else if (score >= 80) {
|
||||||
|
grade = 'B';
|
||||||
|
} else if (score >= 70) {
|
||||||
|
grade = 'C';
|
||||||
|
} else if (score >= 60) {
|
||||||
|
grade = 'D';
|
||||||
|
} else { // GOOD: Terminating 'else' clause for all other scores
|
||||||
|
grade = 'F';
|
||||||
|
}
|
||||||
|
System.out.println("Grade = " + grade);
|
||||||
9
java/ql/src/Advisory/Types/GenericsConstructor.qhelp
Normal file
9
java/ql/src/Advisory/Types/GenericsConstructor.qhelp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<include src="Generics_Common.qhelp" />
|
||||||
|
|
||||||
|
</qhelp>
|
||||||
15
java/ql/src/Advisory/Types/GenericsConstructor.ql
Normal file
15
java/ql/src/Advisory/Types/GenericsConstructor.ql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-parameterized constructor invocation
|
||||||
|
* @description Parameterizing a call to a constructor of a generic type increases type safety and
|
||||||
|
* code readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/raw-constructor-invocation
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from ClassInstanceExpr cie
|
||||||
|
where cie.getConstructor().getDeclaringType() instanceof RawType
|
||||||
|
select cie, "This is a non-parameterized constructor invocation of a generic type."
|
||||||
9
java/ql/src/Advisory/Types/GenericsReturnType.qhelp
Normal file
9
java/ql/src/Advisory/Types/GenericsReturnType.qhelp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<include src="Generics_Common.qhelp" />
|
||||||
|
|
||||||
|
</qhelp>
|
||||||
17
java/ql/src/Advisory/Types/GenericsReturnType.ql
Normal file
17
java/ql/src/Advisory/Types/GenericsReturnType.ql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-parameterized method return type
|
||||||
|
* @description Using a parameterized instance of a generic type for a method return type increases
|
||||||
|
* type safety and code readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/raw-return-type
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from Method m
|
||||||
|
where
|
||||||
|
m.fromSource() and
|
||||||
|
m.getReturnType() instanceof RawType
|
||||||
|
select m, "This method has a non-parameterized return type."
|
||||||
9
java/ql/src/Advisory/Types/GenericsVariable.qhelp
Normal file
9
java/ql/src/Advisory/Types/GenericsVariable.qhelp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<include src="Generics_Common.qhelp" />
|
||||||
|
|
||||||
|
</qhelp>
|
||||||
17
java/ql/src/Advisory/Types/GenericsVariable.ql
Normal file
17
java/ql/src/Advisory/Types/GenericsVariable.ql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @name Non-parameterized variable
|
||||||
|
* @description Declaring a field, parameter, or local variable as a parameterized type increases
|
||||||
|
* type safety and code readability.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision medium
|
||||||
|
* @id java/raw-variable
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from Variable v
|
||||||
|
where
|
||||||
|
v.fromSource() and
|
||||||
|
v.getType() instanceof RawType
|
||||||
|
select v, "This declaration uses a non-parameterized type."
|
||||||
6
java/ql/src/Advisory/Types/Generics_Common.java
Normal file
6
java/ql/src/Advisory/Types/Generics_Common.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
public List constructRawList(Object o) {
|
||||||
|
List list; // Raw variable declaration
|
||||||
|
list = new ArrayList(); // Raw constructor call
|
||||||
|
list.add(o);
|
||||||
|
return list; // Raw method return type (see signature above)
|
||||||
|
}
|
||||||
56
java/ql/src/Advisory/Types/Generics_Common.qhelp
Normal file
56
java/ql/src/Advisory/Types/Generics_Common.qhelp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
The use of generics in Java improves compile-time type safety and
|
||||||
|
code readability.
|
||||||
|
Users of a class or interface that has been designed using generic types
|
||||||
|
should therefore make use of parameterized instances
|
||||||
|
in variable declarations, method return types, and constructor calls.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Provide type parameters to generic classes and interfaces where possible.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Note that converting legacy code to use generics may
|
||||||
|
have to be done carefully in order to preserve the existing
|
||||||
|
functionality of an API; for detailed guidance, see the references.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>The following example is poorly written because it uses raw types. This makes it
|
||||||
|
more error prone because the compiler is less able to perform type checks.</p>
|
||||||
|
<sample src="Generics_Common.java" />
|
||||||
|
|
||||||
|
<p>A parameterized version can be easily made and is much safer.</p>
|
||||||
|
<sample src="Generics_CommonGood.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
J. Bloch, <em>Effective Java (second edition)</em>,
|
||||||
|
Item 23.
|
||||||
|
Addison-Wesley, 2008.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Help - Eclipse Platform:
|
||||||
|
<a href="http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fpreferences%2Fjava%2Fcompiler%2Fref-preferences-errors-warnings.htm">Java Compiler Errors/Warnings Preferences</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The Java Tutorials:
|
||||||
|
<a href="http://docs.oracle.com/javase/tutorial/java/generics/">Generics</a>,
|
||||||
|
<a href="http://docs.oracle.com/javase/tutorial/extra/generics/convert.html">Converting Legacy Code to Use Generics</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
6
java/ql/src/Advisory/Types/Generics_CommonGood.java
Normal file
6
java/ql/src/Advisory/Types/Generics_CommonGood.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
public <T> List<T> constructParameterizedList(T o) {
|
||||||
|
List<T> list; // Parameterized variable declaration
|
||||||
|
list = new ArrayList<T>(); // Parameterized constructor call
|
||||||
|
list.add(o);
|
||||||
|
return list; // Parameterized method return type (see signature above)
|
||||||
|
}
|
||||||
89
java/ql/src/AlertSuppression.ql
Normal file
89
java/ql/src/AlertSuppression.ql
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* @name Alert suppression
|
||||||
|
* @description Generates information about alert suppressions.
|
||||||
|
* @kind alert-suppression
|
||||||
|
* @id java/alert-suppression
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alert suppression comment.
|
||||||
|
*/
|
||||||
|
class SuppressionComment extends Javadoc {
|
||||||
|
string annotation;
|
||||||
|
|
||||||
|
SuppressionComment() {
|
||||||
|
isEolComment(this) and
|
||||||
|
exists(string text | text = getChild(0).getText() |
|
||||||
|
// match `lgtm[...]` anywhere in the comment
|
||||||
|
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||||
|
or
|
||||||
|
// match `lgtm` at the start of the comment and after semicolon
|
||||||
|
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the text of this suppression comment.
|
||||||
|
*/
|
||||||
|
string getText() {
|
||||||
|
result = getChild(0).getText()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the suppression annotation in this comment. */
|
||||||
|
string getAnnotation() {
|
||||||
|
result = annotation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||||
|
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||||
|
*/
|
||||||
|
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||||
|
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
|
||||||
|
startcolumn = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the scope of this suppression. */
|
||||||
|
SuppressionScope getScope() {
|
||||||
|
this = result.getSuppressionComment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scope of an alert suppression comment.
|
||||||
|
*/
|
||||||
|
class SuppressionScope extends @javadoc {
|
||||||
|
SuppressionScope() {
|
||||||
|
this instanceof SuppressionComment
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a suppression comment with this scope. */
|
||||||
|
SuppressionComment getSuppressionComment() {
|
||||||
|
result = this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this element is at the specified location.
|
||||||
|
* The location spans column `startcolumn` of line `startline` to
|
||||||
|
* column `endcolumn` of line `endline` in file `filepath`.
|
||||||
|
* For more information, see
|
||||||
|
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||||
|
*/
|
||||||
|
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||||
|
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a textual representation of this element. */
|
||||||
|
string toString() {
|
||||||
|
result = "suppression range"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from SuppressionComment c
|
||||||
|
select
|
||||||
|
c, // suppression comment
|
||||||
|
c.getText(), // text of suppression comment (excluding delimiters)
|
||||||
|
c.getAnnotation(), // text of suppression annotation
|
||||||
|
c.getScope() // scope of suppression
|
||||||
32
java/ql/src/Architecture/Dependencies/MutualDependency.java
Normal file
32
java/ql/src/Architecture/Dependencies/MutualDependency.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
public class MutualDependency {
|
||||||
|
// Violation: BadModel and BadView are mutually dependent
|
||||||
|
private static class BadModel {
|
||||||
|
private int i;
|
||||||
|
private BadView view;
|
||||||
|
|
||||||
|
public int getI() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setI(int i) {
|
||||||
|
this.i = i;
|
||||||
|
if(view != null) view.modelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setView(BadView view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BadView {
|
||||||
|
private BadModel model;
|
||||||
|
|
||||||
|
public BadView(BadModel model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modelChanged() {
|
||||||
|
System.out.println("Model Changed: " + model.getI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
java/ql/src/Architecture/Dependencies/MutualDependency.qhelp
Normal file
82
java/ql/src/Architecture/Dependencies/MutualDependency.qhelp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
A mutual dependency exists when two code entities (for example, types or packages) depend directly on each other.
|
||||||
|
Mutual dependencies are caused by unwanted dependencies in one or both directions. There
|
||||||
|
are many different kinds of dependency; here are a few examples of how an inter-type dependency
|
||||||
|
from <code>T1</code> to <code>T2</code> can occur:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>T1</code> derives from a type involving <code>T2</code>, for example <code>T2</code> itself or <code>List<T2></code>.</li>
|
||||||
|
<li><code>T1</code> declares a field of a type involving <code>T2</code>.</li>
|
||||||
|
<li><code>T1</code> declares a method whose return type involves <code>T2</code>.</li>
|
||||||
|
<li>A method of <code>T1</code> declares a local variable whose type involves <code>T2</code>.</li>
|
||||||
|
<li>A method of <code>T1</code> catches an exception of a type involving <code>T2</code>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Mutual dependencies prevent you from considering either entity in isolation,
|
||||||
|
affecting readability and testability. For example, if types <code>T1</code> and <code>T2</code> depend on each other, then it is
|
||||||
|
generally impossible to fully understand <code>T1</code> without understanding <code>T2</code>, and vice-versa. Moreover, neither
|
||||||
|
type can be tested without the other being present. Whilst mocking can alleviate this latter problem
|
||||||
|
to some extent, breaking the mutual dependency is a better solution. For example, suppose we could
|
||||||
|
remove all of the dependencies from <code>T2</code> to <code>T1</code> - in that case, we would be able to test <code>T2</code> in isolation, and
|
||||||
|
completely side-step the need to provide a <code>T1</code>, mocked or otherwise.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Breaking mutual dependencies involves finding ways of removing the unwanted individual dependencies that
|
||||||
|
cause them. The way to do this depends on the kind of dependency in question, with some kinds (for example,
|
||||||
|
dependencies caused by inheritance) being much harder to break than others. A full list of ways to
|
||||||
|
break cycles is beyond the scope of this help topic, however, a few high-level techniques
|
||||||
|
for breaking a dependency from <code>T1</code> to <code>T2</code> include:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Introducing an interface that is implemented by <code>T2</code>. <code>T1</code>
|
||||||
|
can then be refactored to use <code>T2</code> only via the interface, which breaks the cycle.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Moving the depended-on code in <code>T2</code> to a third (possibly new) entity.
|
||||||
|
<code>T1</code> can then depend on this third entity instead of on <code>T2</code>,
|
||||||
|
breaking the cycle. <code>T2</code> is allowed to depend on the third entity as
|
||||||
|
well, although it does not have to if there is no need.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Merging <code>T1</code> and <code>T2</code> together (for example, if there was an artificial separation between two parts
|
||||||
|
of the same concept). This is not a generally-applicable solution, but is sometimes the right thing to
|
||||||
|
do. It has the effect of internalizing the cycle, which is sufficient to solve the problem.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For more information on how to break
|
||||||
|
unwanted dependencies, see the references (particularly [Lakos]).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>In this example <code>BadModel</code> and <code>BadView</code> are mutually dependent.</p>
|
||||||
|
<sample src="MutualDependency.java" />
|
||||||
|
<p>The interface technique can be used to break the dependency between the model and the view. The <code>ModelListener</code> interface allows <code>BetterView</code> to interact with <code>BetterModel</code> without dependency.</p>
|
||||||
|
<sample src="MutualDependencyFix.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>J. Lakos. <em>Large-Scale C++ Software Design</em>. Addison-Wesley, 1996.</li>
|
||||||
|
<li>M. Fowler. <em>Refactoring</em>. Addison-Wesley, 1999.</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
36
java/ql/src/Architecture/Dependencies/MutualDependency.ql
Normal file
36
java/ql/src/Architecture/Dependencies/MutualDependency.ql
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @name Mutually-dependent types
|
||||||
|
* @description Mutual dependency between types makes code difficult to understand and test.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision low
|
||||||
|
* @id java/mutually-dependent-types
|
||||||
|
* @tags testability
|
||||||
|
* maintainability
|
||||||
|
* modularity
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
from RefType t1, RefType t2
|
||||||
|
where
|
||||||
|
depends(t1, t2) and
|
||||||
|
depends(t2, t1) and
|
||||||
|
// Prevent symmetrical results.
|
||||||
|
t1.getName() < t2.getName() and
|
||||||
|
t1.fromSource() and
|
||||||
|
t2.fromSource() and
|
||||||
|
// Exclusions.
|
||||||
|
not (
|
||||||
|
t1 instanceof AnonymousClass or
|
||||||
|
t1 instanceof BoundedType or
|
||||||
|
t2 instanceof AnonymousClass or
|
||||||
|
t2 instanceof BoundedType or
|
||||||
|
t1.getName().toLowerCase().matches("%visitor%") or
|
||||||
|
t2.getName().toLowerCase().matches("%visitor%") or
|
||||||
|
t1.getAMethod().getName().toLowerCase().matches("%visit%") or
|
||||||
|
t2.getAMethod().getName().toLowerCase().matches("%visit%") or
|
||||||
|
t1.getPackage() = t2.getPackage()
|
||||||
|
)
|
||||||
|
select t1, "This type and type $@ are mutually dependent.",
|
||||||
|
t2, t2.getName()
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
public class NoMutualDependency {
|
||||||
|
// Better: A new interface breaks the dependency
|
||||||
|
// from the model to the view
|
||||||
|
private interface ModelListener {
|
||||||
|
void modelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BetterModel {
|
||||||
|
private int i;
|
||||||
|
private ModelListener listener;
|
||||||
|
|
||||||
|
public int getI() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setI(int i) {
|
||||||
|
this.i = i;
|
||||||
|
if (listener != null) listener.modelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(ModelListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BetterView implements ModelListener {
|
||||||
|
private BetterModel model;
|
||||||
|
|
||||||
|
public BetterView(BetterModel model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modelChanged() {
|
||||||
|
System.out.println("Model Changed: " + model.getI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BadModel badModel = new BadModel();
|
||||||
|
BadView badView = new BadView(badModel);
|
||||||
|
badModel.setView(badView);
|
||||||
|
badModel.setI(23);
|
||||||
|
badModel.setI(9);
|
||||||
|
|
||||||
|
BetterModel betterModel = new BetterModel();
|
||||||
|
BetterView betterView = new BetterView(betterModel);
|
||||||
|
betterModel.setListener(betterView);
|
||||||
|
betterModel.setI(24);
|
||||||
|
betterModel.setI(12);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import java
|
||||||
|
import semmle.code.xml.MavenPom
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the source code in the project represented by the given pom depends on any code
|
||||||
|
* within the given container (folder, jar file etc.).
|
||||||
|
*/
|
||||||
|
predicate pomDependsOnContainer(Pom f, Container g) {
|
||||||
|
exists(RefType source, RefType target |
|
||||||
|
source.getFile().getParentContainer*() = f.getFile().getParentContainer() and
|
||||||
|
target.getFile().getParentContainer*() = g and
|
||||||
|
depends(source, target)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the source code in the project represented by the sourcePom depends on any code
|
||||||
|
* within the project represented by the targetPom.
|
||||||
|
*/
|
||||||
|
predicate pomDependsOnPom(Pom sourcePom, Pom targetPom) {
|
||||||
|
exists(RefType source, RefType target |
|
||||||
|
source.getFile().getParentContainer*() = sourcePom.getFile().getParentContainer() and
|
||||||
|
target.getFile().getParentContainer*() = targetPom.getFile().getParentContainer() and
|
||||||
|
depends(source, target)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>For projects that build with Maven, unnecessary dependencies add a variety
|
||||||
|
of maintenance burdens. Most immediately, unnecessary dependencies increase
|
||||||
|
build time, because Maven rebuilds an artifact whenever its declared
|
||||||
|
dependencies are modified. This rule identifies Maven dependencies that
|
||||||
|
are declared in a POM file but are not used by the underlying source code.</p>
|
||||||
|
|
||||||
|
<p>If the dependency's source code is part of the code base being analyzed,
|
||||||
|
then the result is reported by one version of the rule. Otherwise, the
|
||||||
|
dependency is reported by a separate version of the rule. This allows
|
||||||
|
the two types of unused Maven dependencies to be reported separately.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
<p>Try removing the dependency from the POM file. Then run all build and test
|
||||||
|
targets that are relevant for the modified POM file. If all of the relevant
|
||||||
|
build and test targets still succeed, then leave the dependency out permanently.
|
||||||
|
Doing so will make future maintenance of the relevant source code easier.</p>
|
||||||
|
|
||||||
|
<p>In some cases, there may be a true dependency on the code that is not
|
||||||
|
detected by the analysis. If any of the build and test targets fail
|
||||||
|
after the dependency is removed, then the result is a false positive,
|
||||||
|
and the dependency should be restored.</p>
|
||||||
|
</recommendation>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>Apache Maven Project:
|
||||||
|
<a href="http://maven.apache.org/pom.html#Dependencies">Maven POM Reference: Dependencies</a>.
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<include src="UnusedMavenDependency.qhelp" />
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @name Unused Maven dependency (binary)
|
||||||
|
* @description Unnecessary Maven dependencies are a maintenance burden.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision low
|
||||||
|
* @id java/unused-maven-binary-dependency
|
||||||
|
*/
|
||||||
|
|
||||||
|
import UnusedMavenDependencies
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A whitelist of binary dependencies that should never be highlighted as unusued.
|
||||||
|
*/
|
||||||
|
predicate whitelist(Dependency d) {
|
||||||
|
/*
|
||||||
|
* jsr305 contains package annotations. If a project uses those exclusively, we will
|
||||||
|
* consider it "unused".
|
||||||
|
*/
|
||||||
|
d.getShortCoordinate() = "com.google.code.findbugs:jsr305"
|
||||||
|
}
|
||||||
|
|
||||||
|
from PomDependency d, Pom source
|
||||||
|
where
|
||||||
|
source.getADependency() = d and
|
||||||
|
/*
|
||||||
|
* There is not a Pom file for the target of this dependency, so we assume that it was resolved by
|
||||||
|
* a binary file in the local maven repository.
|
||||||
|
*/
|
||||||
|
not exists(Pom target | target = d.getPom()) and
|
||||||
|
/*
|
||||||
|
* In order to accurately identify whether this binary dependency is required, we must have identified
|
||||||
|
* a Maven repository. If we have not found a repository, it's likely that it has a custom path of
|
||||||
|
* which we are unaware, so do not report any problems.
|
||||||
|
*/
|
||||||
|
exists(MavenRepo mr) and
|
||||||
|
/*
|
||||||
|
* We either haven't indexed a relevant jar file, which suggests that nothing statically depended upon
|
||||||
|
* it, or we have indexed the relevant jar file, but no source code in the project defined by the pom
|
||||||
|
* depends on any code within the detected jar.
|
||||||
|
*/
|
||||||
|
not pomDependsOnContainer(source, d.getJar()) and
|
||||||
|
/*
|
||||||
|
* If something that depends on us depends on the jar represented by this dependency, and it doesn't
|
||||||
|
* depend directly on the jar itself, we don't consider it to be "unused".
|
||||||
|
*/
|
||||||
|
not exists(Pom pomThatDependsOnSource |
|
||||||
|
pomThatDependsOnSource.getAnExportedPom+() = source
|
||||||
|
|
|
||||||
|
pomDependsOnContainer(pomThatDependsOnSource, d.getJar()) and
|
||||||
|
not exists(File f | f = pomThatDependsOnSource.getADependency().getJar() and f = d.getJar())) and
|
||||||
|
// Filter out those dependencies on the whitelist
|
||||||
|
not whitelist(d)
|
||||||
|
select d, "Maven dependency on the binary package " + d.getShortCoordinate() + " is unused."
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<include src="UnusedMavenDependency.qhelp" />
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @name Unused Maven dependency (source)
|
||||||
|
* @description Unnecessary Maven dependencies are a maintenance burden.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision low
|
||||||
|
* @id java/unused-maven-source-dependency
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.xml.MavenPom
|
||||||
|
import UnusedMavenDependencies
|
||||||
|
|
||||||
|
from PomDependency d, Pom source, Pom target
|
||||||
|
where
|
||||||
|
source.getADependency() = d and
|
||||||
|
/*
|
||||||
|
* We have a targetPom file, so this is a "source" dependency, rather than a binary dependency
|
||||||
|
* from the Maven repository. Note, although .pom files exist in the local maven repository, they
|
||||||
|
* are usually not indexed because they are outside the source directory. We assume that they have
|
||||||
|
* not been indexed.
|
||||||
|
*/
|
||||||
|
target = d.getPom() and
|
||||||
|
/*
|
||||||
|
* If we have a pom for the target of this dependency, then it is unused iff neither it, nor any
|
||||||
|
* of its transitive dependencies are required.
|
||||||
|
*/
|
||||||
|
not exists(Pom exported |
|
||||||
|
exported = target.getAnExportedPom*()
|
||||||
|
|
|
||||||
|
pomDependsOnContainer(source, exported.getAnExportedDependency().getJar()) or
|
||||||
|
pomDependsOnPom(source, exported)
|
||||||
|
)
|
||||||
|
select d, "Maven dependency onto " + d.getShortCoordinate() + " is unused."
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
class X1 {
|
||||||
|
private int i;
|
||||||
|
|
||||||
|
public X1(int i) {
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Y1 makeY1(float j) {
|
||||||
|
return new Y1(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y1 {
|
||||||
|
private float j;
|
||||||
|
|
||||||
|
public Y1(float j) {
|
||||||
|
this.j = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Z1 makeZ1(double k) {
|
||||||
|
return new Z1(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Violation
|
||||||
|
class Z1 {
|
||||||
|
private double k;
|
||||||
|
|
||||||
|
public Z1(double k) {
|
||||||
|
this.k = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void foo() {
|
||||||
|
System.out.println(i * j * k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class DeeplyNestedClass {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new X1(23).makeY1(9.0f).makeZ1(84.0).foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Classes (especially complex ones) that are nested multiple levels deep can be difficult to understand because they have access to variables
|
||||||
|
from all of the classes that enclose them. Such classes can also be difficult to unit test. Specific exceptions are made for:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Anonymous classes - these are generally used as a substitute for closures.</li>
|
||||||
|
<li>Enumerations, and simple classes that contain no methods - these are unlikely to hinder readability.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The solution is to move one or more of the nested classes into a higher scope, less deeply-nested (see example below). When you move a nested class, you must:</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Ensure that the class can still access the required variables from its previously enclosing scopes.</li>
|
||||||
|
<li>
|
||||||
|
Consider the dependencies, particularly when you move a non-static nested class out of the
|
||||||
|
containing class. Generally, a non-static class should be refactored to depend only on
|
||||||
|
the contents of the classes that previously enclosed it. This avoids introducing a dependency cycle where the non-static class depends on the previously-enclosing classes themselves.
|
||||||
|
</li></ul>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>In the following example <code>Z1</code> is difficult to read because it is deeply nested.</p>
|
||||||
|
|
||||||
|
<sample src="DeeplyNestedClass.java" />
|
||||||
|
<p>In this example, there are no nested classes and you can clearly see which variables affect which class.</p>
|
||||||
|
<sample src="DeeplyNestedClassFix.java" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
The Java Tutorials: <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html">Nested Classes</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @name Deeply-nested class
|
||||||
|
* @description Deeply-nested classes are difficult to understand, since they have access to many scopes
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision low
|
||||||
|
* @id java/deeply-nested-class
|
||||||
|
* @tags testability
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
|
||||||
|
from NestedClass c
|
||||||
|
where
|
||||||
|
c.getNestingDepth() > 1 and
|
||||||
|
exists(Method m | m.getDeclaringType() = c | not m instanceof StaticInitializer) and
|
||||||
|
not c instanceof AnonymousClass and
|
||||||
|
not c instanceof EnumType
|
||||||
|
select c, "This class is deeply nested, which can hinder readability."
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
class X2 {
|
||||||
|
private int i;
|
||||||
|
|
||||||
|
public X2(int i) {
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Y2 makeY2(float j) {
|
||||||
|
return new Y2(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y2 {
|
||||||
|
private int i;
|
||||||
|
private float j;
|
||||||
|
|
||||||
|
public Y2(int i, float j) {
|
||||||
|
this.i = i;
|
||||||
|
this.j = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Z2 makeZ2(double k) {
|
||||||
|
return new Z2(i, j, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z2 {
|
||||||
|
private int i;
|
||||||
|
private float j;
|
||||||
|
private double k;
|
||||||
|
|
||||||
|
public Z2(int i, float j, double k) {
|
||||||
|
this.i = i;
|
||||||
|
this.j = j;
|
||||||
|
this.k = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void foo() {
|
||||||
|
System.out.println(i * j * k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotNestedClass {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new X2(23).makeY2(9.0f).makeZ2(84.0).foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Before refactoring:
|
||||||
|
class Item { .. }
|
||||||
|
class Basket {
|
||||||
|
// ..
|
||||||
|
float getTotalPrice(Item i) {
|
||||||
|
float price = i.getPrice() + i.getTax();
|
||||||
|
if (i.isOnSale())
|
||||||
|
price = price - i.getSaleDiscount() * price;
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After refactoring:
|
||||||
|
class Item {
|
||||||
|
// ..
|
||||||
|
float getTotalPrice() {
|
||||||
|
float price = getPrice() + getTax();
|
||||||
|
if (isOnSale())
|
||||||
|
price = price - getSaleDiscount() * price;
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p><em>Feature envy</em> refers to situations where a method is "in the wrong place", because
|
||||||
|
it does not use many methods or variables of its own class, but uses a whole range of methods or variables from
|
||||||
|
some other class. This violates the principle of putting data and behavior in the
|
||||||
|
same place, and exposes internals of the other class to the method.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>For each method that may exhibit feature envy, see if it needs to be declared in
|
||||||
|
its present location, or if you can move it to the class it is "envious" of.
|
||||||
|
A common example is a method that calls a large number of getters on
|
||||||
|
another class to perform a calculation that does not rely on
|
||||||
|
anything from its own class. In such cases, you should move the method to the class containing the data.
|
||||||
|
If the calculation depends on some values from the method's current class, they can either be passed as
|
||||||
|
arguments or accessed using getters from the other class.</p>
|
||||||
|
|
||||||
|
<p>If it is inappropriate to move the entire method, see if all the dependencies
|
||||||
|
on the other class are concentrated in just one part of the method. If so, you can move them into a method
|
||||||
|
of their own. You can then move this method to the other class and call it from the original method.</p>
|
||||||
|
|
||||||
|
<p>If a class is envious of functionality defined in a superclass, perhaps the
|
||||||
|
superclass needs to be rewritten to become more extensible and allow its subtypes to
|
||||||
|
define new behavior without them depending so deeply on the superclass's implementation. The
|
||||||
|
<em>template method</em> pattern may be useful in achieving this.</p>
|
||||||
|
|
||||||
|
<p>Modern IDEs provide several refactorings that may be useful in addressing
|
||||||
|
instances of feature envy, typically under the names of "Move method" and "Extract
|
||||||
|
method".</p>
|
||||||
|
|
||||||
|
<p>Occasionally, behavior can be misinterpreted as feature envy when in fact it is justified. The
|
||||||
|
most common examples are complex design patterns like <em>visitor</em> or <em>strategy</em>, where
|
||||||
|
the goal is to separate data from behavior.
|
||||||
|
|
||||||
|
</p></recommendation>
|
||||||
|
<example>
|
||||||
|
<p>In the following example, initially the method <code>getTotalPrice</code> is in the
|
||||||
|
<code>Basket</code> class, but it only uses data belonging to the <code>Item</code> class.
|
||||||
|
Therefore, it represents an instance of feature envy. To refactor
|
||||||
|
it, <code>getTotalPrice</code> can be moved to <code>Item</code> and its
|
||||||
|
parameter can be removed. The resulting
|
||||||
|
code is easier to understand and keep consistent.</p>
|
||||||
|
|
||||||
|
<sample src="FeatureEnvy.java" />
|
||||||
|
|
||||||
|
<p>The refactored code is still appropriate, even if some data from the <code>Basket</code> class is necessary for the computation of the total price.
|
||||||
|
For example, if the <code>Basket</code> class applies a bulk discount when a sufficient number of items are
|
||||||
|
in the basket, an "additional discount" parameter can be added to
|
||||||
|
<code>Item.getTotalPrice(..)</code>. Alternatively, the application of
|
||||||
|
the discount can be performed in a method in <code>Basket</code> that calls
|
||||||
|
<code>Item.getTotalPrice</code>.</p>
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
|
||||||
|
<em>Design patterns: elements of reusable object-oriented software</em>.
|
||||||
|
Addison-Wesley Longman Publishing Co., Inc., Boston, MA, 1995.</li>
|
||||||
|
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 93–94. Addison-Wesley Professional, 2004.
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* @name Feature envy
|
||||||
|
* @description A method that uses more methods or variables from another (unrelated) class than
|
||||||
|
* from its own class violates the principle of putting data and behavior in the same
|
||||||
|
* place.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @precision low
|
||||||
|
* @id java/feature-envy
|
||||||
|
* @tags maintainability
|
||||||
|
* modularity
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
Member getAUsedMember(Method m) {
|
||||||
|
result.(Field).getAnAccess().getEnclosingCallable() = m or
|
||||||
|
result.(Callable).getAReference().getEnclosingCallable() = m
|
||||||
|
}
|
||||||
|
|
||||||
|
int dependencyCount(Method source, RefType target) {
|
||||||
|
result = strictcount(Member m | m = getAUsedMember(source) and m = target.getAMember())
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate methodDependsOn(Method m, RefType target) {
|
||||||
|
exists(dependencyCount(m, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate dependsOn(RefType source, RefType target) {
|
||||||
|
methodDependsOn(source.getACallable(), target)
|
||||||
|
}
|
||||||
|
|
||||||
|
int selfDependencyCount(Method source) {
|
||||||
|
result = sum(dependencyCount(source, source.getDeclaringType().getEnclosingType*()))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate dependsHighlyOn(Method source, RefType target, int selfCount, int depCount) {
|
||||||
|
depCount = dependencyCount(source, target) and
|
||||||
|
selfCount = selfDependencyCount(source) and
|
||||||
|
depCount > 2*selfCount and
|
||||||
|
depCount > 4
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate query(Method m, RefType targetType, int selfCount, int depCount) {
|
||||||
|
exists(RefType sourceType | sourceType = m.getDeclaringType() |
|
||||||
|
dependsHighlyOn(m, targetType, selfCount, depCount) and
|
||||||
|
// Interfaces are depended upon by their very nature
|
||||||
|
not targetType instanceof Interface and
|
||||||
|
// Anonymous classes are often used as callbacks, which heavily depend on other classes
|
||||||
|
not sourceType instanceof AnonymousClass and
|
||||||
|
// Do not move initializer methods
|
||||||
|
not m instanceof InitializerMethod and
|
||||||
|
// Do not move up/down the class hierarchy
|
||||||
|
not (
|
||||||
|
sourceType.getASupertype*().getSourceDeclaration() = targetType or
|
||||||
|
targetType.getASupertype*().getSourceDeclaration() = sourceType
|
||||||
|
) and
|
||||||
|
// Do not move between nested types
|
||||||
|
not (sourceType.getEnclosingType*() = targetType or targetType.getEnclosingType*() = sourceType) and
|
||||||
|
// Tests are allowed to be invasive and depend on the tested classes highly
|
||||||
|
not sourceType instanceof TestClass and
|
||||||
|
// Check that the target type already depends on every type used by the method
|
||||||
|
forall(RefType dependency | methodDependsOn(m, dependency) | dependsOn(targetType, dependency))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from Method m, RefType other, int selfCount, int depCount
|
||||||
|
where
|
||||||
|
query(m, other, selfCount, depCount) and
|
||||||
|
// Don't include types that are used from many different places - we only highlight
|
||||||
|
// relatively local fixes that could reasonably be implemented.
|
||||||
|
count(Method yetAnotherMethod | query(yetAnotherMethod, other, _, _)) < 10
|
||||||
|
select m, "Method " + m.getName() + " is too closely tied to $@: " + depCount +
|
||||||
|
" dependencies to it, but only " + selfCount + " dependencies to its own type.",
|
||||||
|
other, other.getName()
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>A <em>hub class</em> is a class that depends on many other classes, and on which many other classes
|
||||||
|
depend.</p>
|
||||||
|
|
||||||
|
<p>For the purposes of this rule, a <em>dependency</em> is any use of one class in another.
|
||||||
|
Examples include:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Using another class as the declared type of a variable or field</li>
|
||||||
|
<li>Using another class as an argument type for a method</li>
|
||||||
|
<li>Using another class as a superclass in the <code>extends</code> declaration</li>
|
||||||
|
<li>Calling a method defined in the class</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>A class can be regarded as a hub class when both the incoming dependencies and the outgoing
|
||||||
|
source dependencies are particularly high. (Outgoing source dependencies are dependencies on other
|
||||||
|
source classes, rather than library classes like <code>java.lang.Object</code>.)</p>
|
||||||
|
|
||||||
|
<p>It is undesirable to have many hub classes because they are extremely difficult to maintain. This is
|
||||||
|
because many other classes depend on a hub class, and so the other classes have to be tested and
|
||||||
|
possibly adapted after each change to the hub class. Also, when one of a hub class's direct
|
||||||
|
dependencies changes, the behavior of the hub class and all of its dependencies has to be
|
||||||
|
checked and possibly adapted.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>One common reason for a class to be regarded as a hub class is that it tries to do too much,
|
||||||
|
including unrelated functionality that depends on different parts of the code base. If
|
||||||
|
possible, split such classes into several better encapsulated classes.</p>
|
||||||
|
|
||||||
|
<p> Another common reason is that the class is a "struct-like" class that has many fields of different
|
||||||
|
types. Introducing some intermediate grouping containers to make it clearer what fields
|
||||||
|
belong together may be a good option.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
|
||||||
|
<em>Design patterns: elements of reusable object-oriented software</em>.
|
||||||
|
Addison-Wesley Longman Publishing Co., Inc., Boston, MA, 1995.</li>
|
||||||
|
<li>W. C. Wake, <em>Refactoring Workbook</em>. Addison-Wesley Professional, 2004.</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @name Hub classes
|
||||||
|
* @description Hub classes, which are classes that use, and are used by, many other classes, are
|
||||||
|
* complex and difficult to change without affecting the rest of the system.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision low
|
||||||
|
* @id java/hub-class
|
||||||
|
* @tags maintainability
|
||||||
|
* modularity
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
from RefType t, int aff, int eff
|
||||||
|
where
|
||||||
|
t.fromSource() and
|
||||||
|
aff = t.getMetrics().getAfferentCoupling() and
|
||||||
|
eff = t.getMetrics().getEfferentSourceCoupling() and
|
||||||
|
aff > 15 and eff > 15
|
||||||
|
select t as Class,
|
||||||
|
"Hub class: this class depends on " + eff.toString() + " classes and is used by " + aff.toString() + " classes."
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p><em>Inappropriate intimacy</em> is an anti-pattern that describes a pair of otherwise unrelated classes
|
||||||
|
that are too tightly coupled: each class uses a significant number of methods and fields of
|
||||||
|
the other. This makes both classes difficult to maintain, change and understand. Inappropriate
|
||||||
|
intimacy is the same as the "feature envy" anti-pattern but in both directions: each class is
|
||||||
|
"envious" of some functionality or data defined in the other class.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>The solution might be as simple as moving some misplaced methods to their rightful place, or
|
||||||
|
perhaps some tangled bits of code need to be extracted to their own methods first before being moved.</p>
|
||||||
|
|
||||||
|
<p>Sometimes the entangled parts (both fields and methods) indicate a
|
||||||
|
missing object or level of abstraction. It might make sense to combine them into a new
|
||||||
|
type that can be used in both classes. You may need to introduce delegation to
|
||||||
|
hide some implementation details.</p>
|
||||||
|
|
||||||
|
<p>It may be necessary to convert the bidirectional association into a
|
||||||
|
unidirectional relationship, possibly by using dependency inversion.</p>
|
||||||
|
|
||||||
|
<p>Modern IDEs provide refactoring support for this sort of issue, usually
|
||||||
|
with the names "Move method", "Extract method" or "Extract class".</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
|
||||||
|
<em>Design patterns: elements of reusable object-oriented software</em>.
|
||||||
|
Addison-Wesley Longman Publishing Co., Inc., Boston, MA, 1995.</li>
|
||||||
|
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 95–96. Addison-Wesley Professional, 2004.</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* @name Inappropriate Intimacy
|
||||||
|
* @description Two otherwise unrelated classes that share too much information about each other are
|
||||||
|
* difficult to maintain, change and understand.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @precision low
|
||||||
|
* @id java/coupled-types
|
||||||
|
* @tags maintainability
|
||||||
|
* modularity
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
predicate enclosingRefType(Variable v, RefType type) {
|
||||||
|
v.(Field).getDeclaringType() = type or
|
||||||
|
v.(LocalVariableDecl).getCallable().getDeclaringType() = type or
|
||||||
|
v.(Parameter).getCallable().getDeclaringType() = type
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate remoteVarAccess(RefType source, RefType target, VarAccess va) {
|
||||||
|
va.getEnclosingCallable().getDeclaringType() = source and
|
||||||
|
enclosingRefType(va.getVariable(), target) and
|
||||||
|
source != target
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate remoteFunAccess(RefType source, RefType target, MethodAccess fc) {
|
||||||
|
fc.getEnclosingCallable().getDeclaringType() = source and
|
||||||
|
fc.getMethod().getDeclaringType() = target and
|
||||||
|
source != target
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate candidateTypePair(RefType source, RefType target) {
|
||||||
|
remoteVarAccess(source, target, _) or remoteFunAccess(source, target, _)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate variableDependencyCount(RefType source, RefType target, int res) {
|
||||||
|
candidateTypePair(source, target) and
|
||||||
|
res = count(VarAccess va | remoteVarAccess(source, target, va))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate functionDependencyCount(RefType source, RefType target, int res) {
|
||||||
|
candidateTypePair(source, target) and
|
||||||
|
res = count(MethodAccess fc | remoteFunAccess(source, target, fc))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate dependencyCount(RefType source, RefType target, int res) {
|
||||||
|
exists(int varCount, int funCount |
|
||||||
|
variableDependencyCount(source, target, varCount) and
|
||||||
|
functionDependencyCount(source, target, funCount) and
|
||||||
|
res = varCount + funCount and
|
||||||
|
res > 20
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from RefType a, RefType b, int ca, int cb
|
||||||
|
where
|
||||||
|
dependencyCount(a, b, ca) and
|
||||||
|
dependencyCount(b, a, cb) and
|
||||||
|
ca > 20 and
|
||||||
|
cb > 20 and
|
||||||
|
ca >= cb and
|
||||||
|
not exists(CompilationUnit cu | cu = a.getCompilationUnit() and cu = b.getCompilationUnit())
|
||||||
|
select a, "Type " + a.getName() + " is too closely tied to $@ (" + ca.toString() +
|
||||||
|
" dependencies one way and " + cb.toString() + " the other).", b, b.getName()
|
||||||
36
java/ql/src/Compatibility/JDK9/JdkInternalAccess.qhelp
Normal file
36
java/ql/src/Compatibility/JDK9/JdkInternalAccess.qhelp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Java 9 removes access to various unsupported JDK-internal APIs by default.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
<p>
|
||||||
|
Examine the use of unsupported JDK-internal APIs and consider replacing them with supported APIs
|
||||||
|
as recommended in the references.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Oracle JDK Documentation:
|
||||||
|
<a href="https://docs.oracle.com/javase/9/migrate/toc.htm">Oracle JDK 9 Migration Guide</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
OpenJDK Documentation:
|
||||||
|
<a href="https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool">Java Dependency Analysis Tool</a>,
|
||||||
|
<a href="http://openjdk.java.net/jeps/260">JEP 260: Encapsulate Most Internal APIs</a>,
|
||||||
|
<a href="http://openjdk.java.net/jeps/261">JEP 261: Module System</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
121
java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql
Normal file
121
java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* @name Access to unsupported JDK-internal API
|
||||||
|
* @description Use of unsupported JDK-internal APIs may cause compatibility issues
|
||||||
|
* when upgrading to newer versions of Java, in particular Java 9.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision high
|
||||||
|
* @id java/jdk-internal-api-access
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
import JdkInternals
|
||||||
|
import JdkInternalsReplacement
|
||||||
|
|
||||||
|
predicate importedType(Import i, RefType t) {
|
||||||
|
i.(ImportType).getImportedType() = t or
|
||||||
|
i.(ImportStaticTypeMember).getTypeHoldingImport() = t or
|
||||||
|
i.(ImportStaticOnDemand).getTypeHoldingImport() = t or
|
||||||
|
i.(ImportOnDemandFromType).getTypeHoldingImport() = t
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate importedPackage(Import i, Package p) {
|
||||||
|
i.(ImportOnDemandFromPackage).getPackageHoldingImport() = p
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate typeReplacement(RefType t, string repl) {
|
||||||
|
exists(string old | jdkInternalReplacement(old, repl) |
|
||||||
|
t.getQualifiedName() = old
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate packageReplacementForType(RefType t, string repl) {
|
||||||
|
exists(string old, string pkgName |
|
||||||
|
jdkInternalReplacement(old, repl) and t.getPackage().getName() = pkgName
|
||||||
|
|
|
||||||
|
pkgName = old or
|
||||||
|
pkgName.prefix(old.length()+1) = old + "."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate packageReplacement(Package p, string repl) {
|
||||||
|
exists(string old | jdkInternalReplacement(old, repl) |
|
||||||
|
p.getName() = old or
|
||||||
|
p.getName().prefix(old.length()+1) = old + "."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate replacement(RefType t, string repl) {
|
||||||
|
typeReplacement(t, repl) or
|
||||||
|
not typeReplacement(t, _) and packageReplacementForType(t, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class JdkInternalAccess extends Element {
|
||||||
|
abstract string getAccessedApi();
|
||||||
|
abstract string getReplacement();
|
||||||
|
}
|
||||||
|
|
||||||
|
class JdkInternalTypeAccess extends JdkInternalAccess, TypeAccess {
|
||||||
|
JdkInternalTypeAccess() {
|
||||||
|
jdkInternalApi(this.getType().(RefType).getPackage().getName())
|
||||||
|
}
|
||||||
|
override string getAccessedApi() {
|
||||||
|
result = getType().(RefType).getQualifiedName()
|
||||||
|
}
|
||||||
|
override string getReplacement() {
|
||||||
|
exists(RefType t | this.getType() = t |
|
||||||
|
(replacement(t, result) or not replacement(t, _) and result = "unknown")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JdkInternalImport extends JdkInternalAccess, Import {
|
||||||
|
JdkInternalImport() {
|
||||||
|
exists(RefType t | importedType(this, t) |
|
||||||
|
jdkInternalApi(t.getPackage().getName())
|
||||||
|
) or
|
||||||
|
exists(Package p | importedPackage(this, p) |
|
||||||
|
jdkInternalApi(p.getName())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override string getAccessedApi() {
|
||||||
|
exists(RefType t | result = t.getQualifiedName() | importedType(this, t)) or
|
||||||
|
exists(Package p | result = p.getName() | importedPackage(this, p))
|
||||||
|
}
|
||||||
|
override string getReplacement() {
|
||||||
|
exists(RefType t |
|
||||||
|
importedType(this, t) and
|
||||||
|
(replacement(t, result) or not replacement(t, _) and result = "unknown")
|
||||||
|
) or
|
||||||
|
exists(Package p |
|
||||||
|
importedPackage(this, p) and
|
||||||
|
(packageReplacement(p, result) or not packageReplacement(p, _) and result = "unknown")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate jdkPackage(Package p) {
|
||||||
|
exists(string pkgName |
|
||||||
|
p.getName() = pkgName or
|
||||||
|
p.getName().prefix(pkgName.length()+1) = pkgName + "."
|
||||||
|
|
|
||||||
|
pkgName = "com.sun" or
|
||||||
|
pkgName = "sun" or
|
||||||
|
pkgName = "java" or
|
||||||
|
pkgName = "javax" or
|
||||||
|
pkgName = "com.oracle.net" or
|
||||||
|
pkgName = "genstubs" or
|
||||||
|
pkgName = "jdk" or
|
||||||
|
pkgName = "build.tools" or
|
||||||
|
pkgName = "org.omg.CORBA" or
|
||||||
|
pkgName = "org.ietf.jgss"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from JdkInternalAccess ta, string repl, string msg
|
||||||
|
where
|
||||||
|
repl = ta.getReplacement() and
|
||||||
|
(if (repl="unknown") then msg = "" else msg = " (" + repl + ")") and
|
||||||
|
not jdkInternalApi(ta.getCompilationUnit().getPackage().getName()) and
|
||||||
|
not jdkPackage(ta.getCompilationUnit().getPackage())
|
||||||
|
select ta, "Access to unsupported JDK-internal API '" + ta.getAccessedApi() + "'." + msg
|
||||||
1036
java/ql/src/Compatibility/JDK9/JdkInternals.qll
Normal file
1036
java/ql/src/Compatibility/JDK9/JdkInternals.qll
Normal file
File diff suppressed because it is too large
Load Diff
54
java/ql/src/Compatibility/JDK9/JdkInternalsReplacement.qll
Normal file
54
java/ql/src/Compatibility/JDK9/JdkInternalsReplacement.qll
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Provides a QL encoding of the suggested replacements for unsupported JDK-internal APIs listed at:
|
||||||
|
*
|
||||||
|
* http://hg.openjdk.java.net/jdk9/jdk9/langtools/file/6ba2130e87bd/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
|
||||||
|
*/
|
||||||
|
|
||||||
|
predicate jdkInternalReplacement(string old, string new) {
|
||||||
|
exists(string r, int eqIdx | jdkInternalReplacement(r) and eqIdx = r.indexOf("=") |
|
||||||
|
old = r.prefix(eqIdx) and
|
||||||
|
new = r.suffix(eqIdx+1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
predicate jdkInternalReplacement(string r) {
|
||||||
|
r = "com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3" or
|
||||||
|
r = "com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6" or
|
||||||
|
r = "com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8" or
|
||||||
|
r = "com.sun.org.apache.xml.internal.resolver=Use javax.xml.catalog @since 9" or
|
||||||
|
r = "com.sun.net.ssl=Use javax.net.ssl @since 1.4" or
|
||||||
|
r = "com.sun.net.ssl.internal.ssl.Provider=Use java.security.Security.getProvider(provider-name) @since 1.3" or
|
||||||
|
r = "com.sun.rowset=Use javax.sql.rowset.RowSetProvider @since 1.7" or
|
||||||
|
r = "com.sun.tools.javac.tree=Use com.sun.source @since 1.6" or
|
||||||
|
r = "com.sun.tools.javac=Use javax.tools and javax.lang.model @since 1.6" or
|
||||||
|
r = "java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739" or
|
||||||
|
r = "java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739" or
|
||||||
|
r = "jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9" or
|
||||||
|
r = "sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9" or
|
||||||
|
r = "sun.font.FontUtilities=See java.awt.Font.textRequiresLayout @since 9" or
|
||||||
|
r = "sun.reflect.Reflection=Use java.lang.StackWalker @since 9" or
|
||||||
|
r = "sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260" or
|
||||||
|
r = "sun.misc.Unsafe=See http://openjdk.java.net/jeps/260" or
|
||||||
|
r = "sun.misc.Signal=See http://openjdk.java.net/jeps/260" or
|
||||||
|
r = "sun.misc.SignalHandler=See http://openjdk.java.net/jeps/260" or
|
||||||
|
r = "sun.security.action=Use java.security.PrivilegedAction @since 1.1" or
|
||||||
|
r = "sun.security.krb5=Use com.sun.security.jgss" or
|
||||||
|
r = "sun.security.provider.PolicyFile=Use java.security.Policy.getInstance(\"JavaPolicy\", new URIParameter(uri)) @since 1.6" or
|
||||||
|
r = "sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3" or
|
||||||
|
r = "sun.security.util.HostnameChecker=Use javax.net.ssl.SSLParameters.setEndpointIdentificationAlgorithm(\"HTTPS\") @since 1.7 or javax.net.ssl.HttpsURLConnection.setHostnameVerifier() @since 1.4" or
|
||||||
|
r = "sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1" or
|
||||||
|
r = "sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4" or
|
||||||
|
r = "sun.tools.jar=Use java.util.jar or jar tool @since 1.2" or
|
||||||
|
// Internal APIs removed in JDK 9
|
||||||
|
r = "com.apple.eawt=Use java.awt.Desktop and JEP 272 @since 9" or
|
||||||
|
r = "com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187" or
|
||||||
|
r = "com.sun.image.codec.jpeg=Use javax.imageio @since 1.4" or
|
||||||
|
r = "sun.awt.image.codec=Use javax.imageio @since 1.4" or
|
||||||
|
r = "sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8" or
|
||||||
|
r = "sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8" or
|
||||||
|
r = "sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9" or
|
||||||
|
r = "sun.misc.Service=Use java.util.ServiceLoader @since 1.6" or
|
||||||
|
r = "sun.misc=Removed. See http://openjdk.java.net/jeps/260" or
|
||||||
|
r = "sun.reflect=Removed. See http://openjdk.java.net/jeps/260"
|
||||||
|
}
|
||||||
34
java/ql/src/Compatibility/JDK9/UnderscoreIdentifier.qhelp
Normal file
34
java/ql/src/Compatibility/JDK9/UnderscoreIdentifier.qhelp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
The underscore character is a reserved keyword in Java 9 and
|
||||||
|
therefore disallowed as a one-character identifier.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
<p>
|
||||||
|
Rename any identifiers that consist of a one-character underscore.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Oracle JDK Documentation:
|
||||||
|
<a href="https://docs.oracle.com/javase/9/migrate/toc.htm">Oracle JDK 9 Migration Guide</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
JDK Bug System:
|
||||||
|
<a href="https://bugs.openjdk.java.net/browse/JDK-8061549">Disallow _ as a one-character identifier</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
33
java/ql/src/Compatibility/JDK9/UnderscoreIdentifier.ql
Normal file
33
java/ql/src/Compatibility/JDK9/UnderscoreIdentifier.ql
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @name Underscore used as identifier
|
||||||
|
* @description Use of a single underscore character as an identifier
|
||||||
|
* results in a compiler error with Java source level 9 or later.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision high
|
||||||
|
* @id java/underscore-identifier
|
||||||
|
* @tags maintainability
|
||||||
|
*/
|
||||||
|
import java
|
||||||
|
|
||||||
|
class IdentifierElement extends Element {
|
||||||
|
IdentifierElement() {
|
||||||
|
this instanceof CompilationUnit or
|
||||||
|
this.(RefType).isSourceDeclaration() or
|
||||||
|
this.(Callable).isSourceDeclaration() or
|
||||||
|
this instanceof Variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from IdentifierElement e, string msg
|
||||||
|
where
|
||||||
|
e.fromSource() and
|
||||||
|
not e.(Constructor).isDefaultConstructor() and
|
||||||
|
(
|
||||||
|
e.getName() = "_" and
|
||||||
|
msg = "."
|
||||||
|
or
|
||||||
|
e.(CompilationUnit).getPackage().getName().splitAt(".") = "_" and
|
||||||
|
msg = " in package name '" + e.(CompilationUnit).getPackage().getName() + "'."
|
||||||
|
)
|
||||||
|
select e, "Use of underscore as a one-character identifier" + msg
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user