Skip to content

Commit dbc95ca

Browse files
committed
language reference entry for non-extending subtypes
1 parent b4963c7 commit dbc95ca

File tree

1 file changed

+54
-1
lines changed

1 file changed

+54
-1
lines changed

docs/codeql/ql-language-reference/types.rst

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ To define a class, you write:
7878
#. The keyword ``class``.
7979
#. The name of the class. This is an `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
8080
starting with an uppercase letter.
81-
#. The types to extend.
81+
#. The base types that the class is derived from via `extends` and/or `instanceof`
8282
#. The :ref:`body of the class <class-bodies>`, enclosed in braces.
8383

8484
For example:
@@ -112,6 +112,8 @@ the :ref:`class domain type <domain-types>`). A class inherits all member predic
112112
base types.
113113

114114
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`.".
115117

116118
To be valid, a class:
117119
- Must not extend itself.
@@ -380,6 +382,57 @@ from ``OneTwoThree`` and ``int``.
380382
must :ref:`override <overriding-member-predicates>` those definitions to avoid ambiguity.
381383
:ref:`Super expressions <super>` are often useful in this situation.
382384

385+
386+
.. _instanceof-extensions:
387+
388+
Non-extending subtypes
389+
======================
390+
391+
Besides extending base types, classes can also declare `instanceof` relationships with other types.
392+
393+
.. code-block:: ql
394+
class Foo extends int {
395+
Foo() { this in [1 .. 10] }
396+
397+
string foo_method() { result = "foo" }
398+
}
399+
400+
class Bar extends int instanceof Foo {
401+
string bar_method() { result = super.foo_method() }
402+
}
403+
404+
In this example, the characteristic predicate from `Foo` also applies to `Bar`.
405+
However, `foo_method` is not exposed in `Bar`, so the query `select any(Bar b).foo_method()`
406+
results in a compile time error. Note from the example that it is still possible to access
407+
methods from instanceof supertypes from within the specialising class with the `super` keyword.
408+
409+
Crucially, the base class methods are not just hidden.
410+
Instead, the extension relationship is sewered.
411+
This has deep implications on method resolution when complex class hierarchies are involved.
412+
The following example demonstrates this.
413+
414+
415+
.. code-block:: ql
416+
class Interface extends int {
417+
Interface() { this in [1 .. 100] }
418+
string foo() { result = "" }
419+
}
420+
421+
class Foo extends Interface {
422+
Foo() { this in [1 .. 10] }
423+
override string foo() { result = "foo" }
424+
}
425+
426+
class Bar extends Interface instanceof Foo {
427+
override string foo() { result = "bar" }
428+
}
429+
430+
Here, the method `Bar::foo` does not override `Foo::foo`.
431+
Instead, it overrides only `Interface::foo`.
432+
This means that `select any(Foo b).foo()` yields only `foo`.
433+
Had `bar been defined as `extends Foo`, then `select any(Foo b).foo()` would yield `bar`.
434+
435+
383436
.. _character-types:
384437
.. _domain-types:
385438

0 commit comments

Comments
 (0)