diff --git a/Cargo.lock b/Cargo.lock index 4a9c122916..541f03ac7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,6 +286,7 @@ dependencies = [ "serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index e31857f6b7..d29b22a8be 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -472,9 +472,9 @@ class TranslateASTVisitor final } // Template required because Decl and Stmt don't share a common base class - void encode_entry_raw(void *ast, ASTEntryTag tag, SourceLocation loc, - const QualType ty, bool rvalue, bool isVaList, - bool encodeMacroExpansions, + void encode_entry_raw(void *ast, ASTEntryTag tag, SourceRange loc, + const QualType ty, bool rvalue, + bool isVaList, bool encodeMacroExpansions, const std::vector &childIds, std::function extra) { if (!markForExport(ast, tag)) @@ -501,17 +501,19 @@ class TranslateASTVisitor final cbor_encoder_close_container(&local, &childEnc); // 3 - File number - // 4 - Line number - // 5 - Column number - encodeSourcePos(&local, loc, isVaList); + // 4 - Begin Line number + // 5 - Begin Column number + // 6 - End Line number + // 7 - End Column number + encodeSourceSpan(&local, loc, isVaList); - // 6 - Type ID (only for expressions) + // 8 - Type ID (only for expressions) encode_qualtype(&local, ty); - // 7 - Is Rvalue (only for expressions) + // 9 - Is Rvalue (only for expressions) cbor_encode_boolean(&local, rvalue); - // 8 - Macro expansion stack, starting with initial macro call and ending + // 10 - Macro expansion stack, starting with initial macro call and ending // with the innermost replacement. cbor_encoder_create_array(&local, &childEnc, encodeMacroExpansions ? curMacroExpansionStack.size() : 0); @@ -523,7 +525,7 @@ class TranslateASTVisitor final } cbor_encoder_close_container(&local, &childEnc); - // 9.. - Extra entries + // 11.. - Extra entries extra(&local); cbor_encoder_close_container(encoder, &local); @@ -543,13 +545,8 @@ class TranslateASTVisitor final auto ty = ast->getType(); auto isVaList = false; auto encodeMacroExpansions = true; -#if CLANG_VERSION_MAJOR < 8 - SourceLocation loc = ast->getLocStart(); -#else - SourceLocation loc = ast->getBeginLoc(); -#endif // CLANG_VERSION_MAJOR - encode_entry_raw(ast, tag, loc, ty, ast->isRValue(), isVaList, encodeMacroExpansions, - childIds, extra); + encode_entry_raw(ast, tag, ast->getSourceRange(), ty, ast->isRValue(), isVaList, + encodeMacroExpansions, childIds, extra); typeEncoder.VisitQualType(ty); } @@ -560,13 +557,8 @@ class TranslateASTVisitor final auto rvalue = false; auto isVaList = false; auto encodeMacroExpansions = false; -#if CLANG_VERSION_MAJOR < 8 - SourceLocation loc = ast->getLocStart(); -#else - SourceLocation loc = ast->getBeginLoc(); -#endif // CLANG_VERSION_MAJOR - encode_entry_raw(ast, tag, loc, s, rvalue, isVaList, encodeMacroExpansions, - childIds, extra); + encode_entry_raw(ast, tag, ast->getSourceRange(), s, rvalue, isVaList, + encodeMacroExpansions, childIds, extra); } void encode_entry( @@ -575,7 +567,7 @@ class TranslateASTVisitor final std::function extra = [](CborEncoder *) {}) { auto rvalue = false; auto encodeMacroExpansions = false; - encode_entry_raw(ast, tag, ast->getLocation(), T, rvalue, + encode_entry_raw(ast, tag, ast->getSourceRange(), T, rvalue, isVaList(ast, T), encodeMacroExpansions, childIds, extra); } @@ -583,7 +575,7 @@ class TranslateASTVisitor final /// definition location is not the same as the canonical declaration /// location. void encode_entry( - Decl *ast, ASTEntryTag tag, SourceLocation loc, + Decl *ast, ASTEntryTag tag, SourceRange loc, const std::vector &childIds, const QualType T, std::function extra = [](CborEncoder *) {}) { auto rvalue = false; @@ -714,7 +706,8 @@ class TranslateASTVisitor final std::vector childIds(Info.Expressions.begin(), Info.Expressions.end()); - encode_entry_raw(Mac, tag, Mac->getDefinitionLoc(), QualType(), false, + auto range = SourceRange(Mac->getDefinitionLoc(), Mac->getDefinitionEndLoc()); + encode_entry_raw(Mac, tag, range, QualType(), false, false, false, childIds, [Name](CborEncoder *local) { cbor_encode_string(local, Name.str()); }); @@ -731,15 +724,41 @@ class TranslateASTVisitor final manager.isMacroBodyExpansion(loc)) loc = manager.getFileLoc(loc); + auto fileid = getExporterFileId(manager.getFileID(loc), isVaList); auto line = manager.getPresumedLineNumber(loc); auto col = manager.getPresumedColumnNumber(loc); - auto fileid = getExporterFileId(manager.getFileID(loc), isVaList); cbor_encode_uint(enc, fileid); cbor_encode_uint(enc, line); cbor_encode_uint(enc, col); } + void encodeSourceSpan(CborEncoder *enc, SourceRange loc, bool isVaList = false) { + auto &manager = Context->getSourceManager(); + + auto begin = loc.getBegin(); + auto end = loc.getEnd(); + // A check to see if the Source Location is a Macro + if (manager.isMacroArgExpansion(begin) || + manager.isMacroBodyExpansion(begin)) + begin = manager.getFileLoc(begin); + if (manager.isMacroArgExpansion(end) || + manager.isMacroBodyExpansion(end)) + end = manager.getFileLoc(end); + + auto fileid = getExporterFileId(manager.getFileID(begin), isVaList); + auto begin_line = manager.getPresumedLineNumber(begin); + auto begin_col = manager.getPresumedColumnNumber(begin); + auto end_line = manager.getPresumedLineNumber(end); + auto end_col = manager.getPresumedColumnNumber(end); + + cbor_encode_uint(enc, fileid); + cbor_encode_uint(enc, begin_line); + cbor_encode_uint(enc, begin_col); + cbor_encode_uint(enc, end_line); + cbor_encode_uint(enc, end_col); + } + uint64_t getExporterFileId(FileID id, bool isVaList) { if (id.isInvalid()) return 0; @@ -1572,10 +1591,10 @@ class TranslateASTVisitor final if (!FD->isCanonicalDecl()) { // Emit non-canonical decl so we have a placeholder to attach comments to std::vector childIds = {FD->getCanonicalDecl()}; - auto loc = FD->getLocation(); + auto span = FD->getSourceRange(); if (FD->doesThisDeclarationHaveABody()) - loc = FD->getCanonicalDecl()->getLocation(); - encode_entry(FD, TagNonCanonicalDecl, loc, childIds, FD->getType()); + span = FD->getCanonicalDecl()->getSourceRange(); + encode_entry(FD, TagNonCanonicalDecl, span, childIds, FD->getType()); return true; } @@ -1600,8 +1619,9 @@ class TranslateASTVisitor final childIds.push_back(body); auto functionType = FD->getType(); + auto span = paramsFD->getSourceRange(); encode_entry( - FD, TagFunctionDecl, paramsFD->getLocation(), childIds, functionType, + FD, TagFunctionDecl, span, childIds, functionType, [this, FD](CborEncoder *array) { auto name = FD->getNameAsString(); cbor_encode_string(array, name); diff --git a/c2rust-ast-exporter/src/clang_ast.rs b/c2rust-ast-exporter/src/clang_ast.rs index 8f3eb42759..056a1eb4bf 100644 --- a/c2rust-ast-exporter/src/clang_ast.rs +++ b/c2rust-ast-exporter/src/clang_ast.rs @@ -22,18 +22,56 @@ impl LRValue { } } -#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)] +#[derive(Copy, Debug, Clone, PartialOrd, PartialEq, Ord, Eq)] pub struct SrcLoc { pub fileid: u64, pub line: u64, pub column: u64, } +#[derive(Copy, Debug, Clone, PartialOrd, PartialEq, Ord, Eq)] +pub struct SrcSpan { + pub fileid: u64, + pub begin_line: u64, + pub begin_column: u64, + pub end_line: u64, + pub end_column: u64, +} + +impl From for SrcSpan { + fn from(loc: SrcLoc) -> Self { + Self { + fileid: loc.fileid, + begin_line: loc.line, + begin_column: loc.column, + end_line: loc.line, + end_column: loc.column, + } + } +} + +impl SrcSpan { + pub fn begin(&self) -> SrcLoc { + SrcLoc { + fileid: self.fileid, + line: self.begin_line, + column: self.begin_column, + } + } + pub fn end(&self) -> SrcLoc { + SrcLoc { + fileid: self.fileid, + line: self.end_line, + column: self.end_column, + } + } +} + #[derive(Debug, Clone)] pub struct AstNode { pub tag: ASTEntryTag, pub children: Vec>, - pub loc: SrcLoc, + pub loc: SrcSpan, pub type_id: Option, pub rvalue: LRValue, @@ -152,10 +190,10 @@ pub fn process(items: Value) -> error::Result { .map(|x| expect_opt_u64(x).unwrap()) .collect::>>(); - let type_id: Option = expect_opt_u64(&entry[6]).unwrap(); + let type_id: Option = expect_opt_u64(&entry[8]).unwrap(); let fileid = entry[3].as_u64().unwrap(); - let macro_expansions = entry[8] + let macro_expansions = entry[10] .as_array() .unwrap() .iter() @@ -165,19 +203,21 @@ pub fn process(items: Value) -> error::Result { let node = AstNode { tag: import_ast_tag(tag), children, - loc: SrcLoc { + loc: SrcSpan { fileid, - line: entry[4].as_u64().unwrap(), - column: entry[5].as_u64().unwrap(), + begin_line: entry[4].as_u64().unwrap(), + begin_column: entry[5].as_u64().unwrap(), + end_line: entry[6].as_u64().unwrap(), + end_column: entry[7].as_u64().unwrap(), }, type_id, - rvalue: if entry[7].as_boolean().unwrap() { + rvalue: if entry[9].as_boolean().unwrap() { LRValue::RValue } else { LRValue::LValue }, macro_expansions, - extras: entry[9..].to_vec(), + extras: entry[11..].to_vec(), }; asts.insert(entry_id, node); diff --git a/c2rust-refactor/src/rewrite/base.rs b/c2rust-refactor/src/rewrite/base.rs index a4027cc764..7ae363b3bc 100644 --- a/c2rust-refactor/src/rewrite/base.rs +++ b/c2rust-refactor/src/rewrite/base.rs @@ -220,7 +220,7 @@ where diff::Result::Left(_) => { // There's an item on the left corresponding to nothing on the right. // Delete the item from the left. - let old_span = rewind_span_over_whitespace(ast(&old[i]).splice_span(), &rcx); + let old_span = ast(&old[i]).splice_span(); // let old_span = ast(&old[i]).splice_span(); let old_span = match old_ids[i] { SeqItemId::Node(id) => extend_span_comments(&id, old_span, &rcx), @@ -482,7 +482,7 @@ pub fn extend_span_comments(id: &NodeId, span: Span, rcx: &RewriteCtxt) -> Span /// Extend a node span to cover comments around it. Returns Ok(span) if all /// comments were covered, and Err(span) if only some could be covered. -pub fn extend_span_comments_strict(id: &NodeId, span: Span, rcx: &RewriteCtxt) -> Result { +pub fn extend_span_comments_strict(id: &NodeId, mut span: Span, rcx: &RewriteCtxt) -> Result { let source_map = rcx.session().source_map(); let comments = match rcx.comments().get(id) { @@ -516,11 +516,11 @@ pub fn extend_span_comments_strict(id: &NodeId, span: Span, rcx: &RewriteCtxt) - let mut all_matched = true; - let mut span = rewind_span_over_whitespace(span, rcx); for comment in &before { + let cur_span = rewind_span_over_whitespace(span, rcx); let comment_size = usize::sum(comment.lines.iter().map(|l| l.len())); - let comment_start = BytePos::from_usize(span.lo().to_usize() - comment_size); - let comment_span = span.shrink_to_lo().with_lo(comment_start); + let comment_start = BytePos::from_usize(cur_span.lo().to_usize() - comment_size); + let comment_span = cur_span.shrink_to_lo().with_lo(comment_start); let source = match source_map.span_to_snippet(comment_span) { Ok(snippet) => snippet, Err(_) => { @@ -529,15 +529,14 @@ pub fn extend_span_comments_strict(id: &NodeId, span: Span, rcx: &RewriteCtxt) - } }; let matches = source.lines().zip(&comment.lines).all(|(src_line, comment_line)| { + if src_line.trim() != comment_line.trim() { + debug!("comment {:?} did not match source {:?}", comment_line, src_line); + } src_line.trim() == comment_line.trim() }); if matches { - // Extend to previous newline because this is an isolated comment - let comment_span = rewind_span_over_whitespace(comment_span, rcx); - - span = span.with_lo(comment_span.lo()); + span = cur_span.with_lo(comment_span.lo()); } else { - debug!("comment {:?} did not match source {:?}", comment, source); all_matched = false; break; } diff --git a/c2rust-transpile/Cargo.toml b/c2rust-transpile/Cargo.toml index e96cd34fa7..10d35757f6 100644 --- a/c2rust-transpile/Cargo.toml +++ b/c2rust-transpile/Cargo.toml @@ -27,6 +27,7 @@ handlebars = "1.1.0" itertools = "0.8" pathdiff = "0.1.0" regex = "1" +smallvec = "0.6" strum = "0.15" strum_macros = "0.15" log = "0.4" diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index a296fcbeb4..c6fc637964 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -343,7 +343,7 @@ impl ConversionContext { fn convert(&mut self, untyped_context: &AstContext) -> () { for raw_comment in &untyped_context.comments { let comment = Located { - loc: Some(raw_comment.loc.clone()), + loc: Some(raw_comment.loc.into()), kind: raw_comment.string.clone(), }; self.typed_context.comments.push(comment); diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 40a2f37c98..735b5f682d 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,6 +1,6 @@ use crate::c_ast::*; -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum SomeId { Stmt(CStmtId), Expr(CExprId), @@ -291,7 +291,7 @@ fn immediate_children(context: &TypedAstContext, s_or_e: SomeId) -> Vec } } -fn immediate_children_all_types(context: &TypedAstContext, s_or_e: SomeId) -> Vec { +pub fn immediate_children_all_types(context: &TypedAstContext, s_or_e: SomeId) -> Vec { match s_or_e { SomeId::Stmt(stmt_id) => immediate_stmt_children(&context[stmt_id].kind), SomeId::Expr(expr_id) => immediate_expr_children_all_types(&context[expr_id].kind), @@ -371,3 +371,43 @@ impl<'context> Iterator for DFNodes<'context> { result } } + +struct VisitNode { + id: SomeId, + seen: bool, +} + +impl VisitNode { + fn new(id: SomeId) -> Self { + Self { + id, + seen: false, + } + } +} + +pub trait NodeVisitor { + /// Visit nodes in pre-order traversal. Returns true if we should traverse + /// children. If we are not traversing children, the node will still be + /// visited by `post`. + fn pre(&mut self, _id: SomeId) -> bool { true } + fn post(&mut self, _id: SomeId) {} + fn visit_tree(&mut self, context: &TypedAstContext, root: SomeId) { + let mut stack = vec![VisitNode::new(root)]; + while let Some(mut node) = stack.pop() { + if !node.seen { + let id = node.id; + node.seen = true; + stack.push(node); + + if self.pre(id) { + let children = immediate_children_all_types(context, id); + // Add children in reverse order since we visit the end of the stack first + stack.extend(children.into_iter().rev().map(VisitNode::new)); + } + } else { + self.post(node.id); + } + } + } +} diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 0888c29e00..8e0929d4c0 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,14 +1,14 @@ use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use std::cmp::Ordering; use std::fmt::{self, Debug, Display}; -use std::iter::FromIterator; use std::mem; use std::ops::Index; use std::path::{Path, PathBuf}; -pub use c2rust_ast_exporter::clang_ast::{SrcFile, SrcLoc}; +pub use c2rust_ast_exporter::clang_ast::{SrcFile, SrcLoc, SrcSpan}; #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] pub struct CTypeId(pub u64); @@ -34,12 +34,13 @@ pub type CEnumConstantId = CDeclId; // Enum's need to point to child 'DeclKind:: pub use self::conversion::*; pub use self::print::Printer; -use super::diagnostics::Diagnostic; mod conversion; pub mod iterators; mod print; +use iterators::{DFNodes, SomeId}; + /// AST context containing all of the nodes in the Clang AST #[derive(Debug, Clone)] pub struct TypedAstContext { @@ -78,20 +79,19 @@ pub struct TypedAstContext { /// Comments associated with a typed AST context #[derive(Debug, Clone)] pub struct CommentContext { - decl_comments: HashMap>, - stmt_comments: HashMap>, + comments_by_file: HashMap>>>, } #[derive(Debug, Clone)] -pub struct DisplaySrcLoc { +pub struct DisplaySrcSpan { file: Option, - loc: SrcLoc, + loc: SrcSpan, } -impl Display for DisplaySrcLoc { +impl Display for DisplaySrcSpan { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref file) = self.file { - write!(f, "{}:{}:{}", file.display(), self.loc.line, self.loc.column) + write!(f, "{}:{}:{}", file.display(), self.loc.begin_line, self.loc.begin_column) } else { Debug::fmt(self, f) } @@ -103,10 +103,19 @@ pub type FileId = usize; /// Represents some AST node possibly with source location information bundled with it #[derive(Debug, Clone)] pub struct Located { - pub loc: Option, + pub loc: Option, pub kind: T, } +impl Located { + pub fn begin_loc(&self) -> Option { + self.loc.map(|loc| loc.begin()) + } + pub fn end_loc(&self) -> Option { + self.loc.map(|loc| loc.end()) + } +} + impl TypedAstContext { pub fn new(clang_files: &[SrcFile]) -> TypedAstContext { let mut files: Vec = vec![]; @@ -155,9 +164,9 @@ impl TypedAstContext { } } - pub fn display_loc(&self, loc: &Option) -> Option { + pub fn display_loc(&self, loc: &Option) -> Option { loc.as_ref().map(|loc| { - DisplaySrcLoc { + DisplaySrcSpan { file: self.files[self.file_map[loc.fileid as usize]].path.clone(), loc: loc.clone(), } @@ -209,7 +218,16 @@ impl TypedAstContext { } pub fn file_id(&self, located: &Located) -> Option { - located.loc.as_ref().map(|loc| self.file_map[loc.fileid as usize]) + located.loc.as_ref().and_then(|loc| self.file_map.get(loc.fileid as usize).copied()) + } + + pub fn get_src_loc(&self, id: SomeId) -> Option { + match id { + SomeId::Stmt(id) => self.index(id).loc, + SomeId::Expr(id) => self.index(id).loc, + SomeId::Decl(id) => self.index(id).loc, + SomeId::Type(id) => self.index(id).loc, + } } pub fn iter_decls(&self) -> indexmap::map::Iter { @@ -434,7 +452,6 @@ impl TypedAstContext { } pub fn prune_unused_decls(&mut self) { - use self::iterators::{DFNodes, SomeId}; // Starting from a set of root declarations, walk each one to find declarations it // depends on. Then walk each of those, recursively. @@ -568,7 +585,7 @@ impl TypedAstContext { (None, None) => Ordering::Equal, (None, _) => Ordering::Less, (_, None) => Ordering::Greater, - (Some(a), Some(b)) => self.compare_src_locs(a, b), + (Some(a), Some(b)) => self.compare_src_locs(&a.begin(), &b.begin()), } }); self.c_decls_top = decls_top; @@ -578,133 +595,107 @@ impl TypedAstContext { impl CommentContext { pub fn empty() -> CommentContext { CommentContext { - decl_comments: HashMap::new(), - stmt_comments: HashMap::new(), + comments_by_file: HashMap::new(), } } - // Try to match up every comment with a declaration or a statement + /// Build a CommentContext from the comments in this `ast_context` pub fn new(ast_context: &mut TypedAstContext) -> CommentContext { - // Group and sort declarations by file and by position - let mut decls: HashMap> = HashMap::new(); - for (decl_id, ref loc_decl) in &ast_context.c_decls { - if let Some(ref loc) = loc_decl.loc { - decls - .entry(loc.fileid) - .or_insert(vec![]) - .push((loc.clone(), *decl_id)); - } - } - decls.iter_mut().for_each(|(_, v)| v.sort()); - - // Group and sort statements by file and by position - let mut stmts: HashMap> = HashMap::new(); - for (stmt_id, ref loc_stmt) in &ast_context.c_stmts { - if let Some(ref loc) = loc_stmt.loc { - stmts - .entry(loc.fileid) - .or_insert(vec![]) - .push((loc.clone(), *stmt_id)); - } - } - stmts.iter_mut().for_each(|(_, v)| v.sort()); - - let mut decl_comments_map: HashMap> = HashMap::new(); - let mut stmt_comments_map: HashMap> = HashMap::new(); - - let empty_vec1 = &vec![]; - let empty_vec2 = &vec![]; - - // Match comments to declarations and statements - while let Some(Located { loc, kind: comment }) = ast_context.comments.pop() { - if let Some(loc) = loc { - let this_file_decls = decls.get(&loc.fileid).unwrap_or(empty_vec1); - let this_file_stmts = stmts.get(&loc.fileid).unwrap_or(empty_vec2); - - // Find the closest declaration and statement - let decl_ix = this_file_decls - .binary_search_by_key(&loc.line, |&(ref l, _)| l.line) - .unwrap_or_else(|x| x); - let stmt_ix = this_file_stmts - .binary_search_by_key(&loc.line, |&(ref l, _)| l.line) - .unwrap_or_else(|x| x); - - let mut insert_decl_comment = |decl_id: CDeclId, loc, comment| { - let decl_id = if let CDeclKind::NonCanonicalDecl { canonical_decl } = ast_context[decl_id].kind { - canonical_decl - } else { - decl_id - }; - decl_comments_map - .entry(decl_id) - .or_insert(BTreeMap::new()) - .insert(loc, comment); - }; - let mut insert_stmt_comment = |stmt_id: CStmtId, loc, comment| { - stmt_comments_map - .entry(stmt_id) - .or_insert(BTreeMap::new()) - .insert(loc, comment); - }; - - // Prefer the one that is higher up (biasing towards declarations if there is a tie) - match (this_file_decls.get(decl_ix), this_file_stmts.get(stmt_ix)) { - (Some(&(ref l1, d)), Some(&(ref l2, s))) => { - if l1 > l2 { - insert_stmt_comment(s, loc, comment); - } else { - insert_decl_comment(d, loc, comment); - } - } - (Some(&(_, d)), None) => { - insert_decl_comment(d, loc, comment); - } - (None, Some(&(_, s))) => { - insert_stmt_comment(s, loc, comment); - } - (None, None) => { - diag!( - Diagnostic::Comments, - "Didn't find a target node for the comment '{}'", - comment, - ); - } - }; + let mut comments_by_file: HashMap>> = HashMap::new(); + + // Group comments by their file + for comment in &ast_context.comments { + // Comments without a valid FileId are probably clang + // compiler-internal definitions + if let Some(file_id) = ast_context.file_id(&comment) { + comments_by_file + .entry(file_id) + .or_default() + .push(comment.clone()); } } - // Flatten out the nested comment maps - let decl_comments = decl_comments_map - .into_iter() - .map(|(decl_id, map)| { - let mut comments = Vec::from_iter(map); - // Sort comments attached to this decl by source location - comments.sort_unstable_by(|a, b| { - ast_context.compare_src_locs(&a.0, &b.0) - }); + // Sort in REVERSE! Last element is the first in file source + // ordering. This makes it easy to pop the next comment off. + for comments in comments_by_file.values_mut() { + comments.sort_by(|a, b| { + ast_context.compare_src_locs( + &b.loc.unwrap().begin(), + &a.loc.unwrap().begin(), + ) + }); + } - (decl_id, comments.into_iter().map(|(_, v)| v).collect()) - }) - .collect(); - let stmt_comments = stmt_comments_map + let comments_by_file = comments_by_file .into_iter() - .map(|(decl_id, map)| (decl_id, map.into_iter().map(|(_, v)| v).collect())) + .map(|(k, v)| (k, RefCell::new(v))) .collect(); CommentContext { - decl_comments, - stmt_comments, + comments_by_file, + } + } + + pub fn get_comments_before(&self, loc: SrcLoc, ctx: &TypedAstContext) -> Vec { + let file_id = ctx.file_map[loc.fileid as usize]; + let mut extracted_comments = vec![]; + let mut comments = match self.comments_by_file.get(&file_id) { + None => return extracted_comments, + Some(comments) => comments.borrow_mut(), + }; + while !comments.is_empty() { + let next_comment_loc = comments + .last() + .unwrap() + .begin_loc() + .expect("All comments must have a source location"); + if ctx.compare_src_locs(&next_comment_loc, &loc) != Ordering::Less { + break; + } + + extracted_comments.push(comments.pop().unwrap().kind); } + extracted_comments } - // Extract the comment for a given declaration - pub fn get_decl_comment(&self, decl_id: CDeclId) -> Option<&[String]> { - self.decl_comments.get(&decl_id).map(Vec::as_ref) + pub fn get_comments_before_located( + &self, + located: &Located, + ctx: &TypedAstContext, + ) -> Vec { + match located.begin_loc() { + None => vec![], + Some(loc) => self.get_comments_before(loc, ctx), + } } - // Extract the comment for a given statement - pub fn get_stmt_comment(&self, stmt_id: CStmtId) -> Option<&[String]> { - self.stmt_comments.get(&stmt_id).map(Vec::as_ref) + pub fn peek_next_comment_on_line(&self, loc: SrcLoc, ctx: &TypedAstContext) -> Option> { + let file_id = ctx.file_map[loc.fileid as usize]; + let comments = self.comments_by_file.get(&file_id)?.borrow(); + comments.last().and_then(|comment| { + let next_comment_loc = comment + .begin_loc() + .expect("All comments must have a source location"); + if next_comment_loc.line != loc.line { + None + } else { + Some(comment.clone()) + } + }) + } + + /// Advance over the current comment in `file` + pub fn advance_comment(&self, file: FileId) { + if let Some(comments) = self.comments_by_file.get(&file) { + let _ = comments.borrow_mut().pop(); + } + } + + pub fn get_remaining_comments(&mut self, file_id: FileId) -> Vec { + match self.comments_by_file.remove(&file_id) { + Some(comments) => comments.into_inner().into_iter().map(|c| c.kind).collect(), + None => vec![], + } } } diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index f529204731..f97aa0790d 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -30,7 +30,7 @@ use syntax; use syntax::ast::{Arm, Expr, ExprKind, Lit, LitIntType, LitKind, Pat, Stmt, StmtKind}; use syntax::print::pprust; use syntax::ptr::P; -use syntax_pos::DUMMY_SP; +use syntax_pos::{DUMMY_SP, Span}; use indexmap::{IndexMap, IndexSet}; @@ -113,7 +113,7 @@ impl StructureLabel { self, lift_me: &IndexSet, store: &mut DeclStmtStore, - ) -> StructureLabel { + ) -> StructureLabel { match self { StructureLabel::GoTo(l) => StructureLabel::GoTo(l), StructureLabel::ExitTo(l) => StructureLabel::ExitTo(l), @@ -135,6 +135,7 @@ pub enum Structure { Simple { entries: IndexSet