Skip to content

Commit cf74387

Browse files
d10cCopilot
andcommitted
Merge a learned tag into an existing comment on the same line
When `--learn` discovers an unexpected `Alert` result on a line that already carries a rewritable expectation comment, fold the new tag into that comment instead of appending a second `// $ ...` comment beside it. This keeps one comment per line and, in particular, lets a freshly learned tag join an expectation that belongs to a different query sharing the source file (e.g. `// $ Alert[q/other]` becomes `// $ Alert Alert[q/other]`). `mergedNewTag` feeds the learned default-column tag into `desiredExpectation`, so the existing whole-comment rewrite renders it alongside the surviving and foreign expectations. The append disjunct in `learnEdits` is now gated on there being no rewritable comment on the result's line, so append and merge are mutually exclusive: a line with a rewritable comment is always merged, never double-commented. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent b7364b2 commit cf74387

1 file changed

Lines changed: 58 additions & 15 deletions

File tree

‎shared/util/codeql/util/test/InlineExpectationsTest.qll‎

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,40 @@ module TestPostProcessing {
11541154
)
11551155
}
11561156

1157+
/**
1158+
* Holds if some non-optional `Alert` result with no matching expectation fires on `line` of
1159+
* `relativePath` -- an *unexpected result* that `--learn` should record with a new `Alert`
1160+
* expectation, either by appending a fresh comment or by merging into an existing one.
1161+
*/
1162+
private predicate hasUnexpectedAlertOnLine(string relativePath, int line) {
1163+
exists(Test::ActualTestResult actualResult |
1164+
actualResult.getTag() = "Alert" and
1165+
actualResult.getValue() = "" and
1166+
not actualResult.isOptional() and
1167+
not exists(
1168+
Test::getAMatchingExpectation(actualResult.getLocation(), actualResult.toString(),
1169+
actualResult.getTag(), actualResult.getValue(), false)
1170+
) and
1171+
parseLocationString(actualResult.getLocation().getRelativeUrl(), relativePath, _, _, line, _)
1172+
)
1173+
}
1174+
1175+
/**
1176+
* Holds if `--learn` should merge a freshly learned `Alert` expectation into the existing,
1177+
* rewritable comment at `commentLoc` (in `column` `""`, the default), because an unexpected
1178+
* `Alert` result fires on that comment's line. Merging keeps the new tag alongside the
1179+
* comment's existing expectations rather than appending a second comment to the line.
1180+
*/
1181+
private predicate mergedNewTag(TestLocation commentLoc, string column, string text) {
1182+
exists(string relativePath, int line |
1183+
Test::isRewritableComment(commentLoc) and
1184+
parseLocationString(commentLoc.getRelativeUrl(), relativePath, line, _, _, _) and
1185+
hasUnexpectedAlertOnLine(relativePath, line) and
1186+
column = "" and
1187+
text = "Alert"
1188+
)
1189+
}
1190+
11571191
/**
11581192
* Holds if, after `--learn`, the inline expectation comment at `commentLoc` should carry the
11591193
* expectation `text` in `column` (`""` for the default column, or a named column such as
@@ -1162,12 +1196,14 @@ module TestPostProcessing {
11621196
* This combines the surviving expectations this test understands (see `learnedExpectation`)
11631197
* with the expectations it ignores (see `Test::getAForeignExpectation`), which are preserved
11641198
* verbatim so that rewriting a comment for one query never drops another query's expectation on
1165-
* the same line.
1199+
* the same line, and with any freshly learned tag merged into the comment (see `mergedNewTag`).
11661200
*/
11671201
private predicate desiredExpectation(TestLocation commentLoc, string column, string text) {
11681202
learnedExpectation(commentLoc, column, text)
11691203
or
11701204
Test::getAForeignExpectation(commentLoc, column, text)
1205+
or
1206+
mergedNewTag(commentLoc, column, text)
11711207
}
11721208

11731209
/**
@@ -1272,29 +1308,32 @@ module TestPostProcessing {
12721308
*
12731309
* The following edits are emitted:
12741310
*
1275-
* - an actual result with no matching expectation gets a new `// $ Alert` comment appended
1276-
* (an *unexpected result*); and
1277-
* - an existing expectation comment that this test fully owns is rewritten as a whole so that
1278-
* it matches the current results: obsolete default and `// $ SPURIOUS:` expectations are
1279-
* dropped (a *missing result* or a *fixed spurious result*), a `// $ MISSING:` expectation
1280-
* whose result now fires is promoted to the default column (a *fixed missing result*), and
1281-
* the surviving expectations are re-rendered. If nothing survives, the comment is deleted.
1311+
* - an actual result with no matching expectation records a new `Alert` expectation (an
1312+
* *unexpected result*): if the result's line already has a rewritable comment the tag is
1313+
* merged into it (see below), otherwise a fresh `// $ Alert` comment is appended; and
1314+
* - an existing rewritable expectation comment is rewritten as a whole so that it matches the
1315+
* current results: obsolete default and `// $ SPURIOUS:` expectations are dropped (a *missing
1316+
* result* or a *fixed spurious result*), a `// $ MISSING:` expectation whose result now fires
1317+
* is promoted to the default column (a *fixed missing result*), a freshly learned tag on the
1318+
* line is merged in, and the resulting expectations are re-rendered. If nothing remains, the
1319+
* comment is deleted.
12821320
*
12831321
* The rewrite handles comments that carry several expectations across the default,
12841322
* `SPURIOUS:`, and `MISSING:` columns. Expectations this test ignores (for example a tag
12851323
* annotated with a different query's ID) are preserved verbatim, so the comment keeps any
12861324
* expectation belonging to a different query that shares the same source file; see
1287-
* `isRewritableComment` and `Test::getAForeignExpectation`. Appending a new tag by merging it
1288-
* into an existing comment rather than adding a separate one is left for a follow-up.
1325+
* `isRewritableComment` and `Test::getAForeignExpectation`.
12891326
*/
12901327
query predicate learnEdits(
12911328
string file, int line, string operation, int startColumn, int endColumn, string text
12921329
) {
1293-
// Unexpected result: append a new `// $ Alert` comment on the alert's line. The comment
1294-
// must go on the result's *end* line, because an expectation matches a result when the
1295-
// expectation's start line equals the result's end line (see `onSameLine`). For most
1296-
// languages a result spans a single line, but some (e.g. Rust) include leading trivia in
1297-
// the location, so the start and end lines differ.
1330+
// Unexpected result with no comment to merge into: append a new `// $ Alert` comment on the
1331+
// alert's line. The comment must go on the result's *end* line, because an expectation
1332+
// matches a result when the expectation's start line equals the result's end line (see
1333+
// `onSameLine`). For most languages a result spans a single line, but some (e.g. Rust)
1334+
// include leading trivia in the location, so the start and end lines differ. If the line
1335+
// already has a rewritable comment, the tag is merged into it by the rewrite disjunct below
1336+
// (see `mergedNewTag`) rather than appended as a separate comment.
12981337
exists(Test::ActualTestResult actualResult, string relativePath, int el, string comment |
12991338
actualResult.getTag() = "Alert" and
13001339
actualResult.getValue() = "" and
@@ -1304,6 +1343,10 @@ module TestPostProcessing {
13041343
actualResult.getTag(), actualResult.getValue(), false)
13051344
) and
13061345
parseLocationString(actualResult.getLocation().getRelativeUrl(), relativePath, _, _, el, _) and
1346+
not exists(TestLocation existing |
1347+
Test::isRewritableComment(existing) and
1348+
parseLocationString(existing.getRelativeUrl(), relativePath, el, _, _, _)
1349+
) and
13071350
comment = renderExpectationComment(relativePath, "Alert") and
13081351
file = relativePath and
13091352
line = el and

0 commit comments

Comments
 (0)