]> git.proxmox.com Git - rustc.git/blob - src/librustc_passes/ast_validation.rs
New upstream version 1.23.0+dfsg1
[rustc.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 }