Merge pull request #6520 from smowton/smowton/feature/allow-local-interfaces

Java: Allow local interfaces
This commit is contained in:
Chris Smowton
2021-09-03 12:01:36 +01:00
committed by GitHub
18 changed files with 2083 additions and 78 deletions

View File

@@ -440,10 +440,10 @@ isAnonymClass(
int parent: @classinstancexpr ref
);
#keyset[classid] #keyset[parent]
isLocalClass(
int classid: @class ref,
int parent: @localclassdeclstmt ref
#keyset[typeid] #keyset[parent]
isLocalClassOrInterface(
int typeid: @classorinterface ref,
int parent: @localtypedeclstmt ref
);
isDefConstr(
@@ -526,7 +526,7 @@ case @stmt.kind of
| 15 = @labeledstmt
| 16 = @assertstmt
| 17 = @localvariabledeclstmt
| 18 = @localclassdeclstmt
| 18 = @localtypedeclstmt
| 19 = @constructorinvocationstmt
| 20 = @superconstructorinvocationstmt
| 21 = @case

View File

@@ -169,7 +169,7 @@
<v>223232</v>
</e>
<e>
<k>@localclassdeclstmt</k>
<k>@localtypedeclstmt</k>
<v>349</v>
</e>
<e>
@@ -16910,11 +16910,11 @@
</dependencies>
</relation>
<relation>
<name>isLocalClass</name>
<name>isLocalClassOrInterface</name>
<cardinality>349</cardinality>
<columnsizes>
<e>
<k>classid</k>
<k>typeid</k>
<v>349</v>
</e>
<e>
@@ -16924,7 +16924,7 @@
</columnsizes>
<dependencies>
<dep>
<src>classid</src>
<src>typeid</src>
<trg>parent</trg>
<val>
<hist>
@@ -16941,7 +16941,7 @@
</dep>
<dep>
<src>parent</src>
<trg>classid</trg>
<trg>typeid</trg>
<val>
<hist>
<budget>12</budget>

View File

@@ -456,7 +456,7 @@ private module ControlFlowGraphImpl {
or
this instanceof EmptyStmt
or
this instanceof LocalClassDeclStmt
this instanceof LocalTypeDeclStmt
or
this instanceof AssertStmt
}

View File

@@ -40,12 +40,12 @@ class Member extends Element, Annotatable, Modifiable, @member {
/**
* Gets the immediately enclosing callable, if this member is declared in
* an anonymous or local class.
* an anonymous or local class or interface.
*/
Callable getEnclosingCallable() {
exists(NestedClass nc | this.getDeclaringType() = nc |
nc.(AnonymousClass).getClassInstanceExpr().getEnclosingCallable() = result or
nc.(LocalClass).getLocalClassDeclStmt().getEnclosingCallable() = result
exists(NestedType nt | this.getDeclaringType() = nt |
nt.(AnonymousClass).getClassInstanceExpr().getEnclosingCallable() = result or
nt.(LocalClassOrInterface).getLocalTypeDeclStmt().getEnclosingCallable() = result
)
}
}

View File

@@ -877,8 +877,8 @@ private class PpLocalVariableDeclStmt extends PpAst, LocalVariableDeclStmt {
}
}
private class PpLocalClassDeclStmt extends PpAst, LocalClassDeclStmt {
override PpAst getChild(int i) { i = 0 and result = this.getLocalClass() }
private class PpLocalTypeDeclStmt extends PpAst, LocalTypeDeclStmt {
override PpAst getChild(int i) { i = 0 and result = this.getLocalType() }
}
/*

View File

@@ -303,19 +303,25 @@ final class ClassInstanceExprNode extends ExprStmtNode {
}
/**
* A node representing a `LocalClassDeclStmt`.
* A node representing a `LocalTypeDeclStmt`.
*/
final class LocalClassDeclStmtNode extends ExprStmtNode {
LocalClassDeclStmtNode() { element instanceof LocalClassDeclStmt }
final class LocalTypeDeclStmtNode extends ExprStmtNode {
LocalTypeDeclStmtNode() { element instanceof LocalTypeDeclStmt }
override ElementNode getChild(int childIndex) {
result = super.getChild(childIndex)
or
childIndex = 0 and
result.getElement() = element.(LocalClassDeclStmt).getLocalClass()
result.getElement() = element.(LocalTypeDeclStmt).getLocalType()
}
}
/**
* DEPRECATED: Renamed `LocalTypeDeclStmtNode` to reflect the fact that
* as of Java 16 interfaces can also be declared locally, not just classes.
*/
deprecated class LocalClassDeclStmtNode = LocalTypeDeclStmtNode;
/**
* A node representing a `ForStmt`.
*/

View File

@@ -786,20 +786,38 @@ class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt {
override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" }
}
/** A statement that declares a local class. */
class LocalClassDeclStmt extends Stmt, @localclassdeclstmt {
/** Gets the local class declared by this statement. */
LocalClass getLocalClass() { isLocalClass(result, this) }
/** A statement that declares a local class or interface. */
class LocalTypeDeclStmt extends Stmt, @localtypedeclstmt {
/** Gets the local type declared by this statement. */
LocalClassOrInterface getLocalType() { isLocalClassOrInterface(result, this) }
override string pp() { result = "class " + this.getLocalClass().toString() }
/**
* DEPRECATED: Renamed `getLocalType` to reflect the fact that
* as of Java 16 interfaces can also be declared locally, not just classes.
*/
deprecated LocalClassOrInterface getLocalClass() { result = this.getLocalType() }
override string toString() { result = "class ..." }
private string getDeclKeyword() {
result = "class" and this.getLocalType() instanceof Class
or
result = "interface" and this.getLocalType() instanceof Interface
}
override string getHalsteadID() { result = "LocalClassDeclStmt" }
override string pp() { result = this.getDeclKeyword() + " " + this.getLocalType().toString() }
override string getAPrimaryQlClass() { result = "LocalClassDeclStmt" }
override string toString() { result = this.getDeclKeyword() + " ..." }
override string getHalsteadID() { result = "LocalTypeDeclStmt" }
override string getAPrimaryQlClass() { result = "LocalTypeDeclStmt" }
}
/**
* DEPRECATED: Renamed `LocalTypeDeclStmt` to reflect the fact that
* as of Java 16 interfaces can also be declared locally, not just classes.
*/
deprecated class LocalClassDeclStmt = LocalTypeDeclStmt;
/** An explicit `this(...)` constructor invocation. */
class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructorinvocationstmt {
/** Gets an argument of this constructor invocation. */

View File

@@ -6,8 +6,8 @@
* (`Interface`).
*
* Reference types can be at the top level (`TopLevelType`) or nested (`NestedType`).
* Classes can also be local (`LocalClass`) or anonymous (`AnonymousClass`).
* Enumerated types (`EnumType`) are a special kind of class.
* Classes and interfaces can also be local (`LocalClassOrInterface`, `LocalClass`) or anonymous (`AnonymousClass`).
* Enumerated types (`EnumType`) and records (`Record`) are special kinds of classes.
*/
import Member
@@ -269,7 +269,7 @@ predicate declaresMember(Type t, @member m) {
// Since the type `@member` in the dbscheme includes all `@reftype`s,
// anonymous and local classes need to be excluded here.
not m instanceof AnonymousClass and
not m instanceof LocalClass
not m instanceof LocalClassOrInterface
}
/**
@@ -608,20 +608,10 @@ class SrcRefType extends RefType {
}
/** A class declaration. */
class Class extends RefType, @class {
class Class extends ClassOrInterface, @class {
/** Holds if this class is an anonymous class. */
predicate isAnonymous() { isAnonymClass(this, _) }
/** Holds if this class is a local class. */
predicate isLocal() { isLocalClass(this, _) }
/** Holds if this class is package protected, that is, neither public nor private nor protected. */
predicate isPackageProtected() {
not isPrivate() and
not isProtected() and
not isPublic()
}
override RefType getSourceDeclaration() { classes(this, _, _, result) }
/**
@@ -630,11 +620,13 @@ class Class extends RefType, @class {
* Note that a class may inherit annotations from super-classes.
*/
override Annotation getAnAnnotation() {
result = RefType.super.getAnAnnotation()
result = ClassOrInterface.super.getAnAnnotation()
or
exists(AnnotationType tp | tp = result.getType() |
tp.isInherited() and
not exists(Annotation ann | ann = RefType.super.getAnAnnotation() | ann.getType() = tp) and
not exists(Annotation ann | ann = ClassOrInterface.super.getAnAnnotation() |
ann.getType() = tp
) and
result = this.getASupertype().(Class).getAnAnnotation()
)
}
@@ -643,8 +635,6 @@ class Class extends RefType, @class {
}
/**
* PREVIEW FEATURE in Java 14. Subject to removal in a future release.
*
* A record declaration.
*/
class Record extends Class {
@@ -727,12 +717,25 @@ class AnonymousClass extends NestedClass {
override string getAPrimaryQlClass() { result = "AnonymousClass" }
}
/** A local class. */
class LocalClass extends NestedClass {
LocalClass() { this.isLocal() }
/** A local class or interface. */
class LocalClassOrInterface extends NestedType, ClassOrInterface {
LocalClassOrInterface() { this.isLocal() }
/** Gets the statement that declares this local class. */
LocalClassDeclStmt getLocalClassDeclStmt() { isLocalClass(this, result) }
LocalTypeDeclStmt getLocalTypeDeclStmt() { isLocalClassOrInterface(this, result) }
/**
* DEPRECATED: renamed `getLocalTypeDeclStmt` to reflect the fact that
* as of Java 16 interfaces can also be declared locally.
*/
deprecated LocalTypeDeclStmt getLocalClassDeclStmt() { result = this.getLocalTypeDeclStmt() }
override string getAPrimaryQlClass() { result = "LocalClassOrInterface" }
}
/** A local class. */
class LocalClass extends LocalClassOrInterface, NestedClass {
LocalClass() { this.isLocal() }
override string getAPrimaryQlClass() { result = "LocalClass" }
}
@@ -842,12 +845,12 @@ class InnerClass extends NestedClass {
predicate hasEnclosingInstance() {
// JLS 15.9.2. Determining Enclosing Instances
not this.(AnonymousClass).getClassInstanceExpr().isInStaticContext() and
not this.(LocalClass).getLocalClassDeclStmt().getEnclosingCallable().isStatic()
not this.(LocalClass).getLocalTypeDeclStmt().getEnclosingCallable().isStatic()
}
}
/** An interface. */
class Interface extends RefType, @interface {
class Interface extends ClassOrInterface, @interface {
override RefType getSourceDeclaration() { interfaces(this, _, _, result) }
override predicate isAbstract() {
@@ -855,21 +858,19 @@ class Interface extends RefType, @interface {
any()
}
/** Holds if this interface is package protected, that is, neither public nor private nor protected. */
predicate isPackageProtected() {
not isPrivate() and
not isProtected() and
not isPublic()
}
override string getAPrimaryQlClass() { result = "Interface" }
}
/** A class or interface. */
class ClassOrInterface extends RefType {
ClassOrInterface() {
this instanceof Class or
this instanceof Interface
class ClassOrInterface extends RefType, @classorinterface {
/** Holds if this class or interface is local. */
predicate isLocal() { isLocalClassOrInterface(this, _) }
/** Holds if this class or interface is package protected, that is, neither public nor private nor protected. */
predicate isPackageProtected() {
not isPrivate() and
not isProtected() and
not isPublic()
}
}

View File

@@ -238,7 +238,7 @@ private module SsaImpl {
/** Gets the definition point of a nested class in the parent scope. */
private ControlFlowNode parentDef(NestedClass nc) {
nc.(AnonymousClass).getClassInstanceExpr() = result or
nc.(LocalClass).getLocalClassDeclStmt() = result
nc.(LocalClass).getLocalTypeDeclStmt() = result
}
/**

View File

@@ -79,7 +79,7 @@ private module SsaImpl {
/** Gets the definition point of a nested class in the parent scope. */
private ControlFlowNode parentDef(NestedClass nc) {
nc.(AnonymousClass).getClassInstanceExpr() = result or
nc.(LocalClass).getLocalClassDeclStmt() = result
nc.(LocalClass).getLocalTypeDeclStmt() = result
}
/**

View File

@@ -234,10 +234,10 @@ abstract class BusinessInterface extends Interface {
abstract SessionEJB getAnEJB();
/** Holds if this business interface is declared local. */
abstract predicate isLocal();
abstract predicate isDeclaredLocal();
/** Holds if this business interface is declared remote. */
abstract predicate isRemote();
abstract predicate isDeclaredRemote();
}
/**
@@ -259,14 +259,14 @@ class XmlSpecifiedBusinessInterface extends BusinessInterface {
)
}
override predicate isLocal() {
override predicate isDeclaredLocal() {
exists(EjbJarXMLFile f |
this.getQualifiedName() =
f.getASessionElement().getABusinessLocalElement().getACharactersSet().getCharacters()
)
}
override predicate isRemote() {
override predicate isDeclaredRemote() {
exists(EjbJarXMLFile f |
this.getQualifiedName() =
f.getASessionElement().getABusinessRemoteElement().getACharactersSet().getCharacters()
@@ -295,9 +295,9 @@ class AnnotatedBusinessInterface extends BusinessInterface {
result.getAnAnnotation().(BusinessInterfaceAnnotation).getANamedType() = this
}
override predicate isLocal() { this instanceof LocalAnnotatedBusinessInterface }
override predicate isDeclaredLocal() { this instanceof LocalAnnotatedBusinessInterface }
override predicate isRemote() { this instanceof RemoteAnnotatedBusinessInterface }
override predicate isDeclaredRemote() { this instanceof RemoteAnnotatedBusinessInterface }
}
/**
@@ -540,7 +540,7 @@ class XmlSpecifiedLocalHomeInterface extends LegacyEjbLocalHomeInterface {
class RemoteInterface extends Interface {
RemoteInterface() {
this instanceof RemoteAnnotatedBusinessInterface or
this.(XmlSpecifiedBusinessInterface).isRemote() or
this.(XmlSpecifiedBusinessInterface).isDeclaredRemote() or
exists(SessionEJB ejb | this = ejb.getARemoteInterface())
}

View File

@@ -37,8 +37,8 @@ where
// Remove local classes defined in the dead method - they are reported separately as a dead
// class. We keep anonymous class counts, because anonymous classes are not reported
// separately.
sum(LocalClass localClass |
localClass.getLocalClassDeclStmt().getEnclosingCallable() = deadMethod
sum(LocalClassOrInterface localClass |
localClass.getLocalTypeDeclStmt().getEnclosingCallable() = deadMethod
|
localClass.getNumberOfLinesOfCode()
)

View File

@@ -122,8 +122,8 @@ where
not abortsControlFlow(s) and
// Exclude the double semicolon case `if (cond) s;;`.
not t instanceof EmptyStmt and
// `LocalClassDeclStmt`s yield false positives since their `Location` doesn't include the `class` keyword.
not t instanceof LocalClassDeclStmt
// `LocalTypeDeclStmt`s yield false positives since their `Location` doesn't include the `class` keyword.
not t instanceof LocalTypeDeclStmt
select s,
"Indentation suggests that $@ belongs to $@, but this is not the case; consider adding braces or adjusting indentation.",
t, "the next statement", c, "the control structure"

View File

@@ -11,7 +11,7 @@ import java
abstract private class GeneratedType extends ClassOrInterface {
GeneratedType() {
not this instanceof AnonymousClass and
not this instanceof LocalClass and
not this.isLocal() and
not this.getPackage() instanceof ExcludedPackage
}