Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions java/change-notes/2021-09-13-javadoc-type-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lgtm,codescanning
* The query `java/unknown-javadoc-parameter` now raises alerts for type parameters of generic classes that are documented but don't exist (perhaps due to using the wrong syntax, e.g. `@param T` instead of `@param <T>`).
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ public void html(int ordinate){ ... }
*/
public <T> void parameterized(T parameter){ ... }

/**
* BAD: The following param tag refers to a non-existent type parameter.
*
* @param <X> The type of the elements.
*/
class Generic<T> { ...}

/**
* GOOD: A proper Javadoc comment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

<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
Javadoc comments for public methods, constructors and generic classes should use the <code>@param</code> tag to describe the available
parameters and type 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>
<p>The Javadoc comment for a method, constructor or generic class should always use non-empty <code>@param</code> values that match actual parameter or type parameter names.</p>

</recommendation>
<example>
Expand Down
33 changes: 23 additions & 10 deletions java/ql/src/Advisory/Documentation/SpuriousJavadocParam.ql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @name Spurious Javadoc @param tags
* @description Javadoc @param tags that do not match any parameters in the method or constructor are confusing.
* @description Javadoc @param tags that do not match any parameters in the method or constructor or
* any type parameters of the annotated class are confusing.
* @kind problem
* @problem.severity recommendation
* @precision very-high
Expand All @@ -10,21 +11,33 @@

import java

from Callable callable, ParamTag paramTag, string what, string msg
from Documentable documentable, ParamTag paramTag, string msg
where
callable.(Documentable).getJavadoc().getAChild() = paramTag and
(if callable instanceof Constructor then what = "constructor" else what = "method") and
documentable.getJavadoc().getAChild() = paramTag 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 |
documentable instanceof Callable and
exists(string what |
if documentable instanceof Constructor then what = "constructor" else what = "method"
|
// The tag's value is neither matched by a callable parameter name ...
not documentable.(Callable).getAParameter().getName() = paramTag.getParamName() and
// ... nor by a type parameter name.
not exists(TypeVariable tv | tv.getGenericCallable() = documentable |
"<" + tv.getName() + ">" = paramTag.getParamName()
) and
msg =
"@param tag \"" + paramTag.getParamName() + "\" does not match any actual parameter of " +
what + " \"" + documentable.getName() + "()\"."
)
or
documentable instanceof ClassOrInterface and
not exists(TypeVariable tv | tv.getGenericType() = documentable |
"<" + tv.getName() + ">" = paramTag.getParamName()
) and
msg =
"@param tag \"" + paramTag.getParamName() + "\" does not match any actual parameter of " +
what + " \"" + callable.getName() + "()\"."
"@param tag \"" + paramTag.getParamName() +
"\" does not match any actual type parameter of type \"" + documentable.getName() + "\"."
else
// The tag has no value at all.
msg = "This @param tag does not have a value."
Expand Down
14 changes: 14 additions & 0 deletions java/ql/test/query-tests/SpuriousJavadocParam/Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,18 @@ class SomeClass {
*/
SomeClass(int i, int j) {}
}

/**
* @param <T> exists
* @param T wrong syntax
* @param <X> does not exist
*/
class GenericClass<T> {}

/**
* @param <T> exists
* @param T wrong syntax
* @param <X> does not exist
*/
interface GenericInterface<T> {}
}
4 changes: 4 additions & 0 deletions java/ql/test/query-tests/SpuriousJavadocParam/test.expected
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
| Test.java:91:6:91:12 | @param | This @param tag does not have a value. |
| Test.java:97:6:97:12 | @param | This @param tag does not have a value. |
| Test.java:104:8:104:14 | @param | @param tag "k" does not match any actual parameter of constructor "SomeClass()". |
| Test.java:111:6:111:12 | @param | @param tag "T" does not match any actual type parameter of type "GenericClass". |
| Test.java:112:6:112:12 | @param | @param tag "<X>" does not match any actual type parameter of type "GenericClass". |
| Test.java:118:6:118:12 | @param | @param tag "T" does not match any actual type parameter of type "GenericInterface". |
| Test.java:119:6:119:12 | @param | @param tag "<X>" does not match any actual type parameter of type "GenericInterface". |