diff --git a/Cargo.lock b/Cargo.lock index 49bd6b4d5f59..49af57f4174d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,7 +249,6 @@ name = "buramu" version = "0.1.0" dependencies = [ "chrono", - "lazy_static", "rayon", "regex", ] @@ -492,7 +491,6 @@ dependencies = [ "encoding", "flate2", "globset", - "lazy_static", "num_cpus", "rand", "rayon", @@ -532,7 +530,6 @@ dependencies = [ "clap", "codeql-extractor", "encoding", - "lazy_static", "rayon", "regex", "serde_json", @@ -550,7 +547,6 @@ dependencies = [ "clap", "codeql-extractor", "encoding", - "lazy_static", "rayon", "regex", "serde", diff --git a/Cargo.toml b/Cargo.toml index b4edfedb9138..55791ed6fb55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,57 @@ exclude = [ "rust/ql/integration-tests", ] +[workspace.lints.rust] +redundant_imports = "warn" + [workspace.lints.clippy] all = { level = "warn", priority = -1 } -explicit_iter_loop = "warn" -manual_let_else = "warn" +pedantic = { level = "warn", priority = -1 } + +# Allowlist of pedantic rules that we've observed to be of questionable value +missing_errors_doc = "allow" +missing_panics_doc = "allow" +must_use_candidate = "allow" +implicit_hasher = "allow" +if_not_else = "allow" +# This lint has a lot of results in generated code. +unnecessary_wraps = "allow" +# Spelling out the full method name can often be tedious. +# E.g., `|v| v.as_slice()` vs `vec::Vec::as_slice` +redundant_closure_for_method_calls = "allow" +# The next three are only false positives and questionable results +cast_possible_truncation = "allow" +cast_possible_wrap = "allow" +cast_sign_loss = "allow" +# False positives +struct_excessive_bools = "allow" +# False positives for things like "matches" and "matched" +similar_names = "allow" +match_same_arms = "allow" +struct_field_names = "allow" + +# Additional lints from the `restriction` and `nursery` group +as_pointer_underscore = "warn" +clone_on_ref_ptr = "warn" +collection_is_never_read = "warn" +deref_by_slicing = "warn" +derive_partial_eq_without_eq = "warn" +disallowed_script_idents = "warn" +empty_drop = "warn" +empty_enum_variants_with_brackets = "warn" +empty_structs_with_brackets = "warn" +fn_to_numeric_cast_any = "warn" +if_then_some_else_none = "warn" +needless_pass_by_ref_mut = "warn" +or_fun_call = "warn" +redundant_clone = "warn" +redundant_type_annotations = "warn" +renamed_function_params = "warn" +semicolon_inside_block = "warn" +undocumented_unsafe_blocks = "warn" +unnecessary_safety_comment = "warn" +unnecessary_safety_doc = "warn" +unnecessary_struct_initialization = "warn" +unneeded_field_pattern = "warn" +unused_result_ok = "warn" +too_long_first_doc_paragraph = "warn" diff --git a/MODULE.bazel b/MODULE.bazel index 04f9f865718e..409af33fd8f1 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -142,7 +142,6 @@ use_repo( "vendor_rs__globset-0.4.18", "vendor_rs__itertools-0.14.0", "vendor_rs__itertools-0.15.0", - "vendor_rs__lazy_static-1.5.1", "vendor_rs__mustache-0.9.0", "vendor_rs__num-traits-0.2.19", "vendor_rs__num_cpus-1.17.0", diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000000..e901773fa799 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +# Names that do not need to be backtick quoted within documentation comments. +doc-valid-idents = ["CodeQL", ".."] diff --git a/cpp/extractor/bmn/src/compiler.rs b/cpp/extractor/bmn/src/compiler.rs index 8da60fdb66af..2c847e56e0e5 100644 --- a/cpp/extractor/bmn/src/compiler.rs +++ b/cpp/extractor/bmn/src/compiler.rs @@ -303,8 +303,8 @@ fn find_posix( let (command_path, command_version) = get_compiler_version(kind, name)?; let (commandpp_path, commandpp_version) = get_compiler_version(kind, namepp)?; - let mimic_c_flags = vec![OsString::from("--mimic"), command_path.clone()]; - let mimic_cpp_flags = vec![OsString::from("--mimic"), commandpp_path.clone()]; + let mimic_c_flags = vec![OsString::from("--mimic"), command_path]; + let mimic_cpp_flags = vec![OsString::from("--mimic"), commandpp_path]; let default_compiler_include_checker = CompilerDefaultIncludes::from(telemetry, name, namepp); @@ -518,9 +518,13 @@ fn get_compiler_override( &clang_c_compiler, &clang_cpp_compiler, ) - .ok_or(anyhow!( - "Could not find clang compilers: {clang_c_compiler:?}, {clang_cpp_compiler:?}" - )) + .ok_or_else(|| { + anyhow!( + "Could not find clang compilers: {}, {}", + clang_c_compiler.display(), + clang_cpp_compiler.display() + ) + }) } else if override_instruction == "gnu" { // These can be just a command name, e.g. "gcc-18", or a full path, e.g. // "/usr/bin/gcc-18" @@ -528,18 +532,24 @@ fn get_compiler_override( environment::get_required_os(environment::CODEQL_EXTRACTOR_CPP_BMN_COMPILER_GNU_C)?; let gnu_cpp_compiler = environment::get_required_os(environment::CODEQL_EXTRACTOR_CPP_BMN_COMPILER_GNU_CPP)?; - find_posix(telemetry, Compiler::Gnu, &gnu_c_compiler, &gnu_cpp_compiler).ok_or(anyhow!( - "Could not find gnu compilers: {gnu_c_compiler:?}, {gnu_cpp_compiler:?}" - )) + find_posix(telemetry, Compiler::Gnu, &gnu_c_compiler, &gnu_cpp_compiler).ok_or_else(|| { + anyhow!( + "Could not find gnu compilers: {}, {}", + gnu_c_compiler.display(), + gnu_cpp_compiler.display() + ) + }) } else if override_instruction == "msvc" { // These can be just a command name, e.g. "cl", or a full path, e.g. // "c:\msvc\cl.exe" let msvc_compiler = environment::get_required_os(environment::CODEQL_EXTRACTOR_CPP_BMN_COMPILER_MSVC)?; - use_cl(&msvc_compiler).ok_or(anyhow!("Could not find msvc compiler: {msvc_compiler:?}")) + use_cl(&msvc_compiler) + .ok_or_else(|| anyhow!("Could not find msvc compiler: {}", msvc_compiler.display())) } else { Err(anyhow!( - "Unsupported compiler override instruction: {override_instruction:?}" + "Unsupported compiler override instruction: {}", + override_instruction.display() )) } } diff --git a/cpp/extractor/bmn/src/dependency_installation.rs b/cpp/extractor/bmn/src/dependency_installation.rs index f65c80146231..f654eea759be 100644 --- a/cpp/extractor/bmn/src/dependency_installation.rs +++ b/cpp/extractor/bmn/src/dependency_installation.rs @@ -45,7 +45,7 @@ fn run_command(command: &mut Command) -> anyhow::Result<()> { } else { OsString::from("Failed to read output") }; - Err(anyhow!("\"{command_name}\" failed: {error:?}")) + Err(anyhow!("\"{command_name}\" failed: {}", error.display())) } Err(e) => Err(anyhow!("Error waiting for process \"{command_name}\": {e}")), } @@ -86,7 +86,7 @@ fn install_package(package_name: &str) -> anyhow::Result<()> { } /// Struct to manage package installation state -struct PackageInstaller {} +struct PackageInstaller; impl PackageInstaller { /// Create a new `PackageInstaller` and perform an initial `apt-get update` @@ -95,11 +95,15 @@ impl PackageInstaller { bail!("'sudo apt-get' not available."); } apt_update()?; - Ok(Self {}) + Ok(Self) } /// Ensure the given package is installed - #[allow(clippy::unused_self)] + #[allow( + clippy::unused_self, + clippy::needless_pass_by_ref_mut, + reason = "Package installation mutates system state, so require exclusive access." + )] fn install_package(&mut self, package_name: &str) -> anyhow::Result<()> { install_package(package_name) } diff --git a/cpp/extractor/bmn/src/directory_tree.rs b/cpp/extractor/bmn/src/directory_tree.rs index a6cb1f61fe1b..4424e3b7fcef 100644 --- a/cpp/extractor/bmn/src/directory_tree.rs +++ b/cpp/extractor/bmn/src/directory_tree.rs @@ -171,8 +171,6 @@ mod tests { mod from_path { use super::*; use crate::test_utils::get_resource; - use std::collections::HashSet; - use std::ffi::OsString; fn check_files(tree: &DirectoryTree, file_name: &str, expected: &[PathBuf]) { let files = tree diff --git a/cpp/extractor/bmn/src/extraction_command_runner.rs b/cpp/extractor/bmn/src/extraction_command_runner.rs index 2de71bc2a57b..789e1d01fab3 100644 --- a/cpp/extractor/bmn/src/extraction_command_runner.rs +++ b/cpp/extractor/bmn/src/extraction_command_runner.rs @@ -383,9 +383,7 @@ mod tests { use super::*; use crate::project_definitions::Language; use all_asserts::{assert_false, assert_true}; - use std::ffi::OsString; use std::path::PathBuf; - use std::time::Duration; fn make_command( args: &[&str], @@ -407,7 +405,6 @@ mod tests { #[allow(clippy::unnecessary_wraps)] fn fake_status(exit_code: i32) -> io::Result { - use std::process::ExitStatus; #[cfg(unix)] { use std::os::unix::process::ExitStatusExt; diff --git a/cpp/extractor/bmn/src/file_filter.rs b/cpp/extractor/bmn/src/file_filter.rs index 8b952b659568..bc5ad47d6bd3 100644 --- a/cpp/extractor/bmn/src/file_filter.rs +++ b/cpp/extractor/bmn/src/file_filter.rs @@ -97,7 +97,8 @@ fn relativize_path(path: PathBuf) -> Result { Ok(PathBuf::from_iter(sliced)) } _ => Err(anyhow!( - "Path cannot start with .. or the drive prefix: {path:?}" + "Path cannot start with .. or the drive prefix: {}", + path.display() )), } } @@ -331,11 +332,11 @@ impl FileFilter { if include_filter_count == 0 { // No include, everything not excluded should be accepted, so add an include all filter // at the beginning to include everything else - index_filters.insert(0, FilterRule::Include(wildcard.clone())); + index_filters.insert(0, FilterRule::Include(wildcard)); } else { // If only include filters are specified, add an exclude all filter at the beginning to // exclude everything else - index_filters.insert(0, FilterRule::Exclude(wildcard.clone())); + index_filters.insert(0, FilterRule::Exclude(wildcard)); } Ok(FileFilter { @@ -459,7 +460,6 @@ impl FileFilter { mod tests { use super::*; use all_asserts::{assert_false, assert_true}; - use std::path::PathBuf; // Using the current directory as root for tests to avoid issues with absolute paths on Windows fn get_cwd() -> PathBuf { diff --git a/cpp/extractor/bmn/src/gnu_compiler_default_include_finder.rs b/cpp/extractor/bmn/src/gnu_compiler_default_include_finder.rs index 9b208da27bf3..a0f1e60fe932 100644 --- a/cpp/extractor/bmn/src/gnu_compiler_default_include_finder.rs +++ b/cpp/extractor/bmn/src/gnu_compiler_default_include_finder.rs @@ -95,7 +95,8 @@ fn execute_command_with_args_get_stderr(cmd: &OsStr, args: &[&str]) -> Result Ok(output.stderr), _ => Err(anyhow!( - "Error invoking the compiler {cmd:?} to get the default include folders." + "Error invoking the compiler {} to get the default include folders.", + cmd.display() )), } } @@ -597,7 +598,7 @@ End of search list."#; assert_true!(output.is_err()); assert_eq!( output.unwrap_err().to_string(), - format!("Error invoking the compiler {cmd:?} to get the default include folders.") + "Error invoking the compiler command-not-existing to get the default include folders." ); } @@ -608,7 +609,7 @@ End of search list."#; assert_true!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - format!("Error invoking the compiler {cmd:?} to get the default include folders.") + "Error invoking the compiler command-not-existing to get the default include folders." ); } } diff --git a/cpp/extractor/bmn/src/include_finder.rs b/cpp/extractor/bmn/src/include_finder.rs index 482a1f9dd6fe..5006c411ba63 100644 --- a/cpp/extractor/bmn/src/include_finder.rs +++ b/cpp/extractor/bmn/src/include_finder.rs @@ -291,7 +291,6 @@ mod tests { use crate::include_scanner; use crate::test_utils::{create_populate_tmp_dir, get_resource}; use all_asserts::assert_true; - use std::path::Path; fn create_tree(files: &[&str]) -> DirectoryTree { DirectoryTree::from_files(files.iter().map(PathBuf::from).collect::>()) @@ -655,7 +654,7 @@ mod tests { assert_eq!( result.include_directories, - vec![path.clone(), path.clone().join("dir2")] + vec![path.clone(), path.join("dir2")] ); assert!(!result.unresolvable.is_empty()); assert_eq!( @@ -678,7 +677,7 @@ mod tests { let result = find_includes(&tree, &empty_tree(), &no_default_includes, &includes); - assert_eq!(result.include_directories, vec![path.clone()]); + assert_eq!(result.include_directories, vec![path]); assert_eq!( result.unresolvable, vec![ diff --git a/cpp/extractor/bmn/src/include_scanner.rs b/cpp/extractor/bmn/src/include_scanner.rs index 5aa75a2ae895..b2a2117c4a15 100644 --- a/cpp/extractor/bmn/src/include_scanner.rs +++ b/cpp/extractor/bmn/src/include_scanner.rs @@ -32,12 +32,12 @@ fn get_includes_cached(filename: PathBuf) -> Vec { .map(|global_include| { IncludeDirective::system(replace_backslashes(&global_include[1]), &filename) }) - .or( + .or_else(|| { // #include "(.*)" LOCAL_INCLUDE_RE.captures(line).map(|local_include| { IncludeDirective::local(replace_backslashes(&local_include[1]), &filename) - }), - ) + }) + }) }) .collect() } @@ -65,7 +65,7 @@ mod tests { IncludeDirective::system("missing_system.hpp", &file_name), ]; - let actual = get_includes(file_name.clone()); + let actual = get_includes(file_name); assert_eq!(actual, expected); } diff --git a/cpp/extractor/bmn/src/logger.rs b/cpp/extractor/bmn/src/logger.rs index ee2560c9eb46..bbd54748b025 100644 --- a/cpp/extractor/bmn/src/logger.rs +++ b/cpp/extractor/bmn/src/logger.rs @@ -1,7 +1,7 @@ use crate::environment; use std::sync::LazyLock; -#[derive(PartialEq, PartialOrd)] +#[derive(PartialEq, Eq, PartialOrd)] pub enum Verbosity { Silent, Error, diff --git a/cpp/extractor/bmn/src/main.rs b/cpp/extractor/bmn/src/main.rs index 54e57d02d6bb..c6e860d7a99a 100644 --- a/cpp/extractor/bmn/src/main.rs +++ b/cpp/extractor/bmn/src/main.rs @@ -52,8 +52,8 @@ fn get_extractor_executable() -> Result { let extractor = PathBuf::from_iter([&root_dir, "tools", &platform, &binary_name]); if !extractor.exists() { Err(anyhow::anyhow!( - "Extractor executable not found at: {:?}", - extractor + "Extractor executable not found at: {}", + extractor.display() )) } else { Ok(extractor) diff --git a/cpp/extractor/bmn/src/overlays.rs b/cpp/extractor/bmn/src/overlays.rs index 0e8e80bee6f3..e3f194170a88 100644 --- a/cpp/extractor/bmn/src/overlays.rs +++ b/cpp/extractor/bmn/src/overlays.rs @@ -54,21 +54,23 @@ pub fn get_overlay_settings() -> Result { (Some(overlay_changes), Some(metadata_in), None) => { let changes_file = fs::File::open(overlay_changes)?; let json: Value = from_reader(changes_file)?; - let obj = json.as_object().ok_or(anyhow!( - "Overlay changes file does not contain a JSON object." - ))?; + let obj = json + .as_object() + .ok_or_else(|| anyhow!("Overlay changes file does not contain a JSON object."))?; // TODO: Log/warn if there are any fields other than "changes" let files = obj.get("changes").ok_or_else(|| { anyhow!("Overlay changes file does not contain a 'changes' field.") })?; - let files_array = files.as_array().ok_or(anyhow!( - "Overlay changes file 'changes' field is not an array." - ))?; + let files_array = files + .as_array() + .ok_or_else(|| anyhow!("Overlay changes file 'changes' field is not an array."))?; let changed_files = files_array .iter() .map(|v| { Ok(v.as_str() - .ok_or(anyhow!("Overlay changes file contains non-string entries."))? + .ok_or_else(|| { + anyhow!("Overlay changes file contains non-string entries.") + })? .into()) }) .collect::>()?; diff --git a/cpp/extractor/bmn/src/package_database.rs b/cpp/extractor/bmn/src/package_database.rs index fd70b5bcd314..1adad34788a0 100644 --- a/cpp/extractor/bmn/src/package_database.rs +++ b/cpp/extractor/bmn/src/package_database.rs @@ -150,19 +150,27 @@ pub fn get_package_database() -> anyhow::Result { { if !path.exists() { return Err(anyhow!( - "Local deptrace database path specified but file does not exist: {path:?}.", + "Local deptrace database path specified but file does not exist: {}.", + path.display() )); } debug!( "Using local deptrace database from {}: {path:?}", environment::CODEQL_EXTRACTOR_CPP_LOCAL_DEPTRACE_DB, ); - fs::read_to_string(&path) - .map_err(|e| anyhow!("Failed to read local deptrace database from {path:?}: {e}"))? + fs::read_to_string(&path).map_err(|e| { + anyhow!( + "Failed to read local deptrace database from {}: {e}", + path.display() + ) + })? } else { let archive_file_path = get_deptrace_db_zip_path()?; extract_content_from_zip(&archive_file_path).map_err(|e| { - anyhow!("Failed to extract package database from {archive_file_path:?}: {e}") + anyhow!( + "Failed to extract package database from {}: {e}", + archive_file_path.display() + ) })? }; PackageDatabase::from_deptrace_content(&content) @@ -213,17 +221,31 @@ fn infer_os_version_name() -> Option { /// as a string. fn extract_content_from_zip(archive_zip_name: &Path) -> anyhow::Result { let inner_file_name = "deptrace-db.txt"; - let file = File::open(archive_zip_name) - .map_err(|e| anyhow!("Could not open archive zip name {archive_zip_name:?}: {e}"))?; - let mut archive = ZipArchive::new(file) - .map_err(|e| anyhow!("Could not read zip archive {archive_zip_name:?}: {e}"))?; + let file = File::open(archive_zip_name).map_err(|e| { + anyhow!( + "Could not open archive zip name {}: {e}", + archive_zip_name.display() + ) + })?; + let mut archive = ZipArchive::new(file).map_err(|e| { + anyhow!( + "Could not read zip archive {}: {e}", + archive_zip_name.display() + ) + })?; let mut zipped_file = archive.by_name(inner_file_name).map_err(|e| { - anyhow!("Could not find {inner_file_name} in archive {archive_zip_name:?}: {e}") + anyhow!( + "Could not find {inner_file_name} in archive {}: {e}", + archive_zip_name.display() + ) })?; let mut buffer = String::new(); zipped_file.read_to_string(&mut buffer).map_err(|e| { - anyhow!("Could not read {inner_file_name} from archive {archive_zip_name:?}: {e}") + anyhow!( + "Could not read {inner_file_name} from archive {}: {e}", + archive_zip_name.display() + ) })?; Ok(buffer) @@ -233,7 +255,6 @@ fn extract_content_from_zip(archive_zip_name: &Path) -> anyhow::Result { mod tests { use super::*; use all_asserts::assert_true; - use std::fs::File; use std::io::Write; use tempfile::tempdir; diff --git a/cpp/extractor/bmn/src/project_definitions.rs b/cpp/extractor/bmn/src/project_definitions.rs index 08cc4b355de4..80d9089f7ea8 100644 --- a/cpp/extractor/bmn/src/project_definitions.rs +++ b/cpp/extractor/bmn/src/project_definitions.rs @@ -224,9 +224,9 @@ mod tests { assert_ne!(file1, file3); let mut set = HashSet::new(); - set.insert(file1.clone()); - set.insert(file2.clone()); - set.insert(file3.clone()); + set.insert(file1); + set.insert(file2); + set.insert(file3); assert_eq!(set.len(), 2); } diff --git a/misc/bazel/3rdparty/rust_workspace_deps/BUILD.bazel b/misc/bazel/3rdparty/rust_workspace_deps/BUILD.bazel index 01cf30c6bdf3..20e67a6711c0 100644 --- a/misc/bazel/3rdparty/rust_workspace_deps/BUILD.bazel +++ b/misc/bazel/3rdparty/rust_workspace_deps/BUILD.bazel @@ -211,18 +211,6 @@ alias( tags = ["manual"], ) -alias( - name = "lazy_static-1.5.1", - actual = "@vendor_rs__lazy_static-1.5.1//:lazy_static", - tags = ["manual"], -) - -alias( - name = "lazy_static", - actual = "@vendor_rs__lazy_static-1.5.1//:lazy_static", - tags = ["manual"], -) - alias( name = "mustache-0.9.0", actual = "@vendor_rs__mustache-0.9.0//:mustache", diff --git a/misc/bazel/3rdparty/rust_workspace_deps/crates.bzl b/misc/bazel/3rdparty/rust_workspace_deps/crates.bzl index f8a1af628fb8..b5e41d6139f7 100644 --- a/misc/bazel/3rdparty/rust_workspace_deps/crates.bzl +++ b/misc/bazel/3rdparty/rust_workspace_deps/crates.bzl @@ -356,7 +356,6 @@ _NORMAL_DEPENDENCIES = { "ql/buramu": { _COMMON_CONDITION: { "chrono": Label("@vendor_rs//chrono-0.4.45"), - "lazy_static": Label("@vendor_rs//lazy_static-1.5.1"), "rayon": Label("@vendor_rs//rayon-1.12.0"), "regex": Label("@vendor_rs//regex-1.13.1"), }, @@ -384,7 +383,6 @@ _NORMAL_DEPENDENCIES = { _COMMON_CONDITION: { "clap": Label("@vendor_rs//clap-4.6.7"), "encoding": Label("@vendor_rs//encoding-0.2.33"), - "lazy_static": Label("@vendor_rs//lazy_static-1.5.1"), "rayon": Label("@vendor_rs//rayon-1.12.0"), "regex": Label("@vendor_rs//regex-1.13.1"), "serde_json": Label("@vendor_rs//serde_json-1.0.154"), @@ -461,7 +459,6 @@ _NORMAL_DEPENDENCIES = { "encoding": Label("@vendor_rs//encoding-0.2.33"), "flate2": Label("@vendor_rs//flate2-1.1.9"), "globset": Label("@vendor_rs//globset-0.4.18"), - "lazy_static": Label("@vendor_rs//lazy_static-1.5.1"), "num_cpus": Label("@vendor_rs//num_cpus-1.17.0"), "rayon": Label("@vendor_rs//rayon-1.12.0"), "regex": Label("@vendor_rs//regex-1.13.1"), @@ -502,7 +499,6 @@ _NORMAL_DEPENDENCIES = { _COMMON_CONDITION: { "clap": Label("@vendor_rs//clap-4.6.7"), "encoding": Label("@vendor_rs//encoding-0.2.33"), - "lazy_static": Label("@vendor_rs//lazy_static-1.5.1"), "rayon": Label("@vendor_rs//rayon-1.12.0"), "regex": Label("@vendor_rs//regex-1.13.1"), "serde": Label("@vendor_rs//serde-1.0.229"), @@ -4816,7 +4812,6 @@ def crate_repositories(): struct(repo = "vendor_rs__globset-0.4.18", is_dev_dep = False), struct(repo = "vendor_rs__itertools-0.14.0", is_dev_dep = False), struct(repo = "vendor_rs__itertools-0.15.0", is_dev_dep = False), - struct(repo = "vendor_rs__lazy_static-1.5.1", is_dev_dep = False), struct(repo = "vendor_rs__mustache-0.9.0", is_dev_dep = False), struct(repo = "vendor_rs__num-traits-0.2.19", is_dev_dep = False), struct(repo = "vendor_rs__num_cpus-1.17.0", is_dev_dep = False), diff --git a/misc/bazel/3rdparty/rust_workspace_deps/lazy_static-1.5.1/BUILD.bazel b/misc/bazel/3rdparty/rust_workspace_deps/lazy_static-1.5.1/BUILD.bazel deleted file mode 100644 index 3140ccb8ad63..000000000000 --- a/misc/bazel/3rdparty/rust_workspace_deps/lazy_static-1.5.1/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -############################################################################### -# @generated -# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To -# regenerate this file, run the following: -# -# bazel run @@//misc/bazel/3rdparty:vendor_rust_workspace_deps -############################################################################### - -package(default_visibility = ["//visibility:public"]) - -alias( - name = "lazy_static-1.5.1", - actual = "@vendor_rs__lazy_static-1.5.1//:lazy_static", - tags = ["manual"], -) diff --git a/misc/bazel/3rdparty/rust_workspace_deps/lazy_static/BUILD.bazel b/misc/bazel/3rdparty/rust_workspace_deps/lazy_static/BUILD.bazel deleted file mode 100644 index 04eca0f691a9..000000000000 --- a/misc/bazel/3rdparty/rust_workspace_deps/lazy_static/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -############################################################################### -# @generated -# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To -# regenerate this file, run the following: -# -# bazel run @@//misc/bazel/3rdparty:vendor_rust_workspace_deps -############################################################################### - -package(default_visibility = ["//visibility:public"]) - -alias( - name = "lazy_static", - actual = "@vendor_rs__lazy_static-1.5.1//:lazy_static", - tags = ["manual"], -) diff --git a/ql/buramu/Cargo.toml b/ql/buramu/Cargo.toml index ab5b5fde76f1..f004f3e973a2 100644 --- a/ql/buramu/Cargo.toml +++ b/ql/buramu/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" workspace = true [dependencies] -lazy_static = "1.4.0" chrono = "0.4.37" rayon = "1.9.0" regex = "1.10.4" diff --git a/ql/buramu/src/main.rs b/ql/buramu/src/main.rs index 11305c34bf57..9efe9e4c7e9d 100644 --- a/ql/buramu/src/main.rs +++ b/ql/buramu/src/main.rs @@ -1,7 +1,7 @@ -use lazy_static::lazy_static; use rayon::prelude::*; use regex::Regex; use std::collections::HashMap; +use std::sync::LazyLock; use std::{io::BufRead, process::Command}; // A map from filenames to lists of line numbers (for just the lines with deprecations) @@ -53,7 +53,7 @@ fn get_blame_dates_for_filedeprecation(file: &str, linenos: &[String]) -> LastMo let mut command = Command::new("git"); command.arg("blame"); for lineno in linenos { - command.arg("-L").arg(format!("{},{}", lineno, lineno)); + command.arg("-L").arg(format!("{lineno},{lineno}")); } command.arg(file); let output = command.output().expect("failed to execute process"); @@ -69,10 +69,8 @@ fn get_blame_dates_for_filedeprecation(file: &str, linenos: &[String]) -> LastMo blame_dates } -lazy_static! { - static ref BLAME_RE: Regex = - Regex::new("(\\d{4}-\\d{2}-\\d{2}).*[+-]\\d{4}\\s+(\\d+)\\)").unwrap(); -} +static BLAME_RE: LazyLock = + LazyLock::new(|| Regex::new("(\\d{4}-\\d{2}-\\d{2}).*[+-]\\d{4}\\s+(\\d+)\\)").unwrap()); fn get_last_modified(line: &str) -> LastModifiedLine { let caps = BLAME_RE.captures(line).unwrap(); @@ -97,7 +95,7 @@ fn main() { .par_iter() .map(|(file, linenos)| (file, get_blame_dates_for_filedeprecation(file, linenos))); deprecations.for_each(|(file, linenos_and_dates)| { - println!("file: {}", file); + println!("file: {file}"); for (date, linenos) in &linenos_and_dates { println!(" last_modified: {} {}", date, linenos.join(" ")); } diff --git a/ql/buramu/tree-sitter-blame/bindings/rust/lib.rs b/ql/buramu/tree-sitter-blame/bindings/rust/lib.rs index 6a0362de7d72..5be6d041fda3 100644 --- a/ql/buramu/tree-sitter-blame/bindings/rust/lib.rs +++ b/ql/buramu/tree-sitter-blame/bindings/rust/lib.rs @@ -27,6 +27,7 @@ extern "C" { } /// The tree-sitter [`LanguageFn`] for this grammar. +// SAFETY: tree_sitter_blame is generated by the Tree-sitter CLI in src/parser.c. pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_blame) }; /// The content of the [`node-types.json`][] file for this grammar. diff --git a/ql/extractor/src/autobuilder.rs b/ql/extractor/src/autobuilder.rs index 9a89cf01b8df..77f171ce285b 100644 --- a/ql/extractor/src/autobuilder.rs +++ b/ql/extractor/src/autobuilder.rs @@ -7,7 +7,7 @@ use codeql_extractor::autobuilder; #[derive(Args)] // The autobuilder takes no command-line options, but this may change in the future. -pub struct Options {} +pub struct Options; pub fn run(_: Options) -> std::io::Result<()> { let database = env::var("CODEQL_EXTRACTOR_QL_WIP_DATABASE") diff --git a/ruby/extractor/Cargo.toml b/ruby/extractor/Cargo.toml index 27640687f866..4ca43af279ce 100644 --- a/ruby/extractor/Cargo.toml +++ b/ruby/extractor/Cargo.toml @@ -19,7 +19,6 @@ tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } rayon = "1.12.0" regex = "1.13.1" encoding = "0.2" -lazy_static = "1.5.1" serde_json = "1.0.154" codeql-extractor = { path = "../../shared/tree-sitter-extractor" } diff --git a/ruby/extractor/src/autobuilder.rs b/ruby/extractor/src/autobuilder.rs index 7b6e148eb794..a4a09e6bada5 100644 --- a/ruby/extractor/src/autobuilder.rs +++ b/ruby/extractor/src/autobuilder.rs @@ -7,7 +7,7 @@ use codeql_extractor::autobuilder; #[derive(Args)] // The autobuilder takes no command-line options, but this may change in the future. -pub struct Options {} +pub struct Options; pub fn run(_: Options) -> std::io::Result<()> { let database = env::var("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE") diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs index 91852a5b0057..e5bac064366b 100644 --- a/ruby/extractor/src/extractor.rs +++ b/ruby/extractor/src/extractor.rs @@ -1,12 +1,12 @@ use clap::Args; use codeql_extractor::file_paths::PathTransformer; -use lazy_static::lazy_static; use rayon::prelude::*; use std::borrow::Cow; use std::collections::HashSet; use std::fs; use std::io::BufRead; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use tree_sitter::{Language, Parser, Range}; use codeql_extractor::{diagnostics, extractor, file_paths, node_types, trap}; @@ -26,7 +26,8 @@ pub struct Options { file_list: String, } -pub fn run(options: Options) -> std::io::Result<()> { +#[allow(clippy::too_many_lines)] +pub fn run(options: &Options) -> std::io::Result<()> { extractor::set_tracing_level("ruby"); tracing::info!("Extraction started"); let diagnostics = diagnostics::DiagnosticLoggers::new("ruby"); @@ -159,7 +160,7 @@ pub fn run(options: Options) -> std::io::Result<()> { match encoding .decode(&source, encoding::types::DecoderTrap::Replace) { - Ok(str) => source = str.as_bytes().to_owned(), + Ok(str) => str.as_bytes().clone_into(&mut source), Err(msg) => { needs_conversion = false; diagnostics_writer.write( @@ -222,7 +223,7 @@ pub fn run(options: Options) -> std::io::Result<()> { } else { std::fs::copy(&path, &src_archive_file)?; } - write_trap(&trap_dir, path, &trap_writer, trap_compression, path_transformer.as_ref()) + write_trap(&trap_dir, &path, &trap_writer, trap_compression, path_transformer.as_ref()) }) .expect("failed to extract files"); @@ -231,7 +232,7 @@ pub fn run(options: Options) -> std::io::Result<()> { extractor::populate_empty_location(&mut trap_writer); let res = write_trap( &trap_dir, - path, + &path, &trap_writer, trap_compression, path_transformer.as_ref(), @@ -246,9 +247,8 @@ pub fn run(options: Options) -> std::io::Result<()> { res } -lazy_static! { - static ref CP_NUMBER: regex::Regex = regex::Regex::new("cp([0-9]+)").unwrap(); -} +static CP_NUMBER: LazyLock = + LazyLock::new(|| regex::Regex::new("cp([0-9]+)").unwrap()); /// Returns the `encoding::Encoding` corresponding to the given encoding name, if one exists. fn encoding_from_name(encoding_name: &str) -> Option<&(dyn encoding::Encoding + Send + Sync)> { @@ -264,14 +264,14 @@ fn encoding_from_name(encoding_name: &str) -> Option<&(dyn encoding::Encoding + fn write_trap( trap_dir: &Path, - path: PathBuf, + path: &Path, trap_writer: &trap::Writer, trap_compression: trap::Compression, path_transformer: Option<&PathTransformer>, ) -> std::io::Result<()> { let trap_file = file_paths::path_for( trap_dir, - &path, + path, trap_compression.extension(), path_transformer, ); @@ -371,6 +371,23 @@ fn get_overlay_changed_files() -> Option> { ) } +fn find_coding_word(content: &[u8], mut index: usize) -> Option { + const CODING: [char; 12] = ['C', 'c', 'O', 'o', 'D', 'd', 'I', 'i', 'N', 'n', 'G', 'g']; + + let mut word_index = 0; + while index < content.len() && word_index < CODING.len() && content[index] as char != '\n' { + if content[index] as char == CODING[word_index] + || content[index] as char == CODING[word_index + 1] + { + word_index += 2; + } else { + word_index = 0; + } + index += 1; + } + (word_index >= CODING.len()).then_some(index) +} + fn scan_coding_comment(content: &[u8]) -> std::option::Option> { let mut index = 0; // skip UTF-8 BOM marker if there is one @@ -384,9 +401,9 @@ fn scan_coding_comment(content: &[u8]) -> std::option::Option> { { index += 2; while index < content.len() && content[index] as char != '\n' { - index += 1 + index += 1; } - index += 1 + index += 1; } index = skip_space(content, index); @@ -395,21 +412,7 @@ fn scan_coding_comment(content: &[u8]) -> std::option::Option> { } index += 1; - const CODING: [char; 12] = ['C', 'c', 'O', 'o', 'D', 'd', 'I', 'i', 'N', 'n', 'G', 'g']; - let mut word_index = 0; - while index < content.len() && word_index < CODING.len() && content[index] as char != '\n' { - if content[index] as char == CODING[word_index] - || content[index] as char == CODING[word_index + 1] - { - word_index += 2 - } else { - word_index = 0; - } - index += 1; - } - if word_index < CODING.len() { - return None; - } + index = find_coding_word(content, index)?; index = skip_space(content, index); if index < content.len() && content[index] as char != ':' && content[index] as char != '=' { diff --git a/ruby/extractor/src/main.rs b/ruby/extractor/src/main.rs index e6721d4e2243..7768a535bc30 100644 --- a/ruby/extractor/src/main.rs +++ b/ruby/extractor/src/main.rs @@ -16,7 +16,7 @@ fn main() -> std::io::Result<()> { let cli = Cli::parse(); match cli { - Cli::Extract(options) => extractor::run(options), + Cli::Extract(options) => extractor::run(&options), Cli::Generate(options) => generator::run(options), Cli::Autobuild(options) => autobuilder::run(options), } diff --git a/rust/ast-generator/patches/rust-analyzer.patch b/rust/ast-generator/patches/rust-analyzer.patch index ffe12683b4aa..cac63dc2b3d8 100644 --- a/rust/ast-generator/patches/rust-analyzer.patch +++ b/rust/ast-generator/patches/rust-analyzer.patch @@ -22,7 +22,64 @@ index 0aad1e2..68f679f 100644 use self::ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc}; pub(crate) fn generate(check: bool) { -@@ -775,7 +779,7 @@ fn pluralize(s: &str) -> String { +@@ -317,7 +321,7 @@ fn generate_nodes(grammar: &AstSrc) -> String { + let trait_name = format_ident!("{}", trait_name); + let kinds: Vec<_> = nodes + .iter() +- .map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string()))) ++ .map(|name| format_ident!("{}", to_upper_snake_case(&name.name))) + .collect(); + let nodes = nodes.iter().map(|node| format_ident!("{}", node.name)); + ( +@@ -521,9 +525,12 @@ + contextual_keywords.iter().map(fmt_kw_as_variant).collect::>(); + let contextual_keywords_tokens = contextual_keywords.iter().map(|it| format_ident!("{it}")); + let contextual_keywords_str_match_arm = grammar.contextual_keywords.iter().map(|kw| { +- match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw) { +- Some((_, ed)) => quote! { #kw if edition < #ed }, +- None => quote! { #kw }, ++ if let Some((_, ed)) = ++ grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw) ++ { ++ quote! { #kw if edition < #ed } ++ } else { ++ quote! { #kw } + } + }); + let contextual_keywords_variants_match_arm = grammar +@@ -531,9 +538,12 @@ + .iter() + .map(|kw_s| { + let kw = fmt_kw_as_variant(kw_s); +- match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw_s) { +- Some((_, ed)) => quote! { #kw if edition < #ed }, +- None => quote! { #kw }, ++ if let Some((_, ed)) = ++ grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw_s) ++ { ++ quote! { #kw if edition < #ed } ++ } else { ++ quote! { #kw } + } + }) + .collect::>(); +@@ -731,6 +741,6 @@ fn to_upper_snake_case(s: &str) -> String { + let mut prev = false; + for c in s.chars() { + if c.is_ascii_uppercase() && prev { +- buf.push('_') ++ buf.push('_'); + } + prev = true; +@@ -745,6 +755,6 @@ fn to_lower_snake_case(s: &str) -> String { + let mut prev = false; + for c in s.chars() { + if c.is_ascii_uppercase() && prev { +- buf.push('_') ++ buf.push('_'); + } + prev = true; +@@ -775,7 +785,7 @@ fn pluralize(s: &str) -> String { } impl Field { @@ -31,8 +88,9 @@ index 0aad1e2..68f679f 100644 matches!(self, Field::Node { cardinality: Cardinality::Many, .. }) } fn token_kind(&self) -> Option { -@@ -787,7 +791,7 @@ impl Field { - _ => None, +@@ -787,7 +797,7 @@ impl Field { +- _ => None, ++ Field::Node { .. } => None, } } - fn method_name(&self) -> String { @@ -40,7 +98,7 @@ index 0aad1e2..68f679f 100644 match self { Field::Token { name, token, .. } => { if let Some(name) = name { -@@ -836,7 +840,7 @@ impl Field { +@@ -836,7 +846,7 @@ impl Field { } } } @@ -49,7 +107,7 @@ index 0aad1e2..68f679f 100644 match self { Field::Token { .. } => format_ident!("SyntaxToken"), Field::Node { ty, .. } => format_ident!("{}", ty), -@@ -849,7 +853,7 @@ fn clean_token_name(name: &str) -> String { +@@ -849,7 +859,7 @@ fn clean_token_name(name: &str) -> String { if cleaned.is_empty() { name.to_owned() } else { cleaned.to_owned() } } @@ -58,6 +116,38 @@ index 0aad1e2..68f679f 100644 let mut res = AstSrc { tokens: "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident" +@@ -865,15 +875,12 @@ + let name = grammar[node].name.clone(); + let rule = &grammar[node].rule; + let _g = panic_context::enter(name.clone()); +- match lower_enum(grammar, rule) { +- Some(variants) => { +- let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants }; +- res.enums.push(enum_src); +- } +- None => { +- let mut fields = Vec::new(); +- lower_rule(&mut fields, grammar, None, rule); +- res.nodes.push(AstNodeSrc { doc: Vec::new(), name, traits: Vec::new(), fields }); +- } ++ if let Some(variants) = lower_enum(grammar, rule) { ++ let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants }; ++ res.enums.push(enum_src); ++ } else { ++ let mut fields = Vec::new(); ++ lower_rule(&mut fields, grammar, None, rule); ++ res.nodes.push(AstNodeSrc { doc: Vec::new(), name, traits: Vec::new(), fields }); + } + } +@@ -979,7 +986,7 @@ fn lower_rule( + } + Rule::Seq(rules) | Rule::Alt(rules) => { + for rule in rules { +- lower_rule(acc, grammar, label, rule) ++ lower_rule(acc, grammar, label, rule); + } + } + Rule::Opt(rule) => lower_rule(acc, grammar, label, rule), diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs index 1ae139f..d4d791e 100644 --- a/xtask/src/codegen/grammar/ast_src.rs diff --git a/rust/ast-generator/src/main.rs b/rust/ast-generator/src/main.rs index f136fd57fc41..d08f426ecbe4 100644 --- a/rust/ast-generator/src/main.rs +++ b/rust/ast-generator/src/main.rs @@ -191,7 +191,7 @@ fn should_predicate_be_extracted(name: &str) -> bool { } fn project_root() -> PathBuf { - let dir = env::var("CARGO_MANIFEST_DIR").unwrap().to_owned(); + let dir = env::var("CARGO_MANIFEST_DIR").unwrap(); PathBuf::from(dir).parent().unwrap().to_owned() } @@ -200,7 +200,7 @@ fn to_lower_snake_case(s: &str) -> String { let mut prev = false; for c in s.chars() { if c.is_ascii_uppercase() && prev { - buf.push('_') + buf.push('_'); } prev = true; @@ -229,10 +229,10 @@ struct Schema { } fn get_bases(name: &str, super_types: &BTreeMap>) -> Vec { - super_types - .get(name) - .map(|tys| tys.iter().map(|t| class_name(t)).collect()) - .unwrap_or_else(|| vec!["AstNode".to_string()]) + super_types.get(name).map_or_else( + || vec!["AstNode".to_string()], + |tys| tys.iter().map(|t| class_name(t)).collect(), + ) } fn enum_src_to_schema_class( @@ -279,7 +279,7 @@ fn node_src_to_schema_class( fn fix_blank_lines(s: &str) -> String { // mustache is not very good at avoiding blank lines // adopting the workaround from https://github.com/groue/GRMustache/issues/46#issuecomment-19498046 - s.split("\n") + s.split('\n') .filter(|line| !line.trim().is_empty()) .map(|line| if line == "¶" { "" } else { line }) .join("\n") @@ -288,7 +288,7 @@ fn fix_blank_lines(s: &str) -> String { fn write_schema( grammar: &AstSrc, - super_types: BTreeMap>, + super_types: &BTreeMap>, mustache_ctx: &mustache::Context, ) -> mustache::Result { let mut schema = Schema::default(); @@ -296,13 +296,13 @@ fn write_schema( grammar .enums .iter() - .map(|node| enum_src_to_schema_class(node, &super_types)), + .map(|node| enum_src_to_schema_class(node, super_types)), ); schema.classes.extend( grammar .nodes .iter() - .map(|node| node_src_to_schema_class(node, &super_types)), + .map(|node| node_src_to_schema_class(node, super_types)), ); let template = mustache_ctx.compile_path("schema")?; let res = template.render_to_string(&schema)?; @@ -413,7 +413,7 @@ fn enum_to_extractor_info(node: &AstEnumSrc) -> ExtractorEnumInfo { EnumVariantInfo { name, snake_case_name, - variant_ast_name: v.to_string(), + variant_ast_name: v.clone(), emission, } }) @@ -519,7 +519,7 @@ fn main() -> anyhow::Result<()> { for node in &grammar.enums { for variant in &node.variants { let set = super_types.entry(variant.to_owned()).or_default(); - set.insert(node.name.to_owned()); + set.insert(node.name.clone()); } } // sort things while ensuring super clases are defined before they are used @@ -533,7 +533,7 @@ fn main() -> anyhow::Result<()> { template_path: root.join("ast-generator").join("templates"), template_extension: "mustache".to_string(), }; - let schema = write_schema(&grammar, super_types, &mustache_ctx)?; + let schema = write_schema(&grammar, &super_types, &mustache_ctx)?; let schema_path = root.join("schema/ast.py"); codegen::ensure_file_contents( crate::flags::CodegenType::Grammar, diff --git a/rust/extractor/src/config.rs b/rust/extractor/src/config.rs index 88f7ada715c6..aa1d2421de6c 100644 --- a/rust/extractor/src/config.rs +++ b/rust/extractor/src/config.rs @@ -97,13 +97,10 @@ impl Config { pub fn extract() -> anyhow::Result { let cli_args = CliConfig::parse(); let mut figment = Figment::new() - .merge(Env::raw().filter_map(|f| { - if f.eq("CODEQL_VERBOSITY") { - Some("LOGGING_VERBOSITY".into()) - } else { - None - } - })) + .merge( + Env::raw() + .filter_map(|f| f.eq("CODEQL_VERBOSITY").then(|| "LOGGING_VERBOSITY".into())), + ) .merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_")) .merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_OPTION_")) .merge(Serialized::defaults(cli_args)); @@ -246,7 +243,7 @@ impl Config { } fn to_cfg_override(spec: &str) -> CfgAtom { - if let Some((key, value)) = spec.split_once("=") { + if let Some((key, value)) = spec.split_once('=') { CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(value), diff --git a/rust/extractor/src/config/deserialize.rs b/rust/extractor/src/config/deserialize.rs index 6e1b9b398b63..d66868a7e981 100644 --- a/rust/extractor/src/config/deserialize.rs +++ b/rust/extractor/src/config/deserialize.rs @@ -16,9 +16,8 @@ impl<'de, T: From> Visitor<'de> for VectorVisitor { formatter.write_str("either a sequence, or a comma or newline separated string") } - fn visit_str(self, value: &str) -> Result, E> { - Ok(value - .split(['\n', ',']) + fn visit_str(self, v: &str) -> Result, E> { + Ok(v.split(['\n', ',']) .map(|s| T::from(s.to_owned())) .collect()) } @@ -44,9 +43,8 @@ impl<'de, S: BuildHasher + Default> Visitor<'de> for MapVisitor { ) } - fn visit_str(self, value: &str) -> Result { - Ok(value - .split(['\n', ',']) + fn visit_str(self, v: &str) -> Result { + Ok(v.split(['\n', ',']) .map(|s| match s.split_once('=') { Some((key, value)) => (key.to_owned(), Some(value.to_owned())), None => (s.to_owned(), None), @@ -62,7 +60,7 @@ impl<'de, S: BuildHasher + Default> Visitor<'de> for MapVisitor { while let Some(el) = seq.next_element::()? { match el.split_once('=') { None => { - ret.insert(el.to_owned(), None); + ret.insert(el, None); } Some((key, value)) => { ret.insert(key.to_owned(), Some(value.to_owned())); diff --git a/rust/extractor/src/configuration.rs b/rust/extractor/src/configuration.rs index cbf94ee068d3..bee817689ff1 100644 --- a/rust/extractor/src/configuration.rs +++ b/rust/extractor/src/configuration.rs @@ -97,8 +97,12 @@ impl Ord for DependencySource<'_> { fn cmp(&self, other: &Self) -> Ordering { // Compare by cfg options first, then by manifest parent directory // (arbitrary but deterministic). - cmp_option_by(&self.cfg_options, &other.cfg_options, compare_cfg_options) - .then_with(|| manifest_parent(self.manifest).cmp(manifest_parent(other.manifest))) + cmp_option_by( + self.cfg_options.as_ref(), + other.cfg_options.as_ref(), + compare_cfg_options, + ) + .then_with(|| manifest_parent(self.manifest).cmp(manifest_parent(other.manifest))) } } @@ -211,7 +215,7 @@ impl<'a> DependencyFiles<'a> { mod tests { use super::*; use ra_ap_base_db::{ - CrateGraphBuilder, CrateOrigin, CrateWorkspaceData, FileChange, FileSet, SourceRoot, + CrateGraphBuilder, CrateOrigin, CrateWorkspaceData, Env, FileChange, FileSet, SourceRoot, }; use ra_ap_intern::Symbol; use ra_ap_span::Edition; @@ -247,7 +251,7 @@ mod tests { None, cfg(features), None, - Default::default(), + Env::default(), CrateOrigin::Library { repo: None, name: Symbol::intern(name), diff --git a/rust/extractor/src/crate_graph.rs b/rust/extractor/src/crate_graph.rs index 072bffdc1bf1..2775eb91dce8 100644 --- a/rust/extractor/src/crate_graph.rs +++ b/rust/extractor/src/crate_graph.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use ra_ap_base_db::Crate; use ra_ap_cfg::CfgAtom; use ra_ap_ide_db::RootDatabase; -use ra_ap_vfs::{Vfs, VfsPath}; +use ra_ap_vfs::Vfs; use std::hash::Hash; use std::hash::Hasher; @@ -24,22 +24,22 @@ pub fn extract_crate_graph(trap_provider: &trap::TrapFileProvider, db: &RootData let mut crate_id_map = HashMap::::new(); for krate_id in crate_graph.as_ref() { let krate = krate_id.data(db); - let root_module_file: &VfsPath = vfs.file_path(krate.root_file_id); + let root_module_file = vfs.file_path(krate.root_file_id); if let Some(root_module_file) = root_module_file .as_path() - .map(|p| std::fs::canonicalize(p).unwrap_or(p.into())) + .map(|p| std::fs::canonicalize(p).unwrap_or_else(|_| p.into())) { let mut hasher = std::hash::DefaultHasher::new(); krate_id .cfg_options(db) .into_iter() - .sorted_by(cmp_flag) + .sorted_by(|&a, &b| cmp_flag(a, b)) .for_each(|x| format!("{x}").hash(&mut hasher)); krate .dependencies .iter() - .flat_map(|d| crate_id_map.get(&d.crate_id)) + .filter_map(|d| crate_id_map.get(&d.crate_id)) .sorted() .for_each(|x| x.hash(&mut hasher)); let hash = hasher.finish(); @@ -61,7 +61,7 @@ pub fn extract_crate_graph(trap_provider: &trap::TrapFileProvider, db: &RootData let crate_dependencies: Vec = krate .dependencies .iter() - .flat_map(|x| crate_id_map.get(&x.crate_id).map(|y| (&x.name, y))) + .filter_map(|x| crate_id_map.get(&x.crate_id).map(|y| (&x.name, y))) .map(|(name, (module, hash))| generated::NamedCrate { id: trap::TrapId::Star, name: name.to_string(), @@ -76,7 +76,7 @@ pub fn extract_crate_graph(trap_provider: &trap::TrapFileProvider, db: &RootData .display_name .as_ref() .map(|x| x.canonical_name().to_string()), - version: krate_extra.version.to_owned(), + version: krate_extra.version.clone(), cfg_options: krate_id .cfg_options(db) .into_iter() @@ -96,19 +96,19 @@ pub fn extract_crate_graph(trap_provider: &trap::TrapFileProvider, db: &RootData "Failed to write trap file for crate: {}: {}", root_module_file.display(), err.to_string() - ) + ); }); } } } -fn cmp_flag(a: &&CfgAtom, b: &&CfgAtom) -> Ordering { +fn cmp_flag(a: &CfgAtom, b: &CfgAtom) -> Ordering { match (a, b) { (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()), - (CfgAtom::Flag(a), CfgAtom::KeyValue { key: b, value: _ }) => { + (CfgAtom::Flag(a), CfgAtom::KeyValue { key: b, .. }) => { a.as_str().cmp(b.as_str()).then(Ordering::Less) } - (CfgAtom::KeyValue { key: a, value: _ }, CfgAtom::Flag(b)) => { + (CfgAtom::KeyValue { key: a, .. }, CfgAtom::Flag(b)) => { a.as_str().cmp(b.as_str()).then(Ordering::Greater) } (CfgAtom::KeyValue { key: a, value: av }, CfgAtom::KeyValue { key: b, value: bv }) => a diff --git a/rust/extractor/src/diagnostics.rs b/rust/extractor/src/diagnostics.rs index ab88192c5d3e..8b84f6074351 100644 --- a/rust/extractor/src/diagnostics.rs +++ b/rust/extractor/src/diagnostics.rs @@ -40,7 +40,7 @@ enum Message { impl Default for Message { fn default() -> Self { - Message::TextMessage("".to_string()) + Message::TextMessage(String::new()) } } diff --git a/rust/extractor/src/main.rs b/rust/extractor/src/main.rs index 4ede43224d35..92f119b68777 100644 --- a/rust/extractor/src/main.rs +++ b/rust/extractor/src/main.rs @@ -101,7 +101,7 @@ impl<'a> Extractor<'a> { "Failed to write trap file for: {}: {}", file.display(), err.to_string() - ) + ); }); self.steps .push(ExtractionStep::extract(before_extract, source_kind, file)); @@ -165,7 +165,7 @@ impl<'a> Extractor<'a> { )); } _ => {} - }; + } self.steps.push(ExtractionStep::load_source(before, file)); Ok(()) } @@ -239,6 +239,7 @@ fn cwd() -> anyhow::Result { Ok(abs_path) } +#[allow(clippy::too_many_lines)] fn main() -> anyhow::Result<()> { let start = Instant::now(); let config = config::Config::extract().context("failed to load configuration")?; @@ -258,8 +259,7 @@ fn main() -> anyhow::Result<()> { let (flame_layer, _flush_guard) = if let Some(path) = &config.logging_flamegraph { tracing_flame::FlameLayer::with_file(path) .ok() - .map(|(a, b)| (Some(a), Some(b))) - .unwrap_or((None, None)) + .map_or((None, None), |(a, b)| (Some(a), Some(b))) } else { (None, None) }; @@ -282,7 +282,7 @@ fn main() -> anyhow::Result<()> { let files = inputs .iter() .map(|file| { - let file = std::path::absolute(file).unwrap_or(file.to_path_buf()); + let file = std::path::absolute(file).unwrap_or_else(|_| file.clone()); // On Windows, rust analyzer expects non-`//?/` prefixed paths (see [1]), which is what // `std::fs::canonicalize` returns. So we use `dunce::canonicalize` instead. // [1]: https://github.com/rust-lang/rust-analyzer/issues/18894#issuecomment-2580014730 @@ -326,7 +326,7 @@ fn main() -> anyhow::Result<()> { match extractor.load_source(file, &semantics, &vfs) { Ok(()) => extractor.extract_with_semantics(file, &semantics, &vfs, source_mode), Err(e) => extractor.extract_without_semantics(file, source_mode, e), - }; + } } // Fast path when there is no ambiguity in dependency extraction. if dependency_extraction_ambiguity { @@ -396,7 +396,11 @@ fn main() -> anyhow::Result<()> { let entry = entry.context("failed to read builtins directory")?; let path = entry.path(); if path.extension().is_some_and(|ext| ext == "rs") { - extractor.extract_without_semantics(&path, SourceKind::Library, Default::default()); + extractor.extract_without_semantics( + &path, + SourceKind::Library, + RustAnalyzerNoSemantics::default(), + ); } } diff --git a/rust/extractor/src/qltest.rs b/rust/extractor/src/qltest.rs index e90fc5e91c10..3967c78a30b7 100644 --- a/rust/extractor/src/qltest.rs +++ b/rust/extractor/src/qltest.rs @@ -25,6 +25,10 @@ fn dump_lib() -> anyhow::Result<()> { #[derive(serde::Serialize)] enum TestCargoManifest<'a> { + #[allow( + clippy::empty_enum_variants_with_brackets, + reason = "The Mustache template requires a named Workspace section." + )] Workspace {}, Lib { uses_proc_macro: bool, diff --git a/rust/extractor/src/rust_analyzer.rs b/rust/extractor/src/rust_analyzer.rs index d3129af12796..1077ed96b1b5 100644 --- a/rust/extractor/src/rust_analyzer.rs +++ b/rust/extractor/src/rust_analyzer.rs @@ -84,7 +84,7 @@ impl<'a> RustAnalyzer<'a> { load_config: &LoadCargoConfig, ) -> Option<(RootDatabase, Vfs)> { let progress = |t| trace!("progress: {t}"); - let manifest: &ManifestPath = project.manifest_path(); + let manifest = project.manifest_path(); match load_workspace_at(manifest.as_ref(), config, load_config, &progress) { Ok((db, vfs, _macro_server)) => Some((db, vfs)), Err(err) => { @@ -107,17 +107,18 @@ impl<'a> RustAnalyzer<'a> { reason, }), RustAnalyzer::WithSemantics { vfs, semantics } => { - let file_id = path_to_file_id(path, vfs).ok_or( - RustAnalyzerNoSemantics::warning("file not found in project"), - )?; - let input = std::panic::catch_unwind(|| semantics.db.file_text(file_id)).or( - Err(RustAnalyzerNoSemantics::warning( - "no text available for the file in the project", - )), - )?; - let editioned_file_id = semantics.attach_first_edition_opt(file_id).ok_or( - RustAnalyzerNoSemantics::warning("failed to determine rust edition"), - )?; + let file_id = path_to_file_id(path, vfs) + .ok_or_else(|| RustAnalyzerNoSemantics::warning("file not found in project"))?; + let input = + std::panic::catch_unwind(|| semantics.db.file_text(file_id)).map_err(|_| { + RustAnalyzerNoSemantics::warning( + "no text available for the file in the project", + ) + })?; + let editioned_file_id = + semantics.attach_first_edition_opt(file_id).ok_or_else(|| { + RustAnalyzerNoSemantics::warning("failed to determine rust edition") + })?; Ok((*semantics, editioned_file_id, input)) } } @@ -194,7 +195,7 @@ impl TomlReader { fn read(&mut self, manifest: &ManifestPath) -> anyhow::Result> { if let Some(table) = self.cache.get(manifest) { - return Ok(table.clone()); + return Ok(Rc::clone(table)); } let content = fs::read_to_string(manifest).map_err(|e| { error!("failed to read {} ({e})", manifest.as_str()); @@ -204,7 +205,7 @@ impl TomlReader { error!("failed to parse {} ({e})", manifest.as_str()); e })?); - self.cache.insert(manifest.clone(), table.clone()); + self.cache.insert(manifest.clone(), Rc::clone(&table)); Ok(table) } } @@ -273,6 +274,8 @@ fn find_workspace(reader: &mut TomlReader, manifest: &ProjectManifest) -> Option pub fn find_project_manifests( files: &[PathBuf], ) -> anyhow::Result> { + const LOG_LIMIT: usize = 10; + let current = std::env::current_dir()?; let abs_files: Vec<_> = files .iter() @@ -286,7 +289,6 @@ pub fn find_project_manifests( ret.insert(workspace); } let iter = || ret.iter().map(|m| format!(" {m}")); - const LOG_LIMIT: usize = 10; if ret.len() <= LOG_LIMIT { info!("found manifests:\n{}", iter().join("\n")); } else { @@ -304,6 +306,8 @@ pub fn find_project_manifests( } fn from_utf8_lossy(v: &[u8]) -> (Cow<'_, str>, Option) { + const REPLACEMENT: &str = "\u{FFFD}"; + let mut iter = v.utf8_chunks(); let (first_valid, first_invalid) = if let Some(chunk) = iter.next() { let valid = chunk.valid(); @@ -317,7 +321,6 @@ fn from_utf8_lossy(v: &[u8]) -> (Cow<'_, str>, Option) { return (Cow::Borrowed(""), None); }; - const REPLACEMENT: &str = "\u{FFFD}"; let error_start = first_valid.len() as u32; let error_end = error_start + first_invalid.len() as u32; let error_range = TextRange::new(TextSize::new(error_start), TextSize::new(error_end)); diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index 981a600102c4..8c7ea0eb671d 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -189,7 +189,7 @@ impl<'db> Translator<'db> { Some((start, end)) } - pub fn text_range_for_node(&mut self, node: &impl ast::AstNode) -> Option { + pub fn text_range_for_node(&self, node: &impl ast::AstNode) -> Option { if let Some(span_map) = self.builtin_derive_span_map.as_ref() { // Nodes synthesized by a reconstructed built-in derive expansion are not in the // semantics cache; resolve their original source range through the expansion span map. @@ -202,17 +202,13 @@ impl<'db> Translator<'db> { if let Some(semantics) = self.semantics.as_ref() { let file_range = semantics.original_range(node.syntax()); let file_id = self.file_id?; - if file_id == file_range.file_id { - Some(file_range.range) - } else { - None - } + (file_id == file_range.file_id).then_some(file_range.range) } else { Some(node.syntax().text_range()) } } - fn location_for_node(&mut self, node: &impl ast::AstNode) -> Option<(LineCol, LineCol)> { + fn location_for_node(&self, node: &impl ast::AstNode) -> Option<(LineCol, LineCol)> { self.text_range_for_node(node) .and_then(|r| self.location(r)) } @@ -227,7 +223,7 @@ impl<'db> Translator<'db> { UNKNOWN_LOCATION, ), Some((start, end)) => self.trap.emit_location(self.label, label, start, end), - }; + } } pub fn emit_location_token( &mut self, @@ -242,7 +238,7 @@ impl<'db> Translator<'db> { { let token_range = clipped_range + parent_range2.start() - parent_range.start(); if let Some((start, end)) = self.location(token_range) { - self.trap.emit_location(self.label, label, start, end) + self.trap.emit_location(self.label, label, start, end); } } } @@ -390,7 +386,7 @@ impl<'db> Translator<'db> { .text_range() .start(); self.emit_parse_error(node, &SyntaxError::new(error.message, location)); - }; + } } for err in value.value.iter() { self.emit_parse_error(node, err); @@ -596,14 +592,14 @@ impl<'db> Translator<'db> { .and_then(ast::Type::cast) .and_then(|t| self.emit_type(&t)) { - generated::PathSegment::emit_type_repr(label, t, &mut self.trap.writer) + generated::PathSegment::emit_type_repr(label, t, &mut self.trap.writer); } if let Some(t) = type_refs .next() .and_then(ast::PathType::cast) .and_then(|t| self.emit_path_type(&t)) { - generated::PathSegment::emit_trait_type_repr(label, t, &mut self.trap.writer) + generated::PathSegment::emit_trait_type_repr(label, t, &mut self.trap.writer); } // moreover as we're skipping emission of TypeAnchor, we need to attach its comments to // this path segment diff --git a/rust/extractor/src/util.rs b/rust/extractor/src/util.rs index a12ca6de74a1..5bfa5e3fc8b3 100644 --- a/rust/extractor/src/util.rs +++ b/rust/extractor/src/util.rs @@ -2,8 +2,8 @@ use std::cmp::Ordering; /// Compares options using a custom comparator, with `None` ordered before `Some`. pub(crate) fn cmp_option_by( - left: &Option, - right: &Option, + left: Option<&T>, + right: Option<&T>, compare: impl FnOnce(&T, &T) -> Ordering, ) -> Ordering { match (left, right) { diff --git a/shared/tree-sitter-extractor/Cargo.toml b/shared/tree-sitter-extractor/Cargo.toml index f9702d0ff1de..11ecf7a0e9e9 100644 --- a/shared/tree-sitter-extractor/Cargo.toml +++ b/shared/tree-sitter-extractor/Cargo.toml @@ -17,7 +17,6 @@ tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } rayon = "1.12.0" regex = "1.13.1" encoding = "0.2" -lazy_static = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = { version = "0.4.45", features = ["serde"] } diff --git a/shared/tree-sitter-extractor/src/diagnostics.rs b/shared/tree-sitter-extractor/src/diagnostics.rs index 412647ee07a5..f83cc6d26677 100644 --- a/shared/tree-sitter-extractor/src/diagnostics.rs +++ b/shared/tree-sitter-extractor/src/diagnostics.rs @@ -95,7 +95,7 @@ impl LogWriter { source: Source { id: format!("{}/{}", self.extractor, id), name: name.to_owned(), - extractor_name: Some(self.extractor.to_owned()), + extractor_name: Some(self.extractor.clone()), }, markdown_message: String::new(), plaintext_message: String::new(), @@ -134,7 +134,7 @@ impl LogWriter { e ); self.path = None; - self.inner = None + self.inner = None; } Ok(file) => self.inner = Some(std::io::BufWriter::new(file)), } @@ -189,7 +189,7 @@ impl DiagnosticLoggers { }; } THREAD_NUM.with(|n| LogWriter { - extractor: self.extractor.to_owned(), + extractor: self.extractor.clone(), inner: None, path: self .root @@ -235,12 +235,12 @@ impl DiagnosticMessage { start_line: Some(line), .. }) => format!("{}:{}: {}", path, line, self.plaintext_message), - _ => self.plaintext_message.to_owned(), + _ => self.plaintext_message.clone(), } } fn text(&mut self, text: &str) -> &mut Self { - self.plaintext_message = text.to_owned(); + text.clone_into(&mut self.plaintext_message); self } @@ -285,7 +285,7 @@ impl DiagnosticMessage { } pub fn markdown(&mut self, text: &str) -> &mut Self { - self.markdown_message = text.to_owned(); + text.clone_into(&mut self.markdown_message); self } pub fn severity(&mut self, severity: Severity) -> &mut Self { @@ -317,7 +317,7 @@ impl DiagnosticMessage { self } pub fn file(&mut self, path: &str) -> &mut Self { - let loc = self.location.get_or_insert(Default::default()); + let loc = self.location.get_or_insert_default(); loc.file = Some(path.to_owned()); self } @@ -329,7 +329,7 @@ impl DiagnosticMessage { end_line: usize, end_column: usize, ) -> &mut Self { - let loc = self.location.get_or_insert(Default::default()); + let loc = self.location.get_or_insert_default(); loc.file = Some(path.to_owned()); loc.start_line = Some(start_line); loc.start_column = Some(start_column); diff --git a/shared/tree-sitter-extractor/src/extractor/driver.rs b/shared/tree-sitter-extractor/src/extractor/driver.rs index 6e6172b7425b..8a6ee33accc9 100644 --- a/shared/tree-sitter-extractor/src/extractor/driver.rs +++ b/shared/tree-sitter-extractor/src/extractor/driver.rs @@ -45,6 +45,7 @@ pub(crate) trait LanguageExtractor: Sync { /// Sets up the thread pool, builds a combined glob set, and for each input file /// dispatches to the matching language's [`LanguageExtractor::extract_file`], /// writing the resulting TRAP and a source-archive copy. +#[allow(clippy::too_many_lines)] pub(crate) fn run_extractor( prefix: &str, languages: &[L], @@ -103,7 +104,7 @@ pub(crate) fn run_extractor( .iter() .map(|file_list| { File::open(file_list) - .unwrap_or_else(|_| panic!("Unable to open file list at {file_list:?}")) + .unwrap_or_else(|_| panic!("Unable to open file list at {}", file_list.display())) }) .collect(); diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index 1bd8677b7414..5a1856c76fbc 100644 --- a/shared/tree-sitter-extractor/src/extractor/mod.rs +++ b/shared/tree-sitter-extractor/src/extractor/mod.rs @@ -39,7 +39,7 @@ trait AstNode { } } -impl<'a> AstNode for Node<'a> { +impl AstNode for Node<'_> { fn kind(&self) -> &str { Node::kind(self) } @@ -135,17 +135,17 @@ pub fn default_subscriber_with_level( .with_filter( tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else( |_| -> tracing_subscriber::EnvFilter { - let verbosity = verbosity - .as_ref() - .map(|v| match v.to_lowercase().as_str() { - "off" | "errors" => "error", - "warnings" => "warn", - "info" | "progress" => "info", - "debug" | "progress+" => "debug", - "trace" | "progress++" | "progress+++" => "trace", - _ => "warn", - }) - .unwrap_or_else(|| "warn"); + let verbosity = + verbosity + .as_ref() + .map_or("warn", |v| match v.to_lowercase().as_str() { + "off" | "errors" => "error", + "warnings" => "warn", + "info" | "progress" => "info", + "debug" | "progress+" => "debug", + "trace" | "progress++" | "progress+++" => "trace", + _ => "warn", + }); tracing_subscriber::EnvFilter::new(format!( "{language}_extractor={verbosity},codeql_extractor={verbosity}" )) @@ -184,7 +184,7 @@ fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label { "files", vec![ trap::Arg::Label(file_label), - trap::Arg::String("".to_string()), + trap::Arg::String(String::new()), ], ); } @@ -290,6 +290,7 @@ pub fn location_label(writer: &mut trap::Writer, location: trap::Location) -> tr } /// Extracts the source file at `path`, which is assumed to be canonicalized. +/// /// When `desugarer` is `Some`, the parsed tree is first transformed /// through the supplied yeast desugarer before TRAP extraction. Building /// the desugarer (which parses YAML and constructs the schema) is the @@ -356,8 +357,10 @@ pub struct ParsedTree { } /// A piece of side-channel `extra` content (a comment or unexpected text) -/// recovered by a parser. `kind` is a language-defined id written verbatim into -/// the `_trivia_tokeninfo` table (the tree-sitter parser uses the +/// recovered by a parser. +/// +/// `kind` is a language-defined id written verbatim into the +/// `_trivia_tokeninfo` table (the tree-sitter parser uses the /// grammar's node kind id here; a custom parser supplies its own stable /// enumeration). pub struct ExtraToken { @@ -366,8 +369,9 @@ pub struct ExtraToken { pub text: String, } -/// Build a parser closure that parses `source` with a tree-sitter `language`, -/// producing a [`ParsedTree`]: a `yeast::Ast` (via [`yeast::Ast::from_tree`]) +/// Build a parser closure that produces a [`ParsedTree`] using a tree-sitter language. +/// +/// Parses `source` with `language`, producing a `yeast::Ast` (via [`yeast::Ast::from_tree`]) /// plus the `extra` nodes (comments and similar) recovered as side-channel /// tokens. This lets a tree-sitter language plug into the same /// `ParsedTree`-producing interface as a custom parser. @@ -413,8 +417,9 @@ fn collect_extras(node: Node<'_>, source: &[u8], out: &mut Vec) { } } -/// Extract a file from a [`ParsedTree`]-producing parser that desugars. The -/// parser yields a `yeast::Ast` plus side-channel `extra` tokens; the AST is +/// Extract a file from a [`ParsedTree`]-producing parser that desugars. +/// +/// The parser yields a `yeast::Ast` plus side-channel `extra` tokens; the AST is /// rewritten through `desugarer` ([`yeast::Desugarer::run_from_ast`]) before /// TRAP extraction, and the `extra` tokens (comments and similar, which the /// desugared AST does not carry) are emitted from the side channel. Both @@ -478,7 +483,7 @@ struct ExtraNode { } impl AstNode for ExtraNode { - fn kind(&self) -> &str { + fn kind(&self) -> &'static str { "extra" } fn is_named(&self) -> bool { @@ -528,9 +533,9 @@ struct Visitor<'a> { file_label: trap::Label, /// The source code as a UTF-8 byte array source: &'a [u8], - /// A diagnostics::LogWriter to write diagnostic messages + /// A `diagnostics::LogWriter` to write diagnostic messages diagnostics_writer: &'a mut diagnostics::LogWriter, - /// A trap::Writer to accumulate trap entries + /// A `trap::Writer` to accumulate trap entries trap_writer: &'a mut trap::Writer, /// Language-specific name of the AST location table ast_node_location_table_name: String, @@ -547,7 +552,7 @@ struct Visitor<'a> { /// All children append their data to the list. When the visitor leaves a /// node the list containing the child data is popped from the stack and /// matched against the dbscheme for the node. If the expectations are met - /// the corresponding row definitions are added to the trap_output. + /// the corresponding row definitions are added to the trap output. stack: Vec<(trap::Label, usize, Vec)>, } @@ -623,7 +628,7 @@ impl<'a> Visitor<'a> { trap::Arg::Label(id), trap::Arg::Int(severity_code), trap::Arg::String("parse_error".to_string()), - trap::Arg::String(mesg.plaintext_message.to_owned()), + trap::Arg::String(mesg.plaintext_message.clone()), trap::Arg::String(full_error_message), trap::Arg::Label(loc), ], @@ -676,7 +681,7 @@ impl<'a> Visitor<'a> { true, ); return false; - }; + } let id = self.trap_writer.fresh_id(); @@ -684,6 +689,7 @@ impl<'a> Visitor<'a> { true } + #[allow(clippy::too_many_lines)] fn leave_node(&mut self, field_name: Option<&'static str>, node: &N) { if node.is_error() || node.is_missing() { return; @@ -722,7 +728,7 @@ impl<'a> Visitor<'a> { trap::Arg::Int(parent_index), ], ); - }; + } self.trap_writer.add_tuple( &self.tokeninfo_table_name, vec![ @@ -750,13 +756,13 @@ impl<'a> Visitor<'a> { trap::Arg::Int(parent_index), ], ); - }; + } let mut all_args = vec![trap::Arg::Label(id)]; all_args.extend(args); self.trap_writer.add_tuple(table_name, all_args); } } - _ => { + EntryKind::Union { .. } => { self.record_parse_error( loc_label, self.diagnostics_writer @@ -793,7 +799,7 @@ impl<'a> Visitor<'a> { named: node.is_named(), }, }); - }; + } } } @@ -874,7 +880,7 @@ impl<'a> Visitor<'a> { Storage::Table { name: table_name, has_index, - column_name: _, + .. } => { for (index, child_value) in child_values.iter().enumerate() { if !*has_index && index > 0 { @@ -891,7 +897,7 @@ impl<'a> Visitor<'a> { } let mut args = vec![trap::Arg::Label(parent_id)]; if *has_index { - args.push(trap::Arg::Int(index)) + args.push(trap::Arg::Int(index)); } args.push(child_value.clone()); self.trap_writer.add_tuple(table_name, args); @@ -899,7 +905,7 @@ impl<'a> Visitor<'a> { } } } - if is_valid { Some(args) } else { None } + is_valid.then_some(args) } fn type_matches(&self, tp: &TypeName, type_info: &node_types::FieldTypeInfo) -> bool { @@ -945,7 +951,7 @@ impl<'a> Visitor<'a> { fn sliced_source_arg(source: &[u8], n: &N) -> trap::Arg { trap::Arg::String(n.opt_string_content().unwrap_or_else(|| { let range = n.byte_range(); - String::from_utf8_lossy(&source[range.start..range.end]).into_owned() + String::from_utf8_lossy(&source[range]).into_owned() })) } diff --git a/shared/tree-sitter-extractor/src/extractor/simple.rs b/shared/tree-sitter-extractor/src/extractor/simple.rs index 329c9caddc68..271f72c610b3 100644 --- a/shared/tree-sitter-extractor/src/extractor/simple.rs +++ b/shared/tree-sitter-extractor/src/extractor/simple.rs @@ -6,7 +6,9 @@ use crate::extractor::driver::{self, LanguageExtractor}; use crate::node_types::{self, NodeTypeMap}; /// A tree-sitter language extracted directly from its parse tree, with no -/// desugaring. Comments and other `extra` nodes are emitted inline as tokens. +/// desugaring. +/// +/// Comments and other `extra` nodes are emitted inline as tokens. /// Languages that rewrite their syntax tree use /// [`crate::extractor::desugaring`] instead. pub struct LanguageSpec { diff --git a/shared/tree-sitter-extractor/src/file_paths.rs b/shared/tree-sitter-extractor/src/file_paths.rs index 821a4b2309dd..49d0802332eb 100644 --- a/shared/tree-sitter-extractor/src/file_paths.rs +++ b/shared/tree-sitter-extractor/src/file_paths.rs @@ -4,20 +4,22 @@ use std::{ }; /// Given an absolute path, returns a relative path if it's under `source_root`, -/// otherwise the absolute path as-is. This is used for diagnostic locations, which -/// should use relative paths per the CodeQL diagnostic message format spec. +/// otherwise the absolute path as-is. +/// +/// This is used for diagnostic locations, which should use relative paths per +/// the CodeQL diagnostic message format spec. /// Absolute path fallback is handled downstream by the CLI's SARIF generator. pub fn relativize_for_diagnostic(path: &Path, source_root: Option<&Path>) -> String { source_root .and_then(|root| path.strip_prefix(root).ok()) .and_then(|rel| rel.to_str()) - .map(|s| s.replace('\\', "/")) - .unwrap_or_else(|| path.display().to_string()) + .map_or_else(|| path.display().to_string(), |s| s.replace('\\', "/")) } -/// This represents the minimum supported path transformation that is needed to support extracting -/// overlay databases. Specifically, it represents a transformer where one path prefix is replaced -/// with a different prefix. +/// A path-prefix replacement for extracting overlay databases. +/// +/// This is the minimum supported path transformation: one path prefix is +/// replaced with a different prefix. pub struct PathTransformer { pub original: String, pub replacement: String, @@ -65,10 +67,10 @@ pub fn load_path_transformer() -> std::io::Result> { } let replacement = lines[0] .strip_prefix('#') - .ok_or(unsupported_transformer_error())?; + .ok_or_else(unsupported_transformer_error)?; let original = lines[1] .strip_suffix("//") - .ok_or(unsupported_transformer_error())?; + .ok_or_else(unsupported_transformer_error)?; Ok(Some(PathTransformer { original: original.to_owned(), @@ -115,7 +117,7 @@ fn normalize_path(path: &Path) -> String { if path_components.peek().is_none() { // The path points at a root directory, so we need to add a // trailing slash, e.g. `C:/` instead of `C:`. - components.push("".to_string()); + components.push(String::new()); } } std::path::Component::CurDir => {} @@ -135,8 +137,8 @@ pub fn path_from_string(path: &str) -> PathBuf { let mut path = PathBuf::from(path); // make path absolute if path.is_relative() { - path = std::env::current_dir().unwrap().join(path) - }; + path = std::env::current_dir().unwrap().join(path); + } let mut components = path.components(); // make Windows paths verbatim (with `\\?\` prefixes) which allow for extended-length paths. @@ -167,7 +169,7 @@ pub fn path_from_string(path: &str) -> PathBuf { // remove `.` and `..` components for component in components { match component { - std::path::Component::CurDir => continue, + std::path::Component::CurDir => {} std::path::Component::ParentDir => { result.pop(); } @@ -194,7 +196,7 @@ pub fn path_for( match component { std::path::Component::Prefix(prefix) => match prefix.kind() { std::path::Prefix::Disk(letter) | std::path::Prefix::VerbatimDisk(letter) => { - result.push(format!("{}_", letter as char)) + result.push(format!("{}_", letter as char)); } std::path::Prefix::Verbatim(x) | std::path::Prefix::DeviceNS(x) => { result.push(x); diff --git a/shared/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index 81bf0955cd6e..d145face26cc 100644 --- a/shared/tree-sitter-extractor/src/generator/mod.rs +++ b/shared/tree-sitter-extractor/src/generator/mod.rs @@ -6,7 +6,6 @@ use std::io::Write; use std::path::PathBuf; use crate::node_types; -use yeast; pub mod dbscheme; pub mod language; @@ -14,6 +13,7 @@ pub mod ql; pub mod ql_gen; /// Generate a dbscheme and QL library for the given languages. +#[allow(clippy::too_many_lines)] pub fn generate( languages: Vec, dbscheme_path: PathBuf, @@ -65,14 +65,14 @@ pub fn generate( for language in languages { let prefix = node_types::to_snake_case(&language.name); - let ast_node_name = format!("{}_ast_node", prefix); - let node_location_table_name = format!("{}_ast_node_location", prefix); - let node_parent_table_name = format!("{}_ast_node_parent", prefix); - let token_name = format!("{}_token", prefix); - let tokeninfo_name = format!("{}_tokeninfo", prefix); - let trivia_token_name = format!("{}_trivia_token", prefix); - let trivia_tokeninfo_name = format!("{}_trivia_tokeninfo", prefix); - let reserved_word_name = format!("{}_reserved_word", prefix); + let ast_node_name = format!("{prefix}_ast_node"); + let node_location_table_name = format!("{prefix}_ast_node_location"); + let node_parent_table_name = format!("{prefix}_ast_node_parent"); + let token_name = format!("{prefix}_token"); + let tokeninfo_name = format!("{prefix}_tokeninfo"); + let trivia_token_name = format!("{prefix}_trivia_token"); + let trivia_tokeninfo_name = format!("{prefix}_trivia_tokeninfo"); + let reserved_word_name = format!("{prefix}_reserved_word"); // When a desugaring is configured, comments and other `extra` nodes are // preserved from the original parse tree as `TriviaToken`s. let has_trivia_tokens = language.desugar.is_some(); @@ -97,7 +97,7 @@ pub fn generate( } writeln!(&mut dbscheme_writer, "/*- {} dbscheme -*/", language.name)?; dbscheme::write(&mut dbscheme_writer, &dbscheme_entries)?; - let token_case = create_token_case(&token_name, token_kinds); + let token_case = create_token_case(&token_name, &token_kinds); let mut dbscheme_tail = vec![ dbscheme::Entry::Table(create_tokeninfo(&tokeninfo_name, &token_name)), dbscheme::Entry::Case(token_case), @@ -265,7 +265,7 @@ fn make_field_type<'a>( node_types::FieldTypeInfo::Multiple { types, dbscheme_union, - ql_class: _, + .. } => { // This field can have one of several types. Create an ad-hoc QL union // type to represent them. @@ -346,11 +346,7 @@ fn add_field_for_table_storage<'a>( }, // In addition to the field being unique, the combination of // parent+index is unique, so add a keyset for them. - keysets: if has_index { - Some(vec![parent_name, "index"]) - } else { - None - }, + keysets: has_index.then(|| vec![parent_name, "index"]), }; (field_table, field_type_entry) } @@ -592,7 +588,7 @@ fn create_tokeninfo<'a>(name: &'a str, type_name: &'a str) -> dbscheme::Table<'a } } -fn create_token_case<'a>(name: &'a str, token_kinds: Map<&'a str, usize>) -> dbscheme::Case<'a> { +fn create_token_case<'a>(name: &'a str, token_kinds: &Map<&'a str, usize>) -> dbscheme::Case<'a> { let branches: Vec<(usize, &str)> = token_kinds .iter() .map(|(&name, kind_id)| (*kind_id, name)) diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index c780aec681cd..5c6e12a13a6c 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -365,7 +365,7 @@ impl fmt::Display for FormalParameter<'_> { /// Generates a QL library by writing the given `elements` to the `file`. pub fn write(file: &mut dyn std::io::Write, elements: &[TopLevel]) -> std::io::Result<()> { for element in elements { - write!(file, "{}\n\n", element)?; + write!(file, "{element}\n\n")?; } Ok(()) } diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index bd05de8316d8..975dd4d24a4c 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -4,6 +4,7 @@ use crate::{generator::ql, node_types}; /// Creates the hard-coded `AstNode` class that acts as a supertype of all /// classes we generate. +#[allow(clippy::too_many_lines)] pub fn create_ast_node_class<'a>( ast_node: &'a str, node_location_table: &'a str, @@ -205,8 +206,10 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl } } -/// Creates the `TriviaToken` class. Trivia tokens (e.g. comments) are -/// `extra` nodes preserved from the original parse tree even when the tree has +/// Creates the `TriviaToken` class. +/// +/// Trivia tokens (e.g. comments) are `extra` nodes preserved from the original +/// parse tree even when the tree has /// been rewritten by a desugaring pass. They are not part of the regular /// `Token` hierarchy because they do not appear in the (possibly desugared) /// output schema. @@ -510,9 +513,11 @@ pub fn create_discardable_location_predicate() -> ql::Predicate<'static> { } } -/// Creates a discard predicate for `@location_default` entities. This is necessary because the -/// tree-sitter extractors use `*` IDs for locations, which means that locations don't get shared -/// between the base and overlay databases. +/// Creates a discard predicate for `@location_default` entities. +/// +/// This is necessary because the tree-sitter extractors use `*` IDs for +/// locations, which means that locations don't get shared between the base +/// and overlay databases. pub fn create_discard_location_predicate() -> ql::Predicate<'static> { ql::Predicate { name: "discardLocation", @@ -622,6 +627,7 @@ fn create_get_field_expr_for_table_storage<'a>( /// `parent_name` - the name of the parent node /// `field` - the field whose getters we are creating /// `field_type` - the db name of the field's type (possibly being a union we created) +#[allow(clippy::too_many_lines)] fn create_field_getters<'a>( main_table_name: &'a str, main_table_arity: usize, @@ -645,7 +651,7 @@ fn create_field_getters<'a>( // use in `getAFieldOrChild` (where we use a "don't care" expression to // match any index). let (get_value, get_value_any_index) = match &field.storage { - node_types::Storage::Column { name: _ } => { + node_types::Storage::Column { .. } => { let column_index = *main_table_column_index; *main_table_column_index += 1; ( @@ -666,17 +672,17 @@ fn create_field_getters<'a>( node_types::Storage::Table { name: field_table_name, has_index, - column_name: _, + .. } => ( create_get_field_expr_for_table_storage( get_value_result_var_name, field_table_name, - if *has_index { Some("i") } else { None }, + has_index.then_some("i"), ), create_get_field_expr_for_table_storage( get_value_result_var_name, field_table_name, - if *has_index { Some("_") } else { None }, + has_index.then_some("_"), ), ), }; @@ -765,11 +771,7 @@ fn field_getter_return_type<'a>( node_types::FieldTypeInfo::Single(t) => { Some(ql::Type::Facade(&nodes.get(t).unwrap().ql_class_name)) } - node_types::FieldTypeInfo::Multiple { - types: _, - dbscheme_union: _, - ql_class, - } => Some(ql::Type::Facade(ql_class)), + node_types::FieldTypeInfo::Multiple { ql_class, .. } => Some(ql::Type::Facade(ql_class)), node_types::FieldTypeInfo::ReservedWordInt(_) => Some(ql::Type::String), } } @@ -966,22 +968,15 @@ fn is_predicate_inherited( } /// Converts the given node types into CodeQL classes wrapping the dbscheme. +#[allow(clippy::too_many_lines)] pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { let mut classes = Vec::new(); - let mut token_kinds = BTreeSet::new(); let direct_supertypes = compute_direct_supertypes(nodes); let exposed_predicate_signatures = compute_exposed_predicate_signatures(nodes); - for (type_name, node) in nodes { - if let node_types::EntryKind::Token { .. } = &node.kind - && type_name.named - { - token_kinds.insert(&type_name.kind); - } - } for (type_name, node) in nodes { match &node.kind { - node_types::EntryKind::Token { kind_id: _ } => { + node_types::EntryKind::Token { .. } => { if type_name.named { let get_a_primary_ql_class = create_get_a_primary_ql_class(&node.ql_class_name, true); @@ -1037,9 +1032,11 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { name: main_table_name, fields, } => { - if fields.is_empty() { - panic!("Encountered node '{}' with no fields", type_name.kind); - } + assert!( + !fields.is_empty(), + "Encountered node '{}' with no fields", + type_name.kind + ); // Count how many columns there will be in the main table. There // will be one for the id, plus one for each field that's stored @@ -1091,7 +1088,7 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { } main_class.predicates.extend(get_preds); if let Some(get_child_expr) = get_child_expr { - get_child_exprs.push(get_child_expr) + get_child_exprs.push(get_child_expr); } } @@ -1115,15 +1112,16 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { classes } -/// Creates a `PrintAst` module containing a `getChild` predicate that maps each -/// AST node to its children together with the name of the member predicate that -/// produced them (and, for indexed fields, the index). This mirrors the -/// information exposed by `getAFieldOrChild`, but keeps the member predicate +/// Creates a `PrintAst` module with a `getChild` predicate for labelled AST edges. +/// +/// The predicate maps each AST node to its children together with the name of the +/// member predicate that produced them (and, for indexed fields, the index). +/// This mirrors the information exposed by `getAFieldOrChild`, but keeps the member predicate /// name and index so that an AST printer can render labelled edges. pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel<'_> { let mut disjuncts: Vec = Vec::new(); for node in nodes.values() { - if let node_types::EntryKind::Table { name: _, fields } = &node.kind { + if let node_types::EntryKind::Table { fields, .. } = &node.kind { for field in fields { // `ReservedWordInt` fields have string-valued getters, so they // are not children and are excluded (just as they are from @@ -1217,7 +1215,7 @@ mod tests { #[test] fn indexes_predicate_signatures_exposed_by_classes() { - let yaml = r#" + let yaml = r" supertypes: callable: subtypes: [function_like] @@ -1236,7 +1234,7 @@ named: parameter: block: identifier: -"#; +"; let json = yeast::node_types_yaml::convert(yaml).unwrap(); let nodes = node_types::read_node_types_str("test", &json).unwrap(); let signatures = compute_exposed_predicate_signatures(&nodes); @@ -1267,7 +1265,7 @@ named: #[test] fn generates_supertype_getters_and_concrete_overrides() { - let yaml = r#" + let yaml = r" supertypes: callable: subtypes: [function] @@ -1280,7 +1278,7 @@ named: body?: block parameter: block: -"#; +"; let json = yeast::node_types_yaml::convert(yaml).unwrap(); let nodes = node_types::read_node_types_str("test", &json).unwrap(); let generated = convert_nodes(&nodes) diff --git a/shared/tree-sitter-extractor/src/node_types.rs b/shared/tree-sitter-extractor/src/node_types.rs index 43fc3e11683d..9a72712e74f2 100644 --- a/shared/tree-sitter-extractor/src/node_types.rs +++ b/shared/tree-sitter-extractor/src/node_types.rs @@ -5,7 +5,7 @@ use std::path::Path; use std::collections::BTreeSet as Set; use std::fs; -/// A lookup table from TypeName to Entry. +/// A lookup table from `TypeName` to `Entry`. pub type NodeTypeMap = BTreeMap; #[derive(Debug)] @@ -60,7 +60,7 @@ pub struct Field { pub parent: TypeName, pub type_info: FieldTypeInfo, /// The name of the field or None for the anonymous 'children' - /// entry from node_types.json + /// entry from `node_types.json` pub name: Option, /// The name of the predicate to get this field. pub getter_name: String, @@ -70,7 +70,7 @@ pub struct Field { pub storage: Storage, } -fn name_for_field_or_child(name: &Option) -> String { +fn name_for_field_or_child(name: Option<&String>) -> String { match name { Some(name) => name.clone(), None => "child".to_owned(), @@ -110,7 +110,7 @@ pub fn read_node_types_str(prefix: &str, node_types_json: &str) -> std::io::Resu fn convert_type(node_type: &NodeType) -> TypeName { TypeName { - kind: node_type.kind.to_string(), + kind: node_type.kind.clone(), named: node_type.named, } } @@ -119,6 +119,7 @@ fn convert_types(node_types: &[NodeType]) -> Set { node_types.iter().map(convert_type).collect() } +#[allow(clippy::too_many_lines)] pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { let mut entries = NodeTypeMap::new(); let mut token_kinds = Set::new(); @@ -138,7 +139,7 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { let flattened_name = &node_type_name(&node.kind, node.named); let dbscheme_name = escape_name(flattened_name); let ql_class_name = dbscheme_name_to_class_name(&dbscheme_name); - let dbscheme_name = format!("{}_{}", prefix, dbscheme_name); + let dbscheme_name = format!("{prefix}_{dbscheme_name}"); let subtypes = &node.subtypes; if !subtypes.is_empty() { // It's a tree-sitter supertype node, for which we create a union @@ -152,7 +153,7 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { add_field( prefix, &type_name, - Some(field_name.to_string()), + Some(field_name.clone()), field_info, &mut fields, &token_kinds, @@ -187,8 +188,8 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { kind: node.kind.clone(), named: node.named, }; - let table_name = escape_name(&(format!("{}_def", flattened_name))); - let table_name = format!("{}_{}", prefix, table_name); + let table_name = escape_name(&(format!("{flattened_name}_def"))); + let table_name = format!("{prefix}_{table_name}"); let mut fields = Vec::new(); @@ -198,7 +199,7 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { add_field( prefix, &type_name, - Some(field_name.to_string()), + Some(field_name.clone()), field_info, &mut fields, &token_kinds, @@ -234,13 +235,13 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { counter += 1; let unprefixed_name = node_type_name(&type_name.kind, true); Entry { - dbscheme_name: escape_name(&format!("{}_token_{}", prefix, unprefixed_name)), + dbscheme_name: escape_name(&format!("{prefix}_token_{unprefixed_name}")), ql_class_name: dbscheme_name_to_class_name(&escape_name(&unprefixed_name)), kind: EntryKind::Token { kind_id: counter }, } } else { Entry { - dbscheme_name: format!("{}_reserved_word", prefix), + dbscheme_name: format!("{prefix}_reserved_word"), ql_class_name: "ReservedWord".to_owned(), kind: EntryKind::Token { kind_id: 0 }, } @@ -259,7 +260,7 @@ fn add_field( token_kinds: &Set, ) { let parent_flattened_name = node_type_name(&parent_type_name.kind, parent_type_name.named); - let column_name = escape_name(&name_for_field_or_child(&field_name)); + let column_name = escape_name(&name_for_field_or_child(field_name.as_ref())); let storage = if !field_info.multiple && field_info.required { // This field must appear exactly once, so we add it as // a column to the main table for the node type. @@ -271,7 +272,7 @@ fn add_field( "{}_{}_{}", prefix, parent_flattened_name, - name_for_field_or_child(&field_name) + name_for_field_or_child(field_name.as_ref()) )); Storage::Table { has_index, @@ -293,7 +294,7 @@ fn add_field( for (counter, t) in converted_types.into_iter().enumerate() { let dbscheme_variant_name = escape_name(&format!("{}_{}_{}", prefix, parent_flattened_name, t.kind)); - field_token_ints.insert(t.kind.to_owned(), (counter, dbscheme_variant_name)); + field_token_ints.insert(t.kind, (counter, dbscheme_variant_name)); } FieldTypeInfo::ReservedWordInt(field_token_ints) } else if field_info.types.len() == 1 { @@ -306,28 +307,26 @@ fn add_field( "{}_{}_{}_type", prefix, parent_flattened_name, - name_for_field_or_child(&field_name) + name_for_field_or_child(field_name.as_ref()) ), ql_class: "AstNode".to_owned(), } }; let getter_name = format!( "get{}", - dbscheme_name_to_class_name(&escape_name(&name_for_field_or_child(&field_name))) + dbscheme_name_to_class_name(&escape_name(&name_for_field_or_child(field_name.as_ref()))) ); let getter_suffix = getter_name.strip_prefix("get").unwrap_or(&getter_name); let article = match getter_suffix.chars().next().map(|c| c.to_ascii_lowercase()) { Some('a' | 'e' | 'i' | 'o' | 'u') => "An", _ => "A", }; - let any_getter_name = if field_info.multiple { - Some(format!("get{article}{getter_suffix}")) - } else { - None - }; + let any_getter_name = field_info + .multiple + .then(|| format!("get{article}{getter_suffix}")); fields.push(Field { parent: TypeName { - kind: parent_type_name.kind.to_string(), + kind: parent_type_name.kind.clone(), named: parent_type_name.named, }, type_info, @@ -424,7 +423,7 @@ fn escape_name(name: &str) -> String { '@' => result.push_str("at"), _ if c.is_uppercase() => { result.push('_'); - result.push_str(&c.to_lowercase().to_string()) + result.push_str(&c.to_lowercase().to_string()); } _ => result.push(c), } @@ -446,7 +445,7 @@ pub fn to_snake_case(word: &str) -> String { for c in word.chars() { if c.is_uppercase() { if !prev_upper { - result.push('_') + result.push('_'); } prev_upper = true; result.push(c.to_ascii_lowercase()); @@ -457,8 +456,8 @@ pub fn to_snake_case(word: &str) -> String { } result } -/// Given a valid dbscheme name (i.e. in snake case), produces the equivalent QL -/// name (i.e. in CamelCase). For example, "foo_bar_baz" becomes "FooBarBaz". +/// Given a valid dbscheme name (i.e. in `snake_case`), produces the equivalent QL +/// name (i.e. in `CamelCase`). For example, `foo_bar_baz` becomes `FooBarBaz`. fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String { fn to_title_case(word: &str) -> String { let mut first = true; @@ -476,8 +475,7 @@ fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String { dbscheme_name .split('_') .map(to_title_case) - .collect::>() - .join("") + .collect::() } #[test] @@ -495,7 +493,7 @@ fn to_snake_case_test() { #[test] fn supertype_fields_are_preserved() { - let yaml = r#" + let yaml = r" supertypes: callable: subtypes: [function] @@ -506,7 +504,7 @@ named: function: parameter: block: -"#; +"; let json = yeast::node_types_yaml::convert(yaml).unwrap(); let nodes = read_node_types_str("test", &json).unwrap(); let callable = nodes diff --git a/shared/tree-sitter-extractor/src/options.rs b/shared/tree-sitter-extractor/src/options.rs index 4be253104f19..121389965dc2 100644 --- a/shared/tree-sitter-extractor/src/options.rs +++ b/shared/tree-sitter-extractor/src/options.rs @@ -1,12 +1,10 @@ -use num_cpus; - /// Gets the number of threads the extractor should use. -/// This is controlled by the CODEQL_THREADS environment variable. +/// This is controlled by the `CODEQL_THREADS` environment variable. pub fn num_threads() -> Result { let threads_str = std::env::var("CODEQL_THREADS").unwrap_or_else(|_| "-1".into()); let num_cpus = num_cpus::get(); parse_codeql_threads(&threads_str, num_cpus) - .ok_or_else(|| format!("Unable to parse CODEQL_THREADS value '{}'", threads_str)) + .ok_or_else(|| format!("Unable to parse CODEQL_THREADS value '{threads_str}'")) } /// Parses the given string to determine the number of threads the extractor diff --git a/shared/tree-sitter-extractor/src/trap.rs b/shared/tree-sitter-extractor/src/trap.rs index 492349c6d4ab..80ace03fbc7c 100644 --- a/shared/tree-sitter-extractor/src/trap.rs +++ b/shared/tree-sitter-extractor/src/trap.rs @@ -55,7 +55,7 @@ impl Writer { /// Note: You probably want to make sure that the key strings that you use are disjoint /// for disjoint column types; the standard way of doing this is to prefix (or append) /// the column type name to the ID. Thus, you might identify methods in Java by the - /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + /// full ID `methods_com.method.package.DeclaringClass.method(argumentList)`. pub fn global_id(&mut self, key: &str) -> (Label, bool) { if let Some(label) = self.global_keys.get(key) { return (*label, false); @@ -81,7 +81,7 @@ impl Writer { pub fn add_tuple(&mut self, table_name: &str, args: Vec) { self.trap_output - .push(Entry::GenericTuple(table_name.to_owned(), args)) + .push(Entry::GenericTuple(table_name.to_owned(), args)); } pub fn comment(&mut self, text: String) { @@ -123,7 +123,7 @@ pub enum Entry { FreshId(Label), /// Maps the label to a key, e.g. `#7=@"foo"`. MapLabelToKey(Label, String), - /// foo_bar(arg*) + /// `foo_bar(arg*)` GenericTuple(String, Vec), Comment(String), } @@ -174,7 +174,7 @@ pub enum Arg { String(String), } -const MAX_STRLEN: usize = 1048576; +const MAX_STRLEN: usize = 1_048_576; impl fmt::Display for Arg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -218,11 +218,10 @@ pub struct Program(Vec); impl fmt::Display for Program { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut text = String::new(); for trap_entry in &self.0 { - text.push_str(&format!("{trap_entry}\n")); + writeln!(f, "{trap_entry}")?; } - write!(f, "{text}") + Ok(()) } } @@ -277,7 +276,7 @@ fn limit_string(string: &str, max_size: usize) -> &str { // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. // Therefore we decrement the index as long as there are bytes matching this pattern. // This ensures we cut the string at the border between one character and another. - while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + while index > 0 && (p[index] & 0b1100_0000) == 0b1000_0000 { index -= 1; } &string[0..index] @@ -298,7 +297,7 @@ impl Compression { match std::env::var(var_name) { Ok(method) => match Compression::from_string(&method) { Some(c) => Ok(c), - None => Err(format!("Unknown compression method '{}'", method)), + None => Err(format!("Unknown compression method '{method}'")), }, // Default compression method if the env var isn't set: Err(_) => Ok(Compression::Gzip), diff --git a/shared/yeast-macros/src/ast.rs b/shared/yeast-macros/src/ast.rs index f534fe4d83a7..0f341fcef29d 100644 --- a/shared/yeast-macros/src/ast.rs +++ b/shared/yeast-macros/src/ast.rs @@ -209,12 +209,12 @@ mod tests { fn output_leaf_kinds() -> BTreeSet { yeast_schema::node_types_yaml::leaf_kinds_from_yaml( - r#" + r" named: assignment: left: identifier identifier: -"#, +", ) .unwrap() } diff --git a/shared/yeast-macros/src/lower.rs b/shared/yeast-macros/src/lower.rs index 6cf8767e2f1b..e971e09ff68e 100644 --- a/shared/yeast-macros/src/lower.rs +++ b/shared/yeast-macros/src/lower.rs @@ -58,15 +58,15 @@ impl Rule { let captures_ident = Ident::new("__captures", Span::call_site()); let translated_captures_ident = Ident::new("__translated_captures", Span::call_site()); let guard_bindings = - capture_bindings(captures.iter(), quote! { yeast::Id }, &captures_ident); + capture_bindings(captures.iter(), "e! { yeast::Id }, &captures_ident); let raw_bindings = capture_bindings( raw_captures.into_iter(), - quote! { yeast::Id }, + "e! { yeast::Id }, &captures_ident, ); let translated_bindings = capture_bindings( translated_captures.into_iter(), - quote! { yeast::OutputId }, + "e! { yeast::OutputId }, &translated_captures_ident, ); let replacement = self.replacement.lower(); @@ -278,7 +278,7 @@ fn combine_cardinality(parent: Cardinality, child: Cardinality) -> Cardinality { fn capture_bindings<'a>( captures: impl Iterator, - id_type: TokenStream, + id_type: &TokenStream, captures_ident: &Ident, ) -> Vec { captures @@ -367,14 +367,14 @@ impl Template { fn lower_choice(alternatives: &[Alternative]) -> TokenStream { let pick = alternatives.iter().rev().fold( quote! { ::std::option::Option::None }, - |fallback, alternative| alternative.lower_candidate(fallback), + |fallback, alternative| alternative.lower_candidate(&fallback), ); quote! {{ #pick }} } } impl Alternative { - fn lower_candidate(&self, fallback: TokenStream) -> TokenStream { + fn lower_candidate(&self, fallback: &TokenStream) -> TokenStream { let interpolations = RefCell::new(Vec::new()); let context = LoweringContext { choice_alternative_scope: Some(ChoiceAlternativeScope { @@ -445,6 +445,7 @@ impl Alternative { } impl Node { + #[allow(clippy::too_many_lines)] fn lower(&self, context: LoweringContext<'_>) -> TokenStream { let ctx = Ident::new("ctx", Span::call_site()); let kind_str = self.kind.to_string(); diff --git a/shared/yeast-macros/src/template_parse.rs b/shared/yeast-macros/src/template_parse.rs index cce42fd0ab19..4b6b2aa8bd7d 100644 --- a/shared/yeast-macros/src/template_parse.rs +++ b/shared/yeast-macros/src/template_parse.rs @@ -38,11 +38,11 @@ fn parse_one(tokens: &mut Tokens) -> Result