@@ -374,12 +374,14 @@ module Make<InlineExpectationsTestSig Impl> {
374374 }
375375
376376 /**
377- * Holds if `location` is the location of an inline expectation comment that
377+ * Holds if `location` is the location of a comment that
378378 * `codeql test run --learn` may rewrite as a whole.
379379 *
380- * A comment is rewritable when it carries at least one parseable expectation, none of its
381- * expectations is unparseable, and none mixes (in a single comma-separated group) a tag this
382- * test understands with a tag it ignores (for example a query ID that does not match the
380+ * A comment is rewritable when it either carries at least one parseable expectation, or is a
381+ * plain comment carrying only a trailing note (see `getTrailingNote`) that a freshly learned
382+ * tag can be merged into (for example `// note` -> `// $ Alert // note`); and in addition none
383+ * of its expectations is unparseable, and none mixes (in a single comma-separated group) a tag
384+ * this test understands with a tag it ignores (for example a query ID that does not match the
383385 * current query). The last condition keeps the rewrite from having to take apart a group such
384386 * as `Alert,Source[other-query]` whose parts this test treats differently; such comments are
385387 * left untouched.
@@ -391,7 +393,14 @@ module Make<InlineExpectationsTestSig Impl> {
391393 */
392394 predicate isRewritableComment ( Impl:: Location location ) {
393395 exists ( Impl:: ExpectationComment comment | comment .getLocation ( ) = location |
394- getAnExpectation ( comment , _, _, _, _) and
396+ (
397+ getAnExpectation ( comment , _, _, _, _)
398+ or
399+ // A plain comment carrying only a note (no expectation of its own) is rewritable too, so
400+ // that a freshly learned tag can be merged into it rather than appended as a second
401+ // comment (for example `// note` -> `// $ Alert // note`).
402+ getTrailingNote ( location , _) and not getAnExpectation ( comment , _, _, _, _)
403+ ) and
395404 not exists ( InvalidTestExpectation invalid | invalid .getLocation ( ) = location ) and
396405 not exists ( string tags , string owned , string ignored |
397406 getAnExpectation ( comment , _, _, tags , _) and
@@ -423,22 +432,32 @@ module Make<InlineExpectationsTestSig Impl> {
423432 }
424433
425434 /**
426- * Holds if the expectation comment at `location` ends with a trailing regular (non-interpreted)
427- * comment `text`, that is a `//` sequence after the expectations whose remainder the framework
428- * treats as an ordinary comment (see `expectationCommentPattern`). `text` includes the trailing
429- * comment's own `//` marker, for example `// note`.
435+ * Holds if the comment at `location` carries a trailing regular (non-interpreted) note `note`,
436+ * with the note's own comment marker and surrounding whitespace stripped. This is either:
430437 *
431- * `codeql test run --learn` keeps this trailing comment when it rewrites or deletes the
432- * expectation comment, so an explanatory note written next to an expectation is not lost. Only
433- * `//` starts such a trailing comment, mirroring `expectationCommentPattern`'s
434- * `(?:[^/]|/[^/])*` expectation region, which ends only at `//`; a `#` never does, so this is in
435- * practice a feature of `//`-comment languages.
438+ * - the text after a `//` that follows the expectations in an expectation comment (for example
439+ * `note` in `// $ Alert // note` or `# $ Alert // note`), which the framework treats as an
440+ * ordinary comment (see `expectationCommentPattern`); or
441+ * - the whole content of a plain comment that carries no expectation at all (for example `note`
442+ * in `// note` or `# note`), into which `--learn` may merge a freshly learned tag.
443+ *
444+ * `codeql test run --learn` keeps this note when it rewrites, deletes, or merges into the
445+ * comment, re-wrapping it with the appropriate markers (`<marker> $ ... // note` when
446+ * expectations remain, or `<marker> note` when none do), so an explanatory note written next to
447+ * code is never lost. Only `//` delimits such a note within an expectation comment, mirroring
448+ * `expectationCommentPattern`'s `(?:[^/]|/[^/])*` expectation region, which ends only at `//`; a
449+ * `#` never does, so `# $ Alert # note` reads `note` as a tag rather than a note.
436450 */
437- predicate getTrailingComment ( Impl:: Location location , string text ) {
438- exists ( Impl:: ExpectationComment comment |
439- comment .getLocation ( ) = location and
440- text = comment .getContents ( ) .regexpCapture ( "\\s*\\$ (?:[^/]|/[^/])*(//.*)" , 1 )
441- )
451+ predicate getTrailingNote ( Impl:: Location location , string note ) {
452+ exists ( Impl:: ExpectationComment comment | comment .getLocation ( ) = location |
453+ note = comment .getContents ( ) .regexpCapture ( "\\s*\\$ (?:[^/]|/[^/])*//(.*)" , 1 ) .trim ( )
454+ or
455+ // A plain comment with no expectation of its own: its whole content is the note.
456+ not getAnExpectation ( comment , _, _, _, _) and
457+ not exists ( InvalidTestExpectation invalid | invalid .getLocation ( ) = location ) and
458+ note = comment .getContents ( ) .trim ( )
459+ ) and
460+ note != ""
442461 }
443462
444463 query predicate testFailures ( FailureLocatable element , string message ) {
@@ -1307,14 +1326,16 @@ module TestPostProcessing {
13071326 or
13081327 endMarker != "" and endSuffix = " " + endMarker
13091328 ) and
1310- // Preserve a trailing regular comment (e.g. `// $ Alert // note`) that sits after the
1311- // expectations, so rewriting the expectations never drops an explanatory note.
1329+ // Preserve a trailing regular note (e.g. the `note` in `// $ Alert // note`) that sits
1330+ // after the expectations, so rewriting the expectations never drops an explanatory note.
1331+ // The inner delimiter is always `//`, which the framework recognises regardless of the
1332+ // outer comment marker (so a `#`-comment file renders `# $ Alert // note`).
13121333 (
1313- exists ( string trailing |
1314- Test:: getTrailingComment ( commentLoc , trailing ) and trailingSuffix = " " + trailing
1334+ exists ( string note |
1335+ Test:: getTrailingNote ( commentLoc , note ) and trailingSuffix = " // " + note
13151336 )
13161337 or
1317- not Test:: getTrailingComment ( commentLoc , _) and trailingSuffix = ""
1338+ not Test:: getTrailingNote ( commentLoc , _) and trailingSuffix = ""
13181339 ) and
13191340 body =
13201341 concat ( string column |
@@ -1352,10 +1373,10 @@ module TestPostProcessing {
13521373 * `SPURIOUS:`, and `MISSING:` columns. Expectations this test ignores (for example a tag
13531374 * annotated with a different query's ID) are preserved verbatim, so the comment keeps any
13541375 * expectation belonging to a different query that shares the same source file; see
1355- * `isRewritableComment` and `Test::getAForeignExpectation`. A trailing regular comment after
1356- * the expectations (for example the ` // note` in `// $ Alert // note`) is likewise preserved;
1357- * see `Test::getTrailingComment`. Only `//` starts such a trailing comment, so this is in
1358- * practice a feature of `//`-comment languages .
1376+ * `isRewritableComment` and `Test::getAForeignExpectation`. A trailing regular note after the
1377+ * expectations (for example the `note` in ` // $ Alert // note` or `# $ Alert // note`) is
1378+ * likewise preserved, and a freshly learned tag can be merged into a plain comment that carries
1379+ * only such a note ( `// note` -> `// $ Alert // note`); see `Test::getTrailingNote` .
13591380 */
13601381 query predicate learnEdits (
13611382 string file , int line , string operation , int startColumn , int endColumn , string text
@@ -1413,12 +1434,17 @@ module TestPostProcessing {
14131434 text = renderLearnedComment ( relativePath , commentLoc )
14141435 or
14151436 not exists ( string column , string t | desiredExpectation ( commentLoc , column , t ) ) and
1416- // No expectation survives, so drop the `$ ...` comment. If it carried a trailing regular
1417- // comment, keep that in place of the whole comment; otherwise delete to the end of line.
1437+ // No expectation survives, so drop the `$ ...` part of the comment. If it carried a
1438+ // trailing regular note, keep that note re-wrapped in the file's own comment marker (so a
1439+ // `#`-comment file yields `# note`, not an invalid `// note`); otherwise delete to the
1440+ // end of line.
14181441 (
1419- Test:: getTrailingComment ( commentLoc , text )
1442+ exists ( string note |
1443+ Test:: getTrailingNote ( commentLoc , note ) and
1444+ text = Input2:: getStartCommentMarker ( relativePath ) + " " + note
1445+ )
14201446 or
1421- not Test:: getTrailingComment ( commentLoc , _) and text = ""
1447+ not Test:: getTrailingNote ( commentLoc , _) and text = ""
14221448 )
14231449 )
14241450 )
0 commit comments