diff --git a/rust/extractor/src/main.rs b/rust/extractor/src/main.rs index 4ede43224d35..8aac92ba0cf7 100644 --- a/rust/extractor/src/main.rs +++ b/rust/extractor/src/main.rs @@ -41,18 +41,99 @@ struct Extractor<'a> { archiver: &'a Archiver, traps: &'a trap::TrapFileProvider, steps: Vec, + source_files: HashSet, + generated_source_root: PathBuf, + extracted_source_files: HashSet, +} + +fn is_non_rust_include_target(file: &Path) -> bool { + file.extension() + .is_none_or(|ext| !ext.eq_ignore_ascii_case("rs")) +} + +fn is_excluded_source_directory(source_files: &HashSet, file: &Path) -> bool { + let mut directory = file.parent(); + while let Some(path) = directory { + let mut has_selected_source = false; + let mut has_unselected_source = false; + match fs::read_dir(path) { + Ok(entries) => { + for entry in entries.flatten() { + let source = entry.path(); + if source.extension().is_some_and(|ext| ext == "rs") { + let source = dunce::canonicalize(&source).unwrap_or(source); + if source_files.contains(&source) { + has_selected_source = true; + } else { + has_unselected_source = true; + } + } + } + } + Err(error) => warn!( + "unable to inspect source directory {} for excluded files: {error}", + path.display() + ), + } + if has_unselected_source && !has_selected_source { + return true; + } + if has_selected_source { + return false; + } + directory = path.parent(); + } + false +} + +fn should_extract_included_file( + source_files: &HashSet, + generated_source_root: &Path, + file: &Path, + source_kind: SourceKind, +) -> bool { + source_kind == SourceKind::Library + || source_files.contains(file) + || file.starts_with(generated_source_root) + || (is_non_rust_include_target(file) && !is_excluded_source_directory(source_files, file)) } impl<'a> Extractor<'a> { - pub fn new(archiver: &'a Archiver, traps: &'a trap::TrapFileProvider) -> Self { + pub fn new( + archiver: &'a Archiver, + traps: &'a trap::TrapFileProvider, + source_files: HashSet, + generated_source_root: PathBuf, + ) -> Self { Self { archiver, traps, steps: Vec::new(), + source_files, + generated_source_root, + extracted_source_files: HashSet::new(), } } - fn extract(&mut self, rust_analyzer: &RustAnalyzer, file: &Path, source_kind: SourceKind) { + fn is_selected_source(&self, file: &Path) -> bool { + self.source_files.contains(file) || file.starts_with(&self.generated_source_root) + } + + fn should_extract_included_file(&self, file: &Path, source_kind: SourceKind) -> bool { + should_extract_included_file( + &self.source_files, + &self.generated_source_root, + file, + source_kind, + ) + } + + fn extract( + &mut self, + rust_analyzer: &RustAnalyzer, + file: &Path, + source_kind: SourceKind, + ) -> Vec { self.archiver.archive(file); let before_parse = Instant::now(); let ParseResult { @@ -96,6 +177,17 @@ impl<'a> Extractor<'a> { } translator.emit_source_file(&ast); translator.emit_truncated_diagnostics_message(); + translator.included_file_steps.retain(|step| { + step.file.as_ref().is_none_or(|file| { + if self.should_extract_included_file(file, source_kind) { + self.archiver.archive(file); + true + } else { + false + } + }) + }); + self.steps.append(&mut translator.included_file_steps); translator.trap.commit().unwrap_or_else(|err| { error!( "Failed to write trap file for: {}: {}", @@ -105,6 +197,7 @@ impl<'a> Extractor<'a> { }); self.steps .push(ExtractionStep::extract(before_extract, source_kind, file)); + translator.module_files } pub fn extract_with_semantics<'db>( @@ -113,8 +206,27 @@ impl<'a> Extractor<'a> { semantics: &'db Semantics<'db, RootDatabase>, vfs: &'db Vfs, source_kind: SourceKind, + selected_source: bool, ) { - self.extract(&RustAnalyzer::new(vfs, semantics), file, source_kind); + let mut pending = vec![file.to_path_buf()]; + let mut visited = HashSet::new(); + while let Some(file) = pending.pop() { + if !visited.insert(file.clone()) + || selected_source && !self.is_selected_source(&file) + || !selected_source && self.extracted_source_files.contains(&file) + { + continue; + } + if selected_source { + self.extracted_source_files.insert(file.clone()); + } + let modules = self.extract(&RustAnalyzer::new(vfs, semantics), &file, source_kind); + if selected_source { + pending.extend(modules.into_iter().filter(|module| { + self.is_selected_source(module) && !self.source_files.contains(module) + })); + } + } } pub fn extract_without_semantics( @@ -274,11 +386,6 @@ fn main() -> anyhow::Result<()> { info!("{config:#?}\n"); info!("number of inputs: {}", inputs.len()); - let traps = trap::TrapFileProvider::new(&config).context("failed to set up trap files")?; - let archiver = archive::Archiver { - root: config.source_archive_dir.clone(), - }; - let mut extractor = Extractor::new(&archiver, &traps); let files = inputs .iter() .map(|file| { @@ -289,6 +396,20 @@ fn main() -> anyhow::Result<()> { dunce::canonicalize(&file).unwrap_or(file) }) .collect::>(); + let source_files = HashSet::from_iter(files.iter().cloned()); + let generated_source_root = std::path::absolute(config.cargo_target_dir())?; + let generated_source_root = + dunce::canonicalize(&generated_source_root).unwrap_or(generated_source_root); + let traps = trap::TrapFileProvider::new(&config).context("failed to set up trap files")?; + let archiver = archive::Archiver { + root: config.source_archive_dir.clone(), + }; + let mut extractor = Extractor::new( + &archiver, + &traps, + source_files.clone(), + generated_source_root, + ); let manifests = extractor.find_manifests(&files)?; let workspaces = extractor.group_files(&manifests, &files); let cwd = cwd()?; @@ -304,7 +425,6 @@ fn main() -> anyhow::Result<()> { } else { SourceKind::Source }; - let source_files = HashSet::from_iter(files.iter().cloned()); let mut dependencies = DependencyFiles::default(); @@ -324,14 +444,16 @@ fn main() -> anyhow::Result<()> { let semantics = Semantics::new(&db); for file in files { match extractor.load_source(file, &semantics, &vfs) { - Ok(()) => extractor.extract_with_semantics(file, &semantics, &vfs, source_mode), + Ok(()) => { + extractor.extract_with_semantics(file, &semantics, &vfs, source_mode, true) + } Err(e) => extractor.extract_without_semantics(file, source_mode, e), }; } // Fast path when there is no ambiguity in dependency extraction. if dependency_extraction_ambiguity { for (_, file) in dependency_files(&db, &vfs, &source_files) { - extractor.extract_with_semantics(file, &semantics, &vfs, library_mode); + extractor.extract_with_semantics(file, &semantics, &vfs, library_mode, false); } } else { dependencies.add_manifest(manifest, &semantics, &vfs, &source_files); @@ -372,7 +494,13 @@ fn main() -> anyhow::Result<()> { .and_then(|id| semantics.file_to_module_def(id)) .is_some() { - extractor.extract_with_semantics(&file, &semantics, &vfs, library_mode); + extractor.extract_with_semantics( + &file, + &semantics, + &vfs, + library_mode, + false, + ); dependencies.extracted(&file); } } @@ -402,3 +530,51 @@ fn main() -> anyhow::Result<()> { extractor.emit_extraction_diagnostics(start, &config) } + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + #[test] + fn included_non_rust_files_respect_source_selection() { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let root = env::temp_dir().join(format!( + "codeql-rust-included-files-{}-{unique}", + std::process::id() + )); + let selected_source = root.join("main.rs"); + let extensionless_target = root.join("included/items"); + let ignored_source = root.join("ignored/a_source.rs"); + let ignored_target = root.join("ignored/items.inc"); + for path in [ + &selected_source, + &extensionless_target, + &ignored_source, + &ignored_target, + ] { + fs::create_dir_all(path.parent().unwrap()).unwrap(); + fs::write(path, "").unwrap(); + } + let source_files = HashSet::from([dunce::canonicalize(&selected_source).unwrap()]); + let generated_source_root = root.join("target"); + + assert!(should_extract_included_file( + &source_files, + &generated_source_root, + &extensionless_target, + SourceKind::Source + )); + assert!(!should_extract_included_file( + &source_files, + &generated_source_root, + &ignored_target, + SourceKind::Source + )); + + fs::remove_dir_all(root).unwrap(); + } +} diff --git a/rust/extractor/src/rust_analyzer.rs b/rust/extractor/src/rust_analyzer.rs index d3129af12796..340f7ed03dd0 100644 --- a/rust/extractor/src/rust_analyzer.rs +++ b/rust/extractor/src/rust_analyzer.rs @@ -68,6 +68,7 @@ impl From for RustAnalyzer<'static> { pub struct FileSemanticInformation<'a> { pub file_id: EditionedFileId, pub semantics: &'a Semantics<'a, RootDatabase>, + pub vfs: &'a Vfs, } pub struct ParseResult<'a> { @@ -99,8 +100,15 @@ impl<'a> RustAnalyzer<'a> { fn get_file_data( &self, path: &Path, - ) -> Result<(&'a Semantics<'a, RootDatabase>, EditionedFileId, FileText), RustAnalyzerNoSemantics> - { + ) -> Result< + ( + &'a Semantics<'a, RootDatabase>, + &'a Vfs, + EditionedFileId, + FileText, + ), + RustAnalyzerNoSemantics, + > { match self { RustAnalyzer::WithoutSemantics { severity, reason } => Err(RustAnalyzerNoSemantics { severity: *severity, @@ -118,14 +126,14 @@ impl<'a> RustAnalyzer<'a> { let editioned_file_id = semantics.attach_first_edition_opt(file_id).ok_or( RustAnalyzerNoSemantics::warning("failed to determine rust edition"), )?; - Ok((*semantics, editioned_file_id, input)) + Ok((*semantics, *vfs, editioned_file_id, input)) } } } pub fn parse(&self, path: &Path) -> ParseResult<'a> { match self.get_file_data(path) { - Ok((semantics, file_id, input)) => { + Ok((semantics, vfs, file_id, input)) => { let source_file = semantics.parse(file_id); let errors = file_id .parse_errors(semantics.db) @@ -137,7 +145,11 @@ impl<'a> RustAnalyzer<'a> { ast: source_file, text: input.text(semantics.db).clone(), errors, - semantics_info: Ok(FileSemanticInformation { file_id, semantics }), + semantics_info: Ok(FileSemanticInformation { + file_id, + semantics, + vfs, + }), } } Err(reason) => { diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index 2dd7c358a2f3..4e6fc37a739c 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -1,17 +1,19 @@ use super::mappings::Emission; +use crate::diagnostics::ExtractionStep; use crate::generated::{self}; use crate::rust_analyzer::FileSemanticInformation; use crate::trap::{DiagnosticSeverity, TrapFile, TrapId}; use crate::trap::{Label, TrapClass}; -use ra_ap_base_db::EditionedFileId; +use ra_ap_base_db::{EditionedFileId, SourceDatabase}; use ra_ap_hir::Semantics; use ra_ap_hir_expand::builtin::{BuiltinDeriveExpander, find_builtin_derive}; use ra_ap_hir_expand::span_map::ExpansionSpanMap; -use ra_ap_hir_expand::{ExpandResult, ExpandTo, HirFileId, InFile, map_node_range_up_rooted}; +use ra_ap_hir_expand::{ + ExpandResult, ExpandTo, HirFileId, InFile, map_node_range_up_rooted, span_for_offset, +}; use ra_ap_ide_db::RootDatabase; use ra_ap_ide_db::line_index::{LineCol, LineIndex}; use ra_ap_parser::{SyntaxKind, TopEntryPoint}; -use ra_ap_span::TextSize; use ra_ap_syntax::ast::HasAttrs; use ra_ap_syntax::{ AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange, @@ -20,8 +22,10 @@ use ra_ap_syntax::{ use ra_ap_syntax_bridge::{ DocCommentDesugarMode, syntax_node_to_token_tree, token_tree_to_syntax_node, }; +use ra_ap_vfs::Vfs; use std::collections::HashMap; use std::path::{Path, PathBuf}; +use std::time::Instant; impl Emission for Translator<'_> { fn pre_emit(&mut self, node: &ast::Item) -> Option> { @@ -130,8 +134,13 @@ pub struct Translator<'db> { label: Label, line_index: LineIndex, file_id: Option, + vfs: Option<&'db Vfs>, pub semantics: Option<&'db Semantics<'db, RootDatabase>>, source_kind: SourceKind, + included_files: HashMap, + included_file_context: Option, + pub(crate) included_file_steps: Vec, + pub(crate) module_files: Vec, pub(crate) macro_context_depth: usize, parenthesized_expr_attrs: HashMap>>, diagnostic_count: usize, @@ -141,6 +150,12 @@ pub struct Translator<'db> { builtin_derive_span_map: Option, } +struct FileLocation { + path: PathBuf, + label: Label, + line_index: LineIndex, +} + const UNKNOWN_LOCATION: (LineCol, LineCol) = (LineCol { line: 0, col: 0 }, LineCol { line: 0, col: 0 }); @@ -161,8 +176,13 @@ impl<'db> Translator<'db> { label, line_index, file_id: semantic_info.map(|i| i.file_id), + vfs: semantic_info.map(|i| i.vfs), semantics: semantic_info.map(|i| i.semantics), source_kind, + included_files: HashMap::new(), + included_file_context: None, + included_file_steps: Vec::new(), + module_files: Vec::new(), macro_context_depth: 0, parenthesized_expr_attrs: HashMap::new(), diagnostic_count: 0, @@ -170,7 +190,98 @@ impl<'db> Translator<'db> { } } fn location(&self, range: TextRange) -> Option<(LineCol, LineCol)> { - let start = self.line_index.try_line_col(range.start())?; + Self::location_in_index(&self.line_index, range) + } + + fn file_range_for_node( + &self, + node: &impl ast::AstNode, + ) -> Option<(Option, TextRange)> { + self.file_range_for_syntax_range(node.syntax(), node.syntax().text_range()) + } + + fn file_range_for_syntax_range( + &self, + node: &SyntaxNode, + range: TextRange, + ) -> Option<(Option, TextRange)> { + 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. + let semantics = self.semantics.as_ref()?; + let file_range = map_node_range_up_rooted(semantics.db, span_map, range)?; + return Some((Some(file_range.file_id), file_range.range)); + } + if self.included_file_context.is_some() + && !range.is_empty() + && let Some(semantics) = self.semantics + && let Some((file_id, range)) = + self.included_file_range(semantics.hir_file_for(node), range) + { + return Some((Some(file_id), range)); + } + if let Some(semantics) = self.semantics.as_ref() { + let file_range = InFile::new(semantics.hir_file_for(node), range) + .original_node_file_range_rooted(semantics.db); + Some((Some(file_range.file_id), file_range.range)) + } else { + Some((None, range)) + } + } + + fn included_file_range( + &self, + file_id: HirFileId, + range: TextRange, + ) -> Option<(EditionedFileId, TextRange)> { + let semantics = self.semantics?; + let macro_file = file_id.macro_file()?; + let call = macro_file.loc(semantics.db); + let span_map = macro_file.expansion_span_map(semantics.db); + let (start, _) = span_for_offset(semantics.db, span_map, range.start()); + let (end, _) = span_for_offset(semantics.db, span_map, range.end().checked_sub(1.into())?); + if !call.def.is_include() { + let invocation = call.to_node(semantics.db); + let (invocation_file, invocation_range) = + self.included_file_range(invocation.file_id, invocation.value.text_range())?; + // Preserve copied arguments, but attribute definition-origin tokens to the call. + if start.file_id == invocation_file + && end.file_id == invocation_file + && invocation_range.contains_range(start.range) + && invocation_range.contains_range(end.range) + { + return Some((invocation_file, start.range.cover(end.range))); + } + return Some((invocation_file, invocation_range)); + } + // Include expansions reformat tokens and give them non-root syntax contexts. + // Resolve boundary spans (which may have different anchors) to source offsets. + (start.file_id == end.file_id + && (self.included_files.contains_key(&start.file_id) + || Some(start.file_id) == self.file_id)) + .then(|| (start.file_id, start.range.cover(end.range))) + } + + fn location_in_file( + &mut self, + file_id: Option, + range: TextRange, + ) -> Option<(Label, LineCol, LineCol)> { + let Some(file_id) = file_id else { + let (start, end) = self.location(range)?; + return Some((self.label, start, end)); + }; + if Some(file_id) == self.file_id { + let (start, end) = self.location(range)?; + return Some((self.label, start, end)); + } + let file = self.included_files.get(&file_id)?; + let (start, end) = Self::location_in_index(&file.line_index, range)?; + Some((file.label, start, end)) + } + + fn location_in_index(line_index: &LineIndex, range: TextRange) -> Option<(LineCol, LineCol)> { + let start = line_index.try_line_col(range.start())?; let range_end = range.end(); // QL end positions are inclusive, while TextRange offsets are exclusive and point at the position // right after the last character of the range. We need to shift the end offset one character to the left to @@ -180,45 +291,45 @@ impl<'db> Translator<'db> { for i in 1..4 { if let Some(end) = range_end .checked_sub(i.into()) - .and_then(|x| self.line_index.try_line_col(x)) + .and_then(|x| line_index.try_line_col(x)) { return Some((start, end)); } } - let end = self.line_index.try_line_col(range_end)?; + let end = line_index.try_line_col(range_end)?; Some((start, end)) } - pub fn text_range_for_node(&mut 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. - let semantics = self.semantics.as_ref()?; - let file_id = self.file_id?; - let file_range = - map_node_range_up_rooted(semantics.db, span_map, node.syntax().text_range())?; - return (file_id == file_range.file_id).then_some(file_range.range); - } - 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 - } - } else { - Some(node.syntax().text_range()) + fn included_file_context_for(&mut self, expanded: &SyntaxNode) -> Option { + let semantics = self.semantics.as_ref()?; + let macro_file = semantics.hir_file_for(expanded).macro_file()?; + // rust-analyzer resolves the built-in, its argument, and nested include paths. + let file_id = macro_file + .loc(semantics.db) + .include_file_id(semantics.db, macro_file)?; + if Some(file_id) != self.file_id + && let std::collections::hash_map::Entry::Vacant(entry) = + self.included_files.entry(file_id) + { + let vfs = self.vfs.as_ref()?; + let source_file_id = file_id.file_id(semantics.db); + let path = vfs.file_path(source_file_id).as_path()?; + let path = dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf().into()); + let text = semantics.db.file_text(source_file_id); + entry.insert(FileLocation { + label: self.trap.emit_file(&path), + line_index: LineIndex::new(text.text(semantics.db).as_ref()), + path, + }); } - } - - fn location_for_node(&mut self, node: &impl ast::AstNode) -> Option<(LineCol, LineCol)> { - self.text_range_for_node(node) - .and_then(|r| self.location(r)) + Some(file_id) } pub fn emit_location(&mut self, label: Label, node: &impl ast::AstNode) { - match self.location_for_node(node) { + let location = self + .file_range_for_node(node) + .and_then(|(file_id, range)| self.location_in_file(file_id, range)); + match location { None => self.emit_diagnostic( DiagnosticSeverity::Debug, "locations".to_owned(), @@ -226,7 +337,7 @@ impl<'db> Translator<'db> { "missing location for AstNode".to_owned(), UNKNOWN_LOCATION, ), - Some((start, end)) => self.trap.emit_location(self.label, label, start, end), + Some((file, start, end)) => self.trap.emit_location(file, label, start, end), }; } pub fn emit_location_token( @@ -237,12 +348,22 @@ impl<'db> Translator<'db> { ) { let parent_range = parent.syntax().text_range(); let token_range = token.text_range(); - if let Some(clipped_range) = token_range.intersect(parent_range) - && let Some(parent_range2) = self.text_range_for_node(parent) - { - 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) + if let Some(clipped_range) = token_range.intersect(parent_range) { + let location = if self.included_file_context.is_some() { + self.file_range_for_syntax_range(parent.syntax(), clipped_range) + } else { + self.file_range_for_node(parent) + .map(|(file_id, parent_range2)| { + ( + file_id, + clipped_range + parent_range2.start() - parent_range.start(), + ) + }) + }; + if let Some((file_id, range)) = location + && let Some((file, start, end)) = self.location_in_file(file_id, range) + { + self.trap.emit_location(file, label, start, end) } } } @@ -253,6 +374,23 @@ impl<'db> Translator<'db> { message: String, full_message: String, location: (LineCol, LineCol), + ) { + self.emit_diagnostic_at( + severity, + tag, + message, + full_message, + (self.label, location.0, location.1), + ); + } + + fn emit_diagnostic_at( + &mut self, + severity: DiagnosticSeverity, + tag: String, + message: String, + full_message: String, + location: (Label, LineCol, LineCol), ) { let severity = if self.source_kind == SourceKind::Library { match severity { @@ -280,7 +418,7 @@ impl<'db> Translator<'db> { format!( "Too many diagnostic messages, {count} diagnostic messages were suppressed" ), - UNKNOWN_LOCATION, + (self.label, UNKNOWN_LOCATION.0, UNKNOWN_LOCATION.1), ); } } @@ -290,20 +428,30 @@ impl<'db> Translator<'db> { tag: String, message: String, full_message: String, - location: (LineCol, LineCol), + location: (Label, LineCol, LineCol), ) { - let (start, end) = location; + let (file, start, end) = location; + let path = if file.as_untyped() == self.label.as_untyped() { + &self.path + } else { + &self + .included_files + .values() + .find(|f| f.label.as_untyped() == file.as_untyped()) + .expect("diagnostic file must have cached location data") + .path + }; dispatch_to_tracing!( severity, "{}:{}:{}: {}", - self.path.display(), + path.display(), start.line + 1, start.col + 1, &full_message, ); if severity > DiagnosticSeverity::Debug { - let location = self.trap.emit_location_label(self.label, start, end); + let location = self.trap.emit_location_label(file, start, end); self.trap .emit_diagnostic(severity, tag, message, full_message, location); } @@ -317,35 +465,32 @@ impl<'db> Translator<'db> { message: String, full_message: String, ) { - let location = self.location_for_node(node); - self.emit_diagnostic( + let location = self + .file_range_for_node(node) + .and_then(|(file, range)| self.location_in_file(file, range)); + self.emit_diagnostic_at( severity, tag, message, full_message, - location.unwrap_or(UNKNOWN_LOCATION), + location.unwrap_or((self.label, UNKNOWN_LOCATION.0, UNKNOWN_LOCATION.1)), ); } pub fn emit_parse_error(&mut self, owner: &impl ast::AstNode, err: &SyntaxError) { let owner_range: TextRange = owner.syntax().text_range(); - let err_range = err.range(); - if let Some(owner_range2) = self.text_range_for_node(owner) { - let location = if let Some(clipped_range) = err_range.intersect(owner_range) { - let err_range = clipped_range + owner_range2.start() - owner_range.start(); - self.location(err_range) - } else { - self.location(owner_range2) - }; - let message = err.to_string(); - self.emit_diagnostic( - DiagnosticSeverity::Warning, - "parse_error".to_owned(), - message.clone(), - message, - location.unwrap_or(UNKNOWN_LOCATION), - ); - } + let range = err.range().intersect(owner_range).unwrap_or(owner_range); + let location = self + .file_range_for_syntax_range(owner.syntax(), range) + .and_then(|(file, range)| self.location_in_file(file, range)); + let message = err.to_string(); + self.emit_diagnostic_at( + DiagnosticSeverity::Warning, + "parse_error".to_owned(), + message.clone(), + message, + location.unwrap_or((self.label, UNKNOWN_LOCATION.0, UNKNOWN_LOCATION.1)), + ); } pub fn emit_tokens( &mut self, @@ -390,6 +535,14 @@ impl<'db> Translator<'db> { .text_range() .start(); self.emit_parse_error(node, &SyntaxError::new(error.message, location)); + } else if self.included_file_context.is_some() { + self.emit_diagnostic_for_node( + node, + DiagnosticSeverity::Warning, + "parse_error".to_owned(), + error.message.clone(), + error.message, + ); }; } for err in value.value.iter() { @@ -442,9 +595,16 @@ impl<'db> Translator<'db> { .as_ref() .and_then(|s| s.expand_macro_call(mcall)) { - self.emit_macro_expansion_parse_errors(mcall, &expanded.value); let expand_to = ra_ap_hir_expand::ExpandTo::from_call_site(mcall); let kind = expanded.kind(); + let before_expand = Instant::now(); + let previous_include_context = if expand_to == ra_ap_hir_expand::ExpandTo::Items { + self.included_file_context_for(&expanded.value) + .map(|context| self.included_file_context.replace(context)) + } else { + None + }; + self.emit_macro_expansion_parse_errors(mcall, &expanded.value); if let Some(value) = self.emit_expanded_as(expand_to, expanded.value) { generated::MacroCall::emit_macro_call_expansion( label, @@ -452,19 +612,35 @@ impl<'db> Translator<'db> { &mut self.trap.writer, ); } else { - let range = self.text_range_for_node(mcall); self.emit_parse_error(mcall, &SyntaxError::new( format!( "macro expansion failed: the macro '{}' expands to {:?} but a {:?} was expected", mcall.path().map(|p| p.to_string()).unwrap_or_default(), kind, expand_to ), - range.unwrap_or_else(|| TextRange::empty(TextSize::from(0))), + mcall.syntax().text_range(), )); } + if let Some(previous_context) = previous_include_context { + let file_id = self + .included_file_context + .expect("include context must be set"); + let path = if Some(file_id) == self.file_id { + &self.path + } else { + &self.included_files[&file_id].path + }; + self.included_file_steps.push(ExtractionStep::extract( + before_expand, + self.source_kind, + path, + )); + self.included_file_steps + .push(ExtractionStep::load_source(before_expand, path)); + self.included_file_context = previous_context; + } } else if self.semantics.is_some() { // let's not spam warnings if we don't have semantics, we already emitted one - let range = self.text_range_for_node(mcall); self.emit_parse_error( mcall, &SyntaxError::new( @@ -472,7 +648,7 @@ impl<'db> Translator<'db> { "macro expansion failed for '{}'", mcall.path().map(|p| p.to_string()).unwrap_or_default() ), - range.unwrap_or_else(|| TextRange::empty(TextSize::from(0))), + mcall.syntax().text_range(), ), ); } @@ -752,6 +928,18 @@ impl<'db> Translator<'db> { } pub(crate) fn item_post_emit(&mut self, node: &ast::Item, label: Label) { + if self.macro_context_depth == 0 + && let ast::Item::Module(node) = node + && node.semicolon_token().is_some() + && let Some(semantics) = self.semantics + && let Some(module) = semantics.to_def(node) + && let Some(file_id) = module.as_source_file_id(semantics.db) + && let Some(path) = self + .vfs + .and_then(|vfs| vfs.file_path(file_id.file_id(semantics.db)).as_path()) + { + self.module_files.push(path.to_path_buf().into()); + } if !self.is_attribute_macro_target(node) { return; } diff --git a/rust/ql/integration-tests/file-exclusions/src/lib.rs b/rust/ql/integration-tests/file-exclusions/src/lib.rs index 94334e348c65..fe9c258c3282 100644 --- a/rust/ql/integration-tests/file-exclusions/src/lib.rs +++ b/rust/ql/integration-tests/file-exclusions/src/lib.rs @@ -1,5 +1,7 @@ mod maybe_ignore; +include!("maybe_ignore/a_source.rs"); + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/rust/ql/integration-tests/file-exclusions/src/maybe_ignore/a_source.rs b/rust/ql/integration-tests/file-exclusions/src/maybe_ignore/a_source.rs index e69de29bb2d1..59a5e34456d7 100644 --- a/rust/ql/integration-tests/file-exclusions/src/maybe_ignore/a_source.rs +++ b/rust/ql/integration-tests/file-exclusions/src/maybe_ignore/a_source.rs @@ -0,0 +1 @@ +pub fn ignored_source() {} diff --git a/rust/ql/lib/change-notes/2026-11-13-include-module-resolution.md b/rust/ql/lib/change-notes/2026-11-13-include-module-resolution.md new file mode 100644 index 000000000000..0f06fb4ddefc --- /dev/null +++ b/rust/ql/lib/change-notes/2026-11-13-include-module-resolution.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Module declarations and crate-root references in files included with `include!` now resolve correctly, including nested includes, build-script output and its child modules, and active exported macros. Included source files are archived and retain their source classification, semantic availability, and extraction diagnostics. Macro arguments in included files retain their original source locations. \ No newline at end of file diff --git a/rust/ql/lib/codeql/files/FileSystem.qll b/rust/ql/lib/codeql/files/FileSystem.qll index 883ef83ae546..bf36e0e5c488 100644 --- a/rust/ql/lib/codeql/files/FileSystem.qll +++ b/rust/ql/lib/codeql/files/FileSystem.qll @@ -6,6 +6,8 @@ private import codeql.rust.elements.SourceFile private import codeql.rust.elements.AstNode private import codeql.rust.elements.Comment private import codeql.rust.elements.DocComment +private import codeql.rust.elements.MacroCall +private import codeql.rust.elements.MacroItems private import codeql.rust.Diagnostics private import codeql.rust.elements.internal.ExtractorStep @@ -89,23 +91,55 @@ class ExtractedFile extends File { ExtractedFile() { this.fromSource() } private Diagnostic getNoSemanticsDiagnostic() { - result.getTag() = "semantics" and result.getLocation().getFile() = this + result.getTag() = "semantics" and + result.getLocation().getFile() = this } + /** Holds if the standalone extraction reported that semantic information was unavailable. */ + predicate hasNoStandaloneSemanticsDiagnostic() { exists(this.getNoSemanticsDiagnostic()) } + /** * Holds if we have semantical information about this file, which means * we should be able to * * expand any macros * * skip any blocks that are conditionally compiled out */ - predicate hasSemantics() { not exists(this.getNoSemanticsDiagnostic()) } + predicate hasSemantics() { + not exists(this.getNoSemanticsDiagnostic()) + or + exists(ExtractorStep s | s.getAction() = "LoadSource" and s.getFile() = this) + } + + /** Holds if `node` belongs to this file's active include expansion. */ + predicate isInActiveIncludeExpansion(AstNode node) { + node.getFile() = this and + exists(MacroItems included, MacroCall call | + included = node.getParentNode*() and + included = call.getMacroCallExpansion() and + included.getFile() = node.getFile() and + included.getFile() != call.getFile() + ) + } + + /** Holds if `node` is not an inactive standalone copy of an include expansion. */ + predicate isNotInactiveStandaloneCopy(AstNode node) { + node.getFile() = this and + not ( + this.hasNoStandaloneSemanticsDiagnostic() and + this.isInActiveIncludeExpansion(_) and + not this.isInActiveIncludeExpansion(node) + ) + } /** * Holds if we know this file was skipped by conditional compilation. * This is not the same as `not this.hasSemantics()`, as a file * might not have semantics because of some error. */ - predicate isSkippedByCompilation() { this.getNoSemanticsDiagnostic().getSeverityText() = "Info" } + predicate isSkippedByCompilation() { + this.getNoSemanticsDiagnostic().getSeverityText() = "Info" and + not exists(ExtractorStep s | s.getAction() = "LoadSource" and s.getFile() = this) + } } /** diff --git a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll index 0c9d736cc57c..d158d8b93a92 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll @@ -70,7 +70,12 @@ module Impl { result = p.getEnclosingCfgScope() ) or - this.isConstOrStatic(result.(SourceFile).getFile()) + exists(SourceFile source | + this.isConstOrStatic(_) and + source = this.getParentNode*() + | + result = source + ) } /** Holds if this node is inside a CFG scope. */ diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 30ddb6f12c36..572673f601a5 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -133,13 +133,50 @@ private ItemNode getAChildSuccessor0(ItemNode item, string name, SuccessorKind k pragma[nomagic] private NamedItemNode getANamedNonModuleChildSuccessor( - ItemNode item, string name, Namespace ns, int startline, int startcolumn, int endline, - int endcolumn + ItemNode item, string name, Namespace ns, string positionKey ) { - result.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and result = getAChildSuccessor0(item, name, _) and ns = result.getNamespace() and - not result instanceof ModuleItemNode + not result instanceof ModuleItemNode and + getEffectivePositionKey(result, positionKey) +} + +bindingset[component] +private string padLocationComponent(int component) { + exists(string value | + value = component.toString() and + result = ("0000000000" + value).substring(value.length(), value.length() + 10) + ) +} + +bindingset[node] +private string getLocationKey(AstNode node) { + exists(int startLine, int startColumn, int endLine, int endColumn | + node.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn) and + result = + padLocationComponent(startLine) + padLocationComponent(startColumn) + + padLocationComponent(endLine) + padLocationComponent(endColumn) + ) +} + +cached +private predicate getEffectivePositionKey(AstNode node, string key) { + exists(MacroItems included, MacroCall call, string parentKey | + included = node.getParentNode*() and + included = call.getMacroCallExpansion() and + included.getFile() = node.getFile() and + included.getFile() != call.getFile() and + getEffectivePositionKey(call, parentKey) and + key = parentKey + "/" + getLocationKey(node) + ) + or + not exists(MacroItems included, MacroCall call | + included = node.getParentNode*() and + included = call.getMacroCallExpansion() and + included.getFile() = node.getFile() and + included.getFile() != call.getFile() + ) and + key = getLocationKey(node) } pragma[nomagic] @@ -155,12 +192,10 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki or exists(Namespace ns | result = - max(NamedItemNode i, int startline, int startcolumn, int endline, int endcolumn | - i = - getANamedNonModuleChildSuccessor(item, name, ns, startline, startcolumn, endline, - endcolumn) + max(NamedItemNode i, string positionKey | + i = getANamedNonModuleChildSuccessor(item, name, ns, positionKey) | - i order by startline, startcolumn, endline, endcolumn + i order by positionKey ) ) ) @@ -551,24 +586,26 @@ class CrateItemNode extends NamedItemNode instanceof Crate { SourceFileItemNode getSourceFile() { result = super.getSourceFile() } /** - * Gets a source file that belongs to this crate. - * - * This is calculated as those source files that can be reached from the entry - * file of this crate using zero or more `mod` imports, without going through - * the entry point of some other crate. + * Holds if `item` belongs to this crate's active item scope, following macro + * expansions and `mod` imports but not importing another crate's entry point. */ pragma[nomagic] - SourceFileItemNode getASourceFile() { - result = super.getSourceFile() - or - exists(SourceFileItemNode mid, Module mod | - mid = this.getASourceFile() and - mod.getFile() = mid.getFile() and - fileImport(mod, result) and - not result = any(Crate other).getSourceFile() + predicate containsItem(ItemNode item) { + item = super.getSourceFile() + or + this.containsItem(item.getImmediateParent()) + or + exists(Module mod | + this.containsItem(mod) and + fileImport(mod, item.(SourceFileItemNode)) and + not item = any(Crate other).getSourceFile() ) } + /** Gets a source file in this crate's active item scope. */ + pragma[nomagic] + SourceFileItemNode getASourceFile() { this.containsItem(result) } + pragma[nomagic] predicate isLatestVersion(string name) { this = @@ -889,7 +926,7 @@ final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { this.resolveSelfTy().hasCanonicalPathPrefix(c) or this.isBlanketImplementation() and - c.getASourceFile().getFile() = this.getFile() + c.(CrateItemNode).containsItem(this) } /** @@ -1404,6 +1441,16 @@ private Meta getPathAttrMeta(Module m) { result.getMetaPath().getText() = "path" } +/** Holds if `node` belongs to an included file's active expansion. */ +private predicate isInIncludeExpansion(AstNode node) { + exists(MacroItems included, MacroCall call | + included = node.getParentNode*() and + included = call.getMacroCallExpansion() and + included.getFile() = node.getFile() and + included.getFile() != call.getFile() + ) +} + /** * Holds if `m` is a `mod name;` module declaration, where the corresponding * module file needs to be looked up in `lookup` or one of its descandants. @@ -1423,10 +1470,13 @@ private predicate modImport0(Module m, string name, Folder lookup) { m.getFile() = any(CrateItemNode c).getSourceFile().getFile() or m.getFile().getBaseName() = "mod.rs" + or + isInIncludeExpansion(m) ) or // child import - lookup = parent.getFolder(fileName) + lookup = parent.getFolder(fileName) and + not isInIncludeExpansion(m) ) } @@ -1435,6 +1485,8 @@ private predicate modImport0(Module m, string name, Folder lookup) { * nested module, and `pred -> succ` is a module edge leading to `m`. */ private predicate modImportNested(ModuleItemNode m, ModuleItemNode pred, ModuleItemNode succ) { + // An include expansion starts a new file-relative lookup, even inside an inline module. + pred.getFile() = m.getFile() and pred.getAnItemInScope() = succ and ( modImport0(m, _, _) and @@ -1863,11 +1915,11 @@ private module DollarCrateResolution { expansion) pragma[nomagic] - private predicate isInDollarCrateMacroExpansionFromFile(File macroDefFile, PathExt p) { + private predicate isInDollarCrateMacroExpansionFromItem(ItemNode macroDef, PathExt p) { exists(Path macroDefPath, AstNode expansion | isDollarCrateSupportedMacroExpansion(macroDefPath, expansion) and isInDollarCrateMacroExpansion(p, expansion) and - macroDefFile = resolvePathCand(macroDefPath).getFile() + macroDef = resolvePathCand(macroDefPath) ) } @@ -1879,7 +1931,10 @@ private module DollarCrateResolution { */ pragma[nomagic] predicate resolveDollarCrate(PathExt p, CrateItemNode crate) { - isInDollarCrateMacroExpansionFromFile(crate.getASourceFile().getFile(), p) + exists(ItemNode macroDef | + isInDollarCrateMacroExpansionFromItem(macroDef, p) and + crate.containsItem(macroDef) + ) } } @@ -2017,7 +2072,7 @@ private predicate pathUsesNamespace(PathExt p, Namespace n) { */ pragma[nomagic] private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNode macro) { - crate.getASourceFile().getFile() = macro.getFile() and + crate.containsItem(macro) and macro.hasAttr("macro_export") and name = macro.getName() } @@ -2280,14 +2335,14 @@ private predicate externCrateEdge( name = ec.getName() and (if ec.isPublic() then kind.isBoth() else kind.isInternal()) and exists(SourceFile f, string s | - ec.getFile() = f.getFile() and + f = ec.getImmediateParent*() and s = ec.(ExternCrate).getIdentifier().getText() | crateDependencyEdge(f, s, crate) or // `extern crate` is used to import the current crate s = "self" and - ec.getFile() = crate.getASourceFile().getFile() + crate.containsItem(ec) ) } diff --git a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll index a6ab8cd48cab..e0dfd473932d 100644 --- a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll +++ b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll @@ -48,6 +48,8 @@ private module ResolveTest implements TestSig { not n = any(Path parent).getQualifier() and location = n.getLocation() and n.fromSource() and + // The built-in include! macro path is unrelated to module/item path resolution. + not exists(MacroCall mc | mc.getPath() = n and mc.getPath().toString() = "include") and not location.getFile().getAbsolutePath().matches("%proc_macro.rs") and not n.isFromMacroExpansion() and element = n.toString() and diff --git a/rust/ql/src/queries/telemetry/DatabaseQuality.qll b/rust/ql/src/queries/telemetry/DatabaseQuality.qll index 37163ffd7dda..5a6bff7c2e5a 100644 --- a/rust/ql/src/queries/telemetry/DatabaseQuality.qll +++ b/rust/ql/src/queries/telemetry/DatabaseQuality.qll @@ -20,6 +20,10 @@ private class RelevantFile extends File { } } +private predicate isNotInactiveStandaloneCopy(AstNode node) { + node.getFile().(ExtractedFile).isNotInactiveStandaloneCopy(node) +} + module CallTargetStats implements StatsSig { /** * A call-like expression that is relevant for call target statistics. @@ -30,6 +34,7 @@ module CallTargetStats implements StatsSig { private class RelevantInvocationExpr extends InvocationExpr { RelevantInvocationExpr() { this.getFile() instanceof RelevantFile and + isNotInactiveStandaloneCopy(this) and not this instanceof CallExprImpl::DynamicCallExpr and not this = any(Operation o | not o.isOverloaded(_, _, _)) and not this = any(DerefExpr de | TypeInference::inferType(de.getExpr()) instanceof PtrType) @@ -49,11 +54,18 @@ module CallTargetStats implements StatsSig { module MacroCallTargetStats implements StatsSig { int getNumberOfOk() { - result = count(MacroCall c | c.getFile() instanceof RelevantFile and c.hasMacroCallExpansion()) + result = + count(MacroCall c | + c.getFile() instanceof RelevantFile and + isNotInactiveStandaloneCopy(c) and + c.hasMacroCallExpansion() + ) } additional predicate isNotOkCall(MacroCall c) { - c.getFile() instanceof RelevantFile and not c.hasMacroCallExpansion() + c.getFile() instanceof RelevantFile and + isNotInactiveStandaloneCopy(c) and + not c.hasMacroCallExpansion() } int getNumberOfNotOk() { result = count(MacroCall c | isNotOkCall(c)) } @@ -70,6 +82,7 @@ module ExprTypeStats implements StatsSig { result = count(Expr e | e.getFile() instanceof RelevantFile and + isNotInactiveStandaloneCopy(e) and e.fromSource() and hasGoodType(e) ) @@ -79,6 +92,7 @@ module ExprTypeStats implements StatsSig { result = count(Expr e | e.getFile() instanceof RelevantFile and + isNotInactiveStandaloneCopy(e) and e.fromSource() and not hasGoodType(e) ) diff --git a/rust/ql/test/extractor-tests/generated/MacroItems/MacroItems.expected b/rust/ql/test/extractor-tests/generated/MacroItems/MacroItems.expected index a3560c0fdef1..836b8db4bc30 100644 --- a/rust/ql/test/extractor-tests/generated/MacroItems/MacroItems.expected +++ b/rust/ql/test/extractor-tests/generated/MacroItems/MacroItems.expected @@ -1,7 +1,7 @@ instances -| gen_macro_items.rs:5:5:5:12 | MacroItems | +| common_definitions.rs:1:1:5:1 | MacroItems | | gen_macro_items.rs:12:5:13:15 | MacroItems | getItem -| gen_macro_items.rs:5:5:5:12 | MacroItems | 0 | gen_macro_items.rs:5:5:5:38 | use ...::Path | -| gen_macro_items.rs:5:5:5:12 | MacroItems | 1 | gen_macro_items.rs:5:5:5:38 | fn get_parent | +| common_definitions.rs:1:1:5:1 | MacroItems | 0 | common_definitions.rs:1:1:1:20 | use ...::Path | +| common_definitions.rs:1:1:5:1 | MacroItems | 1 | common_definitions.rs:3:1:5:1 | fn get_parent | | gen_macro_items.rs:12:5:13:15 | MacroItems | 0 | gen_macro_items.rs:12:5:13:15 | impl ...::Debug for Bar::<...> { ... } | diff --git a/rust/ql/test/extractor-tests/include-diagnostics/Cargo.lock b/rust/ql/test/extractor-tests/include-diagnostics/Cargo.lock new file mode 100644 index 000000000000..b9856cfaf77d --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "test" +version = "0.0.1" diff --git a/rust/ql/test/extractor-tests/include-diagnostics/main.rs b/rust/ql/test/extractor-tests/include-diagnostics/main.rs new file mode 100644 index 000000000000..875311da5e3e --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/main.rs @@ -0,0 +1,9 @@ +macro_rules! malformed { + () => { + fn broken() { let = ; } + }; +} + +include!("tree/generated.rs"); + +fn main() {} diff --git a/rust/ql/test/extractor-tests/include-diagnostics/options.yml b/rust/ql/test/extractor-tests/include-diagnostics/options.yml new file mode 100644 index 000000000000..cf148dd35f86 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/options.yml @@ -0,0 +1 @@ +qltest_cargo_check: false diff --git a/rust/ql/test/extractor-tests/include-diagnostics/test.expected b/rust/ql/test/extractor-tests/include-diagnostics/test.expected new file mode 100644 index 000000000000..c9d6712679c8 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/test.expected @@ -0,0 +1,5 @@ +| tree/generated.rs:1:1:1:26 | macro expansion failed for 'unknown_included_macro' | +| tree/generated.rs:2:1:2:13 | expected SEMICOLON | +| tree/generated.rs:2:1:2:13 | expected expression | +| tree/generated.rs:2:1:2:13 | expected pattern | +| tree/generated.rs:3:1:3:35 | included failure | diff --git a/rust/ql/test/extractor-tests/include-diagnostics/test.ql b/rust/ql/test/extractor-tests/include-diagnostics/test.ql new file mode 100644 index 000000000000..557f3bd8e781 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/test.ql @@ -0,0 +1,4 @@ +import rust +import codeql.rust.Diagnostics + +query predicate warnings(ExtractionWarning w) { w.getLocation().fromSource() } diff --git a/rust/ql/test/extractor-tests/include-diagnostics/tree/generated.rs b/rust/ql/test/extractor-tests/include-diagnostics/tree/generated.rs new file mode 100644 index 000000000000..73b7bac24c0a --- /dev/null +++ b/rust/ql/test/extractor-tests/include-diagnostics/tree/generated.rs @@ -0,0 +1,3 @@ +unknown_included_macro!(); +malformed!(); +compile_error!("included failure"); diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/Cargo.lock b/rust/ql/test/extractor-tests/include-nested-shadowing/Cargo.lock new file mode 100644 index 000000000000..b9856cfaf77d --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "test" +version = "0.0.1" diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/included/a.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/included/a.rs new file mode 100644 index 000000000000..4b011d91ffc6 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/included/a.rs @@ -0,0 +1,3 @@ +macro_rules! nested_shadow { + () => { struct FromA; }; +} diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/included/b.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/included/b.rs new file mode 100644 index 000000000000..29c36c0d26ad --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/included/b.rs @@ -0,0 +1,3 @@ +macro_rules! nested_shadow { + () => { struct FromB; }; +} diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/included/inner.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/included/inner.rs new file mode 100644 index 000000000000..1318430e001f --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/included/inner.rs @@ -0,0 +1,3 @@ +macro_rules! nested_shadow { + () => { struct FromInner; }; +} diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/included/middle.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/included/middle.rs new file mode 100644 index 000000000000..d27083024d37 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/included/middle.rs @@ -0,0 +1,5 @@ +macro_rules! nested_shadow { + () => { struct FromMiddle; }; +} +include!("inner.rs"); +nested_shadow!(); diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/included/outer.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/included/outer.rs new file mode 100644 index 000000000000..4c76f7b79a02 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/included/outer.rs @@ -0,0 +1,7 @@ +macro_rules! nested_shadow { + () => { struct FromOuter; }; +} +include!("a.rs"); +include!("b.rs"); +include!("middle.rs"); +nested_shadow!(); diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/main.rs b/rust/ql/test/extractor-tests/include-nested-shadowing/main.rs new file mode 100644 index 000000000000..c60f3bc4346e --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/main.rs @@ -0,0 +1 @@ +include!("included/outer.rs"); diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/options.yml b/rust/ql/test/extractor-tests/include-nested-shadowing/options.yml new file mode 100644 index 000000000000..ef13b95abfb0 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/options.yml @@ -0,0 +1,2 @@ +# The duplicate macros intentionally make this fixture invalid Rust. +qltest_cargo_check: false diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/test.expected b/rust/ql/test/extractor-tests/include-nested-shadowing/test.expected new file mode 100644 index 000000000000..720ebc5d26ab --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/test.expected @@ -0,0 +1,2 @@ +| included/middle.rs | 5 | included/inner.rs | 1 | true | +| included/outer.rs | 7 | included/inner.rs | 1 | true | diff --git a/rust/ql/test/extractor-tests/include-nested-shadowing/test.ql b/rust/ql/test/extractor-tests/include-nested-shadowing/test.ql new file mode 100644 index 000000000000..066c940bd3c9 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-nested-shadowing/test.ql @@ -0,0 +1,19 @@ +import rust + +query predicate nestedIncludeMacroResolvesToLaterDefinition( + string callFile, int callLine, string definitionFile, int definitionLine, boolean inMacroExpansion +) { + exists(MacroCall call | + call.getPath().getText() = "nested_shadow" and + call.getFile().(ExtractedFile).isInActiveIncludeExpansion(call) and + ( + call.getFile().getRelativePath().matches("%included/outer.rs") or + call.getFile().getRelativePath().matches("%included/middle.rs") + ) and + callFile = call.getFile().getRelativePath() and + call.getLocation().hasLocationInfo(_, callLine, _, _, _) and + definitionFile = call.resolveMacro().getFile().getRelativePath() and + call.resolveMacro().getLocation().hasLocationInfo(_, definitionLine, _, _, _) and + (if call.isInMacroExpansion() then inMacroExpansion = true else inMacroExpansion = false) + ) +} diff --git a/rust/ql/test/extractor-tests/include-out-dir/Cargo.lock b/rust/ql/test/extractor-tests/include-out-dir/Cargo.lock new file mode 100644 index 000000000000..b9856cfaf77d --- /dev/null +++ b/rust/ql/test/extractor-tests/include-out-dir/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "test" +version = "0.0.1" diff --git a/rust/ql/test/extractor-tests/include-out-dir/build.rs b/rust/ql/test/extractor-tests/include-out-dir/build.rs new file mode 100644 index 000000000000..ab243f19a531 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-out-dir/build.rs @@ -0,0 +1,47 @@ +fn main() { + let out = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + std::fs::write( + out.join("generated.rs"), + r#" +extern crate self as generated_self; + +pub mod child; + +pub fn generated_function() -> i32 { + crate::root_function() +} + +pub fn generated_alias() -> i32 { + generated_self::root_function() +} + +pub const INCLUDED_CONST: i32 = crate::root_const(); +pub static INCLUDED_STATIC: i32 = crate::root_const(); + +pub mod inline { + #[macro_export] + macro_rules! generated_export { + () => { $crate::root_function(); }; + } +} +"#, + ) + .unwrap(); + std::fs::write( + out.join("child.rs"), + r#" +pub mod grandchild; + +pub fn child_function() -> i32 { + grandchild::grandchild_function() +} +"#, + ) + .unwrap(); + std::fs::create_dir_all(out.join("child")).unwrap(); + std::fs::write( + out.join("child/grandchild.rs"), + "pub fn grandchild_function() -> i32 { crate::root_function() }\n", + ) + .unwrap(); +} diff --git a/rust/ql/test/extractor-tests/include-out-dir/main.rs b/rust/ql/test/extractor-tests/include-out-dir/main.rs new file mode 100644 index 000000000000..6fa20169140a --- /dev/null +++ b/rust/ql/test/extractor-tests/include-out-dir/main.rs @@ -0,0 +1,15 @@ +include!(concat!(env!("OUT_DIR"), "/generated.rs")); + +fn root_function() -> i32 { + 42 +} + +const fn root_const() -> i32 { + 42 +} + +fn main() { + generated_function(); + child::child_function(); + crate::generated_export!(); +} diff --git a/rust/ql/test/extractor-tests/include-out-dir/test.expected b/rust/ql/test/extractor-tests/include-out-dir/test.expected new file mode 100644 index 000000000000..358705b6bc95 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-out-dir/test.expected @@ -0,0 +1,16 @@ +generatedFunction +| true | 3 | main::child::child_function | +| true | 3 | main::child::grandchild::grandchild_function | +| true | 3 | main::generated_function | +rootReference +| child_function | grandchild_function | +| generated_alias | root_function | +| generated_function | root_function | +| grandchild_function | root_function | +macroReference +| root_function | +exportedMacro +| generated_export | 1 | +includedInitializerScope +| INCLUDED_CONST | main.rs | +| INCLUDED_STATIC | main.rs | diff --git a/rust/ql/test/extractor-tests/include-out-dir/test.ql b/rust/ql/test/extractor-tests/include-out-dir/test.ql new file mode 100644 index 000000000000..f8ca240c76ec --- /dev/null +++ b/rust/ql/test/extractor-tests/include-out-dir/test.ql @@ -0,0 +1,53 @@ +import rust +import codeql.rust.internal.PathResolution +import codeql.rust.controlflow.ControlFlowGraph +import codeql.rust.elements.SourceFile + +query predicate generatedFunction(boolean fromSource, int cfgNodes, string canonicalPath) { + exists(Function f | + f.getName().getText() = ["generated_function", "child_function", "grandchild_function"] and + (if f.fromSource() then fromSource = true else fromSource = false) and + cfgNodes = count(CfgNode n | n.getAstNode().getEnclosingCallable() = f) and + canonicalPath = f.getCanonicalPath() + ) +} + +query predicate rootReference(string source, string target) { + exists(CallExpr call | + source = call.getEnclosingCallable().(Function).getName().getText() and + source = ["generated_function", "generated_alias", "child_function", "grandchild_function"] and + target = call.getResolvedTarget().(Function).getName().getText() + ) +} + +query predicate macroReference(string target) { + exists(CallExpr call | + call.fromSource() and + call.isInMacroExpansion() and + call.getFunction().(PathExpr).getPath().getQualifier().getText() = "$crate" and + target = call.getResolvedTarget().(Function).getName().getText() + ) +} + +query predicate exportedMacro(string name, int definitions) { + exists(CrateItemNode c | + c.getName() = "main" and + name = "generated_export" and + definitions = count(MacroRules m | m = c.getASuccessor(name)) + ) +} + +query predicate includedInitializerScope(string name, string scopeFile) { + exists(CallExpr call, AstNode initializer | + initializer = call.getParentNode*() and + (initializer instanceof Const or initializer instanceof Static) and + ( + name = initializer.(Const).getName().getText() + or + name = initializer.(Static).getName().getText() + ) and + name = ["INCLUDED_CONST", "INCLUDED_STATIC"] and + call.getResolvedTarget().(Function).getName().getText() = "root_const" and + scopeFile = call.getEnclosingCfgScope().(SourceFile).getFile().getRelativePath() + ) +} diff --git a/rust/ql/test/extractor-tests/include-standalone-and-active/main.rs b/rust/ql/test/extractor-tests/include-standalone-and-active/main.rs new file mode 100644 index 000000000000..2329a8c8bb2b --- /dev/null +++ b/rust/ql/test/extractor-tests/include-standalone-and-active/main.rs @@ -0,0 +1,5 @@ +mod shared; + +include!("shared.rs"); + +fn main() {} diff --git a/rust/ql/test/extractor-tests/include-standalone-and-active/shared.rs b/rust/ql/test/extractor-tests/include-standalone-and-active/shared.rs new file mode 100644 index 000000000000..ddba4d4519ae --- /dev/null +++ b/rust/ql/test/extractor-tests/include-standalone-and-active/shared.rs @@ -0,0 +1,5 @@ +pub fn shared_function() { + shared_callee(); +} + +fn shared_callee() {} diff --git a/rust/ql/test/extractor-tests/include-standalone-and-active/test.expected b/rust/ql/test/extractor-tests/include-standalone-and-active/test.expected new file mode 100644 index 000000000000..d7533c9de223 --- /dev/null +++ b/rust/ql/test/extractor-tests/include-standalone-and-active/test.expected @@ -0,0 +1,2 @@ +| false | +| true | diff --git a/rust/ql/test/extractor-tests/include-standalone-and-active/test.ql b/rust/ql/test/extractor-tests/include-standalone-and-active/test.ql new file mode 100644 index 000000000000..56d75c58218d --- /dev/null +++ b/rust/ql/test/extractor-tests/include-standalone-and-active/test.ql @@ -0,0 +1,11 @@ +import rust + +query predicate telemetryKeepsStandaloneAndIncludedCopies(boolean included) { + exists(Function function, ExtractedFile file | + function.getName().getText() = "shared_function" and + file = function.getFile() and + file.getRelativePath().matches("%shared.rs") and + file.isNotInactiveStandaloneCopy(function) and + (if file.isInActiveIncludeExpansion(function) then included = true else included = false) + ) +} diff --git a/rust/ql/test/extractor-tests/macro-expansion/PrintAst.expected b/rust/ql/test/extractor-tests/macro-expansion/PrintAst.expected index 5a1568f12d83..ba0798529bf9 100644 --- a/rust/ql/test/extractor-tests/macro-expansion/PrintAst.expected +++ b/rust/ql/test/extractor-tests/macro-expansion/PrintAst.expected @@ -141,6 +141,64 @@ included/included.rs: # 2| getIdentifier(): [NameRef] concat # 2| getTokenTree(): [TokenTree] TokenTree # 1| getName(): [Name] included +# 5| getItem(1): [Struct] struct IncludedDerived +# 5| getAttr(0): [Attr] Attr +# 5| getMeta(): [TokenTreeMeta] TokenTreeMeta +# 5| getPath(): [Path] derive +# 5| getSegment(): [PathSegment] derive +# 5| getIdentifier(): [NameRef] derive +# 5| getTokenTree(): [TokenTree] TokenTree +# 6| getName(): [Name] IncludedDerived +# 6| getFieldList(): [StructFieldList] StructFieldList +# 7| getField(0): [StructField] value: usize +# 7| getName(): [Name] value +# 7| getTypeRepr(): [PathTypeRepr] usize +# 7| getPath(): [Path] usize +# 7| getSegment(): [PathSegment] usize +# 7| getIdentifier(): [NameRef] usize +# 10| getItem(2): [MacroRules] MacroRules +# 10| getName(): [Name] identity +# 10| getTokenTree(): [TokenTree] TokenTree +# 16| getItem(3): [Function] fn argument_location +# 16| getParamList(): [ParamList] ParamList +# 16| getFunctionBody(): [BlockExpr] { ... } +# 16| getStmtList(): [StmtList] StmtList +# 17| getStatement(0): [ExprStmt] ExprStmt +# 17| getExpr(): [MacroExpr] MacroExpr +# 17| getMacroCall(): [MacroCall] identity!... +# 17| getPath(): [Path] identity +# 17| getSegment(): [PathSegment] identity +# 17| getIdentifier(): [NameRef] identity +# 17| getTokenTree(): [TokenTree] TokenTree +# 16| getName(): [Name] argument_location +# 22| getItem(4): [Function] fn dangerous_call +# 22| getParamList(): [ParamList] ParamList +# 22| getFunctionBody(): [BlockExpr] { ... } +# 22| getStmtList(): [StmtList] StmtList +# 22| getName(): [Name] dangerous_call +# 24| getItem(5): [MacroRules] MacroRules +# 24| getName(): [Name] definition_location +# 24| getTokenTree(): [TokenTree] TokenTree +# 30| getItem(6): [Function] fn definition_origin +# 30| getParamList(): [ParamList] ParamList +# 30| getFunctionBody(): [BlockExpr] { ... } +# 30| getStmtList(): [StmtList] StmtList +# 31| getStatement(0): [ExprStmt] ExprStmt +# 31| getExpr(): [MacroExpr] MacroExpr +# 31| getMacroCall(): [MacroCall] definition_location!... +# 31| getPath(): [Path] definition_location +# 31| getSegment(): [PathSegment] definition_location +# 31| getIdentifier(): [NameRef] definition_location +# 31| getTokenTree(): [TokenTree] TokenTree +# 30| getName(): [Name] definition_origin +# 34| getItem(7): [MacroRules] MacroRules +# 34| getName(): [Name] shadowed +# 34| getTokenTree(): [TokenTree] TokenTree +# 38| getItem(8): [MacroCall] shadowed!... +# 38| getPath(): [Path] shadowed +# 38| getSegment(): [PathSegment] shadowed +# 38| getIdentifier(): [NameRef] shadowed +# 38| getTokenTree(): [TokenTree] TokenTree # 2| [Comment] //... lib.rs: # 1| [SourceFile] SourceFile @@ -606,415 +664,588 @@ macro_expansion.rs: # 55| getSegment(): [PathSegment] must_use # 55| getIdentifier(): [NameRef] must_use # 52| getName(): [Name] test -# 61| getItem(11): [MacroCall] include!... -# 61| getPath(): [Path] include -# 61| getSegment(): [PathSegment] include -# 61| getIdentifier(): [NameRef] include +# 61| getItem(11): [MacroRules] MacroRules +# 61| getName(): [Name] shadowed # 61| getTokenTree(): [TokenTree] TokenTree -# 61| getMacroCallExpansion(): [MacroItems] MacroItems -# 61| getItem(0): [Function] fn included -# 61| getParamList(): [ParamList] ParamList -# 61| getFunctionBody(): [BlockExpr] { ... } -# 61| getStmtList(): [StmtList] StmtList -# 61| getStatement(0): [ExprStmt] ExprStmt -# 61| getExpr(): [AssignmentExpr] ... = ... -# 61| getLhs(): [UnderscoreExpr] _ -# 61| getRhs(): [MacroExpr] MacroExpr -# 61| getMacroCall(): [MacroCall] concat!... -# 61| getPath(): [Path] concat -# 61| getSegment(): [PathSegment] concat -# 61| getIdentifier(): [NameRef] concat -# 61| getTokenTree(): [TokenTree] TokenTree -# 61| getMacroCallExpansion(): [StringLiteralExpr] "Hello world!" -# 61| getName(): [Name] included -# 63| getItem(12): [Function] fn documented -# 64| getParamList(): [ParamList] ParamList -# 63| getAttr(0): [Attr] Attr -# 63| getMeta(): [KeyValueMeta] KeyValueMeta -# 63| getExpr(): [MacroExpr] MacroExpr -# 63| getMacroCall(): [MacroCall] include_str!... -# 63| getPath(): [Path] include_str -# 63| getSegment(): [PathSegment] include_str -# 63| getIdentifier(): [NameRef] include_str -# 63| getTokenTree(): [TokenTree] TokenTree -# 63| getPath(): [Path] doc -# 63| getSegment(): [PathSegment] doc -# 63| getIdentifier(): [NameRef] doc -# 64| getFunctionBody(): [BlockExpr] { ... } -# 64| getStmtList(): [StmtList] StmtList -# 64| getName(): [Name] documented -# 66| getItem(13): [MacroRules] MacroRules -# 66| getName(): [Name] my_int -# 66| getTokenTree(): [TokenTree] TokenTree -# 70| getItem(14): [Function] fn answer -# 70| getParamList(): [ParamList] ParamList -# 70| getFunctionBody(): [BlockExpr] { ... } -# 70| getStmtList(): [StmtList] StmtList -# 71| getStatement(0): [LetStmt] let ... = 42 -# 71| getInitializer(): [IntegerLiteralExpr] 42 -# 71| getPat(): [IdentPat] a -# 71| getName(): [Name] a -# 71| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr -# 71| getMacroCall(): [MacroCall] my_int!... -# 71| getPath(): [Path] my_int -# 71| getSegment(): [PathSegment] my_int -# 71| getIdentifier(): [NameRef] my_int -# 71| getTokenTree(): [TokenTree] TokenTree -# 71| getMacroCallExpansion(): [PathTypeRepr] i32 -# 71| getPath(): [Path] i32 -# 71| getSegment(): [PathSegment] i32 -# 71| getIdentifier(): [NameRef] i32 -# 72| getTailExpr(): [CastExpr] a as ... -# 72| getExpr(): [VariableAccess] a -# 72| getPath(): [Path] a -# 72| getSegment(): [PathSegment] a -# 72| getIdentifier(): [NameRef] a -# 72| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr -# 72| getMacroCall(): [MacroCall] my_int!... -# 72| getPath(): [Path] my_int -# 72| getSegment(): [PathSegment] my_int -# 72| getIdentifier(): [NameRef] my_int -# 72| getTokenTree(): [TokenTree] TokenTree -# 72| getMacroCallExpansion(): [PathTypeRepr] i32 -# 72| getPath(): [Path] i32 -# 72| getSegment(): [PathSegment] i32 -# 72| getIdentifier(): [NameRef] i32 -# 70| getName(): [Name] answer -# 70| getRetType(): [RetTypeRepr] RetTypeRepr -# 70| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr -# 70| getMacroCall(): [MacroCall] my_int!... -# 70| getPath(): [Path] my_int -# 70| getSegment(): [PathSegment] my_int -# 70| getIdentifier(): [NameRef] my_int -# 70| getTokenTree(): [TokenTree] TokenTree -# 70| getMacroCallExpansion(): [PathTypeRepr] i32 -# 70| getPath(): [Path] i32 -# 70| getSegment(): [PathSegment] i32 -# 70| getIdentifier(): [NameRef] i32 -# 76| getItem(15): [TypeAlias] type MyInt -# 76| getName(): [Name] MyInt -# 76| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr -# 76| getMacroCall(): [MacroCall] my_int!... -# 76| getPath(): [Path] my_int -# 76| getSegment(): [PathSegment] my_int -# 76| getIdentifier(): [NameRef] my_int -# 76| getTokenTree(): [TokenTree] TokenTree -# 76| getMacroCallExpansion(): [PathTypeRepr] i32 -# 76| getPath(): [Path] i32 -# 76| getSegment(): [PathSegment] i32 -# 76| getIdentifier(): [NameRef] i32 -# 78| getItem(16): [Struct] struct MyStruct -# 78| getName(): [Name] MyStruct -# 78| getFieldList(): [StructFieldList] StructFieldList -# 79| getField(0): [StructField] field: ... -# 79| getName(): [Name] field -# 79| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr -# 79| getMacroCall(): [MacroCall] my_int!... -# 79| getPath(): [Path] my_int -# 79| getSegment(): [PathSegment] my_int -# 79| getIdentifier(): [NameRef] my_int -# 79| getTokenTree(): [TokenTree] TokenTree -# 79| getMacroCallExpansion(): [PathTypeRepr] i32 -# 79| getPath(): [Path] i32 -# 79| getSegment(): [PathSegment] i32 -# 79| getIdentifier(): [NameRef] i32 -# 83| getItem(17): [Struct] struct MyDerive -# 83| getDeriveMacroExpansion(0): [MacroItems] MacroItems -# 83| getItem(0): [Impl] impl ...::Debug for MyDerive::<...> { ... } -# 83| getAssocItemList(): [AssocItemList] AssocItemList -# 83| getAssocItem(0): [Function] fn fmt -# 83| getParamList(): [ParamList] ParamList -# 83| getParam(0): [Param] ...: ... -# 83| getTypeRepr(): [RefTypeRepr] RefTypeRepr -# 83| getTypeRepr(): [PathTypeRepr] ...::Formatter -# 83| getPath(): [Path] ...::Formatter -# 83| getQualifier(): [Path] ...::fmt -# 83| getQualifier(): [Path] $crate -# 83| getSegment(): [PathSegment] $crate -# 83| getIdentifier(): [NameRef] $crate -# 83| getSegment(): [PathSegment] fmt -# 83| getIdentifier(): [NameRef] fmt -# 83| getSegment(): [PathSegment] Formatter -# 83| getIdentifier(): [NameRef] Formatter -# 83| getPat(): [IdentPat] f -# 83| getName(): [Name] f -# 83| getSelfParam(): [SelfParam] SelfParam -# 83| getName(): [Name] self -# 83| getFunctionBody(): [BlockExpr] { ... } -# 83| getStmtList(): [StmtList] StmtList -# 83| getTailExpr(): [MatchExpr] match self { ... } -# 83| getScrutinee(): [VariableAccess] self -# 83| getPath(): [Path] self -# 83| getSegment(): [PathSegment] self -# 83| getIdentifier(): [NameRef] self -# 83| getMatchArmList(): [MatchArmList] MatchArmList -# 83| getArm(0): [MatchArm] ... => ... -# 83| getExpr(): [MethodCallExpr] ... .finish() -# 83| getArgList(): [ArgList] ArgList -# 83| getIdentifier(): [NameRef] finish -# 83| getReceiver(): [MethodCallExpr] ... .field(...) -# 83| getArgList(): [ArgList] ArgList -# 83| getArg(0): [StringLiteralExpr] "field" -# 83| getArg(1): [RefExpr] &field -# 85| getExpr(): [VariableAccess] field -# 85| getPath(): [Path] field -# 85| getSegment(): [PathSegment] field -# 85| getIdentifier(): [NameRef] field -# 83| getIdentifier(): [NameRef] field -# 83| getReceiver(): [MethodCallExpr] f.debug_struct(...) -# 83| getArgList(): [ArgList] ArgList -# 83| getArg(0): [StringLiteralExpr] "MyDerive" -# 83| getIdentifier(): [NameRef] debug_struct -# 83| getReceiver(): [VariableAccess] f -# 83| getPath(): [Path] f -# 83| getSegment(): [PathSegment] f -# 83| getIdentifier(): [NameRef] f -# 83| getPat(): [StructPat] MyDerive {...} -# 84| getPath(): [Path] MyDerive -# 84| getSegment(): [PathSegment] MyDerive -# 84| getIdentifier(): [NameRef] MyDerive -# 83| getStructPatFieldList(): [StructPatFieldList] StructPatFieldList -# 83| getField(0): [StructPatField] field: ... -# 85| getIdentifier(): [NameRef] field -# 85| getPat(): [IdentPat] field -# 85| getName(): [Name] field -# 83| getName(): [Name] fmt -# 83| getRetType(): [RetTypeRepr] RetTypeRepr -# 83| getTypeRepr(): [PathTypeRepr] ...::Result -# 83| getPath(): [Path] ...::Result -# 83| getQualifier(): [Path] ...::fmt -# 83| getQualifier(): [Path] $crate -# 83| getSegment(): [PathSegment] $crate -# 83| getIdentifier(): [NameRef] $crate -# 83| getSegment(): [PathSegment] fmt -# 83| getIdentifier(): [NameRef] fmt -# 83| getSegment(): [PathSegment] Result -# 83| getIdentifier(): [NameRef] Result -# 83| getGenericParamList(): [GenericParamList] <...> -# 83| getSelfTy(): [PathTypeRepr] MyDerive::<...> -# 83| getPath(): [Path] MyDerive::<...> -# 83| getSegment(): [PathSegment] MyDerive::<...> -# 83| getGenericArgList(): [GenericArgList] <...> -# 84| getIdentifier(): [NameRef] MyDerive -# 83| getTraitTy(): [PathTypeRepr] ...::Debug -# 83| getPath(): [Path] ...::Debug -# 83| getQualifier(): [Path] ...::fmt -# 83| getQualifier(): [Path] $crate -# 83| getSegment(): [PathSegment] $crate -# 83| getIdentifier(): [NameRef] $crate -# 83| getSegment(): [PathSegment] fmt -# 83| getIdentifier(): [NameRef] fmt -# 83| getSegment(): [PathSegment] Debug -# 83| getIdentifier(): [NameRef] Debug -# 83| getWhereClause(): [WhereClause] WhereClause -# 83| getAttr(0): [Attr] Attr -# 83| getMeta(): [TokenTreeMeta] TokenTreeMeta -# 83| getPath(): [Path] derive -# 83| getSegment(): [PathSegment] derive -# 83| getIdentifier(): [NameRef] derive -# 83| getTokenTree(): [TokenTree] TokenTree -# 84| getName(): [Name] MyDerive -# 84| getFieldList(): [StructFieldList] StructFieldList -# 85| getField(0): [StructField] field: usize -# 85| getName(): [Name] field -# 85| getTypeRepr(): [PathTypeRepr] usize -# 85| getPath(): [Path] usize -# 85| getSegment(): [PathSegment] usize -# 85| getIdentifier(): [NameRef] usize -# 88| getItem(18): [Enum] enum MyDeriveEnum -# 88| getDeriveMacroExpansion(0): [MacroItems] MacroItems -# 88| getItem(0): [Impl] impl ...::PartialEq for MyDeriveEnum::<...> { ... } -# 88| getAssocItemList(): [AssocItemList] AssocItemList -# 88| getAssocItem(0): [Function] fn eq -# 88| getParamList(): [ParamList] ParamList -# 88| getParam(0): [Param] ...: ... -# 88| getTypeRepr(): [RefTypeRepr] RefTypeRepr -# 88| getTypeRepr(): [PathTypeRepr] Self -# 88| getPath(): [Path] Self -# 88| getSegment(): [PathSegment] Self -# 88| getIdentifier(): [NameRef] Self -# 88| getPat(): [IdentPat] other -# 88| getName(): [Name] other -# 88| getSelfParam(): [SelfParam] SelfParam -# 88| getName(): [Name] self -# 88| getFunctionBody(): [BlockExpr] { ... } -# 88| getStmtList(): [StmtList] StmtList -# 88| getTailExpr(): [MatchExpr] match ... { ... } -# 88| getScrutinee(): [TupleExpr] TupleExpr -# 88| getField(0): [VariableAccess] self -# 88| getPath(): [Path] self -# 88| getSegment(): [PathSegment] self -# 88| getIdentifier(): [NameRef] self -# 88| getField(1): [VariableAccess] other -# 88| getPath(): [Path] other -# 88| getSegment(): [PathSegment] other -# 88| getIdentifier(): [NameRef] other -# 88| getMatchArmList(): [MatchArmList] MatchArmList -# 88| getArm(0): [MatchArm] ... => true -# 88| getExpr(): [BooleanLiteralExpr] true -# 88| getPat(): [TuplePat] TuplePat -# 88| getField(0): [PathPat] ...::Variant1 -# 88| getPath(): [Path] ...::Variant1 -# 89| getQualifier(): [Path] MyDeriveEnum -# 89| getSegment(): [PathSegment] MyDeriveEnum -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 90| getSegment(): [PathSegment] Variant1 -# 90| getIdentifier(): [NameRef] Variant1 -# 88| getField(1): [PathPat] ...::Variant1 -# 88| getPath(): [Path] ...::Variant1 -# 89| getQualifier(): [Path] MyDeriveEnum -# 89| getSegment(): [PathSegment] MyDeriveEnum -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 90| getSegment(): [PathSegment] Variant1 -# 90| getIdentifier(): [NameRef] Variant1 -# 88| getArm(1): [MatchArm] ... => true -# 88| getExpr(): [BooleanLiteralExpr] true -# 88| getPat(): [TuplePat] TuplePat -# 88| getField(0): [PathPat] ...::Variant2 -# 88| getPath(): [Path] ...::Variant2 -# 89| getQualifier(): [Path] MyDeriveEnum -# 89| getSegment(): [PathSegment] MyDeriveEnum -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 91| getSegment(): [PathSegment] Variant2 -# 91| getIdentifier(): [NameRef] Variant2 -# 88| getField(1): [PathPat] ...::Variant2 -# 88| getPath(): [Path] ...::Variant2 -# 89| getQualifier(): [Path] MyDeriveEnum -# 89| getSegment(): [PathSegment] MyDeriveEnum -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 91| getSegment(): [PathSegment] Variant2 -# 91| getIdentifier(): [NameRef] Variant2 -# 88| getArm(2): [MatchArm] ... => false -# 88| getExpr(): [BooleanLiteralExpr] false -# 88| getPat(): [IdentPat] _unused -# 88| getName(): [Name] _unused -# 88| getName(): [Name] eq -# 88| getRetType(): [RetTypeRepr] RetTypeRepr -# 88| getTypeRepr(): [PathTypeRepr] bool -# 88| getPath(): [Path] bool -# 88| getSegment(): [PathSegment] bool -# 88| getIdentifier(): [NameRef] bool -# 88| getGenericParamList(): [GenericParamList] <...> -# 88| getSelfTy(): [PathTypeRepr] MyDeriveEnum::<...> -# 88| getPath(): [Path] MyDeriveEnum::<...> -# 88| getSegment(): [PathSegment] MyDeriveEnum::<...> -# 88| getGenericArgList(): [GenericArgList] <...> -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 88| getTraitTy(): [PathTypeRepr] ...::PartialEq -# 88| getPath(): [Path] ...::PartialEq -# 88| getQualifier(): [Path] ...::cmp -# 88| getQualifier(): [Path] $crate -# 88| getSegment(): [PathSegment] $crate -# 88| getIdentifier(): [NameRef] $crate -# 88| getSegment(): [PathSegment] cmp -# 88| getIdentifier(): [NameRef] cmp -# 88| getSegment(): [PathSegment] PartialEq -# 88| getIdentifier(): [NameRef] PartialEq -# 88| getWhereClause(): [WhereClause] WhereClause -# 88| getDeriveMacroExpansion(1): [MacroItems] MacroItems -# 88| getItem(0): [Impl] impl ...::Eq for MyDeriveEnum::<...> { ... } -# 88| getAssocItemList(): [AssocItemList] AssocItemList -# 88| getGenericParamList(): [GenericParamList] <...> -# 88| getSelfTy(): [PathTypeRepr] MyDeriveEnum::<...> -# 88| getPath(): [Path] MyDeriveEnum::<...> -# 88| getSegment(): [PathSegment] MyDeriveEnum::<...> -# 88| getGenericArgList(): [GenericArgList] <...> -# 89| getIdentifier(): [NameRef] MyDeriveEnum -# 88| getTraitTy(): [PathTypeRepr] ...::Eq -# 88| getPath(): [Path] ...::Eq -# 88| getQualifier(): [Path] ...::cmp -# 88| getQualifier(): [Path] $crate -# 88| getSegment(): [PathSegment] $crate -# 88| getIdentifier(): [NameRef] $crate -# 88| getSegment(): [PathSegment] cmp -# 88| getIdentifier(): [NameRef] cmp -# 88| getSegment(): [PathSegment] Eq -# 88| getIdentifier(): [NameRef] Eq -# 88| getWhereClause(): [WhereClause] WhereClause -# 88| getAttr(0): [Attr] Attr -# 88| getMeta(): [TokenTreeMeta] TokenTreeMeta -# 88| getPath(): [Path] derive -# 88| getSegment(): [PathSegment] derive -# 88| getIdentifier(): [NameRef] derive -# 88| getTokenTree(): [TokenTree] TokenTree -# 89| getName(): [Name] MyDeriveEnum -# 89| getVariantList(): [VariantList] VariantList -# 90| getVariant(0): [Variant] Variant1 -# 90| getName(): [Name] Variant1 -# 91| getVariant(1): [Variant] Variant2 -# 91| getName(): [Name] Variant2 -# 94| getItem(19): [Trait] trait MyTrait -# 94| getAssocItemList(): [AssocItemList] AssocItemList -# 95| getAssocItem(0): [Function] fn my_method -# 95| getParamList(): [ParamList] ParamList -# 95| getName(): [Name] my_method -# 95| getRetType(): [RetTypeRepr] RetTypeRepr -# 95| getTypeRepr(): [PathTypeRepr] u32 -# 95| getPath(): [Path] u32 -# 95| getSegment(): [PathSegment] u32 -# 95| getIdentifier(): [NameRef] u32 -# 94| getName(): [Name] MyTrait -# 98| getItem(20): [Union] union MyDeriveUnion -# 99| getDeriveMacroExpansion(0): [MacroItems] MacroItems -# 99| getItem(0): [Const] const CONST_MyDeriveUnion -# 98| getBody(): [IntegerLiteralExpr] 42 -# 99| getName(): [Name] CONST_MyDeriveUnion -# 98| getTypeRepr(): [PathTypeRepr] u32 -# 98| getPath(): [Path] u32 -# 98| getSegment(): [PathSegment] u32 -# 98| getIdentifier(): [NameRef] u32 -# 99| getItem(1): [Impl] impl MyTrait for MyDeriveUnion { ... } -# 99| getAssocItemList(): [AssocItemList] AssocItemList -# 99| getAssocItem(0): [Function] fn my_method -# 98| getParamList(): [ParamList] ParamList -# 99| getFunctionBody(): [BlockExpr] { ... } -# 99| getStmtList(): [StmtList] StmtList -# 99| getTailExpr(): [ConstAccess] CONST_MyDeriveUnion -# 99| getPath(): [Path] CONST_MyDeriveUnion -# 99| getSegment(): [PathSegment] CONST_MyDeriveUnion -# 99| getIdentifier(): [NameRef] CONST_MyDeriveUnion -# 98| getName(): [Name] my_method -# 98| getRetType(): [RetTypeRepr] RetTypeRepr -# 98| getTypeRepr(): [PathTypeRepr] u32 -# 98| getPath(): [Path] u32 -# 98| getSegment(): [PathSegment] u32 -# 98| getIdentifier(): [NameRef] u32 -# 99| getSelfTy(): [PathTypeRepr] MyDeriveUnion -# 99| getPath(): [Path] MyDeriveUnion -# 99| getSegment(): [PathSegment] MyDeriveUnion -# 99| getIdentifier(): [NameRef] MyDeriveUnion -# 98| getTraitTy(): [PathTypeRepr] MyTrait -# 98| getPath(): [Path] MyTrait -# 98| getSegment(): [PathSegment] MyTrait -# 98| getIdentifier(): [NameRef] MyTrait -# 98| getAttr(0): [Attr] Attr -# 98| getMeta(): [TokenTreeMeta] TokenTreeMeta -# 98| getPath(): [Path] derive -# 98| getSegment(): [PathSegment] derive -# 98| getIdentifier(): [NameRef] derive -# 98| getTokenTree(): [TokenTree] TokenTree -# 99| getName(): [Name] MyDeriveUnion -# 99| getStructFieldList(): [StructFieldList] StructFieldList -# 100| getField(0): [StructField] field1: usize -# 100| getName(): [Name] field1 -# 100| getTypeRepr(): [PathTypeRepr] usize -# 100| getPath(): [Path] usize -# 100| getSegment(): [PathSegment] usize -# 100| getIdentifier(): [NameRef] usize -# 101| getField(1): [StructField] field2: f64 -# 101| getName(): [Name] field2 -# 101| getTypeRepr(): [PathTypeRepr] f64 -# 101| getPath(): [Path] f64 -# 101| getSegment(): [PathSegment] f64 -# 101| getIdentifier(): [NameRef] f64 +# 65| getItem(12): [MacroCall] include!... +# 65| getPath(): [Path] include +# 65| getSegment(): [PathSegment] include +# 65| getIdentifier(): [NameRef] include +# 65| getTokenTree(): [TokenTree] TokenTree +included/included.rs: +# 1| getMacroCallExpansion(): [MacroItems] MacroItems +# 1| getItem(0): [Function] fn included +# 1| getParamList(): [ParamList] ParamList +# 1| getFunctionBody(): [BlockExpr] { ... } +# 1| getStmtList(): [StmtList] StmtList +# 2| getStatement(0): [ExprStmt] ExprStmt +# 2| getExpr(): [AssignmentExpr] ... = ... +# 2| getLhs(): [UnderscoreExpr] _ +# 2| getRhs(): [MacroExpr] MacroExpr +# 2| getMacroCall(): [MacroCall] concat!... +# 2| getPath(): [Path] concat +# 2| getSegment(): [PathSegment] concat +# 2| getIdentifier(): [NameRef] concat +# 2| getTokenTree(): [TokenTree] TokenTree +# 2| getMacroCallExpansion(): [StringLiteralExpr] "Hello world!" +# 1| getName(): [Name] included +# 5| getItem(1): [Struct] struct IncludedDerived +# 5| getDeriveMacroExpansion(0): [MacroItems] MacroItems +# 5| getItem(0): [Impl] impl ...::Debug for IncludedDerived::<...> { ... } +# 5| getAssocItemList(): [AssocItemList] AssocItemList +# 5| getAssocItem(0): [Function] fn fmt +# 5| getParamList(): [ParamList] ParamList +# 5| getParam(0): [Param] ...: ... +# 5| getTypeRepr(): [RefTypeRepr] RefTypeRepr +# 5| getTypeRepr(): [PathTypeRepr] ...::Formatter +# 5| getPath(): [Path] ...::Formatter +# 5| getQualifier(): [Path] ...::fmt +# 5| getQualifier(): [Path] $crate +# 5| getSegment(): [PathSegment] $crate +# 5| getIdentifier(): [NameRef] $crate +# 5| getSegment(): [PathSegment] fmt +# 5| getIdentifier(): [NameRef] fmt +# 5| getSegment(): [PathSegment] Formatter +# 5| getIdentifier(): [NameRef] Formatter +# 5| getPat(): [IdentPat] f +# 5| getName(): [Name] f +# 5| getSelfParam(): [SelfParam] SelfParam +# 5| getName(): [Name] self +# 5| getFunctionBody(): [BlockExpr] { ... } +# 5| getStmtList(): [StmtList] StmtList +# 5| getTailExpr(): [MatchExpr] match self { ... } +# 5| getScrutinee(): [VariableAccess] self +# 5| getPath(): [Path] self +# 5| getSegment(): [PathSegment] self +# 5| getIdentifier(): [NameRef] self +# 5| getMatchArmList(): [MatchArmList] MatchArmList +# 5| getArm(0): [MatchArm] ... => ... +# 5| getExpr(): [MethodCallExpr] ... .finish() +# 5| getArgList(): [ArgList] ArgList +# 5| getIdentifier(): [NameRef] finish +# 5| getReceiver(): [MethodCallExpr] ... .field(...) +# 5| getArgList(): [ArgList] ArgList +# 5| getArg(0): [StringLiteralExpr] "value" +# 5| getArg(1): [RefExpr] &value +# 7| getExpr(): [VariableAccess] value +# 7| getPath(): [Path] value +# 7| getSegment(): [PathSegment] value +# 7| getIdentifier(): [NameRef] value +# 5| getIdentifier(): [NameRef] field +# 5| getReceiver(): [MethodCallExpr] f.debug_struct(...) +# 5| getArgList(): [ArgList] ArgList +# 5| getArg(0): [StringLiteralExpr] "IncludedDerived" +# 5| getIdentifier(): [NameRef] debug_struct +# 5| getReceiver(): [VariableAccess] f +# 5| getPath(): [Path] f +# 5| getSegment(): [PathSegment] f +# 5| getIdentifier(): [NameRef] f +# 5| getPat(): [StructPat] IncludedDerived {...} +# 6| getPath(): [Path] IncludedDerived +# 6| getSegment(): [PathSegment] IncludedDerived +# 6| getIdentifier(): [NameRef] IncludedDerived +# 5| getStructPatFieldList(): [StructPatFieldList] StructPatFieldList +# 5| getField(0): [StructPatField] value: ... +# 7| getIdentifier(): [NameRef] value +# 7| getPat(): [IdentPat] value +# 7| getName(): [Name] value +# 5| getName(): [Name] fmt +# 5| getRetType(): [RetTypeRepr] RetTypeRepr +# 5| getTypeRepr(): [PathTypeRepr] ...::Result +# 5| getPath(): [Path] ...::Result +# 5| getQualifier(): [Path] ...::fmt +# 5| getQualifier(): [Path] $crate +# 5| getSegment(): [PathSegment] $crate +# 5| getIdentifier(): [NameRef] $crate +# 5| getSegment(): [PathSegment] fmt +# 5| getIdentifier(): [NameRef] fmt +# 5| getSegment(): [PathSegment] Result +# 5| getIdentifier(): [NameRef] Result +# 5| getGenericParamList(): [GenericParamList] <...> +# 5| getSelfTy(): [PathTypeRepr] IncludedDerived::<...> +# 5| getPath(): [Path] IncludedDerived::<...> +# 5| getSegment(): [PathSegment] IncludedDerived::<...> +# 5| getGenericArgList(): [GenericArgList] <...> +# 6| getIdentifier(): [NameRef] IncludedDerived +# 5| getTraitTy(): [PathTypeRepr] ...::Debug +# 5| getPath(): [Path] ...::Debug +# 5| getQualifier(): [Path] ...::fmt +# 5| getQualifier(): [Path] $crate +# 5| getSegment(): [PathSegment] $crate +# 5| getIdentifier(): [NameRef] $crate +# 5| getSegment(): [PathSegment] fmt +# 5| getIdentifier(): [NameRef] fmt +# 5| getSegment(): [PathSegment] Debug +# 5| getIdentifier(): [NameRef] Debug +# 5| getWhereClause(): [WhereClause] WhereClause +# 5| getAttr(0): [Attr] Attr +# 5| getMeta(): [TokenTreeMeta] TokenTreeMeta +# 5| getPath(): [Path] derive +# 5| getSegment(): [PathSegment] derive +# 5| getIdentifier(): [NameRef] derive +# 5| getTokenTree(): [TokenTree] TokenTree +# 6| getName(): [Name] IncludedDerived +# 6| getFieldList(): [StructFieldList] StructFieldList +# 7| getField(0): [StructField] value: usize +# 7| getName(): [Name] value +# 7| getTypeRepr(): [PathTypeRepr] usize +# 7| getPath(): [Path] usize +# 7| getSegment(): [PathSegment] usize +# 7| getIdentifier(): [NameRef] usize +# 10| getItem(2): [MacroRules] MacroRules +# 10| getName(): [Name] identity +# 10| getTokenTree(): [TokenTree] TokenTree +# 16| getItem(3): [Function] fn argument_location +# 16| getParamList(): [ParamList] ParamList +# 16| getFunctionBody(): [BlockExpr] { ... } +# 16| getStmtList(): [StmtList] StmtList +# 17| getStatement(0): [ExprStmt] ExprStmt +# 17| getExpr(): [MacroExpr] MacroExpr +# 17| getMacroCall(): [MacroCall] identity!... +# 17| getPath(): [Path] identity +# 17| getSegment(): [PathSegment] identity +# 17| getIdentifier(): [NameRef] identity +# 17| getTokenTree(): [TokenTree] TokenTree +# 17| getMacroCallExpansion(): [BlockExpr] { ... } +# 17| getStmtList(): [StmtList] StmtList +# 17| getTailExpr(): [BlockExpr] { ... } +# 17| getStmtList(): [StmtList] StmtList +# 18| getStatement(0): [ExprStmt] ExprStmt +# 18| getExpr(): [CallExpr] dangerous_call(...) +# 18| getArgList(): [ArgList] ArgList +# 18| getFunction(): [PathExpr] dangerous_call +# 18| getPath(): [Path] dangerous_call +# 18| getSegment(): [PathSegment] dangerous_call +# 18| getIdentifier(): [NameRef] dangerous_call +# 16| getName(): [Name] argument_location +# 22| getItem(4): [Function] fn dangerous_call +# 22| getParamList(): [ParamList] ParamList +# 22| getFunctionBody(): [BlockExpr] { ... } +# 22| getStmtList(): [StmtList] StmtList +# 22| getName(): [Name] dangerous_call +# 24| getItem(5): [MacroRules] MacroRules +# 24| getName(): [Name] definition_location +# 24| getTokenTree(): [TokenTree] TokenTree +# 30| getItem(6): [Function] fn definition_origin +# 30| getParamList(): [ParamList] ParamList +# 30| getFunctionBody(): [BlockExpr] { ... } +# 30| getStmtList(): [StmtList] StmtList +# 31| getStatement(0): [ExprStmt] ExprStmt +# 31| getExpr(): [MacroExpr] MacroExpr +# 31| getMacroCall(): [MacroCall] definition_location!... +# 31| getPath(): [Path] definition_location +# 31| getSegment(): [PathSegment] definition_location +# 31| getIdentifier(): [NameRef] definition_location +# 31| getTokenTree(): [TokenTree] TokenTree +# 31| getMacroCallExpansion(): [BlockExpr] { ... } +# 31| getStmtList(): [StmtList] StmtList +# 31| getTailExpr(): [CallExpr] dangerous_call(...) +# 31| getArgList(): [ArgList] ArgList +# 31| getFunction(): [PathExpr] dangerous_call +# 31| getPath(): [Path] dangerous_call +# 31| getSegment(): [PathSegment] dangerous_call +# 31| getIdentifier(): [NameRef] dangerous_call +# 30| getName(): [Name] definition_origin +# 34| getItem(7): [MacroRules] MacroRules +# 34| getName(): [Name] shadowed +# 34| getTokenTree(): [TokenTree] TokenTree +# 38| getItem(8): [MacroCall] shadowed!... +# 38| getPath(): [Path] shadowed +# 38| getSegment(): [PathSegment] shadowed +# 38| getIdentifier(): [NameRef] shadowed +# 38| getTokenTree(): [TokenTree] TokenTree +# 38| getMacroCallExpansion(): [MacroItems] MacroItems +# 38| getItem(0): [Struct] struct IncludedShadowed +# 38| getName(): [Name] IncludedShadowed +macro_expansion.rs: +# 67| getItem(13): [Function] fn documented +# 68| getParamList(): [ParamList] ParamList +# 67| getAttr(0): [Attr] Attr +# 67| getMeta(): [KeyValueMeta] KeyValueMeta +# 67| getExpr(): [MacroExpr] MacroExpr +# 67| getMacroCall(): [MacroCall] include_str!... +# 67| getPath(): [Path] include_str +# 67| getSegment(): [PathSegment] include_str +# 67| getIdentifier(): [NameRef] include_str +# 67| getTokenTree(): [TokenTree] TokenTree +# 67| getPath(): [Path] doc +# 67| getSegment(): [PathSegment] doc +# 67| getIdentifier(): [NameRef] doc +# 68| getFunctionBody(): [BlockExpr] { ... } +# 68| getStmtList(): [StmtList] StmtList +# 68| getName(): [Name] documented +# 70| getItem(14): [MacroRules] MacroRules +# 70| getName(): [Name] my_int +# 70| getTokenTree(): [TokenTree] TokenTree +# 74| getItem(15): [Function] fn answer +# 74| getParamList(): [ParamList] ParamList +# 74| getFunctionBody(): [BlockExpr] { ... } +# 74| getStmtList(): [StmtList] StmtList +# 75| getStatement(0): [LetStmt] let ... = 42 +# 75| getInitializer(): [IntegerLiteralExpr] 42 +# 75| getPat(): [IdentPat] a +# 75| getName(): [Name] a +# 75| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr +# 75| getMacroCall(): [MacroCall] my_int!... +# 75| getPath(): [Path] my_int +# 75| getSegment(): [PathSegment] my_int +# 75| getIdentifier(): [NameRef] my_int +# 75| getTokenTree(): [TokenTree] TokenTree +# 75| getMacroCallExpansion(): [PathTypeRepr] i32 +# 75| getPath(): [Path] i32 +# 75| getSegment(): [PathSegment] i32 +# 75| getIdentifier(): [NameRef] i32 +# 76| getTailExpr(): [CastExpr] a as ... +# 76| getExpr(): [VariableAccess] a +# 76| getPath(): [Path] a +# 76| getSegment(): [PathSegment] a +# 76| getIdentifier(): [NameRef] a +# 76| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr +# 76| getMacroCall(): [MacroCall] my_int!... +# 76| getPath(): [Path] my_int +# 76| getSegment(): [PathSegment] my_int +# 76| getIdentifier(): [NameRef] my_int +# 76| getTokenTree(): [TokenTree] TokenTree +# 76| getMacroCallExpansion(): [PathTypeRepr] i32 +# 76| getPath(): [Path] i32 +# 76| getSegment(): [PathSegment] i32 +# 76| getIdentifier(): [NameRef] i32 +# 74| getName(): [Name] answer +# 74| getRetType(): [RetTypeRepr] RetTypeRepr +# 74| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr +# 74| getMacroCall(): [MacroCall] my_int!... +# 74| getPath(): [Path] my_int +# 74| getSegment(): [PathSegment] my_int +# 74| getIdentifier(): [NameRef] my_int +# 74| getTokenTree(): [TokenTree] TokenTree +# 74| getMacroCallExpansion(): [PathTypeRepr] i32 +# 74| getPath(): [Path] i32 +# 74| getSegment(): [PathSegment] i32 +# 74| getIdentifier(): [NameRef] i32 +# 80| getItem(16): [TypeAlias] type MyInt +# 80| getName(): [Name] MyInt +# 80| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr +# 80| getMacroCall(): [MacroCall] my_int!... +# 80| getPath(): [Path] my_int +# 80| getSegment(): [PathSegment] my_int +# 80| getIdentifier(): [NameRef] my_int +# 80| getTokenTree(): [TokenTree] TokenTree +# 80| getMacroCallExpansion(): [PathTypeRepr] i32 +# 80| getPath(): [Path] i32 +# 80| getSegment(): [PathSegment] i32 +# 80| getIdentifier(): [NameRef] i32 +# 82| getItem(17): [Struct] struct MyStruct +# 82| getName(): [Name] MyStruct +# 82| getFieldList(): [StructFieldList] StructFieldList +# 83| getField(0): [StructField] field: ... +# 83| getName(): [Name] field +# 83| getTypeRepr(): [MacroTypeRepr] MacroTypeRepr +# 83| getMacroCall(): [MacroCall] my_int!... +# 83| getPath(): [Path] my_int +# 83| getSegment(): [PathSegment] my_int +# 83| getIdentifier(): [NameRef] my_int +# 83| getTokenTree(): [TokenTree] TokenTree +# 83| getMacroCallExpansion(): [PathTypeRepr] i32 +# 83| getPath(): [Path] i32 +# 83| getSegment(): [PathSegment] i32 +# 83| getIdentifier(): [NameRef] i32 +# 87| getItem(18): [Struct] struct MyDerive +# 87| getDeriveMacroExpansion(0): [MacroItems] MacroItems +# 87| getItem(0): [Impl] impl ...::Debug for MyDerive::<...> { ... } +# 87| getAssocItemList(): [AssocItemList] AssocItemList +# 87| getAssocItem(0): [Function] fn fmt +# 87| getParamList(): [ParamList] ParamList +# 87| getParam(0): [Param] ...: ... +# 87| getTypeRepr(): [RefTypeRepr] RefTypeRepr +# 87| getTypeRepr(): [PathTypeRepr] ...::Formatter +# 87| getPath(): [Path] ...::Formatter +# 87| getQualifier(): [Path] ...::fmt +# 87| getQualifier(): [Path] $crate +# 87| getSegment(): [PathSegment] $crate +# 87| getIdentifier(): [NameRef] $crate +# 87| getSegment(): [PathSegment] fmt +# 87| getIdentifier(): [NameRef] fmt +# 87| getSegment(): [PathSegment] Formatter +# 87| getIdentifier(): [NameRef] Formatter +# 87| getPat(): [IdentPat] f +# 87| getName(): [Name] f +# 87| getSelfParam(): [SelfParam] SelfParam +# 87| getName(): [Name] self +# 87| getFunctionBody(): [BlockExpr] { ... } +# 87| getStmtList(): [StmtList] StmtList +# 87| getTailExpr(): [MatchExpr] match self { ... } +# 87| getScrutinee(): [VariableAccess] self +# 87| getPath(): [Path] self +# 87| getSegment(): [PathSegment] self +# 87| getIdentifier(): [NameRef] self +# 87| getMatchArmList(): [MatchArmList] MatchArmList +# 87| getArm(0): [MatchArm] ... => ... +# 87| getExpr(): [MethodCallExpr] ... .finish() +# 87| getArgList(): [ArgList] ArgList +# 87| getIdentifier(): [NameRef] finish +# 87| getReceiver(): [MethodCallExpr] ... .field(...) +# 87| getArgList(): [ArgList] ArgList +# 87| getArg(0): [StringLiteralExpr] "field" +# 87| getArg(1): [RefExpr] &field +# 89| getExpr(): [VariableAccess] field +# 89| getPath(): [Path] field +# 89| getSegment(): [PathSegment] field +# 89| getIdentifier(): [NameRef] field +# 87| getIdentifier(): [NameRef] field +# 87| getReceiver(): [MethodCallExpr] f.debug_struct(...) +# 87| getArgList(): [ArgList] ArgList +# 87| getArg(0): [StringLiteralExpr] "MyDerive" +# 87| getIdentifier(): [NameRef] debug_struct +# 87| getReceiver(): [VariableAccess] f +# 87| getPath(): [Path] f +# 87| getSegment(): [PathSegment] f +# 87| getIdentifier(): [NameRef] f +# 87| getPat(): [StructPat] MyDerive {...} +# 88| getPath(): [Path] MyDerive +# 88| getSegment(): [PathSegment] MyDerive +# 88| getIdentifier(): [NameRef] MyDerive +# 87| getStructPatFieldList(): [StructPatFieldList] StructPatFieldList +# 87| getField(0): [StructPatField] field: ... +# 89| getIdentifier(): [NameRef] field +# 89| getPat(): [IdentPat] field +# 89| getName(): [Name] field +# 87| getName(): [Name] fmt +# 87| getRetType(): [RetTypeRepr] RetTypeRepr +# 87| getTypeRepr(): [PathTypeRepr] ...::Result +# 87| getPath(): [Path] ...::Result +# 87| getQualifier(): [Path] ...::fmt +# 87| getQualifier(): [Path] $crate +# 87| getSegment(): [PathSegment] $crate +# 87| getIdentifier(): [NameRef] $crate +# 87| getSegment(): [PathSegment] fmt +# 87| getIdentifier(): [NameRef] fmt +# 87| getSegment(): [PathSegment] Result +# 87| getIdentifier(): [NameRef] Result +# 87| getGenericParamList(): [GenericParamList] <...> +# 87| getSelfTy(): [PathTypeRepr] MyDerive::<...> +# 87| getPath(): [Path] MyDerive::<...> +# 87| getSegment(): [PathSegment] MyDerive::<...> +# 87| getGenericArgList(): [GenericArgList] <...> +# 88| getIdentifier(): [NameRef] MyDerive +# 87| getTraitTy(): [PathTypeRepr] ...::Debug +# 87| getPath(): [Path] ...::Debug +# 87| getQualifier(): [Path] ...::fmt +# 87| getQualifier(): [Path] $crate +# 87| getSegment(): [PathSegment] $crate +# 87| getIdentifier(): [NameRef] $crate +# 87| getSegment(): [PathSegment] fmt +# 87| getIdentifier(): [NameRef] fmt +# 87| getSegment(): [PathSegment] Debug +# 87| getIdentifier(): [NameRef] Debug +# 87| getWhereClause(): [WhereClause] WhereClause +# 87| getAttr(0): [Attr] Attr +# 87| getMeta(): [TokenTreeMeta] TokenTreeMeta +# 87| getPath(): [Path] derive +# 87| getSegment(): [PathSegment] derive +# 87| getIdentifier(): [NameRef] derive +# 87| getTokenTree(): [TokenTree] TokenTree +# 88| getName(): [Name] MyDerive +# 88| getFieldList(): [StructFieldList] StructFieldList +# 89| getField(0): [StructField] field: usize +# 89| getName(): [Name] field +# 89| getTypeRepr(): [PathTypeRepr] usize +# 89| getPath(): [Path] usize +# 89| getSegment(): [PathSegment] usize +# 89| getIdentifier(): [NameRef] usize +# 92| getItem(19): [Enum] enum MyDeriveEnum +# 92| getDeriveMacroExpansion(0): [MacroItems] MacroItems +# 92| getItem(0): [Impl] impl ...::PartialEq for MyDeriveEnum::<...> { ... } +# 92| getAssocItemList(): [AssocItemList] AssocItemList +# 92| getAssocItem(0): [Function] fn eq +# 92| getParamList(): [ParamList] ParamList +# 92| getParam(0): [Param] ...: ... +# 92| getTypeRepr(): [RefTypeRepr] RefTypeRepr +# 92| getTypeRepr(): [PathTypeRepr] Self +# 92| getPath(): [Path] Self +# 92| getSegment(): [PathSegment] Self +# 92| getIdentifier(): [NameRef] Self +# 92| getPat(): [IdentPat] other +# 92| getName(): [Name] other +# 92| getSelfParam(): [SelfParam] SelfParam +# 92| getName(): [Name] self +# 92| getFunctionBody(): [BlockExpr] { ... } +# 92| getStmtList(): [StmtList] StmtList +# 92| getTailExpr(): [MatchExpr] match ... { ... } +# 92| getScrutinee(): [TupleExpr] TupleExpr +# 92| getField(0): [VariableAccess] self +# 92| getPath(): [Path] self +# 92| getSegment(): [PathSegment] self +# 92| getIdentifier(): [NameRef] self +# 92| getField(1): [VariableAccess] other +# 92| getPath(): [Path] other +# 92| getSegment(): [PathSegment] other +# 92| getIdentifier(): [NameRef] other +# 92| getMatchArmList(): [MatchArmList] MatchArmList +# 92| getArm(0): [MatchArm] ... => true +# 92| getExpr(): [BooleanLiteralExpr] true +# 92| getPat(): [TuplePat] TuplePat +# 92| getField(0): [PathPat] ...::Variant1 +# 92| getPath(): [Path] ...::Variant1 +# 93| getQualifier(): [Path] MyDeriveEnum +# 93| getSegment(): [PathSegment] MyDeriveEnum +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 94| getSegment(): [PathSegment] Variant1 +# 94| getIdentifier(): [NameRef] Variant1 +# 92| getField(1): [PathPat] ...::Variant1 +# 92| getPath(): [Path] ...::Variant1 +# 93| getQualifier(): [Path] MyDeriveEnum +# 93| getSegment(): [PathSegment] MyDeriveEnum +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 94| getSegment(): [PathSegment] Variant1 +# 94| getIdentifier(): [NameRef] Variant1 +# 92| getArm(1): [MatchArm] ... => true +# 92| getExpr(): [BooleanLiteralExpr] true +# 92| getPat(): [TuplePat] TuplePat +# 92| getField(0): [PathPat] ...::Variant2 +# 92| getPath(): [Path] ...::Variant2 +# 93| getQualifier(): [Path] MyDeriveEnum +# 93| getSegment(): [PathSegment] MyDeriveEnum +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 95| getSegment(): [PathSegment] Variant2 +# 95| getIdentifier(): [NameRef] Variant2 +# 92| getField(1): [PathPat] ...::Variant2 +# 92| getPath(): [Path] ...::Variant2 +# 93| getQualifier(): [Path] MyDeriveEnum +# 93| getSegment(): [PathSegment] MyDeriveEnum +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 95| getSegment(): [PathSegment] Variant2 +# 95| getIdentifier(): [NameRef] Variant2 +# 92| getArm(2): [MatchArm] ... => false +# 92| getExpr(): [BooleanLiteralExpr] false +# 92| getPat(): [IdentPat] _unused +# 92| getName(): [Name] _unused +# 92| getName(): [Name] eq +# 92| getRetType(): [RetTypeRepr] RetTypeRepr +# 92| getTypeRepr(): [PathTypeRepr] bool +# 92| getPath(): [Path] bool +# 92| getSegment(): [PathSegment] bool +# 92| getIdentifier(): [NameRef] bool +# 92| getGenericParamList(): [GenericParamList] <...> +# 92| getSelfTy(): [PathTypeRepr] MyDeriveEnum::<...> +# 92| getPath(): [Path] MyDeriveEnum::<...> +# 92| getSegment(): [PathSegment] MyDeriveEnum::<...> +# 92| getGenericArgList(): [GenericArgList] <...> +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 92| getTraitTy(): [PathTypeRepr] ...::PartialEq +# 92| getPath(): [Path] ...::PartialEq +# 92| getQualifier(): [Path] ...::cmp +# 92| getQualifier(): [Path] $crate +# 92| getSegment(): [PathSegment] $crate +# 92| getIdentifier(): [NameRef] $crate +# 92| getSegment(): [PathSegment] cmp +# 92| getIdentifier(): [NameRef] cmp +# 92| getSegment(): [PathSegment] PartialEq +# 92| getIdentifier(): [NameRef] PartialEq +# 92| getWhereClause(): [WhereClause] WhereClause +# 92| getDeriveMacroExpansion(1): [MacroItems] MacroItems +# 92| getItem(0): [Impl] impl ...::Eq for MyDeriveEnum::<...> { ... } +# 92| getAssocItemList(): [AssocItemList] AssocItemList +# 92| getGenericParamList(): [GenericParamList] <...> +# 92| getSelfTy(): [PathTypeRepr] MyDeriveEnum::<...> +# 92| getPath(): [Path] MyDeriveEnum::<...> +# 92| getSegment(): [PathSegment] MyDeriveEnum::<...> +# 92| getGenericArgList(): [GenericArgList] <...> +# 93| getIdentifier(): [NameRef] MyDeriveEnum +# 92| getTraitTy(): [PathTypeRepr] ...::Eq +# 92| getPath(): [Path] ...::Eq +# 92| getQualifier(): [Path] ...::cmp +# 92| getQualifier(): [Path] $crate +# 92| getSegment(): [PathSegment] $crate +# 92| getIdentifier(): [NameRef] $crate +# 92| getSegment(): [PathSegment] cmp +# 92| getIdentifier(): [NameRef] cmp +# 92| getSegment(): [PathSegment] Eq +# 92| getIdentifier(): [NameRef] Eq +# 92| getWhereClause(): [WhereClause] WhereClause +# 92| getAttr(0): [Attr] Attr +# 92| getMeta(): [TokenTreeMeta] TokenTreeMeta +# 92| getPath(): [Path] derive +# 92| getSegment(): [PathSegment] derive +# 92| getIdentifier(): [NameRef] derive +# 92| getTokenTree(): [TokenTree] TokenTree +# 93| getName(): [Name] MyDeriveEnum +# 93| getVariantList(): [VariantList] VariantList +# 94| getVariant(0): [Variant] Variant1 +# 94| getName(): [Name] Variant1 +# 95| getVariant(1): [Variant] Variant2 +# 95| getName(): [Name] Variant2 +# 98| getItem(20): [Trait] trait MyTrait +# 98| getAssocItemList(): [AssocItemList] AssocItemList +# 99| getAssocItem(0): [Function] fn my_method +# 99| getParamList(): [ParamList] ParamList +# 99| getName(): [Name] my_method +# 99| getRetType(): [RetTypeRepr] RetTypeRepr +# 99| getTypeRepr(): [PathTypeRepr] u32 +# 99| getPath(): [Path] u32 +# 99| getSegment(): [PathSegment] u32 +# 99| getIdentifier(): [NameRef] u32 +# 98| getName(): [Name] MyTrait +# 102| getItem(21): [Union] union MyDeriveUnion +# 103| getDeriveMacroExpansion(0): [MacroItems] MacroItems +# 103| getItem(0): [Const] const CONST_MyDeriveUnion +# 102| getBody(): [IntegerLiteralExpr] 42 +# 103| getName(): [Name] CONST_MyDeriveUnion +# 102| getTypeRepr(): [PathTypeRepr] u32 +# 102| getPath(): [Path] u32 +# 102| getSegment(): [PathSegment] u32 +# 102| getIdentifier(): [NameRef] u32 +# 103| getItem(1): [Impl] impl MyTrait for MyDeriveUnion { ... } +# 103| getAssocItemList(): [AssocItemList] AssocItemList +# 103| getAssocItem(0): [Function] fn my_method +# 102| getParamList(): [ParamList] ParamList +# 103| getFunctionBody(): [BlockExpr] { ... } +# 103| getStmtList(): [StmtList] StmtList +# 103| getTailExpr(): [ConstAccess] CONST_MyDeriveUnion +# 103| getPath(): [Path] CONST_MyDeriveUnion +# 103| getSegment(): [PathSegment] CONST_MyDeriveUnion +# 103| getIdentifier(): [NameRef] CONST_MyDeriveUnion +# 102| getName(): [Name] my_method +# 102| getRetType(): [RetTypeRepr] RetTypeRepr +# 102| getTypeRepr(): [PathTypeRepr] u32 +# 102| getPath(): [Path] u32 +# 102| getSegment(): [PathSegment] u32 +# 102| getIdentifier(): [NameRef] u32 +# 103| getSelfTy(): [PathTypeRepr] MyDeriveUnion +# 103| getPath(): [Path] MyDeriveUnion +# 103| getSegment(): [PathSegment] MyDeriveUnion +# 103| getIdentifier(): [NameRef] MyDeriveUnion +# 102| getTraitTy(): [PathTypeRepr] MyTrait +# 102| getPath(): [Path] MyTrait +# 102| getSegment(): [PathSegment] MyTrait +# 102| getIdentifier(): [NameRef] MyTrait +# 102| getAttr(0): [Attr] Attr +# 102| getMeta(): [TokenTreeMeta] TokenTreeMeta +# 102| getPath(): [Path] derive +# 102| getSegment(): [PathSegment] derive +# 102| getIdentifier(): [NameRef] derive +# 102| getTokenTree(): [TokenTree] TokenTree +# 103| getName(): [Name] MyDeriveUnion +# 103| getStructFieldList(): [StructFieldList] StructFieldList +# 104| getField(0): [StructField] field1: usize +# 104| getName(): [Name] field1 +# 104| getTypeRepr(): [PathTypeRepr] usize +# 104| getPath(): [Path] usize +# 104| getSegment(): [PathSegment] usize +# 104| getIdentifier(): [NameRef] usize +# 105| getField(1): [StructField] field2: f64 +# 105| getName(): [Name] field2 +# 105| getTypeRepr(): [PathTypeRepr] f64 +# 105| getPath(): [Path] f64 +# 105| getSegment(): [PathSegment] f64 +# 105| getIdentifier(): [NameRef] f64 # 44| [Comment] //... # 56| [Comment] //... -# 63| [Comment] //... -# 70| [Comment] //... -# 71| [Comment] //... -# 72| [Comment] //... +# 67| [Comment] //... +# 74| [Comment] //... +# 75| [Comment] //... # 76| [Comment] //... -# 79| [Comment] //... +# 80| [Comment] //... +# 83| [Comment] //... proc_macro.rs: # 1| [SourceFile] SourceFile # 1| getItem(0): [Use] use ...::TokenStream diff --git a/rust/ql/test/extractor-tests/macro-expansion/included/included.rs b/rust/ql/test/extractor-tests/macro-expansion/included/included.rs index 7397e24bd81e..54e1ef029f67 100644 --- a/rust/ql/test/extractor-tests/macro-expansion/included/included.rs +++ b/rust/ql/test/extractor-tests/macro-expansion/included/included.rs @@ -1,3 +1,38 @@ fn included() { _ = concat!("Hello", " ", "world!"); // this doesn't expand (in included.rs) since 0.0.274 } + +#[derive(Debug)] +struct IncludedDerived { + value: usize, +} + +macro_rules! identity { + ($value:expr) => { + $value + }; +} + +fn argument_location() { + identity!({ + dangerous_call(); + }); +} + +fn dangerous_call() {} + +macro_rules! definition_location { + () => { + dangerous_call() + }; +} + +fn definition_origin() { + definition_location!(); +} + +macro_rules! shadowed { + () => { struct IncludedShadowed; }; +} + +shadowed!(); diff --git a/rust/ql/test/extractor-tests/macro-expansion/macro_expansion.rs b/rust/ql/test/extractor-tests/macro-expansion/macro_expansion.rs index 145672528837..19cb19c51296 100644 --- a/rust/ql/test/extractor-tests/macro-expansion/macro_expansion.rs +++ b/rust/ql/test/extractor-tests/macro-expansion/macro_expansion.rs @@ -58,6 +58,10 @@ fn test() { ); } +macro_rules! shadowed { + () => { struct RootShadowed; }; +} + include!("included/included.rs"); #[doc = include_str!("some.txt")] // this doesn't expand since 0.0.274 diff --git a/rust/ql/test/extractor-tests/macro-expansion/test.expected b/rust/ql/test/extractor-tests/macro-expansion/test.expected index 65ee3f3e7fad..f5ddff7ced74 100644 --- a/rust/ql/test/extractor-tests/macro-expansion/test.expected +++ b/rust/ql/test/extractor-tests/macro-expansion/test.expected @@ -15,12 +15,17 @@ attribute_macros | macro_expansion.rs:31:5:34:5 | fn | 1 | macro_expansion.rs:32:5:34:5 | fn bzz_1 | | macro_expansion.rs:31:5:34:5 | fn | 2 | macro_expansion.rs:32:5:34:5 | fn bzz_2 | derive_macros -| macro_expansion.rs:83:1:86:1 | struct MyDerive | 0 | 0 | macro_expansion.rs:83:1:86:1 | impl ...::Debug for MyDerive::<...> { ... } | -| macro_expansion.rs:88:1:92:1 | enum MyDeriveEnum | 0 | 0 | macro_expansion.rs:88:1:92:1 | impl ...::PartialEq for MyDeriveEnum::<...> { ... } | -| macro_expansion.rs:88:1:92:1 | enum MyDeriveEnum | 1 | 0 | macro_expansion.rs:88:1:92:1 | impl ...::Eq for MyDeriveEnum::<...> { ... } | -| macro_expansion.rs:98:1:102:1 | union MyDeriveUnion | 0 | 0 | macro_expansion.rs:99:7:99:19 | const CONST_MyDeriveUnion | -| macro_expansion.rs:98:1:102:1 | union MyDeriveUnion | 0 | 1 | macro_expansion.rs:99:7:99:19 | impl MyTrait for MyDeriveUnion { ... } | +| included/included.rs:5:1:8:1 | struct IncludedDerived | 0 | 0 | included/included.rs:5:1:7:9 | impl ...::Debug for IncludedDerived::<...> { ... } | +| macro_expansion.rs:87:1:90:1 | struct MyDerive | 0 | 0 | macro_expansion.rs:87:1:90:1 | impl ...::Debug for MyDerive::<...> { ... } | +| macro_expansion.rs:92:1:96:1 | enum MyDeriveEnum | 0 | 0 | macro_expansion.rs:92:1:96:1 | impl ...::PartialEq for MyDeriveEnum::<...> { ... } | +| macro_expansion.rs:92:1:96:1 | enum MyDeriveEnum | 1 | 0 | macro_expansion.rs:92:1:96:1 | impl ...::Eq for MyDeriveEnum::<...> { ... } | +| macro_expansion.rs:102:1:106:1 | union MyDeriveUnion | 0 | 0 | macro_expansion.rs:103:7:103:19 | const CONST_MyDeriveUnion | +| macro_expansion.rs:102:1:106:1 | union MyDeriveUnion | 0 | 1 | macro_expansion.rs:103:7:103:19 | impl MyTrait for MyDeriveUnion { ... } | macro_calls +| included/included.rs:2:9:2:39 | concat!... | included/included.rs:2:17:2:38 | "Hello world!" | +| included/included.rs:17:5:19:6 | identity!... | included/included.rs:17:15:19:5 | { ... } | +| included/included.rs:31:5:31:26 | definition_location!... | included/included.rs:31:5:31:26 | { ... } | +| included/included.rs:38:1:38:12 | shadowed!... | included/included.rs:38:1:38:12 | MacroItems | | macro_expansion.rs:5:9:5:35 | concat!... | macro_expansion.rs:5:17:5:34 | "Hello world!" | | macro_expansion.rs:5:9:5:35 | concat!... | macro_expansion.rs:5:17:5:34 | "Hello world!" | | macro_expansion.rs:31:7:31:15 | ...::format_args_nl!... | macro_expansion.rs:31:7:31:15 | FormatArgsExpr | @@ -37,16 +42,44 @@ macro_calls | macro_expansion.rs:55:9:58:5 | my_macro!... | macro_expansion.rs:56:9:57:13 | MacroExpr | | macro_expansion.rs:56:9:57:13 | ...::format_args!... | macro_expansion.rs:56:9:57:13 | FormatArgsExpr | | macro_expansion.rs:56:9:57:13 | format!... | macro_expansion.rs:56:9:57:13 | ...::must_use(...) | -| macro_expansion.rs:61:1:61:33 | concat!... | macro_expansion.rs:61:1:61:33 | "Hello world!" | -| macro_expansion.rs:61:1:61:33 | include!... | macro_expansion.rs:61:1:61:33 | MacroItems | -| macro_expansion.rs:70:16:70:24 | my_int!... | macro_expansion.rs:70:16:70:22 | i32 | -| macro_expansion.rs:71:12:71:20 | my_int!... | macro_expansion.rs:71:12:71:18 | i32 | -| macro_expansion.rs:72:10:72:18 | my_int!... | macro_expansion.rs:72:10:72:16 | i32 | -| macro_expansion.rs:76:14:76:22 | my_int!... | macro_expansion.rs:76:14:76:20 | i32 | -| macro_expansion.rs:79:12:79:20 | my_int!... | macro_expansion.rs:79:12:79:18 | i32 | +| macro_expansion.rs:65:1:65:33 | include!... | included/included.rs:1:1:38:12 | MacroItems | +| macro_expansion.rs:74:16:74:24 | my_int!... | macro_expansion.rs:74:16:74:22 | i32 | +| macro_expansion.rs:75:12:75:20 | my_int!... | macro_expansion.rs:75:12:75:18 | i32 | +| macro_expansion.rs:76:10:76:18 | my_int!... | macro_expansion.rs:76:10:76:16 | i32 | +| macro_expansion.rs:80:14:80:22 | my_int!... | macro_expansion.rs:80:14:80:20 | i32 | +| macro_expansion.rs:83:12:83:20 | my_int!... | macro_expansion.rs:83:12:83:18 | i32 | unexpanded_macro_calls | included/included.rs:2:9:2:39 | concat!... | +| included/included.rs:17:5:19:6 | identity!... | +| included/included.rs:31:5:31:26 | definition_location!... | +| included/included.rs:38:1:38:12 | shadowed!... | | macro_expansion.rs:56:9:56:31 | concat!... | -| macro_expansion.rs:63:9:63:32 | include_str!... | +| macro_expansion.rs:67:9:67:32 | include_str!... | warnings | macro_expansion.rs:56:9:56:31 | macro expansion failed for 'concat' | +includedArgumentLocation +| included/included.rs:18:9:18:22 | dangerous_call | 18 | +| included/included.rs:18:9:18:22 | dangerous_call | 18 | +| included/included.rs:18:9:18:22 | dangerous_call | 18 | +| included/included.rs:18:9:18:22 | dangerous_call | 18 | +| included/included.rs:18:9:18:24 | dangerous_call(...) | 18 | +| included/included.rs:18:23:18:24 | ArgList | 18 | +| included/included.rs:31:5:31:26 | ArgList | 31 | +| included/included.rs:31:5:31:26 | dangerous_call | 31 | +| included/included.rs:31:5:31:26 | dangerous_call | 31 | +| included/included.rs:31:5:31:26 | dangerous_call | 31 | +| included/included.rs:31:5:31:26 | dangerous_call | 31 | +| included/included.rs:31:5:31:26 | dangerous_call(...) | 31 | +includedMacroShadowing +| included/included.rs | +includedStandaloneMacroCount +| 4 | +includedMacroExpansionMembership +| concat | false | +| concat | true | +| definition_location | false | +| definition_location | true | +| identity | false | +| identity | true | +| shadowed | false | +| shadowed | true | diff --git a/rust/ql/test/extractor-tests/macro-expansion/test.ql b/rust/ql/test/extractor-tests/macro-expansion/test.ql index 6372dc2e93fa..2255aea550e6 100644 --- a/rust/ql/test/extractor-tests/macro-expansion/test.ql +++ b/rust/ql/test/extractor-tests/macro-expansion/test.ql @@ -20,3 +20,40 @@ query predicate unexpanded_macro_calls(MacroCall c) { } query predicate warnings(ExtractionWarning w) { any() } + +query predicate includedArgumentLocation(AstNode node, int line) { + exists(CallExpr call | + call.isInMacroExpansion() and + call.getFunction().(PathExpr).getPath().getText() = "dangerous_call" and + call = node.getParentNode*() and + line = node.getLocation().getStartLine() + ) +} + +query predicate includedMacroShadowing(string definitionFile) { + exists(MacroCall call | + call.getPath().getText() = "shadowed" and + call.getFile().getRelativePath().matches("%included/included.rs") and + definitionFile = call.resolveMacro().getFile().getRelativePath() + ) +} + +query predicate includedStandaloneMacroCount(int macros) { + macros = + count(MacroCall call | + call.getFile().getRelativePath().matches("%included/included.rs") and + not call.getFile().(ExtractedFile).isInActiveIncludeExpansion(call) + ) +} + +query predicate includedMacroExpansionMembership(string name, boolean active) { + exists(MacroCall call | + call.getFile().getRelativePath().matches("%included/included.rs") and + name = call.getPath().getText() and + ( + if call.getFile().(ExtractedFile).isInActiveIncludeExpansion(call) + then active = true + else active = false + ) + ) +} diff --git a/rust/ql/test/library-tests/path-resolution-include-library/Cargo.lock b/rust/ql/test/library-tests/path-resolution-include-library/Cargo.lock new file mode 100644 index 000000000000..b9856cfaf77d --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution-include-library/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "test" +version = "0.0.1" diff --git a/rust/ql/test/library-tests/path-resolution-include-library/main.rs b/rust/ql/test/library-tests/path-resolution-include-library/main.rs new file mode 100644 index 000000000000..e1ac8f453484 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution-include-library/main.rs @@ -0,0 +1,6 @@ +include!("lib.rs"); + +fn main() { + shared::check(); // $ item=check + crate::included_library_export!(); // $ item=included_library_export +} diff --git a/rust/ql/test/library-tests/path-resolution-include-library/shared.rs b/rust/ql/test/library-tests/path-resolution-include-library/shared.rs new file mode 100644 index 000000000000..57a86f7d2163 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution-include-library/shared.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! included_library_export { + () => {}; +} + +pub fn helper() {} + +pub fn check() { + crate::shared::helper(); // $ item=helper +} diff --git a/rust/ql/test/library-tests/path-resolution-include-library/test.expected b/rust/ql/test/library-tests/path-resolution-include-library/test.expected new file mode 100644 index 000000000000..c3a456c80ace --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution-include-library/test.expected @@ -0,0 +1,4 @@ +testFailures +exportedMacro +| main | 1 | +| test | 1 | diff --git a/rust/ql/test/library-tests/path-resolution-include-library/test.ql b/rust/ql/test/library-tests/path-resolution-include-library/test.ql new file mode 100644 index 000000000000..a5c5e5494037 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution-include-library/test.ql @@ -0,0 +1,11 @@ +import rust +import codeql.rust.internal.PathResolution +import utils.test.PathResolutionInlineExpectationsTest + +query predicate exportedMacro(string crateName, int definitions) { + exists(CrateItemNode c | + crateName = c.getName() and + c.fromSource() and + definitions = count(MacroRules m | m = c.getASuccessor("included_library_export")) + ) +} diff --git a/rust/ql/test/library-tests/path-resolution/included/forms/child.rs b/rust/ql/test/library-tests/path-resolution/included/forms/child.rs new file mode 100644 index 000000000000..f10c4d2ebbff --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/forms/child.rs @@ -0,0 +1,3 @@ +pub fn form() { + crate::h(); // $ item=I25 +} // included_form diff --git a/rust/ql/test/library-tests/path-resolution/included/forms/mod.rs b/rust/ql/test/library-tests/path-resolution/included/forms/mod.rs new file mode 100644 index 000000000000..21d0b0d40613 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/forms/mod.rs @@ -0,0 +1 @@ +pub mod child; diff --git a/rust/ql/test/library-tests/path-resolution/included/non_mod/generated.rs b/rust/ql/test/library-tests/path-resolution/included/non_mod/generated.rs new file mode 100644 index 000000000000..911e3d5f944f --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/non_mod/generated.rs @@ -0,0 +1 @@ +pub mod non_mod_child; diff --git a/rust/ql/test/library-tests/path-resolution/included/non_mod/generated/non_mod_child.rs b/rust/ql/test/library-tests/path-resolution/included/non_mod/generated/non_mod_child.rs new file mode 100644 index 000000000000..c588ab3646b2 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/non_mod/generated/non_mod_child.rs @@ -0,0 +1 @@ +pub fn wrong_directory() {} diff --git a/rust/ql/test/library-tests/path-resolution/included/non_mod/non_mod_child.rs b/rust/ql/test/library-tests/path-resolution/included/non_mod/non_mod_child.rs new file mode 100644 index 000000000000..5aec067d71cd --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/non_mod/non_mod_child.rs @@ -0,0 +1 @@ +pub fn from_non_mod() {} // included_non_mod_child diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/disabled_child.rs b/rust/ql/test/library-tests/path-resolution/included/tree/disabled_child.rs new file mode 100644 index 000000000000..57c513c323e8 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/disabled_child.rs @@ -0,0 +1,4 @@ +#[macro_export] +macro_rules! disabled_child_export { + () => {}; +} diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/included_module.rs b/rust/ql/test/library-tests/path-resolution/included/tree/included_module.rs new file mode 100644 index 000000000000..23921c0ac2a7 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/included_module.rs @@ -0,0 +1 @@ +pub fn hidden() {} // I23009 diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/macro_child.rs b/rust/ql/test/library-tests/path-resolution/included/tree/macro_child.rs new file mode 100644 index 000000000000..4804ccac4338 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/macro_child.rs @@ -0,0 +1 @@ +pub fn from_macro() {} // included_macro_child diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/mod.rs b/rust/ql/test/library-tests/path-resolution/included/tree/mod.rs new file mode 100644 index 000000000000..cbabe9a4e024 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/mod.rs @@ -0,0 +1,20 @@ +pub mod included_module; + +include!("nested/mod.rs"); +declare_included_child!(); + +#[cfg(any())] +pub mod disabled_child; + +pub mod inline_export { + #[macro_export] + macro_rules! included_export { + () => {}; + } + + #[cfg(any())] + #[macro_export] + macro_rules! disabled_export { + () => {}; + } +} diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/nested/mod.rs b/rust/ql/test/library-tests/path-resolution/included/tree/nested/mod.rs new file mode 100644 index 000000000000..4601c546bdcb --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/nested/mod.rs @@ -0,0 +1 @@ +pub mod nested_module; diff --git a/rust/ql/test/library-tests/path-resolution/included/tree/nested/nested_module.rs b/rust/ql/test/library-tests/path-resolution/included/tree/nested/nested_module.rs new file mode 100644 index 000000000000..d1f4bef05632 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/included/tree/nested/nested_module.rs @@ -0,0 +1,4 @@ +pub fn nested() { + crate::h(); // $ item=I25 + crate::included_export!(); // $ item=included_export +} // included_nested diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index 781641e98ce0..eec8945c9c8f 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -1190,4 +1190,48 @@ fn main() { AStruct {}.z_on_instance(); // $ item=I123 target=I125 impl_with_attribute_macro::test(); // $ item=impl_with_attribute_macro::test patterns::test(); // $ item=patterns::test + included_module::hidden(); // $ item=I23009 + nested_module::nested(); // $ item=included_nested + qualified_include::child::form(); // $ item=included_form + braced_include::child::form(); // $ item=included_form + bracket_include::child::form(); // $ item=included_form + computed_include::child::form(); // $ item=included_form + shadowed_include::from_shadow(); // $ item=from_shadow + crate::included_export!(); // $ item=included_export + macro_child::from_macro(); // $ item=included_macro_child + non_mod_child::from_non_mod(); // $ item=included_non_mod_child +} + +macro_rules! declare_included_child { + () => { + pub mod macro_child; + }; +} + +include!("included/tree/mod.rs"); +include!("included/non_mod/generated.rs"); + +mod qualified_include { + std::include!("included/forms/mod.rs"); // $ item=include +} + +mod braced_include { + include! { /* comment */ "included/forms/mod.rs", } +} + +mod bracket_include { + include!["included/forms/mod.rs"]; +} + +mod computed_include { + include!(concat!("included/", "forms/mod.rs")); +} + +mod shadowed_include { + macro_rules! include { + ($path:literal) => { + pub fn from_shadow() {} + }; + } + include!("included/tree/mod.rs"); } diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected index 9df115b9bdb1..94845f1e88c4 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -1,4 +1,26 @@ +includedMacroExports +| disabled_child_export | 0 | +| disabled_export | 0 | +| included_export | 1 | +includedSemantics +| included/tree/disabled_child.rs | false | true | +| included/tree/mod.rs | true | false | mod +| included/forms/mod.rs:1:1:1:14 | mod child | +| included/forms/mod.rs:1:1:1:14 | mod child | +| included/forms/mod.rs:1:1:1:14 | mod child | +| included/forms/mod.rs:1:1:1:14 | mod child | +| included/forms/mod.rs:1:1:1:14 | mod child | +| included/non_mod/generated.rs:1:1:1:22 | mod non_mod_child | +| included/non_mod/generated.rs:1:1:1:22 | mod non_mod_child | +| included/tree/mod.rs:1:1:1:24 | mod included_module | +| included/tree/mod.rs:1:1:1:24 | mod included_module | +| included/tree/mod.rs:4:1:4:26 | mod macro_child | +| included/tree/mod.rs:6:1:7:23 | mod disabled_child | +| included/tree/mod.rs:9:1:20:1 | mod inline_export | +| included/tree/mod.rs:9:1:20:1 | mod inline_export | +| included/tree/nested/mod.rs:1:1:1:22 | mod nested_module | +| included/tree/nested/mod.rs:1:1:1:22 | mod nested_module | | invalid/main.rs:8:1:18:1 | mod self_import_from_struct | | lib.rs:1:1:1:11 | mod my | | main.rs:1:1:1:7 | mod my | @@ -39,6 +61,11 @@ mod | main.rs:1033:1:1077:1 | mod self_constructors | | main.rs:1079:1:1108:1 | mod self_types | | main.rs:1110:1:1154:1 | mod const_static | +| main.rs:1214:1:1216:1 | mod qualified_include | +| main.rs:1218:1:1220:1 | mod braced_include | +| main.rs:1222:1:1224:1 | mod bracket_include | +| main.rs:1226:1:1228:1 | mod computed_include | +| main.rs:1230:1:1237:1 | mod shadowed_include | | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:20:1:20:12 | mod my3 | | my2/mod.rs:22:1:23:10 | mod mymod | @@ -54,6 +81,13 @@ mod | my/nested.rs:1:1:17:1 | mod nested1 | | my/nested.rs:2:5:11:5 | mod nested2 | resolvePath +| included/forms/child.rs:2:5:2:9 | crate | main.rs:0:0:0:0 | Crate(main@0.0.1) | +| included/forms/child.rs:2:5:2:12 | ...::h | main.rs:57:1:76:1 | fn h | +| included/tree/mod.rs:3:1:3:7 | include | {EXTERNAL LOCATION} | MacroRules | +| included/tree/nested/nested_module.rs:2:5:2:9 | crate | main.rs:0:0:0:0 | Crate(main@0.0.1) | +| included/tree/nested/nested_module.rs:2:5:2:12 | ...::h | main.rs:57:1:76:1 | fn h | +| included/tree/nested/nested_module.rs:3:5:3:9 | crate | main.rs:0:0:0:0 | Crate(main@0.0.1) | +| included/tree/nested/nested_module.rs:3:5:3:26 | ...::included_export | included/tree/mod.rs:10:5:13:5 | MacroRules | | invalid/main.rs:6:9:6:9 | A | invalid/main.rs:3:11:4:9 | struct A | | invalid/main.rs:15:9:15:12 | self | invalid/main.rs:8:1:18:1 | mod self_import_from_struct | | invalid/main.rs:15:9:15:22 | ...::MyStruct | invalid/main.rs:12:5:12:20 | struct MyStruct | @@ -83,7 +117,7 @@ resolvePath | main.rs:37:17:37:24 | ...::f | main.rs:26:9:28:9 | fn f | | main.rs:39:17:39:23 | println | {EXTERNAL LOCATION} | MacroRules | | main.rs:40:17:40:17 | f | main.rs:26:9:28:9 | fn f | -| main.rs:47:9:47:13 | super | main.rs:1:1:1193:2 | SourceFile | +| main.rs:47:9:47:13 | super | main.rs:1:1:1237:2 | SourceFile | | main.rs:47:9:47:17 | ...::m1 | main.rs:20:1:44:1 | mod m1 | | main.rs:47:9:47:21 | ...::m2 | main.rs:25:5:43:5 | mod m2 | | main.rs:47:9:47:24 | ...::g | main.rs:30:9:34:9 | fn g | @@ -98,7 +132,7 @@ resolvePath | main.rs:68:17:68:19 | Foo | main.rs:66:9:66:21 | struct Foo | | main.rs:71:13:71:15 | Foo | main.rs:60:5:60:17 | struct Foo | | main.rs:73:5:73:5 | f | main.rs:62:5:69:5 | fn f | -| main.rs:75:5:75:8 | self | main.rs:1:1:1193:2 | SourceFile | +| main.rs:75:5:75:8 | self | main.rs:1:1:1237:2 | SourceFile | | main.rs:75:5:75:11 | ...::i | main.rs:78:1:90:1 | fn i | | main.rs:79:5:79:11 | println | {EXTERNAL LOCATION} | MacroRules | | main.rs:81:13:81:15 | Foo | main.rs:55:1:55:13 | struct Foo | @@ -120,7 +154,7 @@ resolvePath | main.rs:112:9:112:15 | println | {EXTERNAL LOCATION} | MacroRules | | main.rs:118:9:118:15 | println | {EXTERNAL LOCATION} | MacroRules | | main.rs:122:9:122:15 | println | {EXTERNAL LOCATION} | MacroRules | -| main.rs:125:13:125:17 | super | main.rs:1:1:1193:2 | SourceFile | +| main.rs:125:13:125:17 | super | main.rs:1:1:1237:2 | SourceFile | | main.rs:125:13:125:21 | ...::m5 | main.rs:110:1:114:1 | mod m5 | | main.rs:126:9:126:9 | f | main.rs:111:5:113:5 | fn f | | main.rs:126:9:126:9 | f | main.rs:117:5:119:5 | fn f | @@ -697,6 +731,38 @@ resolvePath | main.rs:1191:5:1191:35 | ...::test | main.rs:984:5:987:5 | fn test | | main.rs:1192:5:1192:12 | patterns | main.rs:990:1:1031:1 | mod patterns | | main.rs:1192:5:1192:18 | ...::test | main.rs:991:5:1005:5 | fn test | +| main.rs:1193:5:1193:19 | included_module | included/tree/mod.rs:1:1:1:24 | mod included_module | +| main.rs:1193:5:1193:27 | ...::hidden | included/tree/included_module.rs:1:1:1:18 | fn hidden | +| main.rs:1194:5:1194:17 | nested_module | included/tree/nested/mod.rs:1:1:1:22 | mod nested_module | +| main.rs:1194:5:1194:25 | ...::nested | included/tree/nested/nested_module.rs:1:1:4:1 | fn nested | +| main.rs:1195:5:1195:21 | qualified_include | main.rs:1214:1:1216:1 | mod qualified_include | +| main.rs:1195:5:1195:28 | ...::child | included/forms/mod.rs:1:1:1:14 | mod child | +| main.rs:1195:5:1195:34 | ...::form | included/forms/child.rs:1:1:3:1 | fn form | +| main.rs:1196:5:1196:18 | braced_include | main.rs:1218:1:1220:1 | mod braced_include | +| main.rs:1196:5:1196:25 | ...::child | included/forms/mod.rs:1:1:1:14 | mod child | +| main.rs:1196:5:1196:31 | ...::form | included/forms/child.rs:1:1:3:1 | fn form | +| main.rs:1197:5:1197:19 | bracket_include | main.rs:1222:1:1224:1 | mod bracket_include | +| main.rs:1197:5:1197:26 | ...::child | included/forms/mod.rs:1:1:1:14 | mod child | +| main.rs:1197:5:1197:32 | ...::form | included/forms/child.rs:1:1:3:1 | fn form | +| main.rs:1198:5:1198:20 | computed_include | main.rs:1226:1:1228:1 | mod computed_include | +| main.rs:1198:5:1198:27 | ...::child | included/forms/mod.rs:1:1:1:14 | mod child | +| main.rs:1198:5:1198:33 | ...::form | included/forms/child.rs:1:1:3:1 | fn form | +| main.rs:1199:5:1199:20 | shadowed_include | main.rs:1230:1:1237:1 | mod shadowed_include | +| main.rs:1199:5:1199:33 | ...::from_shadow | main.rs:1236:5:1236:12 | fn from_shadow | +| main.rs:1200:5:1200:9 | crate | main.rs:0:0:0:0 | Crate(main@0.0.1) | +| main.rs:1200:5:1200:26 | ...::included_export | included/tree/mod.rs:10:5:13:5 | MacroRules | +| main.rs:1201:5:1201:15 | macro_child | included/tree/mod.rs:4:1:4:26 | mod macro_child | +| main.rs:1201:5:1201:27 | ...::from_macro | included/tree/macro_child.rs:1:1:1:22 | fn from_macro | +| main.rs:1202:5:1202:17 | non_mod_child | included/non_mod/generated.rs:1:1:1:22 | mod non_mod_child | +| main.rs:1202:5:1202:31 | ...::from_non_mod | included/non_mod/non_mod_child.rs:1:1:1:24 | fn from_non_mod | +| main.rs:1211:1:1211:7 | include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1212:1:1212:7 | include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1215:5:1215:7 | std | {EXTERNAL LOCATION} | Crate(std@0.0.0) | +| main.rs:1215:5:1215:16 | ...::include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1219:5:1219:11 | include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1223:5:1223:11 | include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1227:5:1227:11 | include | {EXTERNAL LOCATION} | MacroRules | +| main.rs:1236:5:1236:11 | include | main.rs:1231:5:1235:5 | MacroRules | | my2/mod.rs:4:5:4:11 | println | {EXTERNAL LOCATION} | MacroRules | | my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | @@ -722,7 +788,7 @@ resolvePath | my2/my3/mod.rs:3:5:3:5 | g | my2/mod.rs:3:1:6:1 | fn g | | my2/my3/mod.rs:4:5:4:5 | h | main.rs:57:1:76:1 | fn h | | my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | -| my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:1193:2 | SourceFile | +| my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:1237:2 | SourceFile | | my2/my3/mod.rs:7:5:7:19 | ...::h | main.rs:57:1:76:1 | fn h | | my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | | my2/my3/mod.rs:8:5:8:12 | ...::g | my2/mod.rs:3:1:6:1 | fn g | diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.ql b/rust/ql/test/library-tests/path-resolution/path-resolution.ql index f7955929b839..fa92e8fb9369 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.ql +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.ql @@ -10,3 +10,21 @@ query predicate resolvePath(PathExt p, ItemNode i) { not p.isFromMacroExpansion() and i = resolvePath(p) } + +query predicate includedMacroExports(string name, int definitions) { + exists(CrateItemNode c | + c.getName() = "main" and + c.getSourceFile().getFile().getRelativePath() = "main.rs" and + name = ["included_export", "disabled_export", "disabled_child_export"] and + definitions = count(MacroRules m | m = c.getASuccessor(name)) + ) +} + +query predicate includedSemantics(string file, boolean hasSemantics, boolean skipped) { + exists(ExtractedFile f | + file = f.getRelativePath() and + file = ["included/tree/mod.rs", "included/tree/disabled_child.rs"] and + (if f.hasSemantics() then hasSemantics = true else hasSemantics = false) and + (if f.isSkippedByCompilation() then skipped = true else skipped = false) + ) +}