• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xd009642 / tarpaulin / #503

31 May 2024 08:10PM UTC coverage: 75.218% (+0.5%) from 74.697%
#503

Pull #1563

xd009642
Add a test for getting function names correct
Pull Request #1563: Start revamping the function handling stuff

60 of 64 new or added lines in 6 files covered. (93.75%)

16 existing lines in 2 files now uncovered.

2586 of 3438 relevant lines covered (75.22%)

137469.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.52
/src/source_analysis/items.rs
1
use crate::source_analysis::prelude::*;
2
use syn::*;
3

4
impl SourceAnalysis {
5
    pub(crate) fn process_items(&mut self, items: &[Item], ctx: &Context) -> SubResult {
382✔
6
        let mut res = SubResult::Ok;
382✔
7
        for item in items.iter() {
1,084✔
8
            match item {
702✔
9
                Item::ExternCrate(i) => {
2✔
10
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
2✔
11
                    analysis.ignore_tokens(i);
2✔
12
                }
13
                Item::Use(i) => {
49✔
14
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
49✔
15
                    analysis.ignore_tokens(i);
49✔
16
                }
17
                Item::Mod(i) => self.visit_mod(i, ctx),
141✔
18
                Item::Fn(i) => self.visit_fn(i, ctx, false),
446✔
19
                Item::Struct(i) => {
25✔
20
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
25✔
21
                    analysis.ignore_tokens(i);
25✔
22
                }
23
                Item::Enum(i) => {
4✔
24
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
4✔
25
                    analysis.ignore_tokens(i);
4✔
26
                }
27
                Item::Union(i) => {
1✔
28
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
1✔
29
                    analysis.ignore_tokens(i);
1✔
30
                }
31
                Item::Trait(i) => self.visit_trait(i, ctx),
10✔
32
                Item::Impl(i) => self.visit_impl(i, ctx),
18✔
33
                Item::Macro(ref i) => {
×
34
                    if self.visit_macro_call(&i.mac, ctx).is_unreachable() {
×
35
                        res = SubResult::Unreachable;
×
36
                    }
37
                }
38
                Item::Const(c) => {
6✔
39
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
6✔
40
                    analysis.ignore_tokens(c);
6✔
41
                }
42
                _ => {}
×
43
            }
44
        }
45
        res
382✔
46
    }
47

48
    fn visit_mod(&mut self, module: &ItemMod, ctx: &Context) {
141✔
49
        let _guard = ctx.push_to_symbol_stack(module.ident.to_string());
141✔
50
        let analysis = self.get_line_analysis(ctx.file.to_path_buf());
141✔
51
        analysis.ignore_tokens(module.mod_token);
141✔
52
        let check_insides = self.check_attr_list(&module.attrs, ctx);
141✔
53
        if check_insides {
141✔
54
            if let Some((_, ref items)) = module.content {
163✔
55
                self.process_items(items, ctx);
56
            }
57
        } else {
58
            if let Some((ref braces, _)) = module.content {
70✔
59
                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
60
                analysis.ignore_span(braces.span.join());
61
            }
62
            // Get the file or directory name of the module
63
            let mut p = if let Some(parent) = ctx.file.parent() {
32✔
64
                parent.join(module.ident.to_string())
65
            } else {
66
                PathBuf::from(module.ident.to_string())
3✔
67
            };
68
            if !p.exists() {
33✔
69
                p.set_extension("rs");
33✔
70
            }
71
            ctx.ignore_mods.borrow_mut().insert(p);
35✔
72
        }
73
    }
74

75
    fn visit_fn(&mut self, func: &ItemFn, ctx: &Context, force_cover: bool) {
470✔
76
        let _guard = ctx.push_to_symbol_stack(func.sig.ident.to_string());
470✔
77
        {
78
            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
470✔
79
            let span = func.span();
470✔
80
            analysis.functions.insert(
470✔
81
                ctx.get_qualified_name(),
470✔
82
                (func.sig.span().start().line, span.end().line),
470✔
83
            );
84
        }
85
        let mut test_func = false;
470✔
86
        let mut ignored_attr = false;
470✔
87
        let mut is_inline = false;
470✔
88
        let mut ignore_span = false;
470✔
89
        let is_generic = is_sig_generic(&func.sig);
470✔
90
        for attr in &func.attrs {
1,291✔
91
            let id = attr.path();
92
            if id.is_ident("test") || id.segments.last().is_some_and(|seg| seg.ident == "test") {
663✔
93
                test_func = true;
181✔
94
            } else if id.is_ident("derive") {
417✔
95
                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
×
96
                analysis.ignore_span(attr.span());
×
97
            } else if id.is_ident("inline") {
237✔
98
                is_inline = true;
1✔
99
            } else if id.is_ident("ignore") {
236✔
100
                ignored_attr = true;
×
101
            } else if check_cfg_attr(&attr.meta) {
235✔
102
                ignore_span = true;
13✔
103
                break;
13✔
104
            }
105
        }
106
        if ignore_span
470✔
107
            || (test_func && !ctx.config.include_tests())
634✔
108
            || (ignored_attr && !ctx.config.run_ignored)
428✔
109
        {
110
            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
42✔
111
            analysis.ignore_tokens(func);
42✔
112
        } else {
113
            if is_inline || is_generic || force_cover {
1,293✔
114
                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
20✔
115
                // We need to force cover!
116
                analysis.cover_span(func.block.span(), Some(ctx.file_contents));
20✔
117
            }
118
            if self
428✔
119
                .process_statements(&func.block.stmts, ctx)
428✔
120
                .is_unreachable()
121
            {
122
                // if the whole body of the function is unreachable, that means the function itself
123
                // cannot be called, so is unreachable as a whole
124
                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
5✔
125
                analysis.ignore_tokens(func);
5✔
126
                return;
5✔
127
            }
128
            self.visit_generics(&func.sig.generics, ctx);
423✔
129
            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
423✔
130
            let line_number = func.sig.fn_token.span().start().line;
423✔
131
            let mut start_line = line_number;
423✔
132
            for attr in &func.attrs {
1,500✔
133
                start_line = start_line.min(attr.span().start().line);
359✔
134
            }
135
            if start_line < line_number {
207✔
136
                analysis.add_to_ignore(start_line..line_number);
207✔
137
            }
138
            analysis.ignore.remove(&Lines::Line(line_number));
423✔
139
            // Ignore multiple lines of fn decl
140
            let decl_start = func.sig.fn_token.span().start().line + 1;
423✔
141
            let stmts_start = func.block.span().start().line;
423✔
142
            let lines = decl_start..=stmts_start;
423✔
143
            analysis.add_to_ignore(lines);
423✔
144
        }
145
    }
146

147
    fn visit_trait(&mut self, trait_item: &ItemTrait, ctx: &Context) {
10✔
148
        let _guard = ctx.push_to_symbol_stack(trait_item.ident.to_string());
10✔
149
        let check_cover = self.check_attr_list(&trait_item.attrs, ctx);
10✔
150
        if check_cover {
10✔
151
            for item in &trait_item.items {
31✔
152
                if let TraitItem::Fn(ref i) = *item {
10✔
153
                    if self.check_attr_list(&i.attrs, ctx) {
154
                        let item = i.clone();
8✔
155
                        if let Some(block) = item.default {
22✔
156
                            let item_fn = ItemFn {
157
                                attrs: item.attrs,
7✔
158
                                // Trait functions inherit visibility from the trait
159
                                vis: trait_item.vis.clone(),
7✔
160
                                sig: item.sig,
7✔
161
                                block: Box::new(block),
7✔
162
                            };
163
                            // We visit the function and force cover it
164
                            self.visit_fn(&item_fn, ctx, true);
7✔
165
                        } else {
166
                            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
1✔
167
                            analysis.ignore_tokens(i);
1✔
168
                        }
169
                    } else {
170
                        let analysis = self.get_line_analysis(ctx.file.to_path_buf());
2✔
171
                        analysis.ignore_tokens(i);
2✔
172
                    }
173
                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
10✔
174
                    for a in &i.attrs {
19✔
175
                        analysis.ignore_tokens(a);
3✔
176
                    }
177
                }
178
            }
179
            self.visit_generics(&trait_item.generics, ctx);
9✔
180
        } else {
181
            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
1✔
182
            analysis.ignore_tokens(trait_item);
1✔
183
        }
184
    }
185

186
    fn visit_impl(&mut self, impl_blk: &ItemImpl, ctx: &Context) {
18✔
187
        let _guard = ctx.push_to_symbol_stack(impl_blk.self_ty.to_token_stream().to_string());
18✔
188
        let check_cover = self.check_attr_list(&impl_blk.attrs, ctx);
18✔
189
        if check_cover {
18✔
190
            for item in &impl_blk.items {
53✔
191
                match *item {
192
                    ImplItem::Fn(ref i) => {
17✔
193
                        let item = i.clone();
17✔
194
                        let item_fn = ItemFn {
195
                            attrs: item.attrs,
17✔
196
                            vis: item.vis,
17✔
197
                            sig: item.sig,
17✔
198
                            block: Box::new(item.block),
17✔
199
                        };
200

201
                        // If the impl is on a generic, we need to force cover
202
                        let force_cover = !impl_blk.generics.params.is_empty();
17✔
203

204
                        self.visit_fn(&item_fn, ctx, force_cover);
17✔
205
                    }
206
                    ImplItem::Type(_) => {
1✔
207
                        let analysis = self.get_line_analysis(ctx.file.to_path_buf());
1✔
208
                        analysis.ignore_span(item.span());
1✔
209
                    }
UNCOV
210
                    _ => {}
×
211
                }
212
            }
213
            self.visit_generics(&impl_blk.generics, ctx);
17✔
214
        } else {
215
            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
1✔
216
            analysis.ignore_tokens(impl_blk);
1✔
217
        }
218
    }
219
}
220

221
fn has_generic_arg<'a>(args: impl Iterator<Item = &'a FnArg>) -> bool {
462✔
222
    for arg in args {
815✔
223
        if let FnArg::Typed(pat) = arg {
171✔
224
            if matches!(*pat.ty, Type::ImplTrait(_)) {
170✔
225
                return true;
1✔
226
            }
227
        }
228
    }
229
    false
461✔
230
}
231

232
fn is_sig_generic(sig: &Signature) -> bool {
470✔
233
    !sig.generics.params.is_empty() || has_generic_arg(sig.inputs.iter())
932✔
234
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc