diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index d2798955c46a9..d5dc2d4b8688d 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -291,6 +291,7 @@ use crate::raw_vec::RawVec; /// [`reserve`]: ../../std/vec/struct.Vec.html#method.reserve /// [owned slice]: ../../std/boxed/struct.Box.html #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(all(not(bootstrap), not(test)), rustc_diagnostic_item = "vec_type")] pub struct Vec { buf: RawVec, len: usize, diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index bd31d25dd034b..7e35188bc1082 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -518,7 +518,8 @@ impl Display for Arguments<'_> { label="`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`", )] #[doc(alias = "{:?}")] -#[lang = "debug_trait"] +#[cfg_attr(boostrap_stdarch_ignore_this, lang = "debug_trait")] +#[cfg_attr(not(boostrap_stdarch_ignore_this), rustc_diagnostic_item = "debug_trait")] pub trait Debug { /// Formats the value using the given formatter. /// diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs index a38dbbdd50c57..b3a561ef74be7 100644 --- a/src/librustc/arena.rs +++ b/src/librustc/arena.rs @@ -94,6 +94,10 @@ macro_rules! arena_types { rustc::hir::def_id::CrateNum > >, + [few] diagnostic_items: rustc_data_structures::fx::FxHashMap< + syntax::symbol::Symbol, + rustc::hir::def_id::DefId, + >, [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes, [decode] generic_predicates: rustc::ty::GenericPredicates<'tcx>, [few] lint_levels: rustc::lint::LintLevelMap, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 8d4cd51e4608c..368f5bb64fe6c 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -62,6 +62,7 @@ #![feature(log_syntax)] #![feature(mem_take)] #![feature(associated_type_bounds)] +#![feature(rustc_attrs)] #![recursion_limit="512"] @@ -109,6 +110,7 @@ pub mod middle { pub mod cstore; pub mod dead; pub mod dependency_format; + pub mod diagnostic_items; pub mod entry; pub mod exported_symbols; pub mod free_region; diff --git a/src/librustc/lint/internal.rs b/src/librustc/lint/internal.rs index be73b305e2c50..13834eaf40f57 100644 --- a/src/librustc/lint/internal.rs +++ b/src/librustc/lint/internal.rs @@ -159,29 +159,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { } fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment) -> bool { - if segment.ident.name == sym::TyKind { - if let Some(res) = segment.res { - if let Some(did) = res.opt_def_id() { - return cx.match_def_path(did, TYKIND_PATH); - } + if let Some(res) = segment.res { + if let Some(did) = res.opt_def_id() { + return cx.tcx.is_diagnostic_item(sym::TyKind, did); } } false } -const TYKIND_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::sty, sym::TyKind]; -const TY_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::Ty]; -const TYCTXT_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::context, sym::TyCtxt]; - fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty) -> Option { match &ty.node { TyKind::Path(qpath) => { if let QPath::Resolved(_, path) = qpath { let did = path.res.opt_def_id()?; - if cx.match_def_path(did, TY_PATH) { + if cx.tcx.is_diagnostic_item(sym::Ty, did) { return Some(format!("Ty{}", gen_args(path.segments.last().unwrap()))); - } else if cx.match_def_path(did, TYCTXT_PATH) { + } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) { return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap()))); } } diff --git a/src/librustc/middle/diagnostic_items.rs b/src/librustc/middle/diagnostic_items.rs new file mode 100644 index 0000000000000..dfae169b27824 --- /dev/null +++ b/src/librustc/middle/diagnostic_items.rs @@ -0,0 +1,123 @@ +//! Detecting diagnostic items. +//! +//! Diagnostic items are items that are not language-inherent, but can reasonably be expected to +//! exist for diagnostic purposes. This allows diagnostic authors to refer to specific items +//! directly, without having to guess module paths and crates. +//! Examples are: +//! +//! * Traits like `Debug`, that have no bearing on language semantics +//! +//! * Compiler internal types like `Ty` and `TyCtxt` + +use crate::hir::def_id::{DefId, LOCAL_CRATE}; +use crate::ty::TyCtxt; +use crate::util::nodemap::FxHashMap; + +use syntax::ast; +use syntax::symbol::{Symbol, sym}; +use crate::hir::itemlikevisit::ItemLikeVisitor; +use crate::hir; + +struct DiagnosticItemCollector<'tcx> { + // items from this crate + items: FxHashMap, + tcx: TyCtxt<'tcx>, +} + +impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { + fn visit_item(&mut self, item: &hir::Item) { + self.observe_item(&item.attrs, item.hir_id); + } + + fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { + self.observe_item(&trait_item.attrs, trait_item.hir_id); + } + + fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { + self.observe_item(&impl_item.attrs, impl_item.hir_id); + } +} + +impl<'tcx> DiagnosticItemCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> { + DiagnosticItemCollector { + tcx, + items: Default::default(), + } + } + + fn observe_item(&mut self, attrs: &[ast::Attribute], hir_id: hir::HirId) { + if let Some(name) = extract(attrs) { + let def_id = self.tcx.hir().local_def_id(hir_id); + // insert into our table + collect_item(self.tcx, &mut self.items, name, def_id); + } + } +} + +fn collect_item( + tcx: TyCtxt<'_>, + items: &mut FxHashMap, + name: Symbol, + item_def_id: DefId, +) { + // Check for duplicates. + if let Some(original_def_id) = items.insert(name, item_def_id) { + if original_def_id != item_def_id { + let mut err = match tcx.hir().span_if_local(item_def_id) { + Some(span) => tcx.sess.struct_span_err( + span, + &format!("duplicate diagnostic item found: `{}`.", name)), + None => tcx.sess.struct_err(&format!( + "duplicate diagnostic item in crate `{}`: `{}`.", + tcx.crate_name(item_def_id.krate), + name)), + }; + if let Some(span) = tcx.hir().span_if_local(original_def_id) { + span_note!(&mut err, span, "first defined here."); + } else { + err.note(&format!("first defined in crate `{}`.", + tcx.crate_name(original_def_id.krate))); + } + err.emit(); + } + } +} + +/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. +fn extract(attrs: &[ast::Attribute]) -> Option { + attrs.iter().find_map(|attr| { + if attr.check_name(sym::rustc_diagnostic_item) { + attr.value_str() + } else { + None + } + }) +} + +/// Traverse and collect the diagnostic items in the current +pub fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { + // Initialize the collector. + let mut collector = DiagnosticItemCollector::new(tcx); + + // Collect diagnostic items in this crate. + tcx.hir().krate().visit_all_item_likes(&mut collector); + + tcx.arena.alloc(collector.items) +} + + +/// Traverse and collect all the diagnostic items in all crates. +pub fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap { + // Initialize the collector. + let mut collector = FxHashMap::default(); + + // Collect diagnostic items in other crates. + for &cnum in tcx.crates().iter().chain(std::iter::once(&LOCAL_CRATE)) { + for (&name, &def_id) in tcx.diagnostic_items(cnum).iter() { + collect_item(tcx, &mut collector, name, def_id); + } + } + + tcx.arena.alloc(collector) +} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 334c06618bb28..6b04600eb75f8 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -367,8 +367,6 @@ language_item_table! { MaybeUninitLangItem, "maybe_uninit", maybe_uninit, Target::Union; - DebugTraitLangItem, "debug_trait", debug_trait, Target::Trait; - // Align offset for stride != 1, must not panic. AlignOffsetLangItem, "align_offset", align_offset_fn, Target::Fn; diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index c4f7ca51f4a7a..ef838114f6c36 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -804,7 +804,7 @@ rustc_queries! { } BorrowChecking { - // Lifetime resolution. See `middle::resolve_lifetimes`. + /// Lifetime resolution. See `middle::resolve_lifetimes`. query resolve_lifetimes(_: CrateNum) -> &'tcx ResolveLifetimes { desc { "resolving lifetimes" } } @@ -846,13 +846,30 @@ rustc_queries! { -> &'tcx [(Symbol, Option)] { desc { "calculating the lib features defined in a crate" } } + /// Returns the lang items defined in another crate by loading it from metadata. + // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid + // of that argument? query get_lang_items(_: CrateNum) -> &'tcx LanguageItems { eval_always desc { "calculating the lang items map" } } + + /// Returns all diagnostic items defined in all crates + query all_diagnostic_items(_: CrateNum) -> &'tcx FxHashMap { + eval_always + desc { "calculating the diagnostic items map" } + } + + /// Returns the lang items defined in another crate by loading it from metadata. query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { desc { "calculating the lang items defined in a crate" } } + + /// Returns the diagnostic items defined in a crate + query diagnostic_items(_: CrateNum) -> &'tcx FxHashMap { + desc { "calculating the diagnostic items map in a crate" } + } + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { desc { "calculating the missing lang items in a crate" } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index c0d86a79882ef..e240e0df8b948 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -978,6 +978,7 @@ pub struct FreeRegionInfo { /// /// [rustc guide]: https://rust-lang.github.io/rustc-guide/ty.html #[derive(Copy, Clone)] +#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyCtxt")] pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } @@ -1308,10 +1309,22 @@ impl<'tcx> TyCtxt<'tcx> { self.get_lib_features(LOCAL_CRATE) } + /// Obtain all lang items of this crate and all dependencies (recursively) pub fn lang_items(self) -> &'tcx middle::lang_items::LanguageItems { self.get_lang_items(LOCAL_CRATE) } + /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to + /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. + pub fn get_diagnostic_item(self, name: Symbol) -> Option { + self.all_diagnostic_items(LOCAL_CRATE).get(&name).copied() + } + + /// Check whether the diagnostic item with the given `name` has the given `DefId`. + pub fn is_diagnostic_item(self, name: Symbol, did: DefId) -> bool { + self.diagnostic_items(did.krate).get(&name) == Some(&did) + } + pub fn stability(self) -> &'tcx stability::Index<'tcx> { self.stability_index(LOCAL_CRATE) } @@ -2896,6 +2909,14 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { assert_eq!(id, LOCAL_CRATE); tcx.arena.alloc(middle::lang_items::collect(tcx)) }; + providers.diagnostic_items = |tcx, id| { + assert_eq!(id, LOCAL_CRATE); + middle::diagnostic_items::collect(tcx) + }; + providers.all_diagnostic_items = |tcx, id| { + assert_eq!(id, LOCAL_CRATE); + middle::diagnostic_items::collect_all(tcx) + }; providers.maybe_unused_trait_import = |tcx, id| { tcx.maybe_unused_trait_imports.contains(&id) }; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index eaaaf75f75dd5..56505c04f0f0c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -581,6 +581,7 @@ impl<'a, 'tcx> HashStable> for ty::TyS<'tcx> { } } +#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "Ty")] pub type Ty<'tcx> = &'tcx TyS<'tcx>; impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index f41fffe507d97..d2edf6fb1ee80 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -86,6 +86,7 @@ impl BoundRegion { /// AST structure in `libsyntax/ast.rs` as well. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable, Debug)] +#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyKind")] pub enum TyKind<'tcx> { /// The primitive boolean type. Written as `bool`. Bool, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index d3c94060e274a..26e7b789f8f90 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -570,7 +570,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations { _ => return, } - let debug = match cx.tcx.lang_items().debug_trait() { + let debug = match cx.tcx.get_diagnostic_item(sym::debug_trait) { Some(debug) => debug, None => return, }; diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index ca0cf0a5a661d..d6450f00c8b6a 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -226,6 +226,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } defined_lib_features => { cdata.get_lib_features(tcx) } defined_lang_items => { cdata.get_lang_items(tcx) } + diagnostic_items => { cdata.get_diagnostic_items(tcx) } missing_lang_items => { cdata.get_missing_lang_items(tcx) } missing_extern_crate_item => { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index cdee14d07fb4d..75d7261704722 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -12,6 +12,7 @@ use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc::hir::def::{self, Res, DefKind, CtorOf, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; use rustc::middle::lang_items; use rustc::mir::{self, interpret}; use rustc::mir::interpret::AllocDecodingSession; @@ -757,6 +758,23 @@ impl<'a, 'tcx> CrateMetadata { } } + /// Iterates over the diagnostic items in the given crate. + pub fn get_diagnostic_items( + &self, + tcx: TyCtxt<'tcx>, + ) -> &'tcx FxHashMap { + tcx.arena.alloc(if self.is_proc_macro_crate() { + // Proc macro crates do not export any diagnostic-items to the target. + Default::default() + } else { + self.root + .diagnostic_items + .decode(self) + .map(|(name, def_index)| (name, self.local_def_id(def_index))) + .collect() + }) + } + /// Iterates over each child of the given item. pub fn each_child_of_item(&self, id: DefIndex, mut callback: F, sess: &Session) where F: FnMut(def::Export) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 0eafcebefd745..db212408d8ebd 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -33,7 +33,7 @@ use syntax::ast; use syntax::attr; use syntax::ext::proc_macro::is_proc_macro_attr; use syntax::source_map::Spanned; -use syntax::symbol::{kw, sym, Ident}; +use syntax::symbol::{kw, sym, Ident, Symbol}; use syntax_pos::{self, FileName, SourceFile, Span}; use log::{debug, trace}; @@ -404,6 +404,11 @@ impl<'tcx> EncodeContext<'tcx> { let lang_items_missing = self.encode_lang_items_missing(); let lang_item_bytes = self.position() - i; + // Encode the diagnostic items. + i = self.position(); + let diagnostic_items = self.encode_diagnostic_items(); + let diagnostic_item_bytes = self.position() - i; + // Encode the native libraries used i = self.position(); let native_libraries = self.encode_native_libraries(); @@ -520,6 +525,7 @@ impl<'tcx> EncodeContext<'tcx> { dylib_dependency_formats, lib_features, lang_items, + diagnostic_items, lang_items_missing, native_libraries, foreign_modules, @@ -545,6 +551,7 @@ impl<'tcx> EncodeContext<'tcx> { println!(" dep bytes: {}", dep_bytes); println!(" lib feature bytes: {}", lib_feature_bytes); println!(" lang item bytes: {}", lang_item_bytes); + println!(" diagnostic item bytes: {}", diagnostic_item_bytes); println!(" native bytes: {}", native_lib_bytes); println!(" source_map bytes: {}", source_map_bytes); println!(" impl bytes: {}", impl_bytes); @@ -1555,6 +1562,12 @@ impl EncodeContext<'tcx> { self.lazy(lib_features.to_vec()) } + fn encode_diagnostic_items(&mut self) -> Lazy<[(Symbol, DefIndex)]> { + let tcx = self.tcx; + let diagnostic_items = tcx.diagnostic_items(LOCAL_CRATE); + self.lazy(diagnostic_items.iter().map(|(&name, def_id)| (name, def_id.index))) + } + fn encode_lang_items(&mut self) -> Lazy<[(DefIndex, usize)]> { let tcx = self.tcx; let lang_items = tcx.lang_items(); diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 1a5887bbf4ed8..1a5f0e17ba7ce 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -176,6 +176,7 @@ pub struct CrateRoot<'tcx> { pub lib_features: Lazy<[(Symbol, Option)]>, pub lang_items: Lazy<[(DefIndex, usize)]>, pub lang_items_missing: Lazy<[lang_items::LangItem]>, + pub diagnostic_items: Lazy<[(Symbol, DefIndex)]>, pub native_libraries: Lazy<[NativeLibrary]>, pub foreign_modules: Lazy<[ForeignModule]>, pub source_map: Lazy<[syntax_pos::SourceFile]>, diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs index b934f2e7f64ef..ee7ac3b15d955 100644 --- a/src/libsyntax/feature_gate/builtin_attrs.rs +++ b/src/libsyntax/feature_gate/builtin_attrs.rs @@ -461,6 +461,17 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ lang, Normal, template!(NameValueStr: "name"), lang_items, "language items are subject to change", ), + ( + sym::rustc_diagnostic_item, + Normal, + template!(NameValueStr: "name"), + Gated( + Stability::Unstable, + sym::rustc_attrs, + "diagnostic items compiler internal support for linting", + cfg_fn!(rustc_attrs), + ), + ), ( sym::no_debug, Whitelisted, template!(Word), Gated( diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 8833e03c72be2..f44716e013ec8 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -225,9 +225,10 @@ symbols! { custom_inner_attributes, custom_test_frameworks, c_variadic, - Debug, + debug_trait, declare_lint_pass, decl_macro, + Debug, Decodable, Default, default_lib_allocator, @@ -238,6 +239,7 @@ symbols! { deref, deref_mut, derive, + diagnostic, direct, doc, doc_alias, @@ -569,6 +571,7 @@ symbols! { rustc_conversion_suggestion, rustc_def_path, rustc_deprecated, + rustc_diagnostic_item, rustc_diagnostic_macros, rustc_dirty, rustc_dummy, diff --git a/src/test/ui/tool-attributes/diagnostic_item.rs b/src/test/ui/tool-attributes/diagnostic_item.rs new file mode 100644 index 0000000000000..1d35422ed6241 --- /dev/null +++ b/src/test/ui/tool-attributes/diagnostic_item.rs @@ -0,0 +1,2 @@ +#[rustc_diagnostic_item = "foomp"] //~ ERROR compiler internal support for linting +struct Foomp; diff --git a/src/test/ui/tool-attributes/diagnostic_item.stderr b/src/test/ui/tool-attributes/diagnostic_item.stderr new file mode 100644 index 0000000000000..deff4da6b8052 --- /dev/null +++ b/src/test/ui/tool-attributes/diagnostic_item.stderr @@ -0,0 +1,17 @@ +error[E0658]: diagnostic items compiler internal support for linting + --> $DIR/diagnostic_item.rs:1:1 + | +LL | #[rustc_diagnostic_item = "foomp"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/29642 + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + +error[E0601]: `main` function not found in crate `diagnostic_item` + | + = note: consider adding a `main` function to `$DIR/diagnostic_item.rs` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0601, E0658. +For more information about an error, try `rustc --explain E0601`. diff --git a/src/test/ui/tool-attributes/diagnostic_item2.rs b/src/test/ui/tool-attributes/diagnostic_item2.rs new file mode 100644 index 0000000000000..b32a66b16be18 --- /dev/null +++ b/src/test/ui/tool-attributes/diagnostic_item2.rs @@ -0,0 +1,6 @@ +// check-pass + +#[clippy::diagnostic_item = "mep"] +struct Mep; + +fn main() {} diff --git a/src/test/ui/tool-attributes/diagnostic_item3.rs b/src/test/ui/tool-attributes/diagnostic_item3.rs new file mode 100644 index 0000000000000..c1a236ed1cc32 --- /dev/null +++ b/src/test/ui/tool-attributes/diagnostic_item3.rs @@ -0,0 +1,7 @@ +// check-pass +#![feature(rustc_attrs)] + +#[rustc_diagnostic_item = "foomp"] +struct Foomp; + +fn main() {}