diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/.gitignore b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/.gitignore new file mode 100644 index 00000000000..3b4d77eb40d --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/.gitignore @@ -0,0 +1,3 @@ +*.testproj/ +*.actual +*.log diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/README.md b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/README.md new file mode 100644 index 00000000000..0acd82f4b42 --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/README.md @@ -0,0 +1,35 @@ +# Rust upgrade preservation test + +This directory contains a manual regression test for the Rust dbscheme upgrade from rust-analyzer 0.0.301 to 0.0.328. + +It is **not yet checked by CI**. The test has to be run manually because it requires building an old extractor to create an old-schema database first. + +## Directory structure + +- `old/`: Test query for the **old** schema, showing fields that will be upgraded +- `new/`: Test query and sources for the **new** schema after upgrade + +## Running the test + +From anywhere in the repository: + +```bash +rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/run-test.sh +``` + +Or override the old commit if needed: + +```bash +OLD_COMMIT= rust/ql/lib/upgrades/.../test/run-test.sh +``` + +The script will: +1. Copy test files to a temp directory +2. Stash uncommitted changes and checkout the old commit +3. Build the old extractor with `bazel run //rust:install` +4. Create an old-schema database with `codeql test run` +5. Restore your branch and pop the stash +6. Upgrade the database to the new schema +7. Run the preservation test on the upgraded database + +If the expected output needs to be refreshed after an intentional query change, manually run the final `codeql test run` with `--learn`. diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.expected b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.expected new file mode 100644 index 00000000000..6adf4a878be --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.expected @@ -0,0 +1,20 @@ +formatArgNamePreserved +| upgrade_shapes.rs:22:37:22:41 | FormatArgsArg | upgrade_shapes.rs:22:37:22:37 | FormatArgsArgName | upgrade_shapes.rs:22:41:22:41 | 1 | 37 | 1 | +| upgrade_shapes.rs:22:44:22:48 | FormatArgsArg | upgrade_shapes.rs:22:44:22:44 | FormatArgsArgName | upgrade_shapes.rs:22:48:22:48 | 2 | 44 | 2 | +tryBlockModifierPreserved +| upgrade_shapes.rs:21:13:21:21 | { ... } | upgrade_shapes.rs:21:13:21:21 | TryBlockModifier | +structFieldDefaultPreserved +| upgrade_shapes.rs:9:5:9:17 | field: u8 | upgrade_shapes.rs:9:17:9:17 | ConstArg | upgrade_shapes.rs:9:17:9:17 | 1 | +variantDiscriminantPreserved +| upgrade_shapes.rs:13:5:13:9 | V | upgrade_shapes.rs:13:9:13:9 | ConstArg | upgrade_shapes.rs:13:9:13:9 | 2 | +pathMetaPreserved +| upgrade_shapes.rs:4:3:4:11 | PathMeta | path_meta | +| upgrade_shapes.rs:7:3:7:19 | PathMeta | path_meta | +keyValueMetaPreserved +| upgrade_shapes.rs:5:3:5:15 | KeyValueMeta | key_value | upgrade_shapes.rs:5:15:5:15 | 1 | +tokenTreeMetaPreserved +| upgrade_shapes.rs:6:3:6:18 | TokenTreeMeta | token_tree | upgrade_shapes.rs:6:13:6:18 | TokenTree | +unsafeMetaPreserved +| upgrade_shapes.rs:7:3:7:19 | UnsafeMeta | upgrade_shapes.rs:7:3:7:19 | PathMeta | path_meta | +traitAliasPreserved +| upgrade_shapes.rs:16:1:18:12 | trait Alias | upgrade_shapes.rs:16:7:16:11 | Alias | upgrade_shapes.rs:16:18:16:22 | ... | upgrade_shapes.rs:17:1:18:11 | WhereClause | diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.ql b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.ql new file mode 100644 index 00000000000..bd983a8290c --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/UpgradeShapes.ql @@ -0,0 +1,73 @@ +import codeql.rust.elements + +private predicate inUpgradeShapesFile(Locatable loc) { + loc.getFile().getBaseName() = "upgrade_shapes.rs" +} + +query predicate formatArgNamePreserved( + FormatArgsArg arg, FormatArgsArgName argName, Expr expr, int argNameColumn, string exprText +) { + inUpgradeShapesFile(arg) and + argName = arg.getArgName() and + expr = arg.getExpr() and + argNameColumn = argName.getLocation().getStartColumn() and + exprText = expr.toString() +} + +query predicate tryBlockModifierPreserved(BlockExpr block, TryBlockModifier modifier) { + inUpgradeShapesFile(block) and + modifier = block.getTryBlockModifier() and + modifier.isTry() and + not modifier.hasTypeRepr() +} + +query predicate structFieldDefaultPreserved(StructField field, ConstArg defaultVal, Expr expr) { + inUpgradeShapesFile(field) and + defaultVal = field.getDefaultVal() and + expr = defaultVal.getExpr() +} + +query predicate variantDiscriminantPreserved(Variant variant, ConstArg constArg, Expr expr) { + inUpgradeShapesFile(variant) and + constArg = variant.getConstArg() and + expr = constArg.getExpr() +} + +query predicate pathMetaPreserved(PathMeta meta, string pathText) { + inUpgradeShapesFile(meta) and + pathText = meta.getPath().getText() and + pathText = "path_meta" +} + +query predicate keyValueMetaPreserved(KeyValueMeta meta, string pathText, Expr expr) { + inUpgradeShapesFile(meta) and + pathText = meta.getPath().getText() and + pathText = "key_value" and + expr = meta.getExpr() +} + +query predicate tokenTreeMetaPreserved(TokenTreeMeta meta, string pathText, TokenTree tokenTree) { + inUpgradeShapesFile(meta) and + pathText = meta.getPath().getText() and + pathText = "token_tree" and + tokenTree = meta.getTokenTree() +} + +query predicate unsafeMetaPreserved(UnsafeMeta meta, PathMeta inner, string pathText) { + inUpgradeShapesFile(meta) and + meta.isUnsafe() and + inner = meta.getMeta() and + pathText = inner.getPath().getText() and + pathText = "path_meta" +} + +query predicate traitAliasPreserved( + Trait trait, Name name, TypeBoundList bounds, WhereClause whereClause +) { + inUpgradeShapesFile(trait) and + name = trait.getName() and + name.getText() = "Alias" and + bounds = trait.getTypeBoundList() and + whereClause = trait.getWhereClause() and + not trait.hasAssocItemList() +} diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/qlpack.yml b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/qlpack.yml new file mode 100644 index 00000000000..81a7b0a2503 --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/qlpack.yml @@ -0,0 +1,6 @@ +name: codeql/rust-upgrade-66a489863649185f4a9770f894505803059a1312-test-new +version: 0.0.0 +dependencies: + codeql/rust-all: ${workspace} +extractor: rust +tests: . diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/upgrade_shapes.rs b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/upgrade_shapes.rs new file mode 100644 index 00000000000..e64d8f6582c --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/new/upgrade_shapes.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] +#![feature(more_qualified_paths)] + +#[path_meta] +#[key_value = 1] +#[token_tree(list)] +#[unsafe(path_meta)] +struct S { + field: u8 = 1, +} + +enum E { + V = 2, +} + +trait Alias = Clone +where + T: Copy; + +fn f() { + let _ = try { 1 }; + let _ = format_args!("{b} {a}", a = 1, b = 2); +} diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.expected b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.expected new file mode 100644 index 00000000000..8cb5c2511a4 --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.expected @@ -0,0 +1,22 @@ +formatArgsArgName +| upgrade_shapes.rs:22:31:22:31 | a | upgrade_shapes.rs:22:35:22:35 | 1 | a | +| upgrade_shapes.rs:22:38:22:38 | b | upgrade_shapes.rs:22:42:22:42 | 2 | b | +metaExpr +| upgrade_shapes.rs:5:1:5:15 | #[key_value = 1] | upgrade_shapes.rs:5:15:5:15 | 1 | +metaIsUnsafe +| upgrade_shapes.rs:7:1:7:21 | #[unsafe(path_meta)] | +metaPath +| upgrade_shapes.rs:4:1:4:12 | #[path_meta] | path_meta | +| upgrade_shapes.rs:5:1:5:15 | #[key_value = 1] | key_value | +| upgrade_shapes.rs:6:1:6:19 | #[token_tree(list)] | token_tree | +| upgrade_shapes.rs:7:1:7:21 | #[unsafe(path_meta)] | path_meta | +metaTokenTree +| upgrade_shapes.rs:6:1:6:19 | #[token_tree(list)] | upgrade_shapes.rs:6:13:6:18 | (list) | +structFieldDefault +| upgrade_shapes.rs:9:5:9:17 | field: u8 = 1 | upgrade_shapes.rs:9:17:9:17 | 1 | +traitAlias +| upgrade_shapes.rs:16:1:18:12 | TraitAlias | Alias | upgrade_shapes.rs:16:18:16:23 | = Clone | upgrade_shapes.rs:17:1:18:12 | where... | +tryBlock +| upgrade_shapes.rs:21:13:21:23 | try { 1 } | +variantDiscriminant +| upgrade_shapes.rs:13:5:13:9 | V = 2 | upgrade_shapes.rs:13:9:13:9 | 2 | diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.ql b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.ql new file mode 100644 index 00000000000..ced2690b7db --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/old/OldShapes.ql @@ -0,0 +1,56 @@ +import codeql.rust.elements + +private predicate inUpgradeShapesFile(Locatable loc) { + loc.getFile().getBaseName() = "upgrade_shapes.rs" +} + +query predicate formatArgsArgName(FormatArgsArg arg, Name argName, Expr expr, string nameText) { + inUpgradeShapesFile(arg) and + argName = arg.getName() and + expr = arg.getExpr() and + nameText = argName.getText() +} + +query predicate tryBlock(BlockExpr block) { + inUpgradeShapesFile(block) and + block.isTry() +} + +query predicate structFieldDefault(StructField field, Expr defaultVal) { + inUpgradeShapesFile(field) and + defaultVal = field.getDefault() +} + +query predicate variantDiscriminant(Variant variant, Expr discriminant) { + inUpgradeShapesFile(variant) and + discriminant = variant.getDiscriminant() +} + +query predicate metaPath(Meta meta, string pathText) { + inUpgradeShapesFile(meta) and + pathText = meta.getPath().getText() +} + +query predicate metaExpr(Meta meta, Expr expr) { + inUpgradeShapesFile(meta) and + expr = meta.getExpr() +} + +query predicate metaTokenTree(Meta meta, TokenTree tokenTree) { + inUpgradeShapesFile(meta) and + tokenTree = meta.getTokenTree() +} + +query predicate metaIsUnsafe(Meta meta) { + inUpgradeShapesFile(meta) and + meta.isUnsafe() +} + +query predicate traitAlias( + TraitAlias alias, Name name, TypeBoundList bounds, WhereClause whereClause +) { + inUpgradeShapesFile(alias) and + name = alias.getName() and + bounds = alias.getTypeBoundList() and + whereClause = alias.getWhereClause() +} diff --git a/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/run-test.sh b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/run-test.sh new file mode 100755 index 00000000000..93e79ff4daa --- /dev/null +++ b/rust/ql/lib/upgrades/66a489863649185f4a9770f894505803059a1312/test/run-test.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Manual regression test for the Rust dbscheme upgrade from rust-analyzer 0.0.301 to 0.0.328. +# See README.md for details. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" +OLD_COMMIT="${OLD_COMMIT:-491c373e076}" # origin/main at time of this upgrade + +cd "$REPO_ROOT" + +echo "==> Setting up temp directory for old test..." +OLD_TEST_TMP=$(mktemp -d) +trap 'rm -rf "$OLD_TEST_TMP"' EXIT + +# Copy old test query and new test sources (qlpack, upgrade_shapes.rs) to temp +cp "$SCRIPT_DIR/old/OldShapes.ql" "$SCRIPT_DIR/old/OldShapes.expected" "$OLD_TEST_TMP/" +cp "$SCRIPT_DIR/new/qlpack.yml" "$SCRIPT_DIR/new/upgrade_shapes.rs" "$OLD_TEST_TMP/" + +echo "==> Stashing any uncommitted changes..." +git stash --quiet || true + +restore_branch() { + echo "==> Restoring original branch..." + git checkout --quiet - + git stash pop --quiet 2>/dev/null || true +} +trap 'restore_branch; rm -rf "$OLD_TEST_TMP"' EXIT + +echo "==> Checking out old commit ($OLD_COMMIT)..." +git checkout --quiet "$OLD_COMMIT" + +echo "==> Building old extractor (this may take a while)..." +bazel run //rust:install + +echo "==> Creating old-schema test database..." +rm -rf "$OLD_TEST_TMP"/*.testproj +codeql test run \ + --search-path . \ + --keep-databases \ + "$OLD_TEST_TMP/OldShapes.ql" + +echo "==> Copying dataset for upgrade..." +cp -a "$OLD_TEST_TMP/test.testproj/db-rust" "$OLD_TEST_TMP/upgraded-dataset" + +restore_branch +trap 'rm -rf "$OLD_TEST_TMP"' EXIT + +echo "==> Upgrading dataset to new schema..." +codeql dataset upgrade "$OLD_TEST_TMP/upgraded-dataset" \ + --search-path . \ + --target-dbscheme rust/ql/lib/rust.dbscheme + +echo "==> Running preservation test on upgraded dataset..." +codeql test run \ + --search-path . \ + --dataset="$OLD_TEST_TMP/upgraded-dataset" \ + --check-databases \ + "$SCRIPT_DIR/new/UpgradeShapes.ql" + +echo "==> All tests passed!"