diff --git a/java/ql/lib/change-notes/2022-09-20-CompilationUnit-simple-name-type.md b/java/ql/lib/change-notes/2022-09-20-CompilationUnit-simple-name-type.md new file mode 100644 index 00000000000..1ce21f6e180 --- /dev/null +++ b/java/ql/lib/change-notes/2022-09-20-CompilationUnit-simple-name-type.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Added the predicate `CompilationUnit.getATypeAvailableBySimpleName()`. diff --git a/java/ql/lib/semmle/code/java/CompilationUnit.qll b/java/ql/lib/semmle/code/java/CompilationUnit.qll index 9b4b58e9a9b..621750b9098 100644 --- a/java/ql/lib/semmle/code/java/CompilationUnit.qll +++ b/java/ql/lib/semmle/code/java/CompilationUnit.qll @@ -31,5 +31,50 @@ class CompilationUnit extends Element, File { */ Module getModule() { cumodule(this, result) } + /** + * Gets a type which is available by its simple name in this compilation unit. + * Reasons for this can be: + * - The type is declared in this compilation unit as top-level type + * - The type is imported + * - The type is declared in the same package as this compilation unit + * - The type is declared in the package `java.lang` + */ + ClassOrInterface getATypeAvailableBySimpleName() { + // See "Shadowing", https://docs.oracle.com/javase/specs/jls/se17/html/jls-6.html#jls-6.4.1 + // Note: Currently the logic below does not consider shadowing and might have multiple results + // with the same type name + result.(TopLevelType).getCompilationUnit() = this + or + exists(ImportStaticTypeMember importDecl | + importDecl.getCompilationUnit() = this and + result = importDecl.getATypeImport() + ) + or + exists(ImportType importDecl | + importDecl.getCompilationUnit() = this and + result = importDecl.getImportedType() + ) + or + exists(ImportStaticOnDemand importDecl | + importDecl.getCompilationUnit() = this and + result = importDecl.getATypeImport() + ) + or + exists(ImportOnDemandFromType importDecl | + importDecl.getCompilationUnit() = this and + result = importDecl.getAnImport() + ) + or + exists(ImportOnDemandFromPackage importDecl | + importDecl.getCompilationUnit() = this and + result = importDecl.getAnImport() + ) + or + // From same package or java.lang, see https://docs.oracle.com/javase/specs/jls/se17/html/jls-7.html + result.(TopLevelType).getPackage() = this.getPackage() + or + result.(TopLevelType).getPackage().hasName("java.lang") + } + override string getAPrimaryQlClass() { result = "CompilationUnit" } } diff --git a/java/ql/src/Advisory/Documentation/ImpossibleJavadocThrows.ql b/java/ql/src/Advisory/Documentation/ImpossibleJavadocThrows.ql index 7e2738af1e1..1a5862bb9e8 100644 --- a/java/ql/src/Advisory/Documentation/ImpossibleJavadocThrows.ql +++ b/java/ql/src/Advisory/Documentation/ImpossibleJavadocThrows.ql @@ -11,22 +11,18 @@ import java -RefType getTaggedType(ThrowsTag tag) { +Class getTaggedType(ThrowsTag tag) { result.hasName(tag.getExceptionName()) and - exists(ImportType i | i.getFile() = tag.getFile() | i.getImportedType() = result) + result = tag.getFile().(CompilationUnit).getATypeAvailableBySimpleName() } -predicate canThrow(Callable callable, RefType exception) { - exists(string uncheckedException | - uncheckedException = "RuntimeException" or uncheckedException = "Error" - | - exception.getAnAncestor().hasQualifiedName("java.lang", uncheckedException) - ) +predicate canThrow(Callable callable, Class exception) { + exception instanceof UncheckedThrowableType or callable.getAnException().getType().getADescendant() = exception } -from ThrowsTag throwsTag, RefType thrownType, Callable docMethod +from ThrowsTag throwsTag, Class thrownType, Callable docMethod where getTaggedType(throwsTag) = thrownType and docMethod.getDoc().getJavadoc().getAChild*() = throwsTag and