Count what an item writes, not what kind of item it is
`emitted` was incremented for any item that was not a `use`, `mod`, `extern crate` or type alias -- which includes a `macro_rules!` definition. A `macro_rules!` emits nothing of its own; it is expanded at each call site. So a crate that is nothing but macros passed the "nothing was translated" guard while producing a file containing only the prelude. Four crates I had counted as working are that: `arrayref`, `cfg-if`, `cpufeatures` and `ctor` each produced exactly 367 lines, and the prelude alone is 367 lines. They now fail, which is correct, and the message says which kinds of item emit nothing rather than leaving it to be guessed. This is the exact failure the project was founded to refuse -- a transpiler reporting success for an empty file -- and the guard against it was measuring the wrong thing. It counts bytes written now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dee1a76 parent: 5d49ce5 modified
src/lower.rs +16 -4 | @@ -836,9 +836,10 @@ impl Lowerer { | ||
| 836 | 836 | // the output file looks. The prelude alone is not a translation. |
| 837 | 837 | if self.emitted == 0 { |
| 838 | 838 | return Err(format!( |
| 839 | - "nothing was translated: the input has no items this lowering \ | |
| 840 | - emits{}. Writing a file containing only the prelude would \ | |
| 841 | - report success for work that was not done", | |
| 839 | + "nothing was translated: every item in the input either emits \ | |
| 840 | + nothing of its own -- a `macro_rules!` definition, a `use`, a \ | |
| 841 | + `mod` -- or was dropped{}. Writing a file containing only the \ | |
| 842 | + prelude would report success for work that was not done", | |
| 842 | 843 | if self.dropped_by_cfg > 0 { |
| 843 | 844 | format!( |
| 844 | 845 | " ({} item(s) were dropped by `#[cfg]`; enable them with \ |
| @@ -3100,9 +3101,20 @@ impl Lowerer { | ||
| 3100 | 3101 | } |
| 3101 | 3102 | |
| 3102 | 3103 | fn item_inner(&mut self, item: &Item) -> Result<(), String> { |
| 3103 | - if !matches!(item, Item::Use(_) | Item::ExternCrate(_) | Item::Mod(_) | Item::Type(_)) { | |
| 3104 | + // Counted by what it actually writes, not by what kind of item it is. | |
| 3105 | + // A `macro_rules!` definition is an item and emits nothing -- it is | |
| 3106 | + // expanded at each call site -- so a crate that is nothing but macros | |
| 3107 | + // was reporting success while producing a file containing only the | |
| 3108 | + // prelude. That is the failure this project exists to refuse. | |
| 3109 | + let before = self.out.len() + self.hoisted.len() + self.forwards.len(); | |
| 3110 | + let r = self.item_inner_at(item); | |
| 3111 | + if self.out.len() + self.hoisted.len() + self.forwards.len() > before { | |
| 3104 | 3112 | self.emitted += 1; |
| 3105 | 3113 | } |
| 3114 | + r | |
| 3115 | + } | |
| 3116 | + | |
| 3117 | + fn item_inner_at(&mut self, item: &Item) -> Result<(), String> { | |
| 3106 | 3118 | match item { |
| 3107 | 3119 | Item::Fn(f) => { |
| 3108 | 3120 | let nim = self.fn_name(&self.cur_mod, &f.sig.ident.to_string()); |
| @@ -836,9 +836,10 @@ impl Lowerer { | |||
| 836 | // the output file looks. The prelude alone is not a translation. | 836 | // the output file looks. The prelude alone is not a translation. |
| 837 | if self.emitted == 0 { | 837 | if self.emitted == 0 { |
| 838 | return Err(format!( | 838 | return Err(format!( |
| 839 | - "nothing was translated: the input has no items this lowering \ | 839 | + "nothing was translated: every item in the input either emits \ |
| 840 | - emits{}. Writing a file containing only the prelude would \ | 840 | + nothing of its own -- a `macro_rules!` definition, a `use`, a \ |
| 841 | - report success for work that was not done", | 841 | + `mod` -- or was dropped{}. Writing a file containing only the \ |
| 842 | + prelude would report success for work that was not done", | ||
| 842 | if self.dropped_by_cfg > 0 { | 843 | if self.dropped_by_cfg > 0 { |
| 843 | format!( | 844 | format!( |
| 844 | " ({} item(s) were dropped by `#[cfg]`; enable them with \ | 845 | " ({} item(s) were dropped by `#[cfg]`; enable them with \ |
| @@ -3100,9 +3101,20 @@ impl Lowerer { | |||
| 3100 | } | 3101 | } |
| 3101 | 3102 | ||
| 3102 | fn item_inner(&mut self, item: &Item) -> Result<(), String> { | 3103 | fn item_inner(&mut self, item: &Item) -> Result<(), String> { |
| 3103 | - if !matches!(item, Item::Use(_) | Item::ExternCrate(_) | Item::Mod(_) | Item::Type(_)) { | 3104 | + // Counted by what it actually writes, not by what kind of item it is. |
| 3105 | + // A `macro_rules!` definition is an item and emits nothing -- it is | ||
| 3106 | + // expanded at each call site -- so a crate that is nothing but macros | ||
| 3107 | + // was reporting success while producing a file containing only the | ||
| 3108 | + // prelude. That is the failure this project exists to refuse. | ||
| 3109 | + let before = self.out.len() + self.hoisted.len() + self.forwards.len(); | ||
| 3110 | + let r = self.item_inner_at(item); | ||
| 3111 | + if self.out.len() + self.hoisted.len() + self.forwards.len() > before { | ||
| 3104 | self.emitted += 1; | 3112 | self.emitted += 1; |
| 3105 | } | 3113 | } |
| 3114 | + r | ||
| 3115 | + } | ||
| 3116 | + | ||
| 3117 | + fn item_inner_at(&mut self, item: &Item) -> Result<(), String> { | ||
| 3106 | match item { | 3118 | match item { |
| 3107 | Item::Fn(f) => { | 3119 | Item::Fn(f) => { |
| 3108 | let nim = self.fn_name(&self.cur_mod, &f.sig.ident.to_string()); | 3120 | let nim = self.fn_name(&self.cur_mod, &f.sig.ident.to_string()); |