Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codeql-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ provide:
- "*/ql/src/qlpack.yml"
- "*/ql/lib/qlpack.yml"
- "*/ql/test*/qlpack.yml"
- "*/ql/upgrade-tests/qlpack.yml"
- "*/ql/examples/qlpack.yml"
- "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.testproj/
*.actual
*.log
Original file line number Diff line number Diff line change
@@ -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=<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`.
Original file line number Diff line number Diff line change
@@ -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 |
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: codeql/rust-upgrade-66a489863649185f4a9770f894505803059a1312-test-new
version: 0.0.0
dependencies:
codeql/rust-all: ${workspace}
extractor: rust
tests: .
Original file line number Diff line number Diff line change
@@ -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<T> = Clone
where
T: Copy;

fn f() {
let _ = try { 1 };
let _ = format_args!("{b} {a}", a = 1, b = 2);
}
Original file line number Diff line number Diff line change
@@ -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 |
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/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="$(git rev-parse --show-toplevel)"
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/"

# Stash changes only if there are any (including untracked files)
STASH_REF=""
if ! git diff --quiet HEAD || [ -n "$(git ls-files --others --exclude-standard)" ]; then
echo "==> Stashing uncommitted changes..."
STASH_REF=$(git stash create --include-untracked)
if [ -n "$STASH_REF" ]; then
git stash store -m "run-test.sh auto-stash" "$STASH_REF"
fi
fi

restore_branch() {
echo "==> Restoring original branch..."
git checkout --quiet -
if [ -n "$STASH_REF" ]; then
git stash pop --quiet
fi
}
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!"
9 changes: 9 additions & 0 deletions rust/ql/upgrade-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
target/

# these are all generated, see `rust/extractor/src/qltest.rs` for details
Cargo.toml
/*/**/rust-toolchain.toml
lib.rs
.proc_macro/
.lib/

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.testproj/
*.actual
*.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Upgrade Regression Test for rust-analyzer 0.0.301 → 0.0.328

This test verifies that the dbscheme upgrade and downgrade scripts correctly preserve data when migrating databases between the old and new schemas.

## Running the test

```bash
./run-test.sh
```

This will:
1. Stash any uncommitted changes
2. Check out the old commit (491c373e076)
3. Build the old extractor
4. Create a database with the old schema from `upgrade_shapes.rs`
5. Restore your branch
6. Upgrade the database to the new schema
7. Verify that the old properties are still accessible via the new schema
8. Downgrade the database back to the old schema
9. Verify that the recovered old-schema properties still match

## Files

- `old.ql` / `old.expected`: Query for the old schema (validates extraction and downgrade)
- `new.ql` / `new.expected`: Query for the new schema (validates upgrade preserved data)
- `downgraded.ql` / `downgraded.expected`: Query for the downgraded old schema
- `upgrade_shapes.rs`: Rust source containing test shapes for all affected schema elements
- `run-test.sh`: Test runner script
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
formatArgsArgName
| upgrade_shapes.rs:22:37:22:41 | FormatArgsArg | upgrade_shapes.rs:22:37:22:37 | (no string representation) | upgrade_shapes.rs:22:41:22:41 | 1 | 37 |
| upgrade_shapes.rs:22:44:22:48 | FormatArgsArg | upgrade_shapes.rs:22:44:22:44 | (no string representation) | upgrade_shapes.rs:22:48:22:48 | 2 | 44 |
tryBlock
| upgrade_shapes.rs:21:13:21:21 | { ... } |
structFieldDefault
| upgrade_shapes.rs:9:5:9:17 | field: u8 | upgrade_shapes.rs:9:17:9:17 | 1 |
variantDiscriminant
| upgrade_shapes.rs:13:5:13:9 | V | upgrade_shapes.rs:13:9:13:9 | 2 |
metaPath
| upgrade_shapes.rs:1:4:1:19 | Meta | allow |
| upgrade_shapes.rs:2:4:2:32 | Meta | feature |
| upgrade_shapes.rs:4:3:4:11 | Meta | path_meta |
| upgrade_shapes.rs:5:3:5:15 | Meta | key_value |
| upgrade_shapes.rs:6:3:6:18 | Meta | token_tree |
| upgrade_shapes.rs:7:3:7:19 | Meta | path_meta |
metaExpr
| upgrade_shapes.rs:5:3:5:15 | Meta | upgrade_shapes.rs:5:15:5:15 | 1 |
metaTokenTree
| upgrade_shapes.rs:1:4:1:19 | Meta | upgrade_shapes.rs:1:9:1:19 | TokenTree |
| upgrade_shapes.rs:2:4:2:32 | Meta | upgrade_shapes.rs:2:11:2:32 | TokenTree |
| upgrade_shapes.rs:6:3:6:18 | Meta | upgrade_shapes.rs:6:13:6:18 | TokenTree |
metaIsUnsafe
| upgrade_shapes.rs:7:3:7:19 | Meta |
traitAlias
| upgrade_shapes.rs:16:1:18:12 | TraitAlias | upgrade_shapes.rs:16:7:16:11 | Alias | upgrade_shapes.rs:16:18:16:22 | ... | upgrade_shapes.rs:17:1:18:11 | WhereClause |
Loading
Loading