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
2 changes: 2 additions & 0 deletions codeql-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ provide:
- "*/ql/src/qlpack.yml"
- "*/ql/lib/qlpack.yml"
- "*/ql/test*/qlpack.yml"
- "*/ql/upgrade-tests/qlpack.yml"
- "*/ql/downgrade-tests/qlpack.yml"
- "*/ql/examples/qlpack.yml"
- "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
Expand Down
3 changes: 3 additions & 0 deletions rust/ql/downgrade-tests/.gitignore
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,5 @@
*.testproj/
*.actual
*.log
Cargo.toml
lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Downgrade Regression Test for rust-analyzer 0.0.328 -> 0.0.301

This test verifies that the dbscheme downgrade script correctly preserves recoverable data when migrating databases from the new schema back to the old schema.

## Running the test

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

This will:
1. Build the current extractor
2. Create a database with the new schema from `upgrade_shapes.rs`
3. Validate the new-schema extraction
4. Downgrade the database to the old schema
5. Check out the old commit (491c373e076)
6. Verify that the recovered old-schema properties still match

## Files

- `new.ql` / `new.expected`: Query for the new schema (validates extraction)
- `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:13:22:49 | (no string representation) | upgrade_shapes.rs:22:41:22:41 | 1 | 13 |
| upgrade_shapes.rs:22:44:22:48 | FormatArgsArg | upgrade_shapes.rs:22:13:22:49 | (no string representation) | upgrade_shapes.rs:22:48:22:48 | 2 | 13 |
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 |
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, int argNameColumn) {
inUpgradeShapesFile(arg) and
argName = arg.getName() and
expr = arg.getExpr() and
argNameColumn = argName.getLocation().getStartColumn()
}

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,20 @@
formatArgNamePreserved
| upgrade_shapes.rs:22:37:22:41 | FormatArgsArg | upgrade_shapes.rs:22:37:22:39 | 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:46 | 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:15 | 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:10:7:18 | 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:10:7:18 | 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,65 @@
#!/usr/bin/env bash
# Manual regression test for the Rust dbscheme downgrade from rust-analyzer 0.0.328 to 0.0.301.
# 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
OLD_DBSCHEME="rust/downgrades/109496fd2f20f28a35e50b110859e74882ee80d6/rust.dbscheme"

cd "$REPO_ROOT"

if ! git diff --quiet HEAD || [ -n "$(git ls-files --others --exclude-standard)" ]; then
echo "ERROR: Working directory has uncommitted changes." >&2
echo "Please commit or stash your changes before running this test." >&2
exit 1
fi

ORIGINAL_REF="$(git rev-parse --abbrev-ref HEAD)"
if [ "$ORIGINAL_REF" = "HEAD" ]; then
ORIGINAL_REF="$(git rev-parse HEAD)"
fi

restore_ref() {
echo "==> Restoring original ref ($ORIGINAL_REF)..."
git checkout --quiet "$ORIGINAL_REF"
}
trap 'restore_ref' EXIT

echo "==> Building current extractor (this may take a while)..."
bazel run //rust:install

echo "==> Creating new-schema test database..."
codeql test run \
--search-path . \
--keep-databases \
"$SCRIPT_DIR/new.ql" "$@"

echo "==> Downgrading dataset to old schema..."
DATASET_DIR=("$SCRIPT_DIR"/*.testproj/db-rust)
if [[ ! -d "${DATASET_DIR[0]}" ]]; then
echo "ERROR: No testproj found at $SCRIPT_DIR/*.testproj" >&2
exit 1
fi
codeql dataset upgrade "${DATASET_DIR[0]}" \
--allow-downgrades \
--search-path rust \
--target-dbscheme "$OLD_DBSCHEME"

echo "==> Checking out old commit ($OLD_COMMIT) for downgrade verification..."
git checkout --quiet "$OLD_COMMIT"
git checkout --quiet "$ORIGINAL_REF" -- rust/ql/downgrade-tests codeql-workspace.yml

echo "==> Running preservation test on downgraded dataset..."
codeql test run \
--search-path . \
--dataset "${DATASET_DIR[0]}" \
--check-databases \
"$SCRIPT_DIR/downgraded.ql" "$@"

restore_ref
trap '' EXIT

echo "==> All tests passed!"
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);
}
5 changes: 5 additions & 0 deletions rust/ql/downgrade-tests/qlpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: codeql/rust-downgrade-tests
version: 0.0.0
extractor: rust
dependencies:
codeql/rust-all: ${workspace}
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 |
Loading
Loading