mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #2152 from yh-semmle/java-alert-suppression-annotations
Java: support LGTM alert suppression using `@SuppressWarnings` annotations
This commit is contained in:
96
java/ql/src/AlertSuppressionAnnotations.ql
Normal file
96
java/ql/src/AlertSuppressionAnnotations.ql
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @name Alert suppression using annotations
|
||||
* @description Generates information about alert suppressions
|
||||
* using 'SuppressWarnings' annotations.
|
||||
* @kind alert-suppression
|
||||
* @id java/alert-suppression-annotations
|
||||
*/
|
||||
|
||||
import java
|
||||
import Metrics.Internal.Extents
|
||||
|
||||
/** Gets the LGTM suppression annotation text in the string `s`, if any. */
|
||||
bindingset[s]
|
||||
string getAnnotationText(string s) {
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
result = s.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* An alert suppression annotation.
|
||||
*/
|
||||
class SuppressionAnnotation extends SuppressWarningsAnnotation {
|
||||
string text;
|
||||
|
||||
SuppressionAnnotation() {
|
||||
text = this.getASuppressedWarningLiteral().getValue() and
|
||||
exists(getAnnotationText(text))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text of this suppression annotation.
|
||||
*/
|
||||
string getText() { result = text }
|
||||
|
||||
private Annotation getASiblingAnnotation() {
|
||||
result = getAnnotatedElement().(Annotatable).getAnAnnotation() and
|
||||
(getAnnotatedElement() instanceof Callable or getAnnotatedElement() instanceof RefType)
|
||||
}
|
||||
|
||||
private Annotation firstAnnotation() {
|
||||
result = min(this.getASiblingAnnotation() as m
|
||||
order by
|
||||
m.getLocation().getStartLine(), m.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this annotation 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) {
|
||||
if firstAnnotation().hasLocationInfo(filepath, _, _, _, _)
|
||||
then
|
||||
getAnnotatedElement().hasLocationInfo(filepath, _, _, endline, endcolumn) and
|
||||
firstAnnotation().hasLocationInfo(filepath, startline, startcolumn, _, _)
|
||||
else getAnnotatedElement().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the scope of this suppression. */
|
||||
SuppressionScope getScope() { this = result.getSuppressionAnnotation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope of an alert suppression annotation.
|
||||
*/
|
||||
class SuppressionScope extends @annotation {
|
||||
SuppressionScope() { this instanceof SuppressionAnnotation }
|
||||
|
||||
/** Gets a suppression annotation with this scope. */
|
||||
SuppressionAnnotation getSuppressionAnnotation() { 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
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.(SuppressionAnnotation).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "suppression range" }
|
||||
}
|
||||
|
||||
from SuppressionAnnotation c, string text, string annotationText
|
||||
where
|
||||
text = c.getText() and
|
||||
annotationText = getAnnotationText(text)
|
||||
select c, // suppression entity
|
||||
text, // full text of suppression string
|
||||
annotationText, // LGTM suppression annotation text
|
||||
c.getScope() // scope of suppression
|
||||
@@ -1,15 +1,19 @@
|
||||
import java
|
||||
|
||||
/*
|
||||
/**
|
||||
* Provides classes that modify the default location information
|
||||
* of `RefType`s and `Callable`s to cover their full extent.
|
||||
*
|
||||
* When this library is imported, the `hasLocationInfo` predicate of
|
||||
* `Callable` and `RefTypes` is overridden to specify their entire range
|
||||
* instead of just the range of their name. The latter can still be
|
||||
* obtained by invoking the `getLocation()` predicate.
|
||||
*
|
||||
* The full ranges are required for the purpose of associating a violation
|
||||
* with an individual `Callable` or `RefType` as opposed to a whole `File`.
|
||||
* The full ranges may be used to determine whether a given location
|
||||
* (for example, the location of an alert) is contained within the extent
|
||||
* of a `Callable` or `RefType`.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* A Callable whose `hasLocationInfo` is overridden to specify its entire range
|
||||
* including the body (if any), as opposed to the location of its name only.
|
||||
@@ -48,20 +52,9 @@ class RangeRefType extends RefType {
|
||||
}
|
||||
|
||||
private Member lastMember() {
|
||||
exists(Member m, int i |
|
||||
result = m and
|
||||
m = getAMember() and
|
||||
i = rankOfMember(m) and
|
||||
not exists(Member other | other = getAMember() and rankOfMember(other) > i)
|
||||
)
|
||||
}
|
||||
|
||||
private int rankOfMember(Member m) {
|
||||
this.getAMember() = m and
|
||||
exists(Location mLoc, File f, int maxCol | mLoc = m.getLocation() |
|
||||
f = mLoc.getFile() and
|
||||
maxCol = max(Location loc | loc.getFile() = f | loc.getStartColumn()) and
|
||||
result = mLoc.getStartLine() * maxCol + mLoc.getStartColumn()
|
||||
)
|
||||
result = max(this.getAMember() as m
|
||||
order by
|
||||
m.getLocation().getStartLine(), m.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ class OverrideAnnotation extends Annotation {
|
||||
class SuppressWarningsAnnotation extends Annotation {
|
||||
SuppressWarningsAnnotation() { this.getType().hasQualifiedName("java.lang", "SuppressWarnings") }
|
||||
|
||||
/** Gets the `StringLiteral` of a warning suppressed by this annotation. */
|
||||
StringLiteral getASuppressedWarningLiteral() {
|
||||
result = this.getAValue() or
|
||||
result = this.getAValue().(ArrayInit).getAnInit()
|
||||
}
|
||||
|
||||
/** Gets the name of a warning suppressed by this annotation. */
|
||||
string getASuppressedWarning() {
|
||||
result = this.getAValue().(StringLiteral).getLiteral() or
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
| TestSuppressWarnings.java:2:1:2:49 | SuppressWarnings | lgtm[java/non-sync-override] | lgtm[java/non-sync-override] | TestSuppressWarnings.java:2:1:21:5 | suppression range |
|
||||
| TestSuppressWarnings.java:5:5:5:31 | SuppressWarnings | lgtm[] | lgtm[] | TestSuppressWarnings.java:5:5:8:5 | suppression range |
|
||||
| TestSuppressWarnings.java:10:5:10:104 | SuppressWarnings | lgtm[java/confusing-method-name] not confusing | lgtm[java/confusing-method-name] | TestSuppressWarnings.java:9:5:13:5 | suppression range |
|
||||
| TestSuppressWarnings.java:10:5:10:104 | SuppressWarnings | lgtm[java/non-sync-override] | lgtm[java/non-sync-override] | TestSuppressWarnings.java:9:5:13:5 | suppression range |
|
||||
| TestSuppressWarnings.java:18:5:18:98 | SuppressWarnings | lgtm[java/confusing-method-name] blah blah lgtm[java/non-sync-override] | lgtm[java/confusing-method-name] | TestSuppressWarnings.java:18:5:21:5 | suppression range |
|
||||
| TestSuppressWarnings.java:18:5:18:98 | SuppressWarnings | lgtm[java/confusing-method-name] blah blah lgtm[java/non-sync-override] | lgtm[java/non-sync-override] | TestSuppressWarnings.java:18:5:21:5 | suppression range |
|
||||
@@ -0,0 +1 @@
|
||||
AlertSuppressionAnnotations.ql
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
@SuppressWarnings("lgtm[java/non-sync-override]")
|
||||
@Deprecated
|
||||
class TestSuppressWarnings {
|
||||
@SuppressWarnings("lgtm[]")
|
||||
public void test() {
|
||||
|
||||
}
|
||||
@Deprecated
|
||||
@SuppressWarnings({"lgtm[java/confusing-method-name] not confusing","lgtm[java/non-sync-override]"})
|
||||
public void test2() {
|
||||
|
||||
}
|
||||
@SuppressWarnings("lgtm")
|
||||
public void test3() {
|
||||
|
||||
}
|
||||
@SuppressWarnings({"lgtm[java/confusing-method-name] blah blah lgtm[java/non-sync-override]"})
|
||||
public void test4() {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user