From 65ccf24ce8e51b199d60d06ad41ea35f4cdee15c Mon Sep 17 00:00:00 2001 From: Agustin Chiappe Berrini Date: Wed, 6 Dec 2017 04:28:01 -0500 Subject: [PATCH] and refactor to just move the checking --- src/librustc_passes/ast_validation.rs | 29 ++++++++--- src/libsyntax/parse/lexer/mod.rs | 14 +---- src/libsyntax/parse/token.rs | 10 +--- src/libsyntax_pos/symbol.rs | 52 +++++++++++++++++++ .../issue-10412.rs | 14 +++-- src/test/compile-fail/issue-46311.rs | 14 +++++ .../lifetime-no-keyword.rs | 6 +-- 7 files changed, 100 insertions(+), 39 deletions(-) rename src/test/{parse-fail => compile-fail}/issue-10412.rs (58%) create mode 100644 src/test/compile-fail/issue-46311.rs rename src/test/{parse-fail => compile-fail}/lifetime-no-keyword.rs (72%) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 97cea5c9d64..8f21028c4bd 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -21,7 +21,6 @@ use rustc::session::Session; use syntax::ast::*; use syntax::attr; use syntax::codemap::Spanned; -use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax_pos::Span; use errors; @@ -35,8 +34,16 @@ impl<'a> AstValidator<'a> { &self.session.parse_sess.span_diagnostic } + fn check_lifetime(&self, lifetime: &Lifetime) { + if !lifetime.ident.without_first_quote().is_valid() && + !lifetime.ident.name.is_static_keyword() { + self.err_handler().span_err(lifetime.span, "lifetimes cannot use keyword names"); + } + } + fn check_label(&self, label: Ident, span: Span) { - if label.name == keywords::StaticLifetime.name() || label.name == "'_" { + if label.name.is_static_keyword() || !label.without_first_quote().is_valid() + || label.name == "'_" { self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name)); } } @@ -202,7 +209,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_item(&mut self, item: &'a Item) { match item.node { - ItemKind::Impl(.., Some(..), _, ref impl_items) => { + ItemKind::Impl(.., ref generics, Some(..), _, ref impl_items) => { self.invalid_visibility(&item.vis, item.span, None); for impl_item in impl_items { self.invalid_visibility(&impl_item.vis, impl_item.span, None); @@ -210,11 +217,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_trait_fn_not_const(sig.constness); } } + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) } - ItemKind::Impl(.., None, _, _) => { + ItemKind::Impl(.., ref generics, None, _, _) => { self.invalid_visibility(&item.vis, item.span, Some("place qualifiers on individual impl items instead")); + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) } ItemKind::AutoImpl(..) => { self.invalid_visibility(&item.vis, item.span, None); @@ -225,13 +234,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { Some("place qualifiers on individual foreign items \ instead")); } - ItemKind::Enum(ref def, _) => { + ItemKind::Enum(ref def, ref generics) => { for variant in &def.variants { self.invalid_non_exhaustive_attribute(variant); for field in variant.node.data.fields() { self.invalid_visibility(&field.vis, field.span, None); } } + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) } ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => { if is_auto == IsAuto::Yes { @@ -268,6 +278,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } } + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) } ItemKind::Mod(_) => { // Ensure that `path` attributes on modules are recorded as used (c.f. #35584). @@ -278,7 +289,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.session.buffer_lint(lint, item.id, item.span, msg); } } - ItemKind::Union(ref vdata, _) => { + ItemKind::Union(ref vdata, ref generics) => { if !vdata.is_struct() { self.err_handler().span_err(item.span, "tuple and unit unions are not permitted"); @@ -287,6 +298,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.err_handler().span_err(item.span, "unions cannot have zero fields"); } + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) + } + ItemKind::Fn(.., ref generics, _) | + ItemKind::Ty(_, ref generics) | + ItemKind::Struct(_, ref generics) => { + generics.lifetimes.iter().for_each(|l| self.check_lifetime(&l.lifetime)) } _ => {} } diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 6f20104dda5..5de5e1fd5de 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -14,7 +14,7 @@ use codemap::{CodeMap, FilePathMapping}; use errors::{FatalError, DiagnosticBuilder}; use parse::{token, ParseSess}; use str::char_at; -use symbol::{Symbol, keywords}; +use symbol::{Symbol}; use std_unicode::property::Pattern_White_Space; use std::borrow::Cow; @@ -1296,18 +1296,6 @@ impl<'a> StringReader<'a> { self.mk_ident(&format!("'{}", lifetime_name)) }); - // Conjure up a "keyword checking ident" to make sure that - // the lifetime name is not a keyword. - let keyword_checking_ident = self.with_str_from(start, |lifetime_name| { - self.mk_ident(lifetime_name) - }); - let keyword_checking_token = &token::Ident(keyword_checking_ident); - let last_bpos = self.pos; - if keyword_checking_token.is_reserved_ident() && - !keyword_checking_token.is_keyword(keywords::Static) { - self.err_span_(start, last_bpos, "lifetimes cannot use keyword names"); - } - return Ok(token::Lifetime(ident)); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index ff87f146c0a..94b279d2bdc 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -364,18 +364,12 @@ impl Token { /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(&self) -> bool { - match self.ident() { - Some(id) => id.name >= keywords::As.name() && id.name <= keywords::While.name(), - _ => false, - } + self.ident().map(|id| id.name.is_used_keyword()).unwrap_or(false) } /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(&self) -> bool { - match self.ident() { - Some(id) => id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name(), - _ => false, - } + self.ident().map(|id| id.name.is_unused_keyword()).unwrap_or(false) } pub fn glue(self, joint: Token) -> Option { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 69ddd560213..c904e93e63a 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -35,9 +35,17 @@ impl Ident { Ident::with_empty_ctxt(Symbol::intern(string)) } + pub fn without_first_quote(&self) -> Ident { + Ident { name: self.name.without_first_quote(), ctxt: self.ctxt } + } + pub fn modern(self) -> Ident { Ident { name: self.name, ctxt: self.ctxt.modern() } } + + pub fn is_valid(&self) -> bool { + !self.name.is_used_keyword() && !self.name.is_unused_keyword() + } } impl fmt::Debug for Ident { @@ -113,6 +121,24 @@ impl Symbol { pub fn as_u32(self) -> u32 { self.0 } + + /// Returns `true` if the token is a keyword used in the language. + pub fn is_used_keyword(&self) -> bool { + self >= &keywords::As.name() && self <= &keywords::While.name() + } + + /// Returns `true` if the token is a keyword reserved for possible future use. + pub fn is_unused_keyword(&self) -> bool { + self >= &keywords::Abstract.name() && self <= &keywords::Yield.name() + } + + pub fn is_static_keyword(&self) -> bool { + self == &keywords::StaticLifetime.name() + } + + pub fn without_first_quote(&self) -> Symbol { + Symbol::from(self.as_str().trim_left_matches('\'')) + } } impl<'a> From<&'a str> for Symbol { @@ -428,4 +454,30 @@ mod tests { // gensym of *existing* string gets new number: assert_eq!(i.gensym("dog"), Symbol(4294967293)); } + + #[test] + fn is_used_keyword_test() { + let s = Symbol(5); + assert_eq!(s.is_used_keyword(), true); + } + + #[test] + fn is_unused_keyword_test() { + let s = Symbol(40); + assert_eq!(s.is_unused_keyword(), true); + } + + #[test] + fn is_valid_test() { + let i = Ident { name: Symbol(40), ctxt: SyntaxContext(0) }; + assert_eq!(i.is_valid(), false); + let i = Ident { name: Symbol(61), ctxt: SyntaxContext(0) }; + assert_eq!(i.is_valid(), true); + } + + #[test] + fn without_first_quote_test() { + let i = Ident::from_str("'break"); + assert_eq!(i.without_first_quote().name, keywords::Break.name()); + } } diff --git a/src/test/parse-fail/issue-10412.rs b/src/test/compile-fail/issue-10412.rs similarity index 58% rename from src/test/parse-fail/issue-10412.rs rename to src/test/compile-fail/issue-10412.rs index d723d94c02c..20421bfc7c4 100644 --- a/src/test/parse-fail/issue-10412.rs +++ b/src/test/compile-fail/issue-10412.rs @@ -8,20 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only -Z continue-parse-after-error - - trait Serializable<'self, T> { //~ ERROR lifetimes cannot use keyword names - fn serialize(val : &'self T) -> Vec ; //~ ERROR lifetimes cannot use keyword names - fn deserialize(repr : &[u8]) -> &'self T; //~ ERROR lifetimes cannot use keyword names + fn serialize(val : &'self T) -> Vec ; + fn deserialize(repr : &[u8]) -> &'self T; } -impl<'self> Serializable for &'self str { //~ ERROR lifetimes cannot use keyword names +impl<'self> Serializable for &'self str { //~^ ERROR lifetimes cannot use keyword names - fn serialize(val : &'self str) -> Vec { //~ ERROR lifetimes cannot use keyword names + //~| ERROR missing lifetime specifier + fn serialize(val : &'self str) -> Vec { vec![1] } - fn deserialize(repr: &[u8]) -> &'self str { //~ ERROR lifetimes cannot use keyword names + fn deserialize(repr: &[u8]) -> &'self str { "hi" } } diff --git a/src/test/compile-fail/issue-46311.rs b/src/test/compile-fail/issue-46311.rs new file mode 100644 index 00000000000..82f55f2c142 --- /dev/null +++ b/src/test/compile-fail/issue-46311.rs @@ -0,0 +1,14 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + 'break: loop { //~ ERROR invalid label name `'break` + } +} diff --git a/src/test/parse-fail/lifetime-no-keyword.rs b/src/test/compile-fail/lifetime-no-keyword.rs similarity index 72% rename from src/test/parse-fail/lifetime-no-keyword.rs rename to src/test/compile-fail/lifetime-no-keyword.rs index a8771ae93af..e2465663dd0 100644 --- a/src/test/parse-fail/lifetime-no-keyword.rs +++ b/src/test/compile-fail/lifetime-no-keyword.rs @@ -8,11 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only -Z continue-parse-after-error - fn foo<'a>(a: &'a isize) { } fn bar(a: &'static isize) { } -fn baz(a: &'let isize) { } //~ ERROR lifetimes cannot use keyword names -fn zab(a: &'self isize) { } //~ ERROR lifetimes cannot use keyword names +fn baz<'let>(a: &'let isize) { } //~ ERROR lifetimes cannot use keyword names +fn zab<'self>(a: &'self isize) { } //~ ERROR lifetimes cannot use keyword names fn main() { }