| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| //! Generate comments for the bindings. |
| |
| use database::code_snippet::{ApiSnippets, DocCommentAttr, GeneratedItem}; |
| use database::BindingsGenerator; |
| use ffi_types::Environment; |
| use ir::{Comment, GenericItem, UnsupportedItem, IR}; |
| use std::collections::HashMap; |
| use std::fmt::Write as _; |
| use std::rc::Rc; |
| |
| /// Top-level comments that help identify where the generated bindings came |
| /// from. |
| pub fn generate_top_level_comment(ir: &IR, environment: Environment) -> String { |
| // The "@generated" marker is an informal convention for identifying |
| // automatically generated code. This marker is recognized by `rustfmt` |
| // (see the `format_generated_files` option [1]) and some other tools. |
| // For more info see https://generated.at/. |
| // |
| // [1] |
| // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files |
| // |
| // TODO(b/255784681): It would be nice to include "by $argv[0]"" in the |
| // @generated comment below. OTOH, `std::env::current_exe()` in our |
| // current build environment returns a guid-like path... :-/ |
| // |
| // TODO(b/255784681): Consider including cmdline arguments. |
| let target = &ir.current_target().0; |
| |
| let mut result = format!( |
| "// Automatically @generated Rust bindings for the following C++ target:\n\ |
| // {target}\n" |
| ); |
| |
| if environment == Environment::Production { |
| // Write the features. |
| result.push_str( |
| "\ |
| // Features: ", |
| ); |
| |
| let mut crubit_features: Vec<&str> = ir |
| .target_crubit_features(ir.current_target()) |
| .into_iter() |
| .map(|feature| feature.short_name()) |
| .collect(); |
| crubit_features.sort(); |
| |
| if let Some((last_feature, features)) = crubit_features.split_last() { |
| for feature in features { |
| result.push_str(feature); |
| result.push_str(", "); |
| } |
| result.push_str(last_feature); |
| } else { |
| result.push_str("<none>"); |
| } |
| result.push('\n'); |
| } |
| result |
| } |
| |
| pub fn generate_doc_comment( |
| comment: Option<&str>, |
| source_loc: Option<&str>, |
| environment: Environment, |
| ) -> Option<DocCommentAttr> { |
| let source_loc = match environment { |
| Environment::Production => source_loc, |
| Environment::GoldenTest => None, |
| }; |
| let (comment, sep, source_loc) = match (comment, source_loc) { |
| (None, None) => return None, |
| (None, Some(source_loc)) => ("", "", source_loc), |
| (Some(comment), Some(source_loc)) => (comment, "\n\n", source_loc), |
| (Some(comment), None) => (comment, "", ""), |
| }; |
| // token_stream_printer (and rustfmt) don't put a space between /// and the doc |
| // comment, let's add it here so our comments are pretty. |
| Some(DocCommentAttr(format!(" {comment}{sep}{source_loc}").replace('\n', "\n ").into())) |
| } |
| |
| /// Generates Rust source code for a given `UnsupportedItem`. |
| pub fn generate_unsupported(db: &dyn BindingsGenerator, item: Rc<UnsupportedItem>) -> ApiSnippets { |
| for error in item.errors() { |
| db.errors().report(error); |
| } |
| |
| let source_loc = item.source_loc(); |
| let source_loc = match &source_loc { |
| Some(loc) if db.environment() == Environment::Production => loc.as_ref(), |
| _ => "", |
| }; |
| |
| let mut message = String::new(); |
| if !source_loc.is_empty() { |
| writeln!(&mut message, "{source_loc}").unwrap(); |
| } |
| writeln!( |
| &mut message, |
| "Error while generating bindings for {} '{}':", |
| item.unsupported_kind(), |
| item.name.as_ref() |
| ) |
| .unwrap(); |
| for (index, error) in item.errors().iter().enumerate() { |
| if index != 0 { |
| message.push_str("\n\n"); |
| } |
| write!(&mut message, "{error:#}").unwrap(); |
| } |
| |
| if item.must_bind { |
| db.fatal_errors().report(&message); |
| } |
| |
| ApiSnippets { |
| generated_items: HashMap::from([( |
| item.id, |
| GeneratedItem::Comment { message: message.into() }, |
| )]), |
| ..Default::default() |
| } |
| } |
| |
| /// Generates Rust source code for a given `Comment`. |
| pub fn generate_comment(comment: Rc<Comment>) -> ApiSnippets { |
| ApiSnippets { |
| generated_items: HashMap::from([( |
| comment.id, |
| GeneratedItem::Comment { message: comment.text.clone() }, |
| )]), |
| ..Default::default() |
| } |
| } |