]> git.lizzy.rs Git - rust.git/blob - src/librustc_passes/ast_validation.rs
Always report patterns more complex than `mut IDENT` as errors
[rust.git] / src / librustc_passes / ast_validation.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Validate AST before lowering it to HIR
12 //
13 // This pass is supposed to catch things that fit into AST data structures,
14 // but not permitted by the language. It runs after expansion when AST is frozen,
15 // so it can check for erroneous constructions produced by syntax extensions.
16 // This pass is supposed to perform only simple checks not requiring name resolution
17 // or type checking or some other kind of complex analysis.
18
19 use rustc::lint;
20 use rustc::session::Session;
21 use syntax::ast::*;
22 use syntax::attr;
23 use syntax::codemap::Spanned;
24 use syntax::parse::token;
25 use syntax::symbol::keywords;
26 use syntax::visit::{self, Visitor};
27 use syntax_pos::Span;
28 use errors;
29
30 struct AstValidator<'a> {
31     session: &'a Session,
32 }
33
34 impl<'a> AstValidator<'a> {
35     fn err_handler(&self) -> &errors::Handler {
36         &self.session.parse_sess.span_diagnostic
37     }
38
39     fn check_label(&self, label: Ident, span: Span) {
40         if label.name == keywords::StaticLifetime.name() || label.name == "'_" {
41             self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name));
42         }
43     }
44
45     fn invalid_non_exhaustive_attribute(&self, variant: &Variant) {
46         let has_non_exhaustive = variant.node.attrs.iter()
47             .any(|attr| attr.check_name("non_exhaustive"));
48         if has_non_exhaustive {
49             self.err_handler().span_err(variant.span,
50                                         "#[non_exhaustive] is not yet supported on variants");
51         }
52     }
53
54     fn invalid_visibility(&self, vis: &Visibility, span: Span, note: Option<&str>) {
55         if vis != &Visibility::Inherited {
56             let mut err = struct_span_err!(self.session,
57                                            span,
58                                            E0449,
59                                            "unnecessary visibility qualifier");
60             if vis == &Visibility::Public {
61                 err.span_label(span, "`pub` not needed here");
62             }
63             if let Some(note) = note {
64                 err.note(note);
65             }
66             err.emit();
67         }
68     }
69
70     fn check_decl_no_pat<ReportFn: Fn(Span, bool)>(&self, decl: &FnDecl, report_err: ReportFn) {
71         for arg in &decl.inputs {
72             match arg.pat.node {
73                 PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), _, None) |
74                 PatKind::Wild => {}
75                 PatKind::Ident(BindingMode::ByValue(Mutability::Mutable), _, None) =>
76                     report_err(arg.pat.span, true),
77                 _ => report_err(arg.pat.span, false),
78             }
79         }
80     }
81
82     fn check_trait_fn_not_const(&self, constness: Spanned<Constness>) {
83         match constness.node {
84             Constness::Const => {
85                 struct_span_err!(self.session, constness.span, E0379,
86                                  "trait fns cannot be declared const")
87                     .span_label(constness.span, "trait fns cannot be const")
88                     .emit();
89             }
90             _ => {}
91         }
92     }
93
94     fn no_questions_in_bounds(&self, bounds: &TyParamBounds, where_: &str, is_trait: bool) {
95         for bound in bounds {
96             if let TraitTyParamBound(ref poly, TraitBoundModifier::Maybe) = *bound {
97                 let mut err = self.err_handler().struct_span_err(poly.span,
98                                     &format!("`?Trait` is not permitted in {}", where_));
99                 if is_trait {
100                     err.note(&format!("traits are `?{}` by default", poly.trait_ref.path));
101                 }
102                 err.emit();
103             }
104         }
105     }
106
107     /// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus),
108     /// or path for ranges.
109     ///
110     /// FIXME: do we want to allow expr -> pattern conversion to create path expressions?
111     /// That means making this work:
112     ///
113     /// ```rust,ignore (FIXME)
114     ///     struct S;
115     ///     macro_rules! m {
116     ///         ($a:expr) => {
117     ///             let $a = S;
118     ///         }
119     ///     }
120     ///     m!(S);
121     /// ```
122     fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
123         match expr.node {
124             ExprKind::Lit(..) => {}
125             ExprKind::Path(..) if allow_paths => {}
126             ExprKind::Unary(UnOp::Neg, ref inner)
127                 if match inner.node { ExprKind::Lit(_) => true, _ => false } => {}
128             _ => self.err_handler().span_err(expr.span, "arbitrary expressions aren't allowed \
129                                                          in patterns")
130         }
131     }
132 }
133
134 impl<'a> Visitor<'a> for AstValidator<'a> {
135     fn visit_expr(&mut self, expr: &'a Expr) {
136         match expr.node {
137             ExprKind::While(.., Some(ident)) |
138             ExprKind::Loop(_, Some(ident)) |
139             ExprKind::WhileLet(.., Some(ident)) |
140             ExprKind::ForLoop(.., Some(ident)) |
141             ExprKind::Break(Some(ident), _) |
142             ExprKind::Continue(Some(ident)) => {
143                 self.check_label(ident.node, ident.span);
144             }
145             _ => {}
146         }
147
148         visit::walk_expr(self, expr)
149     }
150
151     fn visit_ty(&mut self, ty: &'a Ty) {
152         match ty.node {
153             TyKind::BareFn(ref bfty) => {
154                 self.check_decl_no_pat(&bfty.decl, |span, _| {
155                     struct_span_err!(self.session, span, E0561,
156                                      "patterns aren't allowed in function pointer types").emit();
157                 });
158             }
159             TyKind::TraitObject(ref bounds, ..) => {
160                 let mut any_lifetime_bounds = false;
161                 for bound in bounds {
162                     if let RegionTyParamBound(ref lifetime) = *bound {
163                         if any_lifetime_bounds {
164                             span_err!(self.session, lifetime.span, E0226,
165                                       "only a single explicit lifetime bound is permitted");
166                             break;
167                         }
168                         any_lifetime_bounds = true;
169                     }
170                 }
171                 self.no_questions_in_bounds(bounds, "trait object types", false);
172             }
173             TyKind::ImplTrait(ref bounds) => {
174                 if !bounds.iter()
175                           .any(|b| if let TraitTyParamBound(..) = *b { true } else { false }) {
176                     self.err_handler().span_err(ty.span, "at least one trait must be specified");
177                 }
178             }
179             _ => {}
180         }
181
182         visit::walk_ty(self, ty)
183     }
184
185     fn visit_path(&mut self, path: &'a Path, _: NodeId) {
186         if path.segments.len() >= 2 && path.is_global() {
187             let ident = path.segments[1].identifier;
188             if token::Ident(ident).is_path_segment_keyword() {
189                 self.err_handler()
190                     .span_err(path.span, &format!("global paths cannot start with `{}`", ident));
191             }
192         }
193
194         visit::walk_path(self, path)
195     }
196
197     fn visit_item(&mut self, item: &'a Item) {
198         match item.node {
199             ItemKind::Use(ref view_path) => {
200                 let path = view_path.node.path();
201                 path.segments.iter().find(|segment| segment.parameters.is_some()).map(|segment| {
202                     self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(),
203                                                 "generic arguments in import path");
204                 });
205             }
206             ItemKind::Impl(.., Some(..), _, ref impl_items) => {
207                 self.invalid_visibility(&item.vis, item.span, None);
208                 for impl_item in impl_items {
209                     self.invalid_visibility(&impl_item.vis, impl_item.span, None);
210                     if let ImplItemKind::Method(ref sig, _) = impl_item.node {
211                         self.check_trait_fn_not_const(sig.constness);
212                     }
213                 }
214             }
215             ItemKind::Impl(.., None, _, _) => {
216                 self.invalid_visibility(&item.vis,
217                                         item.span,
218                                         Some("place qualifiers on individual impl items instead"));
219             }
220             ItemKind::AutoImpl(..) => {
221                 self.invalid_visibility(&item.vis, item.span, None);
222             }
223             ItemKind::ForeignMod(..) => {
224                 self.invalid_visibility(&item.vis,
225                                         item.span,
226                                         Some("place qualifiers on individual foreign items \
227                                               instead"));
228             }
229             ItemKind::Enum(ref def, _) => {
230                 for variant in &def.variants {
231                     self.invalid_non_exhaustive_attribute(variant);
232                     for field in variant.node.data.fields() {
233                         self.invalid_visibility(&field.vis, field.span, None);
234                     }
235                 }
236             }
237             ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => {
238                 if is_auto == IsAuto::Yes {
239                     // Auto traits cannot have generics, super traits nor contain items.
240                     if !generics.ty_params.is_empty() {
241                         self.err_handler().span_err(item.span,
242                                                     "auto traits cannot have generics");
243                     }
244                     if !bounds.is_empty() {
245                         self.err_handler().span_err(item.span,
246                                                     "auto traits cannot have super traits");
247                     }
248                     if !trait_items.is_empty() {
249                         self.err_handler().span_err(item.span,
250                                                     "auto traits cannot contain items");
251                     }
252                 }
253                 self.no_questions_in_bounds(bounds, "supertraits", true);
254                 for trait_item in trait_items {
255                     if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
256                         self.check_trait_fn_not_const(sig.constness);
257                         if block.is_none() {
258                             self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
259                                 if mut_ident {
260                                     self.session.buffer_lint(
261                                         lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
262                                         trait_item.id, span,
263                                         "patterns aren't allowed in methods without bodies");
264                                 } else {
265                                     struct_span_err!(self.session, span, E0642,
266                                         "patterns aren't allowed in methods without bodies").emit();
267                                 }
268                             });
269                         }
270                     }
271                 }
272             }
273             ItemKind::Mod(_) => {
274                 // Ensure that `path` attributes on modules are recorded as used (c.f. #35584).
275                 attr::first_attr_value_str_by_name(&item.attrs, "path");
276                 if item.attrs.iter().any(|attr| attr.check_name("warn_directory_ownership")) {
277                     let lint = lint::builtin::LEGACY_DIRECTORY_OWNERSHIP;
278                     let msg = "cannot declare a new module at this location";
279                     self.session.buffer_lint(lint, item.id, item.span, msg);
280                 }
281             }
282             ItemKind::Union(ref vdata, _) => {
283                 if !vdata.is_struct() {
284                     self.err_handler().span_err(item.span,
285                                                 "tuple and unit unions are not permitted");
286                 }
287                 if vdata.fields().len() == 0 {
288                     self.err_handler().span_err(item.span,
289                                                 "unions cannot have zero fields");
290                 }
291             }
292             _ => {}
293         }
294
295         visit::walk_item(self, item)
296     }
297
298     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
299         match fi.node {
300             ForeignItemKind::Fn(ref decl, _) => {
301                 self.check_decl_no_pat(decl, |span, _| {
302                     struct_span_err!(self.session, span, E0130,
303                                      "patterns aren't allowed in foreign function declarations")
304                         .span_label(span, "pattern not allowed in foreign function").emit();
305                 });
306             }
307             ForeignItemKind::Static(..) | ForeignItemKind::Ty => {}
308         }
309
310         visit::walk_foreign_item(self, fi)
311     }
312
313     fn visit_vis(&mut self, vis: &'a Visibility) {
314         match *vis {
315             Visibility::Restricted { ref path, .. } => {
316                 path.segments.iter().find(|segment| segment.parameters.is_some()).map(|segment| {
317                     self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(),
318                                                 "generic arguments in visibility path");
319                 });
320             }
321             _ => {}
322         }
323
324         visit::walk_vis(self, vis)
325     }
326
327     fn visit_generics(&mut self, g: &'a Generics) {
328         let mut seen_default = None;
329         for ty_param in &g.ty_params {
330             if ty_param.default.is_some() {
331                 seen_default = Some(ty_param.span);
332             } else if let Some(span) = seen_default {
333                 self.err_handler()
334                     .span_err(span, "type parameters with a default must be trailing");
335                 break
336             }
337         }
338         for predicate in &g.where_clause.predicates {
339             if let WherePredicate::EqPredicate(ref predicate) = *predicate {
340                 self.err_handler().span_err(predicate.span, "equality constraints are not yet \
341                                                              supported in where clauses (#20041)");
342             }
343         }
344         visit::walk_generics(self, g)
345     }
346
347     fn visit_pat(&mut self, pat: &'a Pat) {
348         match pat.node {
349             PatKind::Lit(ref expr) => {
350                 self.check_expr_within_pat(expr, false);
351             }
352             PatKind::Range(ref start, ref end, _) => {
353                 self.check_expr_within_pat(start, true);
354                 self.check_expr_within_pat(end, true);
355             }
356             _ => {}
357         }
358
359         visit::walk_pat(self, pat)
360     }
361 }
362
363 pub fn check_crate(session: &Session, krate: &Crate) {
364     visit::walk_crate(&mut AstValidator { session: session }, krate)
365 }