Skip to content

Commit c04e964

Browse files
RasmusWLp0
andauthored
Update ::Range part of CodeQL design patterns
Co-authored-by: Pavel Avgustinov <54942558+p0@users.noreply.github.com>
1 parent f501003 commit c04e964

File tree

1 file changed

+8
-9
lines changed

1 file changed

+8
-9
lines changed

docs/ql-design-patterns.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ A list of design patterns you are recommended to follow.
66

77
To allow both extensibility and refinement of classes, we use what is commonly referred to as the `::Range` pattern (since https://github.com/github/codeql/pull/727), but the actual implementation can use different names.
88

9+
This pattern should be used when you want to model a user-extensible set of values ("extensibility"), while allowing restrictive subclasses, typically for the purposes of overriding predicates ("refinement"). Using a simple `abstract` class gives you the former, but makes it impossible to create overriding methods for all contributing extensions at once. Using a non-`abstract` class provides refinement-based overriding, but requires the original class to range over a closed, non-extensible set.
910
<details>
1011
<summary>Generic example of how to define classes with ::Range</summary>
1112

12-
Instead of
13+
Using a single `abstract` class looks like this:
1314
```ql
1415
/** <QLDoc...> */
1516
abstract class MySpecialExpr extends Expr {
1617
/** <QLDoc...> */
1718
abstract int memberPredicate();
1819
}
19-
```
20-
with
21-
```ql
2220
class ConcreteSubclass extends MySpecialExpr { ... }
2321
```
2422

25-
use
23+
While this allows users of the library to add new types of `MySpecialExpr` (like, in this case, `ConcreteSubclass), there is no way to override the implementations of `memberPredicate` of all extensions at once.
24+
25+
Applying the `::Range` pattern yields the following:
2626

2727
```ql
2828
/**
@@ -54,10 +54,9 @@ module MySpecialExpr {
5454
}
5555
}
5656
```
57-
with
58-
```ql
59-
class ConcreteSubclass extends MySpecialExpr::Range { ... }
60-
```
57+
Now, a concrete subclass can derive from `MySpecialExpr::Range` if it wants to extend the set of values in `MySpecialExpr`, and it will be required to implement the abstract `memberPredicate()`. Conversely, if it wants to refine `MySpecialExpr` and override `memberPredicate` for all extensions, it can do so by deriving from `MySpecialExpr` directly.
58+
59+
The key element of the pattern is to provide a field of type `MySpecialExpr::Range`, equating it to `this` in the characteristic predicate of `MySpecialExpr`. In member predicates, we can use either `this` or `range`, depending on which type has the API we need.
6160

6261
</details>
6362

0 commit comments

Comments
 (0)