@@ -78,7 +78,7 @@ To define a class, you write:
78
78
#. The keyword ``class ``.
79
79
#. The name of the class. This is an `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers >`_
80
80
starting with an uppercase letter.
81
- #. The types to extend.
81
+ #. The supertypes that the class is derived from via ` extends ` and/or ` instanceof `
82
82
#. The :ref: `body of the class <class-bodies >`, enclosed in braces.
83
83
84
84
For example:
@@ -106,12 +106,14 @@ This defines a class ``OneTwoThree``, which contains the values ``1``, ``2``, an
106
106
.. index :: extends
107
107
108
108
``OneTwoThree `` extends ``int ``, that is, it is a subtype of ``int ``. A class in QL must always
109
- extend at least one existing type. Those types are called the ** base types ** of the class. The
110
- values of a class are contained within the intersection of the base types (that is, they are in
111
- the :ref: `class domain type <domain-types >`). A class inherits all member predicates from its
112
- base types.
109
+ have at least one supertype. Supertypes that are referenced with the ` extends ` keyword are called
110
+ the ** base types ** of the class. The values of a class are contained within the intersection of
111
+ the supertypes (that is, they are in the :ref: `class domain type <domain-types >`).
112
+ A class inherits all member predicates from its base types.
113
113
114
114
A class can extend multiple types. For more information, see ":ref: `multiple-inheritance `."
115
+ Classes can also specialise other types without extending the class interface via `instanceof `,
116
+ see ":ref: `instanceof-extensions `.".
115
117
116
118
To be valid, a class:
117
119
- Must not extend itself.
@@ -228,7 +230,7 @@ Concrete classes
228
230
229
231
The classes in the above examples are all **concrete ** classes. They are defined by
230
232
restricting the values in a larger type. The values in a concrete class are precisely those
231
- values in the intersection of the base types that also satisfy the
233
+ values in the intersection of the supertypes that also satisfy the
232
234
:ref: `characteristic predicate <characteristic-predicates >` of the class.
233
235
234
236
.. _abstract-classes :
@@ -380,6 +382,59 @@ from ``OneTwoThree`` and ``int``.
380
382
must :ref: `override <overriding-member-predicates >` those definitions to avoid ambiguity.
381
383
:ref: `Super expressions <super >` are often useful in this situation.
382
384
385
+ .. _instanceof-extensions :
386
+
387
+ Non-extending subtypes
388
+ ======================
389
+
390
+ Besides extending base types, classes can also declare `instanceof ` relationships with other types.
391
+ Declaring a class as `instanceof Foo ` is roughly equivalent to saying `this instanceof Foo ` in the characteristic predicate.
392
+ The main differences are that you can call methods on Bar via `super ` and you can get better optimisation.
393
+
394
+ .. code-block :: ql
395
+
396
+ class Foo extends int {
397
+ Foo() { this in [1 .. 10] }
398
+
399
+ string foo_method() { result = "foo" }
400
+ }
401
+
402
+ class Bar instanceof Foo {
403
+ string toString() { result = super.foo_method() }
404
+ }
405
+
406
+ In this example, the characteristic predicate from `Foo ` also applies to `Bar `.
407
+ However, `foo_method ` is not exposed in `Bar `, so the query `select any(Bar b).foo_method() `
408
+ results in a compile time error. Note from the example that it is still possible to access
409
+ methods from instanceof supertypes from within the specialising class with the `super ` keyword.
410
+
411
+ Crucially, the instanceof **supertypes ** are not **base types **.
412
+ This means that these supertypes do not participate in overriding, and any fields of such
413
+ supertypes are not part of the new class.
414
+ This has implications on method resolution when complex class hierarchies are involved.
415
+ The following example demonstrates this.
416
+
417
+ .. code-block :: ql
418
+
419
+ class Interface extends int {
420
+ Interface() { this in [1 .. 10] }
421
+ string foo() { result = "" }
422
+ }
423
+
424
+ class Foo extends int {
425
+ Foo() { this in [1 .. 5] }
426
+ string foo() { result = "foo" }
427
+ }
428
+
429
+ class Bar extends Interface instanceof Foo {
430
+ override string foo() { result = "bar" }
431
+ }
432
+
433
+ Here, the method `Bar::foo ` does not override `Foo::foo `.
434
+ Instead, it overrides only `Interface::foo `.
435
+ This means that `select any(Foo f).foo() ` yields only `foo `.
436
+ Had `Bar ` been defined as `extends Foo `, then `select any(Foo b) ` would yield `bar `.
437
+
383
438
.. _character-types :
384
439
.. _domain-types :
385
440
0 commit comments