You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/ql-design-patterns.md
+7-15Lines changed: 7 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -31,13 +31,9 @@ Applying the `::Range` pattern yields the following:
31
31
* Extend this class to refine existing API models. If you want to model new APIs,
32
32
* extend `MySpecialExpr::Range` instead.
33
33
*/
34
-
class MySpecialExpr extends Expr {
35
-
MySpecialExpr::Range range;
36
-
37
-
MySpecialExpr() { this = range }
38
-
34
+
class MySpecialExpr extends Expr instanceof MySpecialExpr::Range {
39
35
/** <QLDoc...> */
40
-
int memberPredicate() { result = range.memberPredicate() }
36
+
int memberPredicate() { result = super.memberPredicate() }
41
37
}
42
38
43
39
/** Provides a class for modeling new <...> APIs. */
@@ -56,22 +52,18 @@ module MySpecialExpr {
56
52
```
57
53
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
54
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.
60
-
61
55
</details>
62
56
63
-
Note that in some libraries, the `range` field is in fact called `self`. While we do recommend using `range` for consistency, the name of the field does not matter (and using `range` avoids confusion in contexts like Python analysis that has strong usage of `self`).
64
-
65
57
### Rationale
66
58
67
-
Let's use an example from the Go libraries: https://github.com/github/codeql-go/blob/2ba9bbfd8ba1818b5ee9f6009c86a605189c9ef3/ql/src/semmle/go/Concepts.qll#L119-L157
59
+
Let's use an example from the Python libraries: https://github.com/github/codeql/blob/46751e515c40c6b4c9b61758cc840eec1894a624/python/ql/lib/semmle/python/Concepts.qll#L601-L683
68
60
69
-
`EscapeFunction`, as the name suggests, models various APIs that escape meta-characters. It has a member-predicate `kind()` that tells you what sort of escaping the modelled function does. For example, if the result of that predicate is `"js"`, then this means that the escaping function is meant to make things safe to embed inside JavaScript.
70
-
`EscapeFunction::Range` is subclassed to model various APIs, and `kind()` is implemented accordingly.
71
-
But we can also subclass `EscapeFunction` to, as in the above example, talk about all JS-escaping functions.
61
+
`Escaping`, as the name suggests, models various APIs that escape meta-characters. It has a member-predicate `getKind()` that tells you what sort of escaping the modeled function does. For example, if the result of that predicate is `"html"`, then this means that the escaping function is meant to make things safe to embed inside HTML.
62
+
`Escaping::Range` is subclassed to model various APIs, and `kind()` is implemented accordingly (this typically happens in library models).
63
+
But we can also subclass `Escaping`, as in the above example, where `HtmlEscaping` represents all HTML-escaping functions.
72
64
73
65
You can, of course, do the same without the `::Range` pattern, but it's a little cumbersome:
74
-
If you only had an `abstract class EscapeFunction { ... }`, then `JsEscapeFunction` would need to be implemented in a slightly tricky way to prevent it from extending `EscapeFunction` (instead of refining it). You would have to give it a charpred `this instanceof EscapeFunction`, which looks useless but isn't. And additionally, you'd have to provide trivial `none()` overrides of all the abstract predicates defined in `EscapeFunction`. This is all pretty awkward, and we can avoid it by distinguishing between `EscapeFunction` and `EscapeFunction::Range`.
66
+
If you only had an `abstract class Escaping { ... }`, then `HtmlEscaping` would need to be implemented in a slightly tricky way to prevent it from extending `Escaping` (instead of refining it). You would have to give it a charpred `this instanceof Escaping`, which looks useless but isn't. And additionally, you'd have to provide trivial `none()` overrides of all the abstract predicates defined in `Escaping`. This is all pretty awkward, and we can avoid it by distinguishing between `Escaping` and `Escaping::Range`.
0 commit comments