diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index 937c7bc61010..b3bbf41aaefe 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -2538,6 +2538,18 @@ private class NoOptArg extends AnnotationArg { NoOptArg() { this.getValue() = "noopt" } } +private class CallerArg extends AnnotationArg { + CallerArg() { this.getValue() = "caller" } +} + +private class LocalArg extends AnnotationArg { + LocalArg() { this.getValue() = "local" } +} + +private class LocalQArg extends AnnotationArg { + LocalQArg() { this.getValue() = "local?" } +} + private class MonotonicAggregatesArg extends AnnotationArg { MonotonicAggregatesArg() { this.getValue() = "monotonicAggregates" } } @@ -2597,6 +2609,27 @@ class NoOpt extends Annotation { override string toString() { result = "noopt" } } +/** An `overlay[caller]` annotation. */ +class OverlayCaller extends Annotation { + OverlayCaller() { this.getName() = "overlay" and this.getArgs(0) instanceof CallerArg } + + override string toString() { result = "caller" } +} + +/** An `overlay[local]` annotation. */ +class OverlayLocal extends Annotation { + OverlayLocal() { this.getName() = "overlay" and this.getArgs(0) instanceof LocalArg } + + override string toString() { result = "local" } +} + +/** An `overlay[local?]` annotation. */ +class OverlayLocalQ extends Annotation { + OverlayLocalQ() { this.getName() = "overlay" and this.getArgs(0) instanceof LocalQArg } + + override string toString() { result = "local?" } +} + /** A `language[monotonicAggregates]` annotation. */ class MonotonicAggregates extends Annotation { MonotonicAggregates() { this.getArgs(0) instanceof MonotonicAggregatesArg } diff --git a/ql/ql/src/queries/overlay/InlineOverlayCaller.ql b/ql/ql/src/queries/overlay/InlineOverlayCaller.ql new file mode 100644 index 000000000000..d27a0ade9bbf --- /dev/null +++ b/ql/ql/src/queries/overlay/InlineOverlayCaller.ql @@ -0,0 +1,41 @@ +/** + * @name Cannot inline predicate across overlay frontier + * @description Local inline predicates that are not annotated with `overlay[caller]` are + * not inlined across the overlay frontier. This may negatively affect performance. + * @kind problem + * @problem.severity warning + * @id ql/inline-overlay-caller + * @tags performance + * @precision high + */ + +import ql + +predicate mayBeLocal(AstNode n) { + n.getAnAnnotation() instanceof OverlayLocal + or + n.getAnAnnotation() instanceof OverlayLocalQ + or + // The tree-sitter-ql grammar doesn't handle annotations on file-level + // module declarations correctly. To work around that, we consider any + // node in a file that contains an overlay[local] or overlay[local?] + // annotation to be potentially local. + exists(AstNode m | + n.getLocation().getFile() = m.getLocation().getFile() and + mayBeLocal(m) + ) +} + +from Predicate p +where + mayBeLocal(p) and + p.getAnAnnotation() instanceof Inline and + not p.getAnAnnotation() instanceof OverlayCaller and + not p.isPrivate() +select p, + "This possibly local non-private inline predicate will not " + + "be inlined across the overlay frontier. This may negatively " + + "affect evaluation performance. Consider adding an " + + "`overlay[caller]` annotation to allow inlining across the " + + "overlay frontier. Note that adding an `overlay[caller]` " + + "annotation affects semantics under overlay evaluation."