Skip to content

Commit

Permalink
Merge pull request #17835 from github/redsun82/rust-qltest
Browse files Browse the repository at this point in the history
Rust: move `qltest` to rust code, add `options` with cargo check
  • Loading branch information
redsun82 authored Oct 24, 2024
2 parents e920a4c + 41d0085 commit 55d092f
Show file tree
Hide file tree
Showing 20 changed files with 197 additions and 62 deletions.
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion rust/extractor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.86"
clap = { version = "4.5.16", features = ["derive"] }
figment = { version = "0.10.19", features = ["env"]}
figment = { version = "0.10.19", features = ["env", "yaml"] }
log = "0.4.22"
num-traits = "0.2.19"
ra_ap_base_db = "0.0.232"
Expand All @@ -29,3 +29,4 @@ argfile = "0.2.1"
codeql-extractor = { path = "../../shared/tree-sitter-extractor" }
rust-extractor-macros = { path = "macros" }
itertools = "0.13.0"
glob = "0.3.1"
37 changes: 31 additions & 6 deletions rust/extractor/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use quote::{format_ident, quote};
pub fn extractor_cli_config(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(item as syn::ItemStruct);
let name = &ast.ident;
let new_name = format_ident!("Cli{}", name);
let fields: Vec<_> = ast
let cli_name = format_ident!("Cli{}", name);
let cli_fields = ast
.fields
.iter()
.map(|f| {
Expand Down Expand Up @@ -39,17 +39,42 @@ pub fn extractor_cli_config(_attr: TokenStream, item: TokenStream) -> TokenStrea
}
}
})
.collect();
.collect::<Vec<_>>();
let debug_fields = ast
.fields
.iter()
.map(|f| {
let id = f.ident.as_ref().unwrap();
if id == &format_ident!("inputs") {
quote! {
.field("number of inputs", &self.#id.len())
}
} else {
quote! {
.field(stringify!(#id), &self.#id)
}
}
})
.collect::<Vec<_>>();

let gen = quote! {
#[serde_with::apply(_ => #[serde(default)])]
#[derive(Debug, Deserialize, Default)]
#[derive(Deserialize, Default)]
#ast

impl Debug for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("configuration:")
#(#debug_fields)*
.finish()
}
}

#[serde_with::skip_serializing_none]
#[derive(clap::Parser, Serialize)]
#[command(about, long_about = None)]
struct #new_name {
#(#fields)*
struct #cli_name {
#(#cli_fields)*
}
};
gen.into()
Expand Down
28 changes: 23 additions & 5 deletions rust/extractor/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use anyhow::Context;
use clap::Parser;
use codeql_extractor::trap;
use figment::{
providers::{Env, Serialized},
providers::{Env, Format, Serialized, Yaml},
value::Value,
Figment,
};
use itertools::Itertools;
use num_traits::Zero;
use rust_extractor_macros::extractor_cli_config;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::ops::Not;
use std::path::PathBuf;

Expand Down Expand Up @@ -38,19 +41,34 @@ pub struct Config {
pub verbose: u8,
pub compression: Compression,
pub inputs: Vec<PathBuf>,
pub qltest: bool,
pub qltest_cargo_check: bool,
}

impl Config {
pub fn extract() -> anyhow::Result<Config> {
let args = argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
.context("expanding parameter files")?;
let cli_args = CliConfig::parse_from(args);
Figment::new()
let mut figment = Figment::new()
.merge(Env::prefixed("CODEQL_"))
.merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_"))
.merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_OPTION_"))
.merge(Serialized::defaults(cli_args))
.extract()
.context("loading configuration")
.merge(Serialized::defaults(cli_args));
if let Ok(Value::Bool(_, true)) = figment.find_value("qltest") {
let cwd = std::env::current_dir()?;
let mut option_files = cwd
.ancestors()
// only travel up while we're within the test pack
.take_while_inclusive(|p| !p.join("qlpack.yml").exists())
.map(|p| p.join("options"))
.filter(|p| p.exists())
.collect_vec();
option_files.reverse();
for path in option_files {
figment = figment.merge(Yaml::file_exact(path));
}
}
figment.extract().context("loading configuration")
}
}
22 changes: 14 additions & 8 deletions rust/extractor/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

use anyhow::Context;
use archive::Archiver;
use log::info;
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
use ra_ap_project_model::ProjectManifest;
use rust_analyzer::{ParseResult, RustAnalyzer};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
mod archive;
mod config;
pub mod generated;
mod qltest;
mod rust_analyzer;
mod translate;
pub mod trap;
Expand Down Expand Up @@ -65,16 +66,21 @@ fn extract(
)
});
}

fn main() -> anyhow::Result<()> {
let cfg = config::Config::extract().context("failed to load configuration")?;
let mut cfg = config::Config::extract().context("failed to load configuration")?;
stderrlog::new()
.module(module_path!())
.verbosity(1 + cfg.verbose as usize)
.verbosity(2 + cfg.verbose as usize)
.init()?;
if cfg.qltest {
qltest::prepare(&mut cfg)?;
}
info!("{cfg:#?}\n");

let traps = trap::TrapFileProvider::new(&cfg).context("failed to set up trap files")?;
let archiver = archive::Archiver {
root: cfg.source_archive_dir,
root: cfg.source_archive_dir.clone(),
};
let files: Vec<PathBuf> = cfg
.inputs
Expand Down
72 changes: 72 additions & 0 deletions rust/extractor/src/qltest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::config::Config;
use anyhow::Context;
use glob::glob;
use itertools::Itertools;
use log::info;
use std::ffi::OsStr;
use std::fs;
use std::process::Command;

fn dump_lib() -> anyhow::Result<()> {
let path_iterator = glob("*.rs").context("globbing test sources")?;
let paths = path_iterator
.collect::<Result<Vec<_>, _>>()
.context("fetching test sources")?;
let lib = paths
.iter()
.map(|p| p.file_stem().expect("results of glob must have a name"))
.filter(|&p| !["main", "lib"].map(OsStr::new).contains(&p))
.map(|p| format!("mod {};", p.to_string_lossy()))
.join("\n");
fs::write("lib.rs", lib).context("writing lib.rs")
}

fn dump_cargo_manifest() -> anyhow::Result<()> {
let mut manifest = String::from(
r#"[workspace]
[package]
name = "test"
version="0.0.1"
edition="2021"
[lib]
path="lib.rs"
"#,
);
if fs::exists("main.rs").context("checking existence of main.rs")? {
manifest.push_str(
r#"[[bin]]
name = "main"
path = "main.rs"
"#,
);
}
fs::write("Cargo.toml", manifest).context("writing Cargo.toml")
}

fn set_sources(config: &mut Config) -> anyhow::Result<()> {
let path_iterator = glob("*.rs").context("globbing test sources")?;
config.inputs = path_iterator
.collect::<Result<Vec<_>, _>>()
.context("fetching test sources")?;
Ok(())
}

pub(crate) fn prepare(config: &mut Config) -> anyhow::Result<()> {
dump_lib()?;
set_sources(config)?;
dump_cargo_manifest()?;
if config.qltest_cargo_check {
let status = Command::new("cargo")
.env("RUSTFLAGS", "-Awarnings")
.arg("check")
.arg("-q")
.status()
.context("spawning cargo check")?;
if status.success() {
info!("cargo check successful");
} else {
anyhow::bail!("requested cargo check failed");
}
}
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
| gen_module.rs:3:1:4:8 | Module | getNumberOfAttrs: | 0 | hasItemList: | no | hasName: | yes | hasVisibility: | no |
| gen_module.rs:5:1:7:1 | Module | getNumberOfAttrs: | 0 | hasItemList: | yes | hasName: | yes | hasVisibility: | no |
| lib.rs:2:1:2:15 | Module | getNumberOfAttrs: | 0 | hasItemList: | no | hasName: | yes | hasVisibility: | no |
| lib.rs:1:1:1:15 | Module | getNumberOfAttrs: | 0 | hasItemList: | no | hasName: | yes | hasVisibility: | no |
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
| gen_module.rs:3:1:4:8 | Module | gen_module.rs:4:5:4:7 | Name |
| gen_module.rs:5:1:7:1 | Module | gen_module.rs:5:5:5:7 | Name |
| lib.rs:2:1:2:15 | Module | lib.rs:2:5:2:14 | Name |
| lib.rs:1:1:1:15 | Module | lib.rs:1:5:1:14 | Name |
2 changes: 1 addition & 1 deletion rust/ql/test/extractor-tests/generated/Name/Name.expected
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| gen_name.rs:3:4:3:12 | Name | hasText: | yes |
| lib.rs:2:5:2:12 | Name | hasText: | yes |
| lib.rs:1:5:1:12 | Name | hasText: | yes |
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| gen_name.rs:3:4:3:12 | Name | test_name |
| lib.rs:2:5:2:12 | Name | gen_name |
| lib.rs:1:5:1:12 | Name | gen_name |
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| gen_source_file.rs:1:1:6:2 | SourceFile | getNumberOfAttrs: | 0 | getNumberOfItems: | 1 |
| lib.rs:1:1:2:21 | SourceFile | getNumberOfAttrs: | 0 | getNumberOfItems: | 1 |
| lib.rs:1:1:1:20 | SourceFile | getNumberOfAttrs: | 0 | getNumberOfItems: | 1 |
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| gen_source_file.rs:1:1:6:2 | SourceFile | 0 | gen_source_file.rs:3:1:6:1 | test_source_file |
| lib.rs:1:1:2:21 | SourceFile | 0 | lib.rs:2:1:2:20 | Module |
| lib.rs:1:1:1:20 | SourceFile | 0 | lib.rs:1:1:1:20 | Module |
1 change: 1 addition & 0 deletions rust/ql/test/extractor-tests/generated/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qltest_cargo_check: false
6 changes: 3 additions & 3 deletions rust/ql/test/extractor-tests/utf8/ast.expected
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
| lib.rs:1:1:2:22 | SourceFile |
| lib.rs:2:1:2:21 | Module |
| lib.rs:2:5:2:20 | Name |
| lib.rs:1:1:1:21 | Module |
| lib.rs:1:1:1:21 | SourceFile |
| lib.rs:1:5:1:20 | Name |
| utf8_identifiers.rs:1:1:4:6 | foo |
| utf8_identifiers.rs:1:1:12:2 | SourceFile |
| utf8_identifiers.rs:1:4:1:6 | Name |
Expand Down
1 change: 1 addition & 0 deletions rust/ql/test/library-tests/controlflow/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qltest_cargo_check: false
1 change: 1 addition & 0 deletions rust/ql/test/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qltest_cargo_check: true
1 change: 1 addition & 0 deletions rust/ql/test/query-tests/diagnostics/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qltest_cargo_check: false
1 change: 1 addition & 0 deletions rust/ql/test/query-tests/unusedentities/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qltest_cargo_check: false
11 changes: 11 additions & 0 deletions rust/tools/qltest.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@echo off

set "RUST_BACKTRACE=full"
set "QLTEST_LOG=%CODEQL_EXTRACTOR_RUST_LOG_DIR%/qltest.log"

type NUL && "%CODEQL_EXTRACTOR_RUST_ROOT%/tools/%CODEQL_PLATFORM%/extractor" --qltest >"%QLTEST_LOG%"

if %ERRORLEVEL% neq 0 (
type "%QLTEST_LOG%"
exit /b 1
)
Loading

0 comments on commit 55d092f

Please sign in to comment.