Migrate Java code to separate QL repo.

This commit is contained in:
Pavel Avgustinov
2018-08-30 10:48:05 +01:00
parent d957c151a6
commit 846c9d5860
2319 changed files with 134386 additions and 0 deletions

12
java/ql/src/.project Normal file
View 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
View 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>

View 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

View File

@@ -0,0 +1,8 @@
{
"ql.projects" : {
"." : {
"dbScheme" : "config/semmlecode.dbscheme",
"libraryPath" : []
}
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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()

View File

@@ -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>

View 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."

View 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>

View 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."

View File

@@ -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>

View File

@@ -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()

View File

@@ -0,0 +1,8 @@
/**
* Javadoc for method.
*
* @throws Exception if a problem occurs.
*/
public void noThrow() {
System.out.println("This method does not throw.");
}

View File

@@ -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>

View File

@@ -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."

View File

@@ -0,0 +1,6 @@
/**
* Javadoc for method.
*/
public void noThrow() {
System.out.println("This method does not throw.");
}

View 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)
)
)
}
}

View File

@@ -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];
}
}

View File

@@ -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>

View 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."

View File

@@ -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>

View File

@@ -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."

View File

@@ -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>

View File

@@ -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."

View File

@@ -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>

View 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()

View File

@@ -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 {
// ...

View 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>

View 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."

View 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){ ... }

View File

@@ -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>

View 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

View File

@@ -0,0 +1,7 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="AvoidCloneableInterface.qhelp" /></qhelp>

View 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."

View File

@@ -0,0 +1,7 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="AvoidCloneableInterface.qhelp" /></qhelp>

View 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."

View File

@@ -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());
}
// ...
}

View File

@@ -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>

View 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."

View File

@@ -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>

View 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."

View File

@@ -0,0 +1,8 @@
import java
class ConstantField extends Field {
ConstantField() {
this.isStatic() and
this.isFinal()
}
}

View 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>

View 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."

View 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>

View 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."

View 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>

View 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."

View 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>

View 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."

View 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>

View 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."

View 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
}

View 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>

View 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."

View File

@@ -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;
}

View 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>

View 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."

View File

@@ -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);

View File

@@ -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>

View 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."

View File

@@ -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);

View File

@@ -0,0 +1,9 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="Generics_Common.qhelp" />
</qhelp>

View 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."

View File

@@ -0,0 +1,9 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="Generics_Common.qhelp" />
</qhelp>

View 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."

View File

@@ -0,0 +1,9 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="Generics_Common.qhelp" />
</qhelp>

View 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."

View 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)
}

View 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>

View 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)
}

View 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

View 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());
}
}
}

View 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&lt;T2&gt;</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>

View 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()

View File

@@ -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);
}
}

View File

@@ -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)
)
}

View File

@@ -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>

View File

@@ -0,0 +1,6 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="UnusedMavenDependency.qhelp" />
</qhelp>

View File

@@ -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."

View File

@@ -0,0 +1,6 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="UnusedMavenDependency.qhelp" />
</qhelp>

View File

@@ -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."

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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."

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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&ndash;94. Addison-Wesley Professional, 2004.
</li>
</references>
</qhelp>

View File

@@ -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()

View File

@@ -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>

View File

@@ -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."

View File

@@ -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&ndash;96. Addison-Wesley Professional, 2004.</li>
</references>
</qhelp>

View File

@@ -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()

View 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>

View 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

File diff suppressed because it is too large Load Diff

View 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"
}

View 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>

View 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