name = "proc_macro"
version = "0.0.0"
dependencies = [
+ "rustc_errors 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]
The tracking issue for this feature is: [#43122]
-[#34511]: https://github.com/rust-lang/rust/issues/43122
+[#43122]: https://github.com/rust-lang/rust/issues/43122
------------------------
let beta_offset = s.find('β').unwrap_or(s.len());
// Replace the range up until the β from the string
-let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
-assert_eq!(t, "α is alpha, ");
+s.splice(..beta_offset, "Α is capital alpha; ");
assert_eq!(s, "Α is capital alpha; β is beta");
```
\ No newline at end of file
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-/// Creates a `Vec` containing the arguments.
+/// Creates a [`Vec`] containing the arguments.
///
/// `vec!` allows `Vec`s to be defined with the same syntax as array expressions.
/// There are two forms of this macro:
///
-/// - Create a `Vec` containing a given list of elements:
+/// - Create a [`Vec`] containing a given list of elements:
///
/// ```
/// let v = vec![1, 2, 3];
/// assert_eq!(v[2], 3);
/// ```
///
-/// - Create a `Vec` from a given element and size:
+/// - Create a [`Vec`] from a given element and size:
///
/// ```
/// let v = vec![1; 3];
/// ```
///
/// Note that unlike array expressions this syntax supports all elements
-/// which implement `Clone` and the number of elements doesn't have to be
+/// which implement [`Clone`] and the number of elements doesn't have to be
/// a constant.
///
-/// This will use `clone()` to duplicate an expression, so one should be careful
+/// This will use `clone` to duplicate an expression, so one should be careful
/// using this with types having a nonstandard `Clone` implementation. For
/// example, `vec![Rc::new(1); 5]` will create a vector of five references
/// to the same boxed integer value, not five references pointing to independently
/// boxed integers.
+///
+/// [`Vec`]: ../std/vec/struct.Vec.html
+/// [`Clone`]: ../std/clone/trait.Clone.html
#[cfg(not(test))]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
($($x:expr,)*) => (vec![$($x),*])
}
-/// Use the syntax described in `std::fmt` to create a value of type `String`.
-/// See [`std::fmt`][fmt] for more information.
+/// Creates a `String` using interpolation of runtime expressions.
+///
+/// The first argument `format!` recieves is a format string. This must be a string
+/// literal. The power of the formatting string is in the `{}`s contained.
+///
+/// Additional parameters passed to `format!` replace the `{}`s within the
+/// formatting string in the order given unless named or positional parameters
+/// are used, see [`std::fmt`][fmt] for more information.
+///
+/// A common use for `format!` is concatenation and interpolation of strings.
+/// The same convention is used with [`print!`] and [`write!`] macros,
+/// depending on the intended destination of the string.
///
/// [fmt]: ../std/fmt/index.html
+/// [`print!`]: ../std/macro.print.html
+/// [`write!`]: ../std/macro.write.html
///
/// # Panics
///
}
/// Creates a splicing iterator that removes the specified range in the string,
- /// replaces with the given string, and yields the removed chars.
- /// The given string doesn’t need to be the same length as the range.
+ /// and replaces it with the given string.
+ /// The given string doesn't need to be the same length as the range.
///
- /// Note: The element range is removed when the [`Splice`] is dropped,
- /// even if the iterator is not consumed until the end.
+ /// Note: Unlike [`Vec::splice`], the replacement happens eagerly, and this
+ /// method does not return the removed chars.
///
/// # Panics
///
/// boundary, or if they're out of bounds.
///
/// [`char`]: ../../std/primitive.char.html
- /// [`Splice`]: ../../std/string/struct.Splice.html
+ /// [`Vec::splice`]: ../../std/vec/struct.Vec.html#method.splice
///
/// # Examples
///
/// let beta_offset = s.find('β').unwrap_or(s.len());
///
/// // Replace the range up until the β from the string
- /// let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
- /// assert_eq!(t, "α is alpha, ");
+ /// s.splice(..beta_offset, "Α is capital alpha; ");
/// assert_eq!(s, "Α is capital alpha; β is beta");
/// ```
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
- pub fn splice<'a, 'b, R>(&'a mut self, range: R, replace_with: &'b str) -> Splice<'a, 'b>
+ pub fn splice<R>(&mut self, range: R, replace_with: &str)
where R: RangeArgument<usize>
{
// Memory safety
//
// The String version of Splice does not have the memory safety issues
// of the vector version. The data is just plain bytes.
- // Because the range removal happens in Drop, if the Splice iterator is leaked,
- // the removal will not happen.
- let len = self.len();
- let start = match range.start() {
- Included(&n) => n,
- Excluded(&n) => n + 1,
- Unbounded => 0,
+
+ match range.start() {
+ Included(&n) => assert!(self.is_char_boundary(n)),
+ Excluded(&n) => assert!(self.is_char_boundary(n + 1)),
+ Unbounded => {},
};
- let end = match range.end() {
- Included(&n) => n + 1,
- Excluded(&n) => n,
- Unbounded => len,
+ match range.end() {
+ Included(&n) => assert!(self.is_char_boundary(n + 1)),
+ Excluded(&n) => assert!(self.is_char_boundary(n)),
+ Unbounded => {},
};
- // Take out two simultaneous borrows. The &mut String won't be accessed
- // until iteration is over, in Drop.
- let self_ptr = self as *mut _;
- // slicing does the appropriate bounds checks
- let chars_iter = self[start..end].chars();
-
- Splice {
- start,
- end,
- iter: chars_iter,
- string: self_ptr,
- replace_with,
- }
+ unsafe {
+ self.as_mut_vec()
+ }.splice(range, replace_with.bytes());
}
/// Converts this `String` into a [`Box`]`<`[`str`]`>`.
#[unstable(feature = "fused", issue = "35602")]
impl<'a> FusedIterator for Drain<'a> {}
-
-/// A splicing iterator for `String`.
-///
-/// This struct is created by the [`splice()`] method on [`String`]. See its
-/// documentation for more.
-///
-/// [`splice()`]: struct.String.html#method.splice
-/// [`String`]: struct.String.html
-#[derive(Debug)]
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-pub struct Splice<'a, 'b> {
- /// Will be used as &'a mut String in the destructor
- string: *mut String,
- /// Start of part to remove
- start: usize,
- /// End of part to remove
- end: usize,
- /// Current remaining range to remove
- iter: Chars<'a>,
- replace_with: &'b str,
-}
-
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-unsafe impl<'a, 'b> Sync for Splice<'a, 'b> {}
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-unsafe impl<'a, 'b> Send for Splice<'a, 'b> {}
-
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-impl<'a, 'b> Drop for Splice<'a, 'b> {
- fn drop(&mut self) {
- unsafe {
- let vec = (*self.string).as_mut_vec();
- vec.splice(self.start..self.end, self.replace_with.bytes());
- }
- }
-}
-
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-impl<'a, 'b> Iterator for Splice<'a, 'b> {
- type Item = char;
-
- #[inline]
- fn next(&mut self) -> Option<char> {
- self.iter.next()
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
-impl<'a, 'b> DoubleEndedIterator for Splice<'a, 'b> {
- #[inline]
- fn next_back(&mut self) -> Option<char> {
- self.iter.next_back()
- }
-}
#[test]
fn test_splice() {
let mut s = "Hello, world!".to_owned();
- let t: String = s.splice(7..12, "世界").collect();
+ s.splice(7..12, "世界");
assert_eq!(s, "Hello, 世界!");
- assert_eq!(t, "world");
}
#[test]
#[test]
fn test_splice_inclusive_range() {
let mut v = String::from("12345");
- let t: String = v.splice(2...3, "789").collect();
+ v.splice(2...3, "789");
assert_eq!(v, "127895");
- assert_eq!(t, "34");
- let t2: String = v.splice(1...2, "A").collect();
+ v.splice(1...2, "A");
assert_eq!(v, "1A895");
- assert_eq!(t2, "27");
}
#[test]
#[test]
fn test_splice_empty() {
let mut s = String::from("12345");
- let t: String = s.splice(1..2, "").collect();
+ s.splice(1..2, "");
assert_eq!(s, "1345");
- assert_eq!(t, "2");
}
#[test]
fn test_splice_unbounded() {
let mut s = String::from("12345");
- let t: String = s.splice(.., "").collect();
+ s.splice(.., "");
assert_eq!(s, "");
- assert_eq!(t, "12345");
-}
-
-#[test]
-fn test_splice_forget() {
- let mut s = String::from("12345");
- ::std::mem::forget(s.splice(2..4, "789"));
- assert_eq!(s, "12345");
}
#[test]
/// # Custom Messages
///
/// This macro has a second form, where a custom panic message can
-/// be provided with or without arguments for formatting.
+/// be provided with or without arguments for formatting. See [`std::fmt`]
+/// for syntax for this form.
///
/// [`panic!`]: macro.panic.html
/// [`debug_assert!`]: macro.debug_assert.html
-/// [testing]: ../book/first-edition/testing.html
+/// [testing]: ../book/second-edition/ch11-01-writing-tests.html#checking-results-with-the-assert-macro
+/// [`std::fmt`]: ../std/fmt/index.html
///
/// # Examples
///
/// On panic, this macro will print the values of the expressions with their
/// debug representations.
///
-/// Unlike `assert_eq!`, `debug_assert_eq!` statements are only enabled in non
+/// Unlike [`assert_eq!`], `debug_assert_eq!` statements are only enabled in non
/// optimized builds by default. An optimized build will omit all
/// `debug_assert_eq!` statements unless `-C debug-assertions` is passed to the
/// compiler. This makes `debug_assert_eq!` useful for checks that are too
/// expensive to be present in a release build but may be helpful during
/// development.
///
+/// [`assert_eq!`]: ../std/macro.assert_eq.html
+///
/// # Examples
///
/// ```
/// On panic, this macro will print the values of the expressions with their
/// debug representations.
///
-/// Unlike `assert_ne!`, `debug_assert_ne!` statements are only enabled in non
+/// Unlike [`assert_ne!`], `debug_assert_ne!` statements are only enabled in non
/// optimized builds by default. An optimized build will omit all
/// `debug_assert_ne!` statements unless `-C debug-assertions` is passed to the
/// compiler. This makes `debug_assert_ne!` useful for checks that are too
/// expensive to be present in a release build but may be helpful during
/// development.
///
+/// [`assert_ne!`]: ../std/macro.assert_ne.html
+///
/// # Examples
///
/// ```
/// Helper macro for reducing boilerplate code for matching `Result` together
/// with converting downstream errors.
///
-/// Prefer using `?` syntax to `try!`. `?` is built in to the language and is
-/// more succinct than `try!`. It is the standard method for error propagation.
+/// The `?` operator was added to replace `try!` and should be used instead.
///
-/// `try!` matches the given `Result`. In case of the `Ok` variant, the
+/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the
/// expression has the value of the wrapped value.
///
/// In case of the `Err` variant, it retrieves the inner error. `try!` then
/// error is then immediately returned.
///
/// Because of the early return, `try!` can only be used in functions that
-/// return `Result`.
+/// return [`Result`].
+///
+/// [`Result`]: ../std/result/enum.Result.html
///
/// # Examples
///
/// }
/// }
///
+/// // The prefered method of quick returning Errors
+/// fn write_to_file_question() -> Result<(), MyError> {
+/// let mut file = File::create("my_best_friends.txt")?;
+/// Ok(())
+/// }
+///
+/// // The previous method of quick returning Errors
/// fn write_to_file_using_try() -> Result<(), MyError> {
/// let mut file = try!(File::create("my_best_friends.txt"));
/// try!(file.write_all(b"This is a list of my best friends."));
-/// println!("I wrote to the file");
/// Ok(())
/// }
+///
/// // This is equivalent to:
/// fn write_to_file_using_match() -> Result<(), MyError> {
/// let mut file = try!(File::create("my_best_friends.txt"));
/// Ok(v) => v,
/// Err(e) => return Err(From::from(e)),
/// }
-/// println!("I wrote to the file");
/// Ok(())
/// }
/// ```
/// formatted according to the specified format string and the result will be passed to the writer.
/// The writer may be any value with a `write_fmt` method; generally this comes from an
/// implementation of either the [`std::fmt::Write`] or the [`std::io::Write`] trait. The macro
-/// returns whatever the 'write_fmt' method returns; commonly a [`std::fmt::Result`], or an
+/// returns whatever the `write_fmt` method returns; commonly a [`std::fmt::Result`], or an
/// [`io::Result`].
///
/// See [`std::fmt`] for more information on the format string syntax.
/// * Loops that dynamically terminate.
/// * Iterators that dynamically terminate.
///
+/// If the determination that the code is unreachable proves incorrect, the
+/// program immediately terminates with a [`panic!`]. The function [`unreachable`],
+/// which belongs to the [`std::intrinsics`] module, informs the compilier to
+/// optimize the code out of the release version entirely.
+///
+/// [`panic!`]: ../std/macro.panic.html
+/// [`unreachable`]: ../std/intrinsics/fn.unreachable.html
+/// [`std::intrinsics`]: ../std/intrinsics/index.html
+///
/// # Panics
///
-/// This will always [panic!](macro.panic.html)
+/// This will always [`panic!`]
///
+/// [`panic!`]: ../std/macro.panic.html
/// # Examples
///
/// Match arms:
});
}
-/// A standardized placeholder for marking unfinished code. It panics with the
-/// message `"not yet implemented"` when executed.
+/// A standardized placeholder for marking unfinished code.
+///
+/// It panics with the message `"not yet implemented"` when executed.
///
/// This can be useful if you are prototyping and are just looking to have your
/// code typecheck, or if you're implementing a trait that requires multiple
/// methods, and you're only planning on using one of them.
///
+/// # Panics
+///
+/// This macro always panics.
+///
/// # Examples
///
/// Here's an example of some in-progress code. We have a trait `Foo`:
-Subproject commit 2a5b50b7f7f539a0fd201331d6c1e0534aa332f5
+Subproject commit 04a5e75c99dc92afab490c38fcbbeac9b4bc8104
[dependencies]
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use Span;
+
+use rustc_errors as rustc;
+
+/// An enum representing a diagnostic level.
+#[unstable(feature = "proc_macro", issue = "38356")]
+#[derive(Copy, Clone, Debug)]
+pub enum Level {
+ /// An error.
+ Error,
+ /// A warning.
+ Warning,
+ /// A note.
+ Note,
+ /// A help message.
+ Help,
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+/// A structure representing a diagnostic message and associated children
+/// messages.
+#[unstable(feature = "proc_macro", issue = "38356")]
+#[derive(Clone, Debug)]
+pub struct Diagnostic {
+ level: Level,
+ message: String,
+ span: Option<Span>,
+ children: Vec<Diagnostic>
+}
+
+macro_rules! diagnostic_child_methods {
+ ($spanned:ident, $regular:ident, $level:expr) => (
+ /// Add a new child diagnostic message to `self` with the level
+ /// identified by this methods name with the given `span` and `message`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn $spanned<T: Into<String>>(mut self, span: Span, message: T) -> Diagnostic {
+ self.children.push(Diagnostic::spanned(span, $level, message));
+ self
+ }
+
+ /// Add a new child diagnostic message to `self` with the level
+ /// identified by this method's name with the given `message`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic {
+ self.children.push(Diagnostic::new($level, message));
+ self
+ }
+ )
+}
+
+impl Diagnostic {
+ /// Create a new diagnostic with the given `level` and `message`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
+ Diagnostic {
+ level: level,
+ message: message.into(),
+ span: None,
+ children: vec![]
+ }
+ }
+
+ /// Create a new diagnostic with the given `level` and `message` pointing to
+ /// the given `span`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn spanned<T: Into<String>>(span: Span, level: Level, message: T) -> Diagnostic {
+ Diagnostic {
+ level: level,
+ message: message.into(),
+ span: Some(span),
+ children: vec![]
+ }
+ }
+
+ diagnostic_child_methods!(span_error, error, Level::Error);
+ diagnostic_child_methods!(span_warning, warning, Level::Warning);
+ diagnostic_child_methods!(span_note, note, Level::Note);
+ diagnostic_child_methods!(span_help, help, Level::Help);
+
+ /// Returns the diagnostic `level` for `self`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn level(&self) -> Level {
+ self.level
+ }
+
+ /// Emit the diagnostic.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn emit(self) {
+ ::__internal::with_sess(move |(sess, _)| {
+ let handler = &sess.span_diagnostic;
+ let level = __internal::level_to_internal_level(self.level);
+ let mut diag = rustc::DiagnosticBuilder::new(handler, level, &*self.message);
+
+ if let Some(span) = self.span {
+ diag.set_span(span.0);
+ }
+
+ for child in self.children {
+ let span = child.span.map(|s| s.0);
+ let level = __internal::level_to_internal_level(child.level);
+ diag.sub(level, &*child.message, span);
+ }
+
+ diag.emit();
+ });
+ }
+}
+
+#[unstable(feature = "proc_macro_internals", issue = "27812")]
+#[doc(hidden)]
+pub mod __internal {
+ use super::{Level, rustc};
+
+ pub fn level_to_internal_level(level: Level) -> rustc::Level {
+ match level {
+ Level::Error => rustc::Level::Error,
+ Level::Warning => rustc::Level::Warning,
+ Level::Note => rustc::Level::Note,
+ Level::Help => rustc::Level::Help,
+ Level::__Nonexhaustive => unreachable!("Level::__Nonexhaustive")
+ }
+ }
+}
#[macro_use]
extern crate syntax;
extern crate syntax_pos;
+extern crate rustc_errors;
+
+mod diagnostic;
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+pub use diagnostic::{Diagnostic, Level};
use std::{ascii, fmt, iter};
use std::str::FromStr;
TokenStream(quote::Quote::quote(&span.0))
}
+macro_rules! diagnostic_method {
+ ($name:ident, $level:expr) => (
+ /// Create a new `Diagnostic` with the given `message` at the span
+ /// `self`.
+ #[unstable(feature = "proc_macro", issue = "38356")]
+ pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
+ Diagnostic::spanned(self, $level, message)
+ }
+ )
+}
+
impl Span {
/// The span of the invocation of the current procedural macro.
#[unstable(feature = "proc_macro", issue = "38356")]
pub fn call_site() -> Span {
::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
}
+
+ diagnostic_method!(error, Level::Error);
+ diagnostic_method!(warning, Level::Warning);
+ diagnostic_method!(note, Level::Note);
+ diagnostic_method!(help, Level::Help);
}
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
use hir::def_id::{CrateNum, DefId};
use hir::map::DefPathHash;
+use hir::HirId;
use ich::Fingerprint;
use ty::{TyCtxt, Instance, InstanceDef};
[] HasGlobalAllocator(DefId),
[] ExternCrate(DefId),
[] LintLevels,
+ [] Specializes { impl1: DefId, impl2: DefId },
+ [] InScopeTraits(HirId),
+ [] ModuleExports(HirId),
);
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
// corresponding entry in the `trait_map` we need to hash that.
// Make sure we don't ignore too much by checking that there is
// no entry in a debug_assert!().
- debug_assert!(hcx.tcx.trait_map.get(self).is_none());
+ let hir_id = hcx.tcx.hir.node_to_hir_id(*self);
+ debug_assert!(hcx.tcx.in_scope_traits(hir_id).is_none());
}
NodeIdHashingMode::HashDefPath => {
hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
}
NodeIdHashingMode::HashTraitsInScope => {
- if let Some(traits) = hcx.tcx.trait_map.get(self) {
+ let hir_id = hcx.tcx.hir.node_to_hir_id(*self);
+ if let Some(traits) = hcx.tcx.in_scope_traits(hir_id) {
// The ordering of the candidates is not fixed. So we hash
// the def-ids and then sort them and hash the collection.
let mut candidates: AccumulateVec<[_; 8]> =
pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
-pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs};
+pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
pub use self::specialize::{SpecializesCache, find_associated_item};
pub use self::util::elaborate_predicates;
pub use self::util::supertraits;
*providers = ty::maps::Providers {
is_object_safe: object_safety::is_object_safe_provider,
specialization_graph_of: specialize::specialization_graph_provider,
+ specializes: specialize::specializes,
..*providers
};
}
*providers = ty::maps::Providers {
is_object_safe: object_safety::is_object_safe_provider,
specialization_graph_of: specialize::specialization_graph_provider,
+ specializes: specialize::specializes,
..*providers
};
}
use infer::{InferCtxt, InferOk, TypeFreshener};
use ty::subst::{Kind, Subst, Substs};
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
-use traits;
use ty::fast_reject;
use ty::relate::TypeRelation;
use middle::lang_items;
if other.evaluation == EvaluatedToOk {
if let ImplCandidate(victim_def) = victim.candidate {
let tcx = self.tcx().global_tcx();
- return traits::specializes(tcx, other_def, victim_def) ||
+ return tcx.specializes((other_def, victim_def)) ||
tcx.impls_are_allowed_to_overlap(other_def, victim_def);
}
}
/// Specialization is determined by the sets of types to which the impls apply;
/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies
/// to.
-pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- impl1_def_id: DefId,
- impl2_def_id: DefId) -> bool {
+pub(super) fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ (impl1_def_id, impl2_def_id): (DefId, DefId))
+ -> bool
+{
debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id);
- if let Some(r) = tcx.specializes_cache.borrow().check(impl1_def_id, impl2_def_id) {
- return r;
- }
-
// The feature gate should prevent introducing new specializations, but not
// taking advantage of upstream ones.
if !tcx.sess.features.borrow().specialization &&
let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
// Create a infcx, taking the predicates of impl1 as assumptions:
- let result = tcx.infer_ctxt().enter(|infcx| {
+ tcx.infer_ctxt().enter(|infcx| {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref =
// Attempt to prove that impl2 applies, given all of the above.
fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
- });
-
- tcx.specializes_cache.borrow_mut().insert(impl1_def_id, impl2_def_id, result);
- result
+ })
}
/// Attempt to fulfill all obligations of `target_impl` after unification with
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use super::{OverlapError, specializes};
+use super::OverlapError;
use hir::def_id::DefId;
use traits;
return Ok((false, false));
}
- let le = specializes(tcx, impl_def_id, possible_sibling);
- let ge = specializes(tcx, possible_sibling, impl_def_id);
+ let le = tcx.specializes((impl_def_id, possible_sibling));
+ let ge = tcx.specializes((possible_sibling, impl_def_id));
if le == ge {
// overlap, but no specialization; error out
use errors::DiagnosticBuilder;
use session::Session;
use middle;
-use hir::{TraitMap};
-use hir::def::{Def, ExportMap};
+use hir::{TraitCandidate, HirId};
+use hir::def::{Def, Export};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use hir::map as hir_map;
use hir::map::DefPathHash;
pub sess: &'tcx Session,
- pub specializes_cache: RefCell<traits::SpecializesCache>,
-
pub trans_trait_caches: traits::trans::TransTraitCaches<'tcx>,
pub dep_graph: DepGraph,
/// Map indicating what traits are in scope for places where this
/// is relevant; generated by resolve.
- pub trait_map: TraitMap,
+ trait_map: FxHashMap<HirId, Rc<Vec<TraitCandidate>>>,
/// Export map produced by name resolution.
- pub export_map: ExportMap,
+ export_map: FxHashMap<HirId, Rc<Vec<Export>>>,
pub named_region_map: resolve_lifetime::NamedRegionMap,
tls::enter_global(GlobalCtxt {
sess: s,
trans_trait_caches: traits::trans::TransTraitCaches::new(dep_graph.clone()),
- specializes_cache: RefCell::new(traits::SpecializesCache::new()),
global_arenas: arenas,
global_interners: interners,
dep_graph: dep_graph.clone(),
types: common_types,
named_region_map,
- trait_map: resolutions.trait_map,
- export_map: resolutions.export_map,
+ trait_map: resolutions.trait_map.into_iter().map(|(k, v)| {
+ (hir.node_to_hir_id(k), Rc::new(v))
+ }).collect(),
+ export_map: resolutions.export_map.into_iter().map(|(k, v)| {
+ (hir.node_to_hir_id(k), Rc::new(v))
+ }).collect(),
hir,
def_path_hash_to_def_id,
maps: maps::Maps::new(providers),
Ok(f(&iter.collect::<Result<AccumulateVec<[_; 8]>, _>>()?))
}
}
+
+fn in_scope_traits<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: HirId)
+ -> Option<Rc<Vec<TraitCandidate>>>
+{
+ tcx.gcx.trait_map.get(&id).cloned()
+}
+
+fn module_exports<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: HirId)
+ -> Option<Rc<Vec<Export>>>
+{
+ tcx.gcx.export_map.get(&id).cloned()
+}
+
+pub fn provide(providers: &mut ty::maps::Providers) {
+ providers.in_scope_traits = in_scope_traits;
+ providers.module_exports = module_exports;
+}
use dep_graph::{DepConstructor, DepNode, DepNodeIndex};
use errors::{Diagnostic, DiagnosticBuilder};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use hir::def::Def;
-use hir;
+use hir::def::{Def, Export};
+use hir::{self, TraitCandidate, HirId};
use lint;
use middle::const_val;
use middle::cstore::{ExternCrate, LinkagePreference};
}
}
+impl Key for HirId {
+ fn map_crate(&self) -> CrateNum {
+ LOCAL_CRATE
+ }
+ fn default_span(&self, _tcx: TyCtxt) -> Span {
+ DUMMY_SP
+ }
+}
+
impl Key for DefId {
fn map_crate(&self) -> CrateNum {
self.krate
}
}
+impl<'tcx> QueryDescription for queries::specializes<'tcx> {
+ fn describe(_tcx: TyCtxt, _: (DefId, DefId)) -> String {
+ format!("computing whether impls specialize one another")
+ }
+}
+
+impl<'tcx> QueryDescription for queries::in_scope_traits<'tcx> {
+ fn describe(_tcx: TyCtxt, _: HirId) -> String {
+ format!("fetching the traits in scope at a particular ast node")
+ }
+}
+
+impl<'tcx> QueryDescription for queries::module_exports<'tcx> {
+ fn describe(_tcx: TyCtxt, _: HirId) -> String {
+ format!("fetching the exported items for a module")
+ }
+}
+
// If enabled, send a message to the profile-queries thread
macro_rules! profq_msg {
($tcx:expr, $msg:expr) => {
[] extern_crate: ExternCrate(DefId) -> Rc<Option<ExternCrate>>,
[] lint_levels: lint_levels(CrateNum) -> Rc<lint::LintLevelMap>,
+
+ [] specializes: specializes_node((DefId, DefId)) -> bool,
+ [] in_scope_traits: InScopeTraits(HirId) -> Option<Rc<Vec<TraitCandidate>>>,
+ [] module_exports: ModuleExports(HirId) -> Option<Rc<Vec<Export>>>,
}
fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> {
fn lint_levels<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
DepConstructor::LintLevels
}
+
+fn specializes_node<'tcx>((a, b): (DefId, DefId)) -> DepConstructor<'tcx> {
+ DepConstructor::Specializes { impl1: a, impl2: b }
+}
pub fn provide(providers: &mut ty::maps::Providers) {
util::provide(providers);
+ context::provide(providers);
*providers = ty::maps::Providers {
associated_item,
associated_item_def_ids,
/// Convenience function for internal use, clients should use one of the
/// public methods above.
- fn sub(&mut self,
+ pub(crate) fn sub(&mut self,
level: Level,
message: &str,
span: MultiSpan,
// }
}
+ /// Convenience function for internal use, clients should use one of the
+ /// span_* methods instead.
+ pub fn sub<S: Into<MultiSpan>>(
+ &mut self,
+ level: Level,
+ message: &str,
+ span: Option<S>,
+ ) -> &mut Self {
+ let span = span.map(|s| s.into()).unwrap_or(MultiSpan::new());
+ self.diagnostic.sub(level, message, span, None);
+ self
+ }
+
/// Delay emission of this diagnostic as a bug.
///
/// This can be useful in contexts where an error indicates a bug but
&hir::Visibility)>)
-> Entry<'tcx> {
let tcx = self.tcx;
+ let hir_id = tcx.hir.node_to_hir_id(id);
let def_id = tcx.hir.local_def_id(id);
debug!("IsolatedEncoder::encode_info_for_mod({:?})", def_id);
let data = ModData {
- reexports: match tcx.export_map.get(&id) {
- Some(exports) if *vis == hir::Public => {
+ reexports: match tcx.module_exports(hir_id) {
+ Some(ref exports) if *vis == hir::Public => {
self.lazy_seq_from_slice(exports.as_slice())
}
_ => LazySeq::empty(),
// This code is here instead of in visit_item so that the
// crate module gets processed as well.
if self.prev_level.is_some() {
- if let Some(exports) = self.tcx.export_map.get(&id) {
- for export in exports {
+ let hir_id = self.tcx.hir.node_to_hir_id(id);
+ if let Some(exports) = self.tcx.module_exports(hir_id) {
+ for export in exports.iter() {
if let Some(node_id) = self.tcx.hir.as_local_node_id(export.def.def_id()) {
self.update(node_id, Some(AccessLevel::Exported));
}
let ast::Path { ref segments, span } = *path;
let path: Vec<_> = segments.iter().map(|seg| respan(seg.span, seg.identifier)).collect();
let invocation = self.invocations[&scope];
- self.current_module = invocation.module.get();
+ let module = invocation.module.get();
+ self.current_module = if module.is_trait() { module.parent.unwrap() } else { module };
if path.len() > 1 {
if !self.use_extern_macros && self.gated_errors.insert(span) {
fn assemble_extension_candidates_for_traits_in_scope(&mut self,
expr_id: ast::NodeId)
-> Result<(), MethodError<'tcx>> {
+ if expr_id == ast::DUMMY_NODE_ID {
+ return Ok(())
+ }
let mut duplicates = FxHashSet();
- let opt_applicable_traits = self.tcx.trait_map.get(&expr_id);
+ let expr_hir_id = self.tcx.hir.node_to_hir_id(expr_id);
+ let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id);
if let Some(applicable_traits) = opt_applicable_traits {
- for trait_candidate in applicable_traits {
+ for trait_candidate in applicable_traits.iter() {
let trait_did = trait_candidate.def_id;
if duplicates.insert(trait_did) {
let import_id = trait_candidate.import_id;
self.visit_item(item, None, &mut om);
}
self.inside_public_path = orig_inside_public_path;
- if let Some(exports) = self.cx.tcx.export_map.get(&id) {
- for export in exports {
+ let hir_id = self.cx.tcx.hir.node_to_hir_id(id);
+ if let Some(exports) = self.cx.tcx.module_exports(hir_id) {
+ for export in exports.iter() {
if let Def::Macro(def_id, ..) = export.def {
if def_id.krate == LOCAL_CRATE || self.reexported_macros.contains(&def_id) {
continue // These are `krate.exported_macros`, handled in `self.visit()`.
/// The entry point for panic of Rust threads.
///
+/// This allows a program to to terminate immediately and provide feedback
+/// to the caller of the program. `panic!` should be used when a program reaches
+/// an unrecoverable problem.
+///
+/// This macro is the perfect way to assert conditions in example code and in
+/// tests. `panic!` is closely tied with the `unwrap` method of both [`Option`]
+/// and [`Result`][runwrap] enums. Both implementations call `panic!` when they are set
+/// to None or Err variants.
+///
/// This macro is used to inject panic into a Rust thread, causing the thread to
/// panic entirely. Each thread's panic can be reaped as the `Box<Any>` type,
/// and the single-argument form of the `panic!` macro will be the value which
/// is transmitted.
///
+/// [`Result`] enum is often a better solution for recovering from errors than
+/// using the `panic!` macro. This macro should be used to avoid proceeding using
+/// incorrect values, such as from external sources. Detailed information about
+/// error handling is found in the [book].
+///
/// The multi-argument form of this macro panics with a string and has the
-/// `format!` syntax for building a string.
+/// [`format!`] syntax for building a string.
+///
+/// [runwrap]: ../std/result/enum.Result.html#method.unwrap
+/// [`Option`]: ../std/option/enum.Option.html#method.unwrap
+/// [`Result`]: ../std/result/enum.Result.html
+/// [`format!`]: ../std/macro.format.html
+/// [book]: ../book/second-edition/ch09-01-unrecoverable-errors-with-panic.html
///
/// # Current implementation
///
/// Macro for printing to the standard output.
///
-/// Equivalent to the `println!` macro except that a newline is not printed at
+/// Equivalent to the [`println!`] macro except that a newline is not printed at
/// the end of the message.
///
/// Note that stdout is frequently line-buffered by default so it may be
-/// necessary to use `io::stdout().flush()` to ensure the output is emitted
+/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
/// immediately.
///
/// Use `print!` only for the primary output of your program. Use
-/// `eprint!` instead to print error and progress messages.
+/// [`eprint!`] instead to print error and progress messages.
+///
+/// [`println!`]: ../std/macro.println.html
+/// [flush]: ../std/io/trait.Write.html#tymethod.flush
+/// [`eprint!`]: ../std/macro.eprint.html
///
/// # Panics
///
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}
-/// Macro for printing to the standard output, with a newline. On all
-/// platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
+/// Macro for printing to the standard output, with a newline.
+///
+/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
/// (no additional CARRIAGE RETURN (`\r`/`U+000D`).
///
-/// Use the `format!` syntax to write data to the standard output.
-/// See `std::fmt` for more information.
+/// Use the [`format!`] syntax to write data to the standard output.
+/// See [`std::fmt`] for more information.
///
/// Use `println!` only for the primary output of your program. Use
-/// `eprintln!` instead to print error and progress messages.
+/// [`eprintln!`] instead to print error and progress messages.
///
+/// [`format!`]: ../std/macro.format.html
+/// [`std::fmt`]: ../std/fmt/index.html
+/// [`eprintln!`]: ../std/macro.eprint.html
/// # Panics
///
/// Panics if writing to `io::stdout` fails.
/// Macro for printing to the standard error.
///
-/// Equivalent to the `print!` macro, except that output goes to
-/// `io::stderr` instead of `io::stdout`. See `print!` for
+/// Equivalent to the [`print!`] macro, except that output goes to
+/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for
/// example usage.
///
/// Use `eprint!` only for error and progress messages. Use `print!`
/// instead for the primary output of your program.
///
+/// [`io::stderr`]: ../std/io/struct.Stderr.html
+/// [`print!`]: ../std/macro.print.html
+///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
+///
+/// # Examples
+///
+/// ```
+/// eprint!("Error: Could not complete task");
+/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable]
/// Macro for printing to the standard error, with a newline.
///
-/// Equivalent to the `println!` macro, except that output goes to
-/// `io::stderr` instead of `io::stdout`. See `println!` for
+/// Equivalent to the [`println!`] macro, except that output goes to
+/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for
/// example usage.
///
/// Use `eprintln!` only for error and progress messages. Use `println!`
/// instead for the primary output of your program.
///
+/// [`io::stderr`]: ../std/io/struct.Stderr.html
+/// [`println!`]: ../std/macro.println.html
+///
/// # Panics
///
/// Panics if writing to `io::stderr` fails.
+///
+/// # Examples
+///
+/// ```
+/// eprintln!("Error: Could not complete task");
+/// ```
#[macro_export]
#[stable(feature = "eprint", since = "1.19.0")]
macro_rules! eprintln {
/// The core macro for formatted string creation & output.
///
+ /// This macro functions by taking a formatting string literal containing
+ /// `{}` for each additional argument passed. `format_args!` prepares the
+ /// additional parameters to ensure the output can be interpreted as a string
+ /// and canonicalizes the arguments into a single type. Any value that implements
+ /// the [`Display`] trait can be passed to `format_args!`, as can any
+ /// [`Debug`] implementation be passed to a `{:?}` within the formatting string.
+ ///
/// This macro produces a value of type [`fmt::Arguments`]. This value can be
- /// passed to the functions in [`std::fmt`] for performing useful functions.
+ /// passed to the macros within [`std::fmt`] for performing useful redirection.
/// All other formatting macros ([`format!`], [`write!`], [`println!`], etc) are
- /// proxied through this one.
+ /// proxied through this one. `format_args!`, unlike its derived macros, avoids
+ /// heap allocations.
///
/// For more information, see the documentation in [`std::fmt`].
///
+ /// [`Display`]: ../std/fmt/trait.Display.html
+ /// [`Debug`]: ../std/fmt/trait.Debug.html
/// [`fmt::Arguments`]: ../std/fmt/struct.Arguments.html
/// [`std::fmt`]: ../std/fmt/index.html
/// [`format!`]: ../std/macro.format.html
/// compile time, yielding an expression of type `&'static str`.
///
/// If the environment variable is not defined, then a compilation error
- /// will be emitted. To not emit a compile error, use the `option_env!`
+ /// will be emitted. To not emit a compile error, use the [`option_env!`]
/// macro instead.
///
+ /// [`option_env!`]: ../std/macro.option_env.html
+ ///
/// # Examples
///
/// ```
/// If the named environment variable is present at compile time, this will
/// expand into an expression of type `Option<&'static str>` whose value is
/// `Some` of the value of the environment variable. If the environment
- /// variable is not present, then this will expand to `None`.
+ /// variable is not present, then this will expand to `None`. See
+ /// [`Option<T>`][option] for more information on this type.
///
/// A compile time error is never emitted when using this macro regardless
/// of whether the environment variable is present or not.
///
+ /// [option]: ../std/option/enum.Option.html
+ ///
/// # Examples
///
/// ```
/// A macro which expands to the line number on which it was invoked.
///
+ /// With [`column!`] and [`file!`], these macros provide debugging information for
+ /// developers about the location within the source.
+ ///
/// The expanded expression has type `u32`, and the returned line is not
/// the invocation of the `line!()` macro itself, but rather the first macro
/// invocation leading up to the invocation of the `line!()` macro.
///
+ /// [`column!`]: macro.column.html
+ /// [`file!`]: macro.file.html
+ ///
/// # Examples
///
/// ```
/// A macro which expands to the column number on which it was invoked.
///
+ /// With [`line!`] and [`file!`], these macros provide debugging information for
+ /// developers about the location within the source.
+ ///
/// The expanded expression has type `u32`, and the returned column is not
- /// the invocation of the `column!()` macro itself, but rather the first macro
- /// invocation leading up to the invocation of the `column!()` macro.
+ /// the invocation of the `column!` macro itself, but rather the first macro
+ /// invocation leading up to the invocation of the `column!` macro.
+ ///
+ /// [`line!`]: macro.line.html
+ /// [`file!`]: macro.file.html
///
/// # Examples
///
/// A macro which expands to the file name from which it was invoked.
///
+ /// With [`line!`] and [`column!`], these macros provide debugging information for
+ /// developers about the location within the source.
+ ///
+ ///
/// The expanded expression has type `&'static str`, and the returned file
- /// is not the invocation of the `file!()` macro itself, but rather the
- /// first macro invocation leading up to the invocation of the `file!()`
+ /// is not the invocation of the `file!` macro itself, but rather the
+ /// first macro invocation leading up to the invocation of the `file!`
/// macro.
///
+ /// [`line!`]: macro.line.html
+ /// [`column!`]: macro.column.html
+ ///
/// # Examples
///
/// ```
use fmt;
-#[cfg(any(target_os = "emscripten",
- all(target_os = "linux", any(target_arch = "aarch64",
+#[cfg(any(all(target_os = "linux", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "arm")),
all(target_os = "fuchsia", target_arch = "aarch64")))]
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8;
-#[cfg(not(any(target_os = "emscripten",
- all(target_os = "linux", any(target_arch = "aarch64",
+#[cfg(not(any(all(target_os = "linux", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
#[cfg(target_os = "android")]
use super::android::cvt_pread64;
- #[cfg(not(target_os = "android"))]
+ #[cfg(target_os = "emscripten")]
unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
-> io::Result<isize>
{
- #[cfg(any(target_os = "linux", target_os = "emscripten"))]
use libc::pread64;
- #[cfg(not(any(target_os = "linux", target_os = "emscripten")))]
+ cvt(pread64(fd, buf, count, offset as i32))
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pread64;
+ #[cfg(not(target_os = "linux"))]
use libc::pread as pread64;
cvt(pread64(fd, buf, count, offset))
}
#[cfg(target_os = "android")]
use super::android::cvt_pwrite64;
- #[cfg(not(target_os = "android"))]
+ #[cfg(target_os = "emscripten")]
+ unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ use libc::pwrite64;
+ cvt(pwrite64(fd, buf, count, offset as i32))
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
-> io::Result<isize>
{
- #[cfg(any(target_os = "linux", target_os = "emscripten"))]
+ #[cfg(target_os = "linux")]
use libc::pwrite64;
- #[cfg(not(any(target_os = "linux", target_os = "emscripten")))]
+ #[cfg(not(target_os = "linux"))]
use libc::pwrite as pwrite64;
cvt(pwrite64(fd, buf, count, offset))
}
SeekFrom::End(off) => (libc::SEEK_END, off),
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
};
+ #[cfg(target_os = "emscripten")]
+ let pos = pos as i32;
let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
Ok(n as u64)
}
use io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
-use mem;
use ptr;
use sys::cvt;
}
// NaCl has no signal support.
- if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) {
+ #[cfg(not(any(target_os = "nacl", target_os = "emscripten")))]
+ {
+ use mem;
// Reset signal handling so the child process starts in a
// standardized state. libstd ignores SIGPIPE, and signal-handling
// libraries often set a mask. Child processes inherit ignored
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:attr-on-trait.rs
+
+#![feature(proc_macro)]
+
+extern crate attr_on_trait;
+
+trait Foo {
+ #[attr_on_trait::foo]
+ fn foo() {}
+}
+
+impl Foo for i32 {
+ fn foo(&self) {}
+}
+
+fn main() {
+ 3i32.foo();
+}
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(proc_macro)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_attribute]
+pub fn foo(attr: TokenStream, item: TokenStream) -> TokenStream {
+ drop(attr);
+ assert_eq!(item.to_string(), "fn foo() { }");
+ "fn foo(&self);".parse().unwrap()
+}
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+#![feature(proc_macro)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenNode, Span, Diagnostic};
+
+fn parse(input: TokenStream) -> Result<(), Diagnostic> {
+ let mut count = 0;
+ let mut last_span = Span::default();
+ for tree in input {
+ let span = tree.span;
+ if count >= 3 {
+ return Err(span.error(format!("expected EOF, found `{}`.", tree))
+ .span_note(last_span, "last good input was here")
+ .help("input must be: `===`"))
+ }
+
+ if let TokenNode::Op('=', _) = tree.kind {
+ count += 1;
+ } else {
+ return Err(span.error(format!("expected `=`, found `{}`.", tree)));
+ }
+
+ last_span = span;
+ }
+
+ if count < 3 {
+ return Err(Span::default()
+ .error(format!("found {} equal signs, need exactly 3", count))
+ .help("input must be: `===`"))
+ }
+
+ Ok(())
+}
+
+#[proc_macro]
+pub fn three_equals(input: TokenStream) -> TokenStream {
+ if let Err(diag) = parse(input) {
+ diag.emit();
+ return TokenStream::empty();
+ }
+
+ "3".parse().unwrap()
+}
--- /dev/null
+// Copyright 2016 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:three-equals.rs
+// ignore-stage1
+
+#![feature(proc_macro)]
+
+extern crate three_equals;
+
+use three_equals::three_equals;
+
+fn main() {
+ // This one is okay.
+ three_equals!(===);
+
+ // Need exactly three equals.
+ three_equals!(==);
+
+ // Need exactly three equals.
+ three_equals!(=====);
+
+ // Only equals accepted.
+ three_equals!(abc);
+
+ // Only equals accepted.
+ three_equals!(!!);
+
+ // Only three characters expected.
+ three_equals!(===a);
+}
--- /dev/null
+error: found 2 equal signs, need exactly 3
+ --> $DIR/three-equals.rs:25:5
+ |
+25 | three_equals!(==);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: input must be: `===`
+
+error: expected EOF, found `=`.
+ --> $DIR/three-equals.rs:28:21
+ |
+28 | three_equals!(=====);
+ | ^^
+ |
+note: last good input was here
+ --> $DIR/three-equals.rs:28:21
+ |
+28 | three_equals!(=====);
+ | ^^
+ = help: input must be: `===`
+
+error: expected `=`, found `abc`.
+ --> $DIR/three-equals.rs:31:19
+ |
+31 | three_equals!(abc);
+ | ^^^
+
+error: expected `=`, found `!`.
+ --> $DIR/three-equals.rs:34:19
+ |
+34 | three_equals!(!!);
+ | ^
+
+error: expected EOF, found `a`.
+ --> $DIR/three-equals.rs:37:22
+ |
+37 | three_equals!(===a);
+ | ^
+ |
+note: last good input was here
+ --> $DIR/three-equals.rs:37:21
+ |
+37 | three_equals!(===a);
+ | ^
+ = help: input must be: `===`
+
+error: aborting due to 5 previous errors
+