Auto merge of #64751 - Centril:rollup-hpbmcfj, r=Centril
Rollup of 16 pull requests Successful merges: - #63356 (Issue#63183: Add fs::read_dir() and ReadDir warning about iterator order + example) - #63934 (Fix coherence checking for impl trait in type aliases) - #64016 (Streamline `Compiler`) - #64296 (Document the unstable iter_order_by library feature) - #64443 (rustdoc: general cleanup) - #64622 (Add a cycle detector for generic `Graph`s and `mir::Body`s) - #64689 (Refactor macro by example) - #64698 (Recover on `const X = 42;` and infer type + Error Stash API) - #64702 (Remove unused dependencies) - #64717 (update mem::discriminant test to use assert_eq and assert_ne over comparison operators) - #64720 ( remove rtp.rs, and move rtpSpawn and RTP_ID_ERROR to libc) - #64721 (Fixed issue from #64447) - #64725 (fix one typo) - #64737 (fix several issues in String docs) - #64742 (relnotes: make compatibility section more sterile and fix rustc version) - #64748 (Fix #64744. Account for the Zero sub-pattern case.) Failed merges: r? @ghost
This commit is contained in:
commit
dcd473d7b5
13
Cargo.lock
13
Cargo.lock
@ -3324,8 +3324,6 @@ dependencies = [
|
||||
"log",
|
||||
"rustc",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"syntax",
|
||||
"syntax_pos",
|
||||
]
|
||||
|
||||
@ -3347,7 +3345,6 @@ dependencies = [
|
||||
"log",
|
||||
"memmap",
|
||||
"num_cpus",
|
||||
"parking_lot 0.9.0",
|
||||
"rustc",
|
||||
"rustc_apfloat",
|
||||
"rustc_codegen_utils",
|
||||
@ -3366,7 +3363,6 @@ dependencies = [
|
||||
name = "rustc_codegen_utils"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"log",
|
||||
"punycode",
|
||||
"rustc",
|
||||
@ -3561,7 +3557,6 @@ name = "rustc_mir"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"arena",
|
||||
"byteorder",
|
||||
"either",
|
||||
"graphviz",
|
||||
"log",
|
||||
@ -3614,7 +3609,6 @@ name = "rustc_plugin_impl"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc",
|
||||
"rustc_errors",
|
||||
"rustc_metadata",
|
||||
"syntax",
|
||||
"syntax_pos",
|
||||
@ -3638,7 +3632,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"arena",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"rustc",
|
||||
"rustc_data_structures",
|
||||
@ -3660,7 +3653,6 @@ dependencies = [
|
||||
"rustc_codegen_utils",
|
||||
"rustc_data_structures",
|
||||
"rustc_target",
|
||||
"rustc_typeck",
|
||||
"serde_json",
|
||||
"syntax",
|
||||
"syntax_pos",
|
||||
@ -3691,9 +3683,7 @@ checksum = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee"
|
||||
name = "rustc_traits"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"chalk-engine",
|
||||
"graphviz",
|
||||
"log",
|
||||
"rustc",
|
||||
"rustc_data_structures",
|
||||
@ -4056,7 +4046,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"alloc",
|
||||
"backtrace",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"compiler_builtins",
|
||||
"core",
|
||||
@ -4241,7 +4230,6 @@ dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_target",
|
||||
"scoped-tls",
|
||||
"serialize",
|
||||
@ -4257,7 +4245,6 @@ dependencies = [
|
||||
"log",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_lexer",
|
||||
"rustc_target",
|
||||
"smallvec",
|
||||
"syntax",
|
||||
|
@ -70,10 +70,10 @@ Misc
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- Unfortunately the [`x86_64-unknown-uefi` platform can not be built][62785]
|
||||
with rustc 1.39.0.
|
||||
- The [`armv7-unknown-linux-gnueabihf` platform is also known to have
|
||||
issues][62896] for certain crates such as libc.
|
||||
- The [`x86_64-unknown-uefi` platform can not be built][62785] with rustc
|
||||
1.38.0.
|
||||
- The [`armv7-unknown-linux-gnueabihf` platform is known to have
|
||||
issues][62896] with certain crates such as libc.
|
||||
|
||||
[60260]: https://github.com/rust-lang/rust/pull/60260/
|
||||
[61457]: https://github.com/rust-lang/rust/pull/61457/
|
||||
|
@ -429,7 +429,7 @@ impl String {
|
||||
|
||||
/// Converts a vector of bytes to a `String`.
|
||||
///
|
||||
/// A string slice ([`&str`]) is made of bytes ([`u8`]), and a vector of bytes
|
||||
/// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes
|
||||
/// ([`Vec<u8>`]) is made of bytes, so this function converts between the
|
||||
/// two. Not all byte slices are valid `String`s, however: `String`
|
||||
/// requires that it is valid UTF-8. `from_utf8()` checks to ensure that
|
||||
@ -446,7 +446,7 @@ impl String {
|
||||
/// If you need a [`&str`] instead of a `String`, consider
|
||||
/// [`str::from_utf8`].
|
||||
///
|
||||
/// The inverse of this method is [`as_bytes`].
|
||||
/// The inverse of this method is [`into_bytes`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@ -480,11 +480,11 @@ impl String {
|
||||
/// with this error.
|
||||
///
|
||||
/// [`from_utf8_unchecked`]: struct.String.html#method.from_utf8_unchecked
|
||||
/// [`&str`]: ../../std/primitive.str.html
|
||||
/// [`String`]: struct.String.html
|
||||
/// [`u8`]: ../../std/primitive.u8.html
|
||||
/// [`Vec<u8>`]: ../../std/vec/struct.Vec.html
|
||||
/// [`str::from_utf8`]: ../../std/str/fn.from_utf8.html
|
||||
/// [`as_bytes`]: struct.String.html#method.as_bytes
|
||||
/// [`into_bytes`]: struct.String.html#method.into_bytes
|
||||
/// [`FromUtf8Error`]: struct.FromUtf8Error.html
|
||||
/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
|
||||
#[inline]
|
||||
|
@ -2581,7 +2581,7 @@ pub trait Iterator {
|
||||
/// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (x * x).cmp(&y)), Ordering::Equal);
|
||||
/// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater);
|
||||
/// ```
|
||||
#[unstable(feature = "iter_order_by", issue = "0")]
|
||||
#[unstable(feature = "iter_order_by", issue = "64295")]
|
||||
fn cmp_by<I, F>(mut self, other: I, mut cmp: F) -> Ordering
|
||||
where
|
||||
Self: Sized,
|
||||
@ -2664,7 +2664,7 @@ pub trait Iterator {
|
||||
/// Some(Ordering::Greater)
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "iter_order_by", issue = "0")]
|
||||
#[unstable(feature = "iter_order_by", issue = "64295")]
|
||||
fn partial_cmp_by<I, F>(mut self, other: I, mut partial_cmp: F) -> Option<Ordering>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -2729,7 +2729,7 @@ pub trait Iterator {
|
||||
///
|
||||
/// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y));
|
||||
/// ```
|
||||
#[unstable(feature = "iter_order_by", issue = "0")]
|
||||
#[unstable(feature = "iter_order_by", issue = "64295")]
|
||||
fn eq_by<I, F>(mut self, other: I, mut eq: F) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
|
@ -818,9 +818,9 @@ impl<T> fmt::Debug for Discriminant<T> {
|
||||
///
|
||||
/// enum Foo { A(&'static str), B(i32), C(i32) }
|
||||
///
|
||||
/// assert!(mem::discriminant(&Foo::A("bar")) == mem::discriminant(&Foo::A("baz")));
|
||||
/// assert!(mem::discriminant(&Foo::B(1)) == mem::discriminant(&Foo::B(2)));
|
||||
/// assert!(mem::discriminant(&Foo::B(3)) != mem::discriminant(&Foo::C(3)));
|
||||
/// assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz")));
|
||||
/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
|
||||
/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));
|
||||
/// ```
|
||||
#[stable(feature = "discriminant_value", since = "1.21.0")]
|
||||
pub fn discriminant<T>(v: &T) -> Discriminant<T> {
|
||||
|
@ -262,6 +262,12 @@ impl<'tcx> Body<'tcx> {
|
||||
dominators(self)
|
||||
}
|
||||
|
||||
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
|
||||
/// `START_BLOCK`.
|
||||
pub fn is_cfg_cyclic(&self) -> bool {
|
||||
graph::is_cyclic(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn local_kind(&self, local: Local) -> LocalKind {
|
||||
let index = local.as_usize();
|
||||
|
@ -321,6 +321,7 @@ impl Session {
|
||||
}
|
||||
pub fn compile_status(&self) -> Result<(), ErrorReported> {
|
||||
if self.has_errors() {
|
||||
self.diagnostic().emit_stashed_diagnostics();
|
||||
Err(ErrorReported)
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -432,7 +432,7 @@ fn orphan_check_trait_ref<'tcx>(
|
||||
}
|
||||
|
||||
fn uncovered_tys<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, in_crate: InCrate) -> Vec<Ty<'tcx>> {
|
||||
if ty_is_local_constructor(ty, in_crate) {
|
||||
if ty_is_local_constructor(tcx, ty, in_crate) {
|
||||
vec![]
|
||||
} else if fundamental_ty(ty) {
|
||||
ty.walk_shallow()
|
||||
@ -451,7 +451,7 @@ fn is_possibly_remote_type(ty: Ty<'_>, _in_crate: InCrate) -> bool {
|
||||
}
|
||||
|
||||
fn ty_is_local(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
|
||||
ty_is_local_constructor(ty, in_crate) ||
|
||||
ty_is_local_constructor(tcx, ty, in_crate) ||
|
||||
fundamental_ty(ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate))
|
||||
}
|
||||
|
||||
@ -472,7 +472,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
|
||||
fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
|
||||
debug!("ty_is_local_constructor({:?})", ty);
|
||||
|
||||
match ty.sty {
|
||||
@ -504,6 +504,15 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
|
||||
|
||||
ty::Adt(def, _) => def_id_is_local(def.did, in_crate),
|
||||
ty::Foreign(did) => def_id_is_local(did, in_crate),
|
||||
ty::Opaque(did, _) => {
|
||||
// Check the underlying type that this opaque
|
||||
// type resolves to.
|
||||
// This recursion will eventually terminate,
|
||||
// since we've already managed to successfully
|
||||
// resolve all opaque types by this point
|
||||
let real_ty = tcx.type_of(did);
|
||||
ty_is_local_constructor(tcx, real_ty, in_crate)
|
||||
}
|
||||
|
||||
ty::Dynamic(ref tt, ..) => {
|
||||
if let Some(principal) = tt.principal() {
|
||||
@ -518,8 +527,7 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
|
||||
ty::UnnormalizedProjection(..) |
|
||||
ty::Closure(..) |
|
||||
ty::Generator(..) |
|
||||
ty::GeneratorWitness(..) |
|
||||
ty::Opaque(..) => {
|
||||
ty::GeneratorWitness(..) => {
|
||||
bug!("ty_is_local invoked on unexpected type: {:?}", ty)
|
||||
}
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ impl<'tcx> Graph {
|
||||
/// The parent of a given impl, which is the `DefId` of the trait when the
|
||||
/// impl is a "specialization root".
|
||||
pub fn parent(&self, child: DefId) -> DefId {
|
||||
*self.parent.get(&child).unwrap()
|
||||
*self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,9 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
# for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
|
||||
# refers to the borrowck-specific graphviz adapter traits.
|
||||
dot = { path = "../libgraphviz", package = "graphviz" }
|
||||
rustc = { path = "../librustc" }
|
||||
errors = { path = "../librustc_errors", package = "rustc_errors" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
|
@ -17,7 +17,6 @@ memmap = "0.6"
|
||||
log = "0.4.5"
|
||||
libc = "0.2.44"
|
||||
jobserver = "0.1.11"
|
||||
parking_lot = "0.9"
|
||||
tempfile = "3.1"
|
||||
|
||||
rustc_serialize = { path = "../libserialize", package = "serialize" }
|
||||
|
@ -10,7 +10,6 @@ path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
flate2 = "1.0"
|
||||
log = "0.4"
|
||||
punycode = "0.4.0"
|
||||
rustc-demangle = "0.1.16"
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::super::indexed_vec::IndexVec;
|
||||
use super::{DirectedGraph, WithNumNodes, WithSuccessors};
|
||||
use super::{DirectedGraph, WithNumNodes, WithSuccessors, WithStartNode};
|
||||
use crate::bit_set::BitSet;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -85,3 +85,205 @@ where
|
||||
Some(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows searches to terminate early with a value.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ControlFlow<T> {
|
||||
Break(T),
|
||||
Continue,
|
||||
}
|
||||
|
||||
/// The status of a node in the depth-first search.
|
||||
///
|
||||
/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated
|
||||
/// during DFS.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum NodeStatus {
|
||||
/// This node has been examined by the depth-first search but is not yet `Settled`.
|
||||
///
|
||||
/// Also referred to as "gray" or "discovered" nodes in [CLR][].
|
||||
///
|
||||
/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
|
||||
Visited,
|
||||
|
||||
/// This node and all nodes reachable from it have been examined by the depth-first search.
|
||||
///
|
||||
/// Also referred to as "black" or "finished" nodes in [CLR][].
|
||||
///
|
||||
/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
|
||||
Settled,
|
||||
}
|
||||
|
||||
struct Event<N> {
|
||||
node: N,
|
||||
becomes: NodeStatus,
|
||||
}
|
||||
|
||||
/// A depth-first search that also tracks when all successors of a node have been examined.
|
||||
///
|
||||
/// This is based on the DFS described in [Introduction to Algorithms (1st ed.)][CLR], hereby
|
||||
/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`][] above instead of
|
||||
/// "discovered"/"finished" or "white"/"grey"/"black". Each node begins the search with no status,
|
||||
/// becomes `Visited` when it is first examined by the DFS and is `Settled` when all nodes
|
||||
/// reachable from it have been examined. This allows us to differentiate between "tree", "back"
|
||||
/// and "forward" edges (see [`TriColorVisitor::node_examined`]).
|
||||
///
|
||||
/// Unlike the pseudocode in [CLR][], this implementation is iterative and does not use timestamps.
|
||||
/// We accomplish this by storing `Event`s on the stack that result in a (possible) state change
|
||||
/// for each node. A `Visited` event signifies that we should examine this node if it has not yet
|
||||
/// been `Visited` or `Settled`. When a node is examined for the first time, we mark it as
|
||||
/// `Visited` and push a `Settled` event for it on stack followed by `Visited` events for all of
|
||||
/// its predecessors, scheduling them for examination. Multiple `Visited` events for a single node
|
||||
/// may exist on the stack simultaneously if a node has multiple predecessors, but only one
|
||||
/// `Settled` event will ever be created for each node. After all `Visited` events for a node's
|
||||
/// successors have been popped off the stack (as well as any new events triggered by visiting
|
||||
/// those successors), we will pop off that node's `Settled` event.
|
||||
///
|
||||
/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
|
||||
/// [`NodeStatus`]: ./enum.NodeStatus.html
|
||||
/// [`TriColorVisitor::node_examined`]: ./trait.TriColorVisitor.html#method.node_examined
|
||||
pub struct TriColorDepthFirstSearch<'graph, G>
|
||||
where
|
||||
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
|
||||
{
|
||||
graph: &'graph G,
|
||||
stack: Vec<Event<G::Node>>,
|
||||
visited: BitSet<G::Node>,
|
||||
settled: BitSet<G::Node>,
|
||||
}
|
||||
|
||||
impl<G> TriColorDepthFirstSearch<'graph, G>
|
||||
where
|
||||
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
|
||||
{
|
||||
pub fn new(graph: &'graph G) -> Self {
|
||||
TriColorDepthFirstSearch {
|
||||
graph,
|
||||
stack: vec![],
|
||||
visited: BitSet::new_empty(graph.num_nodes()),
|
||||
settled: BitSet::new_empty(graph.num_nodes()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a depth-first search, starting from the given `root`.
|
||||
///
|
||||
/// This won't visit nodes that are not reachable from `root`.
|
||||
pub fn run_from<V>(mut self, root: G::Node, visitor: &mut V) -> Option<V::BreakVal>
|
||||
where
|
||||
V: TriColorVisitor<G>,
|
||||
{
|
||||
use NodeStatus::{Visited, Settled};
|
||||
|
||||
self.stack.push(Event { node: root, becomes: Visited });
|
||||
|
||||
loop {
|
||||
match self.stack.pop()? {
|
||||
Event { node, becomes: Settled } => {
|
||||
let not_previously_settled = self.settled.insert(node);
|
||||
assert!(not_previously_settled, "A node should be settled exactly once");
|
||||
if let ControlFlow::Break(val) = visitor.node_settled(node) {
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
Event { node, becomes: Visited } => {
|
||||
let not_previously_visited = self.visited.insert(node);
|
||||
let prior_status = if not_previously_visited {
|
||||
None
|
||||
} else if self.settled.contains(node) {
|
||||
Some(Settled)
|
||||
} else {
|
||||
Some(Visited)
|
||||
};
|
||||
|
||||
if let ControlFlow::Break(val) = visitor.node_examined(node, prior_status) {
|
||||
return Some(val);
|
||||
}
|
||||
|
||||
// If this node has already been examined, we are done.
|
||||
if prior_status.is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, push a `Settled` event for this node onto the stack, then
|
||||
// schedule its successors for examination.
|
||||
self.stack.push(Event { node, becomes: Settled });
|
||||
for succ in self.graph.successors(node) {
|
||||
self.stack.push(Event { node: succ, becomes: Visited });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> TriColorDepthFirstSearch<'graph, G>
|
||||
where
|
||||
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode,
|
||||
{
|
||||
/// Performs a depth-first search, starting from `G::start_node()`.
|
||||
///
|
||||
/// This won't visit nodes that are not reachable from the start node.
|
||||
pub fn run_from_start<V>(self, visitor: &mut V) -> Option<V::BreakVal>
|
||||
where
|
||||
V: TriColorVisitor<G>,
|
||||
{
|
||||
let root = self.graph.start_node();
|
||||
self.run_from(root, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// What to do when a node is examined or becomes `Settled` during DFS.
|
||||
pub trait TriColorVisitor<G>
|
||||
where
|
||||
G: ?Sized + DirectedGraph,
|
||||
{
|
||||
/// The value returned by this search.
|
||||
type BreakVal;
|
||||
|
||||
/// Called when a node is examined by the depth-first search.
|
||||
///
|
||||
/// By checking the value of `prior_status`, this visitor can determine whether the edge
|
||||
/// leading to this node was a tree edge (`None`), forward edge (`Some(Settled)`) or back edge
|
||||
/// (`Some(Visited)`). For a full explanation of each edge type, see the "Depth-first Search"
|
||||
/// chapter in [CLR][] or [wikipedia][].
|
||||
///
|
||||
/// If you want to know *both* nodes linked by each edge, you'll need to modify
|
||||
/// `TriColorDepthFirstSearch` to store a `source` node for each `Visited` event.
|
||||
///
|
||||
/// [wikipedia]: https://en.wikipedia.org/wiki/Depth-first_search#Output_of_a_depth-first_search
|
||||
/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
|
||||
fn node_examined(
|
||||
&mut self,
|
||||
_target: G::Node,
|
||||
_prior_status: Option<NodeStatus>,
|
||||
) -> ControlFlow<Self::BreakVal> {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
|
||||
/// Called after all nodes reachable from this one have been examined.
|
||||
fn node_settled(&mut self, _target: G::Node) -> ControlFlow<Self::BreakVal> {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
}
|
||||
|
||||
/// This `TriColorVisitor` looks for back edges in a graph, which indicate that a cycle exists.
|
||||
pub struct CycleDetector;
|
||||
|
||||
impl<G> TriColorVisitor<G> for CycleDetector
|
||||
where
|
||||
G: ?Sized + DirectedGraph,
|
||||
{
|
||||
type BreakVal = ();
|
||||
|
||||
fn node_examined(
|
||||
&mut self,
|
||||
_node: G::Node,
|
||||
prior_status: Option<NodeStatus>,
|
||||
) -> ControlFlow<Self::BreakVal> {
|
||||
match prior_status {
|
||||
Some(NodeStatus::Visited) => ControlFlow::Break(()),
|
||||
_ => ControlFlow::Continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,3 +9,14 @@ fn diamond_post_order() {
|
||||
let result = post_order_from(&graph, 0);
|
||||
assert_eq!(result, vec![3, 1, 2, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_cyclic() {
|
||||
use super::super::is_cyclic;
|
||||
|
||||
let diamond_acyclic = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
|
||||
let diamond_cyclic = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 0)]);
|
||||
|
||||
assert!(!is_cyclic(&diamond_acyclic));
|
||||
assert!(is_cyclic(&diamond_cyclic));
|
||||
}
|
||||
|
@ -81,3 +81,13 @@ where
|
||||
+ WithNumNodes,
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns `true` if the graph has a cycle that is reachable from the start node.
|
||||
pub fn is_cyclic<G>(graph: &G) -> bool
|
||||
where
|
||||
G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes,
|
||||
{
|
||||
iterate::TriColorDepthFirstSearch::new(graph)
|
||||
.run_from_start(&mut iterate::CycleDetector)
|
||||
.is_some()
|
||||
}
|
||||
|
@ -296,7 +296,6 @@ pub fn run_compiler(
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
return sess.compile_status();
|
||||
} else {
|
||||
let mut krate = compiler.parse()?.take();
|
||||
pretty::visit_crate(sess, &mut krate, ppm);
|
||||
@ -307,8 +306,8 @@ pub fn run_compiler(
|
||||
ppm,
|
||||
compiler.output_file().as_ref().map(|p| &**p),
|
||||
);
|
||||
return sess.compile_status();
|
||||
}
|
||||
return sess.compile_status();
|
||||
}
|
||||
|
||||
if callbacks.after_parsing(compiler) == Compilation::Stop {
|
||||
|
@ -1,10 +1,6 @@
|
||||
use crate::Diagnostic;
|
||||
use crate::DiagnosticId;
|
||||
use crate::DiagnosticStyledString;
|
||||
use crate::Applicability;
|
||||
use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
|
||||
use crate::{Applicability, Level, Handler, StashKey};
|
||||
|
||||
use crate::Level;
|
||||
use crate::Handler;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::thread::panicking;
|
||||
@ -117,18 +113,30 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffers the diagnostic for later emission, unless handler
|
||||
/// has disabled such buffering.
|
||||
pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) {
|
||||
/// Stashes diagnostic for possible later improvement in a different,
|
||||
/// later stage of the compiler. The diagnostic can be accessed with
|
||||
/// the provided `span` and `key` through `.steal_diagnostic` on `Handler`.
|
||||
///
|
||||
/// As with `buffer`, this is unless the handler has disabled such buffering.
|
||||
pub fn stash(self, span: Span, key: StashKey) {
|
||||
if let Some((diag, handler)) = self.into_diagnostic() {
|
||||
handler.stash_diagnostic(span, key, diag);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the builder to a `Diagnostic` for later emission,
|
||||
/// unless handler has disabled such buffering.
|
||||
pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
|
||||
if self.0.handler.flags.dont_buffer_diagnostics ||
|
||||
self.0.handler.flags.treat_err_as_bug.is_some()
|
||||
{
|
||||
self.emit();
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
// We need to use `ptr::read` because `DiagnosticBuilder`
|
||||
// implements `Drop`.
|
||||
let handler = self.0.handler;
|
||||
|
||||
// We need to use `ptr::read` because `DiagnosticBuilder` implements `Drop`.
|
||||
let diagnostic;
|
||||
unsafe {
|
||||
diagnostic = std::ptr::read(&self.0.diagnostic);
|
||||
@ -137,7 +145,14 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
// Logging here is useful to help track down where in logs an error was
|
||||
// actually emitted.
|
||||
debug!("buffer: diagnostic={:?}", diagnostic);
|
||||
buffered_diagnostics.push(diagnostic);
|
||||
|
||||
Some((diagnostic, handler))
|
||||
}
|
||||
|
||||
/// Buffers the diagnostic for later emission,
|
||||
/// unless handler has disabled such buffering.
|
||||
pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
|
||||
buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
|
||||
}
|
||||
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
|
@ -1043,14 +1043,13 @@ impl EmitterWriter {
|
||||
}
|
||||
|
||||
fn get_max_line_num(&mut self, span: &MultiSpan, children: &[SubDiagnostic]) -> usize {
|
||||
let mut max = 0;
|
||||
|
||||
let primary = self.get_multispan_max_line_num(span);
|
||||
max = if primary > max { primary } else { max };
|
||||
let mut max = primary;
|
||||
|
||||
for sub in children {
|
||||
let sub_result = self.get_multispan_max_line_num(&sub.span);
|
||||
max = if sub_result > max { primary } else { max };
|
||||
max = std::cmp::max(sub_result, max);
|
||||
}
|
||||
max
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ use emitter::{Emitter, EmitterWriter};
|
||||
use registry::Registry;
|
||||
|
||||
use rustc_data_structures::sync::{self, Lrc, Lock};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::stable_hasher::StableHasher;
|
||||
|
||||
use std::borrow::Cow;
|
||||
@ -302,6 +302,9 @@ pub struct Handler {
|
||||
inner: Lock<HandlerInner>,
|
||||
}
|
||||
|
||||
/// This inner struct exists to keep it all behind a single lock;
|
||||
/// this is done to prevent possible deadlocks in a multi-threaded compiler,
|
||||
/// as well as inconsistent state observation.
|
||||
struct HandlerInner {
|
||||
flags: HandlerFlags,
|
||||
/// The number of errors that have been emitted, including duplicates.
|
||||
@ -326,6 +329,18 @@ struct HandlerInner {
|
||||
/// this handler. These hashes is used to avoid emitting the same error
|
||||
/// twice.
|
||||
emitted_diagnostics: FxHashSet<u128>,
|
||||
|
||||
/// Stashed diagnostics emitted in one stage of the compiler that may be
|
||||
/// stolen by other stages (e.g. to improve them and add more information).
|
||||
/// The stashed diagnostics count towards the total error count.
|
||||
/// When `.abort_if_errors()` is called, these are also emitted.
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
|
||||
}
|
||||
|
||||
/// A key denoting where from a diagnostic was stashed.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum StashKey {
|
||||
ItemNoType,
|
||||
}
|
||||
|
||||
fn default_track_diagnostic(_: &Diagnostic) {}
|
||||
@ -354,7 +369,9 @@ pub struct HandlerFlags {
|
||||
|
||||
impl Drop for HandlerInner {
|
||||
fn drop(&mut self) {
|
||||
if self.err_count == 0 {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
if !self.has_errors() {
|
||||
let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
|
||||
let has_bugs = !bugs.is_empty();
|
||||
for bug in bugs {
|
||||
@ -368,57 +385,71 @@ impl Drop for HandlerInner {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn with_tty_emitter(color_config: ColorConfig,
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<usize>,
|
||||
cm: Option<Lrc<SourceMapperDyn>>)
|
||||
-> Handler {
|
||||
Handler::with_tty_emitter_and_flags(
|
||||
pub fn with_tty_emitter(
|
||||
color_config: ColorConfig,
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<usize>,
|
||||
cm: Option<Lrc<SourceMapperDyn>>,
|
||||
) -> Self {
|
||||
Self::with_tty_emitter_and_flags(
|
||||
color_config,
|
||||
cm,
|
||||
HandlerFlags {
|
||||
can_emit_warnings,
|
||||
treat_err_as_bug,
|
||||
.. Default::default()
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn with_tty_emitter_and_flags(color_config: ColorConfig,
|
||||
cm: Option<Lrc<SourceMapperDyn>>,
|
||||
flags: HandlerFlags)
|
||||
-> Handler {
|
||||
pub fn with_tty_emitter_and_flags(
|
||||
color_config: ColorConfig,
|
||||
cm: Option<Lrc<SourceMapperDyn>>,
|
||||
flags: HandlerFlags,
|
||||
) -> Self {
|
||||
let emitter = Box::new(EmitterWriter::stderr(
|
||||
color_config, cm, false, false, None, flags.external_macro_backtrace));
|
||||
Handler::with_emitter_and_flags(emitter, flags)
|
||||
color_config,
|
||||
cm,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
flags.external_macro_backtrace,
|
||||
));
|
||||
Self::with_emitter_and_flags(emitter, flags)
|
||||
}
|
||||
|
||||
pub fn with_emitter(can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<usize>,
|
||||
e: Box<dyn Emitter + sync::Send>)
|
||||
-> Handler {
|
||||
pub fn with_emitter(
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<usize>,
|
||||
emitter: Box<dyn Emitter + sync::Send>,
|
||||
) -> Self {
|
||||
Handler::with_emitter_and_flags(
|
||||
e,
|
||||
emitter,
|
||||
HandlerFlags {
|
||||
can_emit_warnings,
|
||||
treat_err_as_bug,
|
||||
.. Default::default()
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn with_emitter_and_flags(e: Box<dyn Emitter + sync::Send>, flags: HandlerFlags) -> Handler
|
||||
{
|
||||
Handler {
|
||||
pub fn with_emitter_and_flags(
|
||||
emitter: Box<dyn Emitter + sync::Send>,
|
||||
flags: HandlerFlags
|
||||
) -> Self {
|
||||
Self {
|
||||
flags,
|
||||
inner: Lock::new(HandlerInner {
|
||||
flags,
|
||||
err_count: 0,
|
||||
deduplicated_err_count: 0,
|
||||
emitter: e,
|
||||
emitter,
|
||||
continue_after_error: true,
|
||||
delayed_span_bugs: Vec::new(),
|
||||
taught_diagnostics: Default::default(),
|
||||
emitted_diagnostic_codes: Default::default(),
|
||||
emitted_diagnostics: Default::default(),
|
||||
stashed_diagnostics: Default::default(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -445,36 +476,68 @@ impl Handler {
|
||||
inner.emitted_diagnostics = Default::default();
|
||||
inner.deduplicated_err_count = 0;
|
||||
inner.err_count = 0;
|
||||
inner.stashed_diagnostics.clear();
|
||||
}
|
||||
|
||||
/// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing.
|
||||
/// If the diagnostic with this `(span, key)` already exists, this will result in an ICE.
|
||||
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if let Some(mut old_diag) = inner.stashed_diagnostics.insert((span, key), diag) {
|
||||
// We are removing a previously stashed diagnostic which should not happen.
|
||||
old_diag.level = Bug;
|
||||
old_diag.note(&format!(
|
||||
"{}:{}: already existing stashed diagnostic with (span = {:?}, key = {:?})",
|
||||
file!(), line!(), span, key
|
||||
));
|
||||
inner.emit_diag_at_span(old_diag, span);
|
||||
panic!(ExplicitBug);
|
||||
}
|
||||
}
|
||||
|
||||
/// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
|
||||
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.stashed_diagnostics
|
||||
.remove(&(span, key))
|
||||
.map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
|
||||
}
|
||||
|
||||
/// Emit all stashed diagnostics.
|
||||
pub fn emit_stashed_diagnostics(&self) {
|
||||
self.inner.borrow_mut().emit_stashed_diagnostics();
|
||||
}
|
||||
|
||||
/// Construct a dummy builder with `Level::Cancelled`.
|
||||
///
|
||||
/// Using this will neither report anything to the user (e.g. a warning),
|
||||
/// nor will compilation cancel as a result.
|
||||
pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
|
||||
DiagnosticBuilder::new(self, Level::Cancelled, "")
|
||||
}
|
||||
|
||||
pub fn struct_span_warn<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
|
||||
result.set_span(sp);
|
||||
if !self.flags.can_emit_warnings {
|
||||
result.cancel();
|
||||
}
|
||||
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
|
||||
pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_warn(msg);
|
||||
result.set_span(span);
|
||||
result
|
||||
}
|
||||
pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str,
|
||||
code: DiagnosticId)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
|
||||
result.set_span(sp);
|
||||
|
||||
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
|
||||
/// Also include a code.
|
||||
pub fn struct_span_warn_with_code(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: &str,
|
||||
code: DiagnosticId,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_span_warn(span, msg);
|
||||
result.code(code);
|
||||
if !self.flags.can_emit_warnings {
|
||||
result.cancel();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Warning` level with the `msg`.
|
||||
pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
|
||||
if !self.flags.can_emit_warnings {
|
||||
@ -482,146 +545,151 @@ impl Handler {
|
||||
}
|
||||
result
|
||||
}
|
||||
pub fn struct_span_err<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
|
||||
result.set_span(sp);
|
||||
|
||||
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
|
||||
pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_err(msg);
|
||||
result.set_span(span);
|
||||
result
|
||||
}
|
||||
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str,
|
||||
code: DiagnosticId)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
|
||||
result.set_span(sp);
|
||||
|
||||
/// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`.
|
||||
pub fn struct_span_err_with_code(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: &str,
|
||||
code: DiagnosticId,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_span_err(span, msg);
|
||||
result.code(code);
|
||||
result
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Error` level with the `msg`.
|
||||
// FIXME: This method should be removed (every error should have an associated error code).
|
||||
pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
DiagnosticBuilder::new(self, Level::Error, msg)
|
||||
}
|
||||
pub fn struct_err_with_code(
|
||||
|
||||
/// Construct a builder at the `Error` level with the `msg` and the `code`.
|
||||
pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_err(msg);
|
||||
result.code(code);
|
||||
result
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Fatal` level at the given `span` and with the `msg`.
|
||||
pub fn struct_span_fatal(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: &str,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
let mut result = self.struct_fatal(msg);
|
||||
result.set_span(span);
|
||||
result
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`.
|
||||
pub fn struct_span_fatal_with_code(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: &str,
|
||||
code: DiagnosticId,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
|
||||
result.code(code);
|
||||
result
|
||||
}
|
||||
pub fn struct_span_fatal<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
|
||||
result.set_span(sp);
|
||||
result
|
||||
}
|
||||
pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str,
|
||||
code: DiagnosticId)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
|
||||
result.set_span(sp);
|
||||
let mut result = self.struct_span_fatal(span, msg);
|
||||
result.code(code);
|
||||
result
|
||||
}
|
||||
|
||||
/// Construct a builder at the `Error` level with the `msg`.
|
||||
pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
DiagnosticBuilder::new(self, Level::Fatal, msg)
|
||||
}
|
||||
|
||||
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
|
||||
self.emit_diagnostic(Diagnostic::new(Fatal, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> FatalError {
|
||||
self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
|
||||
FatalError
|
||||
}
|
||||
pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str,
|
||||
code: DiagnosticId)
|
||||
-> FatalError {
|
||||
self.emit_diagnostic(Diagnostic::new_with_code(Fatal, Some(code), msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_fatal_with_code(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
msg: &str,
|
||||
code: DiagnosticId,
|
||||
) -> FatalError {
|
||||
self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span);
|
||||
FatalError
|
||||
}
|
||||
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit_diagnostic(Diagnostic::new(Error, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) {
|
||||
self.emit_diag_at_span(Diagnostic::new(Error, msg), span);
|
||||
}
|
||||
pub fn mut_span_err<S: Into<MultiSpan>>(&self,
|
||||
sp: S,
|
||||
msg: &str)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
|
||||
result.set_span(sp);
|
||||
result
|
||||
|
||||
pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) {
|
||||
self.emit_diag_at_span(Diagnostic::new_with_code(Error, Some(code), msg), span);
|
||||
}
|
||||
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
|
||||
self.emit_diagnostic(Diagnostic::new_with_code(Error, Some(code), msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) {
|
||||
self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
|
||||
}
|
||||
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit_diagnostic(Diagnostic::new(Warning, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_warn_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) {
|
||||
self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span);
|
||||
}
|
||||
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
|
||||
self.emit_diagnostic(Diagnostic::new_with_code(Warning, Some(code), msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ! {
|
||||
self.inner.borrow_mut().span_bug(span, msg)
|
||||
}
|
||||
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
||||
self.inner.borrow_mut().span_bug(sp, msg)
|
||||
|
||||
pub fn delay_span_bug(&self, span: impl Into<MultiSpan>, msg: &str) {
|
||||
self.inner.borrow_mut().delay_span_bug(span, msg)
|
||||
}
|
||||
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.inner.borrow_mut().delay_span_bug(sp, msg)
|
||||
|
||||
pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) {
|
||||
self.emit_diag_at_span(Diagnostic::new(Bug, msg), span);
|
||||
}
|
||||
pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit_diagnostic(Diagnostic::new(Bug, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
|
||||
pub fn span_note_without_error(&self, span: impl Into<MultiSpan>, msg: &str) {
|
||||
self.emit_diag_at_span(Diagnostic::new(Note, msg), span);
|
||||
}
|
||||
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit_diagnostic(Diagnostic::new(Note, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
}
|
||||
pub fn span_note_diag(&self,
|
||||
sp: Span,
|
||||
msg: &str)
|
||||
-> DiagnosticBuilder<'_> {
|
||||
|
||||
pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> {
|
||||
let mut db = DiagnosticBuilder::new(self, Note, msg);
|
||||
db.set_span(sp);
|
||||
db.set_span(span);
|
||||
db
|
||||
}
|
||||
|
||||
pub fn failure(&self, msg: &str) {
|
||||
self.inner.borrow_mut().failure(msg);
|
||||
}
|
||||
|
||||
pub fn fatal(&self, msg: &str) -> FatalError {
|
||||
self.inner.borrow_mut().fatal(msg)
|
||||
}
|
||||
|
||||
pub fn err(&self, msg: &str) {
|
||||
self.inner.borrow_mut().err(msg);
|
||||
}
|
||||
|
||||
pub fn warn(&self, msg: &str) {
|
||||
let mut db = DiagnosticBuilder::new(self, Warning, msg);
|
||||
db.emit();
|
||||
}
|
||||
|
||||
pub fn note_without_error(&self, msg: &str) {
|
||||
let mut db = DiagnosticBuilder::new(self, Note, msg);
|
||||
db.emit();
|
||||
DiagnosticBuilder::new(self, Note, msg).emit();
|
||||
}
|
||||
|
||||
pub fn bug(&self, msg: &str) -> ! {
|
||||
self.inner.borrow_mut().bug(msg)
|
||||
}
|
||||
|
||||
pub fn err_count(&self) -> usize {
|
||||
self.inner.borrow().err_count
|
||||
self.inner.borrow().err_count()
|
||||
}
|
||||
|
||||
pub fn has_errors(&self) -> bool {
|
||||
self.err_count() > 0
|
||||
self.inner.borrow().has_errors()
|
||||
}
|
||||
|
||||
pub fn print_error_count(&self, registry: &Registry) {
|
||||
@ -629,13 +697,18 @@ impl Handler {
|
||||
}
|
||||
|
||||
pub fn abort_if_errors(&self) {
|
||||
self.inner.borrow().abort_if_errors()
|
||||
self.inner.borrow_mut().abort_if_errors()
|
||||
}
|
||||
|
||||
pub fn abort_if_errors_and_should_abort(&self) {
|
||||
self.inner.borrow().abort_if_errors_and_should_abort()
|
||||
self.inner.borrow_mut().abort_if_errors_and_should_abort()
|
||||
}
|
||||
|
||||
/// `true` if we haven't taught a diagnostic with this code already.
|
||||
/// The caller must then teach the user about such a diagnostic.
|
||||
///
|
||||
/// Used to suppress emitting the same error multiple times with extended explanation when
|
||||
/// calling `-Zteach`.
|
||||
pub fn must_teach(&self, code: &DiagnosticId) -> bool {
|
||||
self.inner.borrow_mut().must_teach(code)
|
||||
}
|
||||
@ -648,6 +721,12 @@ impl Handler {
|
||||
self.inner.borrow_mut().emit_diagnostic(diagnostic)
|
||||
}
|
||||
|
||||
fn emit_diag_at_span(&self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.emit_diagnostic(diag.set_span(sp));
|
||||
inner.abort_if_errors_and_should_abort();
|
||||
}
|
||||
|
||||
pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
|
||||
self.inner.borrow_mut().emit_artifact_notification(path, artifact_type)
|
||||
}
|
||||
@ -658,11 +737,6 @@ impl Handler {
|
||||
}
|
||||
|
||||
impl HandlerInner {
|
||||
/// `true` if we haven't taught a diagnostic with this code already.
|
||||
/// The caller must then teach the user about such a diagnostic.
|
||||
///
|
||||
/// Used to suppress emitting the same error multiple times with extended explanation when
|
||||
/// calling `-Zteach`.
|
||||
fn must_teach(&mut self, code: &DiagnosticId) -> bool {
|
||||
self.taught_diagnostics.insert(code.clone())
|
||||
}
|
||||
@ -671,6 +745,12 @@ impl HandlerInner {
|
||||
self.emitter.emit_diagnostic(&db);
|
||||
}
|
||||
|
||||
/// Emit all stashed diagnostics.
|
||||
fn emit_stashed_diagnostics(&mut self) {
|
||||
let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>();
|
||||
diags.iter().for_each(|diag| self.emit_diagnostic(diag));
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
|
||||
if diagnostic.cancelled() {
|
||||
return;
|
||||
@ -713,10 +793,12 @@ impl HandlerInner {
|
||||
}
|
||||
|
||||
fn treat_err_as_bug(&self) -> bool {
|
||||
self.flags.treat_err_as_bug.map(|c| self.err_count >= c).unwrap_or(false)
|
||||
self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn print_error_count(&mut self, registry: &Registry) {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
let s = match self.deduplicated_err_count {
|
||||
0 => return,
|
||||
1 => "aborting due to previous error".to_string(),
|
||||
@ -760,25 +842,41 @@ impl HandlerInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn abort_if_errors_and_should_abort(&self) {
|
||||
if self.err_count > 0 && !self.continue_after_error {
|
||||
fn err_count(&self) -> usize {
|
||||
self.err_count + self.stashed_diagnostics.len()
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> bool {
|
||||
self.err_count() > 0
|
||||
}
|
||||
|
||||
fn abort_if_errors_and_should_abort(&mut self) {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
if self.has_errors() && !self.continue_after_error {
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
fn abort_if_errors(&self) {
|
||||
if self.err_count > 0 {
|
||||
fn abort_if_errors(&mut self) {
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
if self.has_errors() {
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
fn span_bug<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> ! {
|
||||
self.emit_diagnostic(Diagnostic::new(Bug, msg).set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ! {
|
||||
self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp);
|
||||
panic!(ExplicitBug);
|
||||
}
|
||||
|
||||
fn delay_span_bug<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) {
|
||||
fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) {
|
||||
self.emit_diagnostic(diag.set_span(sp));
|
||||
self.abort_if_errors_and_should_abort();
|
||||
}
|
||||
|
||||
fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) {
|
||||
if self.treat_err_as_bug() {
|
||||
// FIXME: don't abort here if report_delayed_bugs is off
|
||||
self.span_bug(sp, msg);
|
||||
@ -793,18 +891,20 @@ impl HandlerInner {
|
||||
}
|
||||
|
||||
fn fatal(&mut self, msg: &str) -> FatalError {
|
||||
if self.treat_err_as_bug() {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit_diagnostic(&Diagnostic::new(Fatal, msg));
|
||||
self.emit_error(Fatal, msg);
|
||||
FatalError
|
||||
}
|
||||
|
||||
fn err(&mut self, msg: &str) {
|
||||
self.emit_error(Error, msg);
|
||||
}
|
||||
|
||||
/// Emit an error; level should be `Error` or `Fatal`.
|
||||
fn emit_error(&mut self, level: Level, msg: &str,) {
|
||||
if self.treat_err_as_bug() {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit_diagnostic(&Diagnostic::new(Error, msg));
|
||||
self.emit_diagnostic(&Diagnostic::new(level, msg));
|
||||
}
|
||||
|
||||
fn bug(&mut self, msg: &str) -> ! {
|
||||
@ -826,7 +926,7 @@ impl HandlerInner {
|
||||
|
||||
fn panic_if_treat_err_as_bug(&self) {
|
||||
if self.treat_err_as_bug() {
|
||||
let s = match (self.err_count, self.flags.treat_err_as_bug.unwrap_or(0)) {
|
||||
let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) {
|
||||
(0, _) => return,
|
||||
(1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(),
|
||||
(1, _) => return,
|
||||
|
@ -221,7 +221,6 @@ pub struct PluginInfo {
|
||||
}
|
||||
|
||||
pub fn register_plugins<'a>(
|
||||
compiler: &Compiler,
|
||||
sess: &'a Session,
|
||||
cstore: &'a CStore,
|
||||
mut krate: ast::Crate,
|
||||
@ -261,9 +260,6 @@ pub fn register_plugins<'a>(
|
||||
});
|
||||
}
|
||||
|
||||
// If necessary, compute the dependency graph (in the background).
|
||||
compiler.dep_graph_future().ok();
|
||||
|
||||
time(sess, "recursion limit", || {
|
||||
middle::recursion_limit::update_limits(sess, &krate);
|
||||
});
|
||||
|
@ -114,29 +114,38 @@ impl Compiler {
|
||||
let crate_name = self.crate_name()?.peek().clone();
|
||||
let krate = self.parse()?.take();
|
||||
|
||||
passes::register_plugins(
|
||||
self,
|
||||
let result = passes::register_plugins(
|
||||
self.session(),
|
||||
self.cstore(),
|
||||
krate,
|
||||
&crate_name,
|
||||
)
|
||||
);
|
||||
|
||||
// Compute the dependency graph (in the background). We want to do
|
||||
// this as early as possible, to give the DepGraph maximum time to
|
||||
// load before dep_graph() is called, but it also can't happen
|
||||
// until after rustc_incremental::prepare_session_directory() is
|
||||
// called, which happens within passes::register_plugins().
|
||||
self.dep_graph_future().ok();
|
||||
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
pub fn crate_name(&self) -> Result<&Query<String>> {
|
||||
self.queries.crate_name.compute(|| {
|
||||
let parse_result = self.parse()?;
|
||||
let krate = parse_result.peek();
|
||||
let result = match self.crate_name {
|
||||
Ok(match self.crate_name {
|
||||
Some(ref crate_name) => crate_name.clone(),
|
||||
None => rustc_codegen_utils::link::find_crate_name(
|
||||
Some(self.session()),
|
||||
&krate.attrs,
|
||||
&self.input
|
||||
),
|
||||
};
|
||||
Ok(result)
|
||||
None => {
|
||||
let parse_result = self.parse()?;
|
||||
let krate = parse_result.peek();
|
||||
rustc_codegen_utils::link::find_crate_name(
|
||||
Some(self.session()),
|
||||
&krate.attrs,
|
||||
&self.input
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -194,7 +203,6 @@ impl Compiler {
|
||||
|
||||
pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
|
||||
self.queries.prepare_outputs.compute(|| {
|
||||
self.lower_to_hir()?;
|
||||
let krate = self.expansion()?;
|
||||
let krate = krate.peek();
|
||||
let crate_name = self.crate_name()?;
|
||||
@ -267,6 +275,11 @@ impl Compiler {
|
||||
})
|
||||
}
|
||||
|
||||
// This method is different to all the other methods in `Compiler` because
|
||||
// it lacks a `Queries` entry. It's also not currently used. It does serve
|
||||
// as an example of how `Compiler` can be used, with additional steps added
|
||||
// between some passes. And see `rustc_driver::run_compiler` for a more
|
||||
// complex example.
|
||||
pub fn compile(&self) -> Result<()> {
|
||||
self.prepare_outputs()?;
|
||||
|
||||
@ -278,12 +291,12 @@ impl Compiler {
|
||||
|
||||
self.global_ctxt()?;
|
||||
|
||||
// Drop AST after creating GlobalCtxt to free memory
|
||||
// Drop AST after creating GlobalCtxt to free memory.
|
||||
mem::drop(self.expansion()?.take());
|
||||
|
||||
self.ongoing_codegen()?;
|
||||
|
||||
// Drop GlobalCtxt after starting codegen to free memory
|
||||
// Drop GlobalCtxt after starting codegen to free memory.
|
||||
mem::drop(self.global_ctxt()?.take());
|
||||
|
||||
self.link().map(|_| ())
|
||||
|
@ -24,6 +24,5 @@ rustc_lexer = { path = "../librustc_lexer" }
|
||||
rustc_serialize = { path = "../libserialize", package = "serialize" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
byteorder = { version = "1.3" }
|
||||
rustc_apfloat = { path = "../librustc_apfloat" }
|
||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
||||
|
@ -15,4 +15,3 @@ rustc = { path = "../librustc" }
|
||||
rustc_metadata = { path = "../librustc_metadata" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
rustc_errors = { path = "../librustc_errors" }
|
||||
|
@ -12,7 +12,6 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
indexmap = "1"
|
||||
log = "0.4"
|
||||
syntax = { path = "../libsyntax" }
|
||||
rustc = { path = "../librustc" }
|
||||
|
@ -18,7 +18,7 @@ use syntax::ext::base::{self, InvocationRes, Indeterminate, SpecialDerives};
|
||||
use syntax::ext::base::{MacroKind, SyntaxExtension};
|
||||
use syntax::ext::expand::{AstFragment, AstFragmentKind, Invocation, InvocationKind};
|
||||
use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind};
|
||||
use syntax::ext::tt::macro_rules;
|
||||
use syntax::ext::compile_declarative_macro;
|
||||
use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name};
|
||||
use syntax::feature_gate::GateIssue;
|
||||
use syntax::symbol::{Symbol, kw, sym};
|
||||
@ -843,7 +843,7 @@ impl<'a> Resolver<'a> {
|
||||
/// Compile the macro into a `SyntaxExtension` and possibly replace it with a pre-defined
|
||||
/// extension partially or entirely for built-in macros and legacy plugin macros.
|
||||
crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
|
||||
let mut result = macro_rules::compile(
|
||||
let mut result = compile_declarative_macro(
|
||||
&self.session.parse_sess, self.session.features_untracked(), item, edition
|
||||
);
|
||||
|
||||
|
@ -14,7 +14,6 @@ rustc = { path = "../librustc" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
rustc_codegen_utils = { path = "../librustc_codegen_utils" }
|
||||
rustc_target = { path = "../librustc_target" }
|
||||
rustc_typeck = { path = "../librustc_typeck" }
|
||||
serde_json = "1"
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
|
@ -9,8 +9,6 @@ name = "rustc_traits"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
graphviz = { path = "../libgraphviz" }
|
||||
log = { version = "0.4" }
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
|
@ -232,7 +232,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
let mut err = if !actual.references_error() {
|
||||
// Suggest clamping down the type if the method that is being attempted to
|
||||
// be used exists at all, and the type is an ambiuous numeric type
|
||||
// be used exists at all, and the type is an ambiguous numeric type
|
||||
// ({integer}/{float}).
|
||||
let mut candidates = all_traits(self.tcx)
|
||||
.into_iter()
|
||||
|
@ -676,7 +676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
} else {
|
||||
// Pattern has wrong number of fields.
|
||||
self.e0023(pat.span, res, &subpats, &variant.fields, expected);
|
||||
self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected);
|
||||
on_error();
|
||||
return tcx.types.err;
|
||||
}
|
||||
@ -687,22 +687,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
pat_span: Span,
|
||||
res: Res,
|
||||
qpath: &hir::QPath,
|
||||
subpats: &'tcx [P<Pat>],
|
||||
fields: &[ty::FieldDef],
|
||||
expected: Ty<'tcx>
|
||||
) {
|
||||
let subpats_ending = pluralise!(subpats.len());
|
||||
let fields_ending = pluralise!(fields.len());
|
||||
let missing_parenthesis = match expected.sty {
|
||||
ty::Adt(_, substs) if fields.len() == 1 => {
|
||||
let field_ty = fields[0].ty(self.tcx, substs);
|
||||
match field_ty.sty {
|
||||
ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let res_span = self.tcx.def_span(res.def_id());
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
@ -723,11 +714,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
))
|
||||
.span_label(res_span, format!("{} defined here", res.descr()));
|
||||
|
||||
// Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`.
|
||||
// More generally, the expected type wants a tuple variant with one field of an
|
||||
// N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
|
||||
// with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
|
||||
let missing_parenthesis = match expected.sty {
|
||||
ty::Adt(_, substs) if fields.len() == 1 => {
|
||||
let field_ty = fields[0].ty(self.tcx, substs);
|
||||
match field_ty.sty {
|
||||
ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if missing_parenthesis {
|
||||
let (left, right) = match subpats {
|
||||
// This is the zero case; we aim to get the "hi" part of the `QPath`'s
|
||||
// span as the "lo" and then the "hi" part of the pattern's span as the "hi".
|
||||
// This looks like:
|
||||
//
|
||||
// help: missing parenthesis
|
||||
// |
|
||||
// L | let A(()) = A(());
|
||||
// | ^ ^
|
||||
[] => {
|
||||
let qpath_span = match qpath {
|
||||
hir::QPath::Resolved(_, path) => path.span,
|
||||
hir::QPath::TypeRelative(_, ps) => ps.ident.span,
|
||||
};
|
||||
(qpath_span.shrink_to_hi(), pat_span)
|
||||
},
|
||||
// Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the
|
||||
// last sub-pattern. In the case of `A(x)` the first and last may coincide.
|
||||
// This looks like:
|
||||
//
|
||||
// help: missing parenthesis
|
||||
// |
|
||||
// L | let A((x, y)) = A((1, 2));
|
||||
// | ^ ^
|
||||
[first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span),
|
||||
};
|
||||
err.multipart_suggestion(
|
||||
"missing parenthesis",
|
||||
vec![(subpats[0].span.shrink_to_lo(), "(".to_string()),
|
||||
(subpats[subpats.len()-1].span.shrink_to_hi(), ")".to_string())],
|
||||
vec![
|
||||
(left, "(".to_string()),
|
||||
(right.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -832,7 +832,7 @@ fn check_method_receiver<'fcx, 'tcx>(
|
||||
}
|
||||
|
||||
fn e0307(fcx: &FnCtxt<'fcx, 'tcx>, span: Span, receiver_ty: Ty<'_>) {
|
||||
fcx.tcx.sess.diagnostic().mut_span_err(
|
||||
fcx.tcx.sess.diagnostic().struct_span_err(
|
||||
span,
|
||||
&format!("invalid `self` parameter type: {:?}", receiver_ty)
|
||||
).note("type of `self` must be `Self` or a type that dereferences to it")
|
||||
|
@ -46,7 +46,7 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::GenericParamKind;
|
||||
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
|
||||
|
||||
use errors::{Applicability, DiagnosticId};
|
||||
use errors::{Applicability, DiagnosticId, StashKey};
|
||||
|
||||
struct OnlySelfBounds(bool);
|
||||
|
||||
@ -1149,18 +1149,41 @@ fn infer_placeholder_type(
|
||||
def_id: DefId,
|
||||
body_id: hir::BodyId,
|
||||
span: Span,
|
||||
item_ident: Ident,
|
||||
) -> Ty<'_> {
|
||||
let ty = tcx.typeck_tables_of(def_id).node_type(body_id.hir_id);
|
||||
let mut diag = bad_placeholder_type(tcx, span);
|
||||
if ty != tcx.types.err {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"replace `_` with the correct type",
|
||||
ty.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
// If this came from a free `const` or `static mut?` item,
|
||||
// then the user may have written e.g. `const A = 42;`.
|
||||
// In this case, the parser has stashed a diagnostic for
|
||||
// us to improve in typeck so we do that now.
|
||||
match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
|
||||
Some(mut err) => {
|
||||
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
|
||||
// We are typeck and have the real type, so remove that and suggest the actual type.
|
||||
err.suggestions.clear();
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"provide a type for the item",
|
||||
format!("{}: {}", item_ident, ty),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
None => {
|
||||
let mut diag = bad_placeholder_type(tcx, span);
|
||||
if ty != tcx.types.err {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"replace `_` with the correct type",
|
||||
ty.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
diag.emit();
|
||||
}
|
||||
}
|
||||
diag.emit();
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
@ -1192,7 +1215,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||
TraitItemKind::Const(ref ty, body_id) => {
|
||||
body_id.and_then(|body_id| {
|
||||
if let hir::TyKind::Infer = ty.node {
|
||||
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span))
|
||||
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1214,7 +1237,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||
}
|
||||
ImplItemKind::Const(ref ty, body_id) => {
|
||||
if let hir::TyKind::Infer = ty.node {
|
||||
infer_placeholder_type(tcx, def_id, body_id, ty.span)
|
||||
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
@ -1246,7 +1269,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||
ItemKind::Static(ref ty, .., body_id)
|
||||
| ItemKind::Const(ref ty, body_id) => {
|
||||
if let hir::TyKind::Infer = ty.node {
|
||||
infer_placeholder_type(tcx, def_id, body_id, ty.span)
|
||||
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
|
||||
} else {
|
||||
icx.to_ty(ty)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||
source: Span::empty(),
|
||||
name: None,
|
||||
attrs: Default::default(),
|
||||
visibility: None,
|
||||
visibility: Inherited,
|
||||
def_id: self.cx.next_def_id(param_env_def_id.krate),
|
||||
stability: None,
|
||||
deprecation: None,
|
||||
|
@ -99,7 +99,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||
source: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
|
||||
name: None,
|
||||
attrs: Default::default(),
|
||||
visibility: None,
|
||||
visibility: Inherited,
|
||||
def_id: self.cx.next_def_id(impl_def_id.krate),
|
||||
stability: None,
|
||||
deprecation: None,
|
||||
|
@ -131,7 +131,7 @@ pub fn try_inline(
|
||||
name: Some(name.clean(cx)),
|
||||
attrs,
|
||||
inner,
|
||||
visibility: Some(clean::Public),
|
||||
visibility: clean::Public,
|
||||
stability: cx.tcx.lookup_stability(did).clean(cx),
|
||||
deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
|
||||
def_id: did,
|
||||
@ -418,7 +418,7 @@ pub fn build_impl(cx: &DocContext<'_>, did: DefId, attrs: Option<Attrs<'_>>,
|
||||
source: tcx.def_span(did).clean(cx),
|
||||
name: None,
|
||||
attrs,
|
||||
visibility: Some(clean::Inherited),
|
||||
visibility: clean::Inherited,
|
||||
stability: tcx.lookup_stability(did).clean(cx),
|
||||
deprecation: tcx.lookup_deprecation(did).clean(cx),
|
||||
def_id: did,
|
||||
|
@ -187,7 +187,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
|
||||
source: Span::empty(),
|
||||
name: Some(prim.to_url_str().to_string()),
|
||||
attrs: attrs.clone(),
|
||||
visibility: Some(Public),
|
||||
visibility: Public,
|
||||
stability: get_stability(cx, def_id),
|
||||
deprecation: get_deprecation(cx, def_id),
|
||||
def_id,
|
||||
@ -199,7 +199,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
|
||||
source: Span::empty(),
|
||||
name: Some(kw.clone()),
|
||||
attrs: attrs,
|
||||
visibility: Some(Public),
|
||||
visibility: Public,
|
||||
stability: get_stability(cx, def_id),
|
||||
deprecation: get_deprecation(cx, def_id),
|
||||
def_id,
|
||||
@ -361,7 +361,7 @@ pub struct Item {
|
||||
pub name: Option<String>,
|
||||
pub attrs: Attributes,
|
||||
pub inner: ItemEnum,
|
||||
pub visibility: Option<Visibility>,
|
||||
pub visibility: Visibility,
|
||||
pub def_id: DefId,
|
||||
pub stability: Option<Stability>,
|
||||
pub deprecation: Option<Deprecation>,
|
||||
@ -1849,7 +1849,7 @@ fn get_real_types(
|
||||
cx: &DocContext<'_>,
|
||||
recurse: i32,
|
||||
) -> FxHashSet<Type> {
|
||||
let arg_s = arg.to_string();
|
||||
let arg_s = arg.print().to_string();
|
||||
let mut res = FxHashSet::default();
|
||||
if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed
|
||||
return res;
|
||||
@ -2311,7 +2311,7 @@ impl Clean<Item> for hir::TraitItem {
|
||||
attrs: self.attrs.clean(cx),
|
||||
source: self.span.clean(cx),
|
||||
def_id: local_did,
|
||||
visibility: None,
|
||||
visibility: Visibility::Inherited,
|
||||
stability: get_stability(cx, local_did),
|
||||
deprecation: get_deprecation(cx, local_did),
|
||||
inner,
|
||||
@ -2496,7 +2496,7 @@ impl Clean<Item> for ty::AssocItem {
|
||||
|
||||
let visibility = match self.container {
|
||||
ty::ImplContainer(_) => self.vis.clean(cx),
|
||||
ty::TraitContainer(_) => None,
|
||||
ty::TraitContainer(_) => Inherited,
|
||||
};
|
||||
|
||||
Item {
|
||||
@ -3293,9 +3293,9 @@ pub enum Visibility {
|
||||
Restricted(DefId, Path),
|
||||
}
|
||||
|
||||
impl Clean<Option<Visibility>> for hir::Visibility {
|
||||
fn clean(&self, cx: &DocContext<'_>) -> Option<Visibility> {
|
||||
Some(match self.node {
|
||||
impl Clean<Visibility> for hir::Visibility {
|
||||
fn clean(&self, cx: &DocContext<'_>) -> Visibility {
|
||||
match self.node {
|
||||
hir::VisibilityKind::Public => Visibility::Public,
|
||||
hir::VisibilityKind::Inherited => Visibility::Inherited,
|
||||
hir::VisibilityKind::Crate(_) => Visibility::Crate,
|
||||
@ -3304,13 +3304,13 @@ impl Clean<Option<Visibility>> for hir::Visibility {
|
||||
let did = register_res(cx, path.res);
|
||||
Visibility::Restricted(did, path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Option<Visibility>> for ty::Visibility {
|
||||
fn clean(&self, _: &DocContext<'_>) -> Option<Visibility> {
|
||||
Some(if *self == ty::Visibility::Public { Public } else { Inherited })
|
||||
impl Clean<Visibility> for ty::Visibility {
|
||||
fn clean(&self, _: &DocContext<'_>) -> Visibility {
|
||||
if *self == ty::Visibility::Public { Public } else { Inherited }
|
||||
}
|
||||
}
|
||||
|
||||
@ -3427,7 +3427,7 @@ impl Clean<Item> for doctree::Variant<'_> {
|
||||
name: Some(self.name.clean(cx)),
|
||||
attrs: self.attrs.clean(cx),
|
||||
source: self.whence.clean(cx),
|
||||
visibility: None,
|
||||
visibility: Inherited,
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id),
|
||||
@ -3470,7 +3470,7 @@ impl Clean<Item> for ty::VariantDef {
|
||||
name: Some(self.ident.clean(cx)),
|
||||
attrs: inline::load_attrs(cx, self.def_id).clean(cx),
|
||||
source: cx.tcx.def_span(self.def_id).clean(cx),
|
||||
visibility: Some(Inherited),
|
||||
visibility: Inherited,
|
||||
def_id: self.def_id,
|
||||
inner: VariantItem(Variant { kind }),
|
||||
stability: get_stability(cx, self.def_id),
|
||||
@ -3573,16 +3573,6 @@ pub enum GenericArg {
|
||||
Const(Constant),
|
||||
}
|
||||
|
||||
impl fmt::Display for GenericArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GenericArg::Lifetime(lt) => lt.fmt(f),
|
||||
GenericArg::Type(ty) => ty.fmt(f),
|
||||
GenericArg::Const(ct) => ct.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum GenericArgs {
|
||||
AngleBracketed {
|
||||
@ -4274,7 +4264,7 @@ fn resolve_type(cx: &DocContext<'_>,
|
||||
return Generic(kw::SelfUpper.to_string());
|
||||
}
|
||||
Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => {
|
||||
return Generic(format!("{:#}", path));
|
||||
return Generic(format!("{:#}", path.print()));
|
||||
}
|
||||
Res::SelfTy(..)
|
||||
| Res::Def(DefKind::TyParam, _)
|
||||
@ -4343,7 +4333,7 @@ impl Clean<Item> for doctree::Macro<'_> {
|
||||
name: Some(name.clone()),
|
||||
attrs: self.attrs.clean(cx),
|
||||
source: self.whence.clean(cx),
|
||||
visibility: Some(Public),
|
||||
visibility: Public,
|
||||
stability: cx.stability(self.hid).clean(cx),
|
||||
deprecation: cx.deprecation(self.hid).clean(cx),
|
||||
def_id: self.def_id,
|
||||
@ -4371,7 +4361,7 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
|
||||
name: Some(self.name.clean(cx)),
|
||||
attrs: self.attrs.clean(cx),
|
||||
source: self.whence.clean(cx),
|
||||
visibility: Some(Public),
|
||||
visibility: Public,
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,14 +46,6 @@ pub enum ItemType {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Eq, PartialEq, Clone)]
|
||||
pub enum NameSpace {
|
||||
Type,
|
||||
Value,
|
||||
Macro,
|
||||
Keyword,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a clean::Item> for ItemType {
|
||||
fn from(item: &'a clean::Item) -> ItemType {
|
||||
let inner = match item.inner {
|
||||
@ -120,7 +112,7 @@ impl From<clean::TypeKind> for ItemType {
|
||||
}
|
||||
|
||||
impl ItemType {
|
||||
pub fn css_class(&self) -> &'static str {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
ItemType::Module => "mod",
|
||||
ItemType::ExternCrate => "externcrate",
|
||||
@ -151,7 +143,7 @@ impl ItemType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_space(&self) -> NameSpace {
|
||||
pub fn name_space(&self) -> &'static str {
|
||||
match *self {
|
||||
ItemType::Struct |
|
||||
ItemType::Union |
|
||||
@ -163,7 +155,7 @@ impl ItemType {
|
||||
ItemType::AssocType |
|
||||
ItemType::OpaqueTy |
|
||||
ItemType::TraitAlias |
|
||||
ItemType::ForeignType => NameSpace::Type,
|
||||
ItemType::ForeignType => NAMESPACE_TYPE,
|
||||
|
||||
ItemType::ExternCrate |
|
||||
ItemType::Import |
|
||||
@ -175,20 +167,20 @@ impl ItemType {
|
||||
ItemType::StructField |
|
||||
ItemType::Variant |
|
||||
ItemType::Constant |
|
||||
ItemType::AssocConst => NameSpace::Value,
|
||||
ItemType::AssocConst => NAMESPACE_VALUE,
|
||||
|
||||
ItemType::Macro |
|
||||
ItemType::ProcAttribute |
|
||||
ItemType::ProcDerive => NameSpace::Macro,
|
||||
ItemType::ProcDerive => NAMESPACE_MACRO,
|
||||
|
||||
ItemType::Keyword => NameSpace::Keyword,
|
||||
ItemType::Keyword => NAMESPACE_KEYWORD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ItemType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.css_class().fmt(f)
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,20 +188,3 @@ pub const NAMESPACE_TYPE: &'static str = "t";
|
||||
pub const NAMESPACE_VALUE: &'static str = "v";
|
||||
pub const NAMESPACE_MACRO: &'static str = "m";
|
||||
pub const NAMESPACE_KEYWORD: &'static str = "k";
|
||||
|
||||
impl NameSpace {
|
||||
pub fn to_static_str(&self) -> &'static str {
|
||||
match *self {
|
||||
NameSpace::Type => NAMESPACE_TYPE,
|
||||
NameSpace::Value => NAMESPACE_VALUE,
|
||||
NameSpace::Macro => NAMESPACE_MACRO,
|
||||
NameSpace::Keyword => NAMESPACE_KEYWORD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NameSpace {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.to_static_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::externalfiles::ExternalHtml;
|
||||
use crate::html::render::SlashChecker;
|
||||
use crate::html::render::ensure_trailing_slash;
|
||||
use crate::html::format::{Buffer, Print};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -180,7 +180,7 @@ pub fn render<T: Print, S: Print>(
|
||||
css_class = page.css_class,
|
||||
logo = {
|
||||
let p = format!("{}{}", page.root_path, layout.krate);
|
||||
let p = SlashChecker(&p);
|
||||
let p = ensure_trailing_slash(&p);
|
||||
if layout.logo.is_empty() {
|
||||
format!("<a href='{path}index.html'>\
|
||||
<div class='logo-container'>\
|
||||
|
@ -752,7 +752,7 @@ impl MarkdownWithToc<'_> {
|
||||
html::push_html(&mut s, p);
|
||||
}
|
||||
|
||||
format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc(), s)
|
||||
format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc().print(), s)
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
675
src/librustdoc/html/render/cache.rs
Normal file
675
src/librustdoc/html/render/cache.rs
Normal file
@ -0,0 +1,675 @@
|
||||
use crate::clean::{self, GetDefId, AttributesExt};
|
||||
use crate::fold::DocFolder;
|
||||
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId};
|
||||
use rustc::middle::privacy::AccessLevels;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::BTreeMap;
|
||||
use syntax::source_map::FileName;
|
||||
use syntax::symbol::sym;
|
||||
use serialize::json::{ToJson, Json, as_json};
|
||||
|
||||
use super::{ItemType, IndexItem, IndexItemFunctionType, Impl, shorten, plain_summary_line};
|
||||
use super::{Type, RenderInfo};
|
||||
|
||||
/// Indicates where an external crate can be found.
|
||||
pub enum ExternalLocation {
|
||||
/// Remote URL root of the external crate
|
||||
Remote(String),
|
||||
/// This external crate can be found in the local doc/ folder
|
||||
Local,
|
||||
/// The external crate could not be found.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// This cache is used to store information about the `clean::Crate` being
|
||||
/// rendered in order to provide more useful documentation. This contains
|
||||
/// information like all implementors of a trait, all traits a type implements,
|
||||
/// documentation for all known traits, etc.
|
||||
///
|
||||
/// This structure purposefully does not implement `Clone` because it's intended
|
||||
/// to be a fairly large and expensive structure to clone. Instead this adheres
|
||||
/// to `Send` so it may be stored in a `Arc` instance and shared among the various
|
||||
/// rendering threads.
|
||||
#[derive(Default)]
|
||||
crate struct Cache {
|
||||
/// Maps a type ID to all known implementations for that type. This is only
|
||||
/// recognized for intra-crate `ResolvedPath` types, and is used to print
|
||||
/// out extra documentation on the page of an enum/struct.
|
||||
///
|
||||
/// The values of the map are a list of implementations and documentation
|
||||
/// found on that implementation.
|
||||
pub impls: FxHashMap<DefId, Vec<Impl>>,
|
||||
|
||||
/// Maintains a mapping of local crate `NodeId`s to the fully qualified name
|
||||
/// and "short type description" of that node. This is used when generating
|
||||
/// URLs when a type is being linked to. External paths are not located in
|
||||
/// this map because the `External` type itself has all the information
|
||||
/// necessary.
|
||||
pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
|
||||
|
||||
/// Similar to `paths`, but only holds external paths. This is only used for
|
||||
/// generating explicit hyperlinks to other crates.
|
||||
pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
|
||||
|
||||
/// Maps local `DefId`s of exported types to fully qualified paths.
|
||||
/// Unlike 'paths', this mapping ignores any renames that occur
|
||||
/// due to 'use' statements.
|
||||
///
|
||||
/// This map is used when writing out the special 'implementors'
|
||||
/// javascript file. By using the exact path that the type
|
||||
/// is declared with, we ensure that each path will be identical
|
||||
/// to the path used if the corresponding type is inlined. By
|
||||
/// doing this, we can detect duplicate impls on a trait page, and only display
|
||||
/// the impl for the inlined type.
|
||||
pub exact_paths: FxHashMap<DefId, Vec<String>>,
|
||||
|
||||
/// This map contains information about all known traits of this crate.
|
||||
/// Implementations of a crate should inherit the documentation of the
|
||||
/// parent trait if no extra documentation is specified, and default methods
|
||||
/// should show up in documentation about trait implementations.
|
||||
pub traits: FxHashMap<DefId, clean::Trait>,
|
||||
|
||||
/// When rendering traits, it's often useful to be able to list all
|
||||
/// implementors of the trait, and this mapping is exactly, that: a mapping
|
||||
/// of trait ids to the list of known implementors of the trait
|
||||
pub implementors: FxHashMap<DefId, Vec<Impl>>,
|
||||
|
||||
/// Cache of where external crate documentation can be found.
|
||||
pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>,
|
||||
|
||||
/// Cache of where documentation for primitives can be found.
|
||||
pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
|
||||
|
||||
// Note that external items for which `doc(hidden)` applies to are shown as
|
||||
// non-reachable while local items aren't. This is because we're reusing
|
||||
// the access levels from the privacy check pass.
|
||||
pub access_levels: AccessLevels<DefId>,
|
||||
|
||||
/// The version of the crate being documented, if given from the `--crate-version` flag.
|
||||
pub crate_version: Option<String>,
|
||||
|
||||
// Private fields only used when initially crawling a crate to build a cache
|
||||
|
||||
stack: Vec<String>,
|
||||
parent_stack: Vec<DefId>,
|
||||
parent_is_trait_impl: bool,
|
||||
search_index: Vec<IndexItem>,
|
||||
stripped_mod: bool,
|
||||
pub deref_trait_did: Option<DefId>,
|
||||
pub deref_mut_trait_did: Option<DefId>,
|
||||
pub owned_box_did: Option<DefId>,
|
||||
masked_crates: FxHashSet<CrateNum>,
|
||||
|
||||
// In rare case where a structure is defined in one module but implemented
|
||||
// in another, if the implementing module is parsed before defining module,
|
||||
// then the fully qualified name of the structure isn't presented in `paths`
|
||||
// yet when its implementation methods are being indexed. Caches such methods
|
||||
// and their parent id here and indexes them at the end of crate parsing.
|
||||
orphan_impl_items: Vec<(DefId, clean::Item)>,
|
||||
|
||||
// Similarly to `orphan_impl_items`, sometimes trait impls are picked up
|
||||
// even though the trait itself is not exported. This can happen if a trait
|
||||
// was defined in function/expression scope, since the impl will be picked
|
||||
// up by `collect-trait-impls` but the trait won't be scraped out in the HIR
|
||||
// crawl. In order to prevent crashes when looking for spotlight traits or
|
||||
// when gathering trait documentation on a type, hold impls here while
|
||||
// folding and add them to the cache later on if we find the trait.
|
||||
orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
|
||||
|
||||
/// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
|
||||
/// we need the alias element to have an array of items.
|
||||
pub(super) aliases: FxHashMap<String, Vec<IndexItem>>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub fn from_krate(
|
||||
renderinfo: RenderInfo,
|
||||
extern_html_root_urls: &BTreeMap<String, String>,
|
||||
dst: &Path,
|
||||
mut krate: clean::Crate,
|
||||
) -> (clean::Crate, String, Cache) {
|
||||
// Crawl the crate to build various caches used for the output
|
||||
let RenderInfo {
|
||||
inlined: _,
|
||||
external_paths,
|
||||
exact_paths,
|
||||
access_levels,
|
||||
deref_trait_did,
|
||||
deref_mut_trait_did,
|
||||
owned_box_did,
|
||||
} = renderinfo;
|
||||
|
||||
let external_paths = external_paths.into_iter()
|
||||
.map(|(k, (v, t))| (k, (v, ItemType::from(t))))
|
||||
.collect();
|
||||
|
||||
let mut cache = Cache {
|
||||
impls: Default::default(),
|
||||
external_paths,
|
||||
exact_paths,
|
||||
paths: Default::default(),
|
||||
implementors: Default::default(),
|
||||
stack: Vec::new(),
|
||||
parent_stack: Vec::new(),
|
||||
search_index: Vec::new(),
|
||||
parent_is_trait_impl: false,
|
||||
extern_locations: Default::default(),
|
||||
primitive_locations: Default::default(),
|
||||
stripped_mod: false,
|
||||
access_levels,
|
||||
crate_version: krate.version.take(),
|
||||
orphan_impl_items: Vec::new(),
|
||||
orphan_trait_impls: Vec::new(),
|
||||
traits: krate.external_traits.replace(Default::default()),
|
||||
deref_trait_did,
|
||||
deref_mut_trait_did,
|
||||
owned_box_did,
|
||||
masked_crates: mem::take(&mut krate.masked_crates),
|
||||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
// Cache where all our extern crates are located
|
||||
for &(n, ref e) in &krate.externs {
|
||||
let src_root = match e.src {
|
||||
FileName::Real(ref p) => match p.parent() {
|
||||
Some(p) => p.to_path_buf(),
|
||||
None => PathBuf::new(),
|
||||
},
|
||||
_ => PathBuf::new(),
|
||||
};
|
||||
let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u);
|
||||
cache.extern_locations.insert(n, (e.name.clone(), src_root,
|
||||
extern_location(e, extern_url, &dst)));
|
||||
|
||||
let did = DefId { krate: n, index: CRATE_DEF_INDEX };
|
||||
cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
|
||||
}
|
||||
|
||||
// Cache where all known primitives have their documentation located.
|
||||
//
|
||||
// Favor linking to as local extern as possible, so iterate all crates in
|
||||
// reverse topological order.
|
||||
for &(_, ref e) in krate.externs.iter().rev() {
|
||||
for &(def_id, prim, _) in &e.primitives {
|
||||
cache.primitive_locations.insert(prim, def_id);
|
||||
}
|
||||
}
|
||||
for &(def_id, prim, _) in &krate.primitives {
|
||||
cache.primitive_locations.insert(prim, def_id);
|
||||
}
|
||||
|
||||
cache.stack.push(krate.name.clone());
|
||||
krate = cache.fold_crate(krate);
|
||||
|
||||
for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) {
|
||||
if cache.traits.contains_key(&trait_did) {
|
||||
for did in dids {
|
||||
cache.impls.entry(did).or_insert(vec![]).push(impl_.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build our search index
|
||||
let index = build_index(&krate, &mut cache);
|
||||
|
||||
(krate, index, cache)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocFolder for Cache {
|
||||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||
if item.def_id.is_local() {
|
||||
debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id);
|
||||
}
|
||||
|
||||
// If this is a stripped module,
|
||||
// we don't want it or its children in the search index.
|
||||
let orig_stripped_mod = match item.inner {
|
||||
clean::StrippedItem(box clean::ModuleItem(..)) => {
|
||||
mem::replace(&mut self.stripped_mod, true)
|
||||
}
|
||||
_ => self.stripped_mod,
|
||||
};
|
||||
|
||||
// If the impl is from a masked crate or references something from a
|
||||
// masked crate then remove it completely.
|
||||
if let clean::ImplItem(ref i) = item.inner {
|
||||
if self.masked_crates.contains(&item.def_id.krate) ||
|
||||
i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) ||
|
||||
i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate a trait method's documentation to all implementors of the
|
||||
// trait.
|
||||
if let clean::TraitItem(ref t) = item.inner {
|
||||
self.traits.entry(item.def_id).or_insert_with(|| t.clone());
|
||||
}
|
||||
|
||||
// Collect all the implementors of traits.
|
||||
if let clean::ImplItem(ref i) = item.inner {
|
||||
if let Some(did) = i.trait_.def_id() {
|
||||
if i.blanket_impl.is_none() {
|
||||
self.implementors.entry(did).or_default().push(Impl {
|
||||
impl_item: item.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Index this method for searching later on.
|
||||
if let Some(ref s) = item.name {
|
||||
let (parent, is_inherent_impl_item) = match item.inner {
|
||||
clean::StrippedItem(..) => ((None, None), false),
|
||||
clean::AssocConstItem(..) |
|
||||
clean::TypedefItem(_, true) if self.parent_is_trait_impl => {
|
||||
// skip associated items in trait impls
|
||||
((None, None), false)
|
||||
}
|
||||
clean::AssocTypeItem(..) |
|
||||
clean::TyMethodItem(..) |
|
||||
clean::StructFieldItem(..) |
|
||||
clean::VariantItem(..) => {
|
||||
((Some(*self.parent_stack.last().unwrap()),
|
||||
Some(&self.stack[..self.stack.len() - 1])),
|
||||
false)
|
||||
}
|
||||
clean::MethodItem(..) | clean::AssocConstItem(..) => {
|
||||
if self.parent_stack.is_empty() {
|
||||
((None, None), false)
|
||||
} else {
|
||||
let last = self.parent_stack.last().unwrap();
|
||||
let did = *last;
|
||||
let path = match self.paths.get(&did) {
|
||||
// The current stack not necessarily has correlation
|
||||
// for where the type was defined. On the other
|
||||
// hand, `paths` always has the right
|
||||
// information if present.
|
||||
Some(&(ref fqp, ItemType::Trait)) |
|
||||
Some(&(ref fqp, ItemType::Struct)) |
|
||||
Some(&(ref fqp, ItemType::Union)) |
|
||||
Some(&(ref fqp, ItemType::Enum)) =>
|
||||
Some(&fqp[..fqp.len() - 1]),
|
||||
Some(..) => Some(&*self.stack),
|
||||
None => None
|
||||
};
|
||||
((Some(*last), path), true)
|
||||
}
|
||||
}
|
||||
_ => ((None, Some(&*self.stack)), false)
|
||||
};
|
||||
|
||||
match parent {
|
||||
(parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
|
||||
debug_assert!(!item.is_stripped());
|
||||
|
||||
// A crate has a module at its root, containing all items,
|
||||
// which should not be indexed. The crate-item itself is
|
||||
// inserted later on when serializing the search-index.
|
||||
if item.def_id.index != CRATE_DEF_INDEX {
|
||||
self.search_index.push(IndexItem {
|
||||
ty: item.type_(),
|
||||
name: s.to_string(),
|
||||
path: path.join("::"),
|
||||
desc: shorten(plain_summary_line(item.doc_value())),
|
||||
parent,
|
||||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
}
|
||||
}
|
||||
(Some(parent), None) if is_inherent_impl_item => {
|
||||
// We have a parent, but we don't know where they're
|
||||
// defined yet. Wait for later to index this item.
|
||||
self.orphan_impl_items.push((parent, item.clone()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of the fully qualified path for this item.
|
||||
let pushed = match item.name {
|
||||
Some(ref n) if !n.is_empty() => {
|
||||
self.stack.push(n.to_string());
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
match item.inner {
|
||||
clean::StructItem(..) | clean::EnumItem(..) |
|
||||
clean::TypedefItem(..) | clean::TraitItem(..) |
|
||||
clean::FunctionItem(..) | clean::ModuleItem(..) |
|
||||
clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
|
||||
clean::ConstantItem(..) | clean::StaticItem(..) |
|
||||
clean::UnionItem(..) | clean::ForeignTypeItem |
|
||||
clean::MacroItem(..) | clean::ProcMacroItem(..)
|
||||
if !self.stripped_mod => {
|
||||
// Re-exported items mean that the same id can show up twice
|
||||
// in the rustdoc ast that we're looking at. We know,
|
||||
// however, that a re-exported item doesn't show up in the
|
||||
// `public_items` map, so we can skip inserting into the
|
||||
// paths map if there was already an entry present and we're
|
||||
// not a public item.
|
||||
if !self.paths.contains_key(&item.def_id) ||
|
||||
self.access_levels.is_public(item.def_id)
|
||||
{
|
||||
self.paths.insert(item.def_id,
|
||||
(self.stack.clone(), item.type_()));
|
||||
}
|
||||
self.add_aliases(&item);
|
||||
}
|
||||
// Link variants to their parent enum because pages aren't emitted
|
||||
// for each variant.
|
||||
clean::VariantItem(..) if !self.stripped_mod => {
|
||||
let mut stack = self.stack.clone();
|
||||
stack.pop();
|
||||
self.paths.insert(item.def_id, (stack, ItemType::Enum));
|
||||
}
|
||||
|
||||
clean::PrimitiveItem(..) => {
|
||||
self.add_aliases(&item);
|
||||
self.paths.insert(item.def_id, (self.stack.clone(),
|
||||
item.type_()));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Maintain the parent stack
|
||||
let orig_parent_is_trait_impl = self.parent_is_trait_impl;
|
||||
let parent_pushed = match item.inner {
|
||||
clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem |
|
||||
clean::StructItem(..) | clean::UnionItem(..) => {
|
||||
self.parent_stack.push(item.def_id);
|
||||
self.parent_is_trait_impl = false;
|
||||
true
|
||||
}
|
||||
clean::ImplItem(ref i) => {
|
||||
self.parent_is_trait_impl = i.trait_.is_some();
|
||||
match i.for_ {
|
||||
clean::ResolvedPath{ did, .. } => {
|
||||
self.parent_stack.push(did);
|
||||
true
|
||||
}
|
||||
ref t => {
|
||||
let prim_did = t.primitive_type().and_then(|t| {
|
||||
self.primitive_locations.get(&t).cloned()
|
||||
});
|
||||
match prim_did {
|
||||
Some(did) => {
|
||||
self.parent_stack.push(did);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
};
|
||||
|
||||
// Once we've recursively found all the generics, hoard off all the
|
||||
// implementations elsewhere.
|
||||
let ret = self.fold_item_recur(item).and_then(|item| {
|
||||
if let clean::Item { inner: clean::ImplItem(_), .. } = item {
|
||||
// Figure out the id of this impl. This may map to a
|
||||
// primitive rather than always to a struct/enum.
|
||||
// Note: matching twice to restrict the lifetime of the `i` borrow.
|
||||
let mut dids = FxHashSet::default();
|
||||
if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
|
||||
match i.for_ {
|
||||
clean::ResolvedPath { did, .. } |
|
||||
clean::BorrowedRef {
|
||||
type_: box clean::ResolvedPath { did, .. }, ..
|
||||
} => {
|
||||
dids.insert(did);
|
||||
}
|
||||
ref t => {
|
||||
let did = t.primitive_type().and_then(|t| {
|
||||
self.primitive_locations.get(&t).cloned()
|
||||
});
|
||||
|
||||
if let Some(did) = did {
|
||||
dids.insert(did);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
|
||||
for bound in generics {
|
||||
if let Some(did) = bound.def_id() {
|
||||
dids.insert(did);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let impl_item = Impl {
|
||||
impl_item: item,
|
||||
};
|
||||
if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) {
|
||||
for did in dids {
|
||||
self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
|
||||
}
|
||||
} else {
|
||||
let trait_did = impl_item.trait_did().unwrap();
|
||||
self.orphan_trait_impls.push((trait_did, dids, impl_item));
|
||||
}
|
||||
None
|
||||
} else {
|
||||
Some(item)
|
||||
}
|
||||
});
|
||||
|
||||
if pushed { self.stack.pop().unwrap(); }
|
||||
if parent_pushed { self.parent_stack.pop().unwrap(); }
|
||||
self.stripped_mod = orig_stripped_mod;
|
||||
self.parent_is_trait_impl = orig_parent_is_trait_impl;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn add_aliases(&mut self, item: &clean::Item) {
|
||||
if item.def_id.index == CRATE_DEF_INDEX {
|
||||
return
|
||||
}
|
||||
if let Some(ref item_name) = item.name {
|
||||
let path = self.paths.get(&item.def_id)
|
||||
.map(|p| p.0[..p.0.len() - 1].join("::"))
|
||||
.unwrap_or("std".to_owned());
|
||||
for alias in item.attrs.lists(sym::doc)
|
||||
.filter(|a| a.check_name(sym::alias))
|
||||
.filter_map(|a| a.value_str()
|
||||
.map(|s| s.to_string().replace("\"", "")))
|
||||
.filter(|v| !v.is_empty())
|
||||
.collect::<FxHashSet<_>>()
|
||||
.into_iter() {
|
||||
self.aliases.entry(alias)
|
||||
.or_insert(Vec::with_capacity(1))
|
||||
.push(IndexItem {
|
||||
ty: item.type_(),
|
||||
name: item_name.to_string(),
|
||||
path: path.clone(),
|
||||
desc: shorten(plain_summary_line(item.doc_value())),
|
||||
parent: None,
|
||||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to find where an external crate is located, given that we're
|
||||
/// rendering in to the specified source destination.
|
||||
fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
|
||||
-> ExternalLocation
|
||||
{
|
||||
use ExternalLocation::*;
|
||||
// See if there's documentation generated into the local directory
|
||||
let local_location = dst.join(&e.name);
|
||||
if local_location.is_dir() {
|
||||
return Local;
|
||||
}
|
||||
|
||||
if let Some(url) = extern_url {
|
||||
let mut url = url.to_string();
|
||||
if !url.ends_with("/") {
|
||||
url.push('/');
|
||||
}
|
||||
return Remote(url);
|
||||
}
|
||||
|
||||
// Failing that, see if there's an attribute specifying where to find this
|
||||
// external crate
|
||||
e.attrs.lists(sym::doc)
|
||||
.filter(|a| a.check_name(sym::html_root_url))
|
||||
.filter_map(|a| a.value_str())
|
||||
.map(|url| {
|
||||
let mut url = url.to_string();
|
||||
if !url.ends_with("/") {
|
||||
url.push('/')
|
||||
}
|
||||
Remote(url)
|
||||
}).next().unwrap_or(Unknown) // Well, at least we tried.
|
||||
}
|
||||
|
||||
/// Builds the search index from the collected metadata
|
||||
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
|
||||
let mut nodeid_to_pathid = FxHashMap::default();
|
||||
let mut crate_items = Vec::with_capacity(cache.search_index.len());
|
||||
let mut crate_paths = Vec::<Json>::new();
|
||||
|
||||
let Cache { ref mut search_index,
|
||||
ref orphan_impl_items,
|
||||
ref paths, .. } = *cache;
|
||||
|
||||
// Attach all orphan items to the type's definition if the type
|
||||
// has since been learned.
|
||||
for &(did, ref item) in orphan_impl_items {
|
||||
if let Some(&(ref fqp, _)) = paths.get(&did) {
|
||||
search_index.push(IndexItem {
|
||||
ty: item.type_(),
|
||||
name: item.name.clone().unwrap(),
|
||||
path: fqp[..fqp.len() - 1].join("::"),
|
||||
desc: shorten(plain_summary_line(item.doc_value())),
|
||||
parent: Some(did),
|
||||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce `NodeId` in paths into smaller sequential numbers,
|
||||
// and prune the paths that do not appear in the index.
|
||||
let mut lastpath = String::new();
|
||||
let mut lastpathid = 0usize;
|
||||
|
||||
for item in search_index {
|
||||
item.parent_idx = item.parent.map(|nodeid| {
|
||||
if nodeid_to_pathid.contains_key(&nodeid) {
|
||||
*nodeid_to_pathid.get(&nodeid).unwrap()
|
||||
} else {
|
||||
let pathid = lastpathid;
|
||||
nodeid_to_pathid.insert(nodeid, pathid);
|
||||
lastpathid += 1;
|
||||
|
||||
let &(ref fqp, short) = paths.get(&nodeid).unwrap();
|
||||
crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
|
||||
pathid
|
||||
}
|
||||
});
|
||||
|
||||
// Omit the parent path if it is same to that of the prior item.
|
||||
if lastpath == item.path {
|
||||
item.path.clear();
|
||||
} else {
|
||||
lastpath = item.path.clone();
|
||||
}
|
||||
crate_items.push(item.to_json());
|
||||
}
|
||||
|
||||
let crate_doc = krate.module.as_ref().map(|module| {
|
||||
shorten(plain_summary_line(module.doc_value()))
|
||||
}).unwrap_or(String::new());
|
||||
|
||||
let mut crate_data = BTreeMap::new();
|
||||
crate_data.insert("doc".to_owned(), Json::String(crate_doc));
|
||||
crate_data.insert("i".to_owned(), Json::Array(crate_items));
|
||||
crate_data.insert("p".to_owned(), Json::Array(crate_paths));
|
||||
|
||||
// Collect the index into a string
|
||||
format!("searchIndex[{}] = {};",
|
||||
as_json(&krate.name),
|
||||
Json::Object(crate_data))
|
||||
}
|
||||
|
||||
fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
|
||||
let (all_types, ret_types) = match item.inner {
|
||||
clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
|
||||
clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
|
||||
clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let inputs = all_types.iter().map(|arg| {
|
||||
get_index_type(&arg)
|
||||
}).filter(|a| a.name.is_some()).collect();
|
||||
let output = ret_types.iter().map(|arg| {
|
||||
get_index_type(&arg)
|
||||
}).filter(|a| a.name.is_some()).collect::<Vec<_>>();
|
||||
let output = if output.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(output)
|
||||
};
|
||||
|
||||
Some(IndexItemFunctionType { inputs, output })
|
||||
}
|
||||
|
||||
fn get_index_type(clean_type: &clean::Type) -> Type {
|
||||
let t = Type {
|
||||
name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()),
|
||||
generics: get_generics(clean_type),
|
||||
};
|
||||
t
|
||||
}
|
||||
|
||||
fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
|
||||
match *clean_type {
|
||||
clean::ResolvedPath { ref path, .. } => {
|
||||
let segments = &path.segments;
|
||||
let path_segment = segments.into_iter().last().unwrap_or_else(|| panic!(
|
||||
"get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
|
||||
clean_type, accept_generic
|
||||
));
|
||||
Some(path_segment.name.clone())
|
||||
}
|
||||
clean::Generic(ref s) if accept_generic => Some(s.clone()),
|
||||
clean::Primitive(ref p) => Some(format!("{:?}", p)),
|
||||
clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
|
||||
// FIXME: add all from clean::Type.
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> {
|
||||
clean_type.generics()
|
||||
.and_then(|types| {
|
||||
let r = types.iter()
|
||||
.filter_map(|t| get_index_type_name(t, false))
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
.collect::<Vec<_>>();
|
||||
if r.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
})
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
//! Table-of-contents creation.
|
||||
|
||||
use std::fmt;
|
||||
use std::string::String;
|
||||
|
||||
/// A (recursive) table of contents
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Toc {
|
||||
/// The levels are strictly decreasing, i.e.
|
||||
///
|
||||
@ -28,7 +25,7 @@ impl Toc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct TocEntry {
|
||||
level: u32,
|
||||
sec_number: String,
|
||||
@ -165,25 +162,23 @@ impl TocBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Toc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Toc {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "<ul>")?;
|
||||
impl Toc {
|
||||
fn print_inner(&self, v: &mut String) {
|
||||
v.push_str("<ul>");
|
||||
for entry in &self.entries {
|
||||
// recursively format this table of contents (the
|
||||
// `{children}` is the key).
|
||||
write!(fmt,
|
||||
"\n<li><a href=\"#{id}\">{num} {name}</a>{children}</li>",
|
||||
// recursively format this table of contents
|
||||
v.push_str(&format!("\n<li><a href=\"#{id}\">{num} {name}</a>",
|
||||
id = entry.id,
|
||||
num = entry.sec_number, name = entry.name,
|
||||
children = entry.children)?
|
||||
num = entry.sec_number, name = entry.name));
|
||||
entry.children.print_inner(&mut *v);
|
||||
v.push_str("</li>");
|
||||
}
|
||||
write!(fmt, "</ul>")
|
||||
v.push_str("</ul>");
|
||||
}
|
||||
crate fn print(&self) -> String {
|
||||
let mut v = String::new();
|
||||
self.print_inner(&mut v);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,8 @@ impl fold::DocFolder for CoverageCalculator {
|
||||
}
|
||||
clean::ImplItem(ref impl_) => {
|
||||
if let Some(ref tr) = impl_.trait_ {
|
||||
debug!("impl {:#} for {:#} in {}", tr, impl_.for_, i.source.filename);
|
||||
debug!("impl {:#} for {:#} in {}",
|
||||
tr.print(), impl_.for_.print(), i.source.filename);
|
||||
|
||||
// don't count trait impls, the missing-docs lint doesn't so we shouldn't
|
||||
// either
|
||||
@ -151,11 +152,11 @@ impl fold::DocFolder for CoverageCalculator {
|
||||
// inherent impls *can* be documented, and those docs show up, but in most
|
||||
// cases it doesn't make sense, as all methods on a type are in one single
|
||||
// impl block
|
||||
debug!("impl {:#} in {}", impl_.for_, i.source.filename);
|
||||
debug!("impl {:#} in {}", impl_.for_.print(), i.source.filename);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("counting {} {:?} in {}", i.type_(), i.name, i.source.filename);
|
||||
debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename);
|
||||
self.items.entry(i.source.filename.clone())
|
||||
.or_default()
|
||||
.count_item(has_docs);
|
||||
|
@ -237,7 +237,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
||||
});
|
||||
|
||||
if parent_node.is_some() {
|
||||
debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
|
||||
debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
|
||||
}
|
||||
|
||||
let current_item = match item.inner {
|
||||
|
@ -153,7 +153,7 @@ impl<'a> DocFolder for Stripper<'a> {
|
||||
// We need to recurse into stripped modules to strip things
|
||||
// like impl methods but when doing so we must not add any
|
||||
// items to the `retained` set.
|
||||
debug!("Stripper: recursing into stripped {} {:?}", i.type_(), i.name);
|
||||
debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = self.fold_item_recur(i);
|
||||
self.update_retained = old;
|
||||
@ -178,20 +178,20 @@ impl<'a> DocFolder for Stripper<'a> {
|
||||
| clean::ForeignTypeItem => {
|
||||
if i.def_id.is_local() {
|
||||
if !self.access_levels.is_exported(i.def_id) {
|
||||
debug!("Stripper: stripping {} {:?}", i.type_(), i.name);
|
||||
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean::StructFieldItem(..) => {
|
||||
if i.visibility != Some(clean::Public) {
|
||||
if i.visibility != clean::Public {
|
||||
return StripItem(i).strip();
|
||||
}
|
||||
}
|
||||
|
||||
clean::ModuleItem(..) => {
|
||||
if i.def_id.is_local() && i.visibility != Some(clean::Public) {
|
||||
if i.def_id.is_local() && i.visibility != clean::Public {
|
||||
debug!("Stripper: stripping module {:?}", i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
|
||||
@ -299,7 +299,7 @@ impl DocFolder for ImportStripper {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::ExternCrateItem(..) | clean::ImportItem(..)
|
||||
if i.visibility != Some(clean::Public) =>
|
||||
if i.visibility != clean::Public =>
|
||||
{
|
||||
None
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ struct Stripper<'a> {
|
||||
impl<'a> DocFolder for Stripper<'a> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
if i.attrs.lists(sym::doc).has_word(sym::hidden) {
|
||||
debug!("strip_hidden: stripping {} {:?}", i.type_(), i.name);
|
||||
debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
|
||||
// use a dedicated hidden item for given item type if any
|
||||
match i.inner {
|
||||
clean::StructFieldItem(..) | clean::ModuleItem(..) => {
|
||||
|
@ -53,9 +53,6 @@ fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] }
|
||||
[target.wasm32-wasi.dependencies]
|
||||
wasi = { version = "0.7.0", features = ['rustc-dep-of-std', 'alloc'] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["std_detect_file_io", "std_detect_dlsym_getauxval"]
|
||||
|
||||
|
@ -114,6 +114,9 @@ pub struct Metadata(fs_imp::FileAttr);
|
||||
/// information like the entry's path and possibly other metadata can be
|
||||
/// learned.
|
||||
///
|
||||
/// The order in which this iterator returns entries is platform and filesystem
|
||||
/// dependent.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent
|
||||
@ -1962,6 +1965,9 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
///
|
||||
/// [changes]: ../io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// The order in which this iterator returns entries is platform and filesystem
|
||||
/// dependent.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
@ -1994,6 +2000,25 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use std::{fs, io};
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut entries = fs::read_dir(".")?
|
||||
/// .map(|res| res.map(|e| e.path()))
|
||||
/// .collect::<Result<Vec<_>, io::Error>>()?;
|
||||
///
|
||||
/// // The order in which `read_dir` returns entries is not guaranteed. If reproducible
|
||||
/// // ordering is required the entries should be explicitly sorted.
|
||||
///
|
||||
/// entries.sort();
|
||||
///
|
||||
/// // The entries have now been sorted by their path.
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
|
||||
fs_imp::readdir(path.as_ref()).map(ReadDir)
|
||||
|
@ -5,4 +5,3 @@ pub use crate::ffi::OsString as EnvKey;
|
||||
mod process_common;
|
||||
#[path = "process_vxworks.rs"]
|
||||
mod process_inner;
|
||||
mod rtp;
|
||||
|
@ -3,7 +3,6 @@ use libc::{self, c_int, c_char};
|
||||
use libc::{RTP_ID};
|
||||
use crate::sys;
|
||||
use crate::sys::cvt;
|
||||
use crate::sys::process::rtp;
|
||||
use crate::sys::process::process_common::*;
|
||||
use crate::sys_common::thread;
|
||||
|
||||
@ -53,7 +52,7 @@ impl Command {
|
||||
t!(cvt(libc::chdir(cwd.as_ptr())));
|
||||
}
|
||||
|
||||
let ret = rtp::rtpSpawn(
|
||||
let ret = libc::rtpSpawn(
|
||||
self.get_argv()[0], // executing program
|
||||
self.get_argv().as_ptr() as *const _, // argv
|
||||
*sys::os::environ() as *const *const c_char,
|
||||
@ -78,7 +77,7 @@ impl Command {
|
||||
libc::close(orig_stderr);
|
||||
}
|
||||
|
||||
if ret != rtp::RTP_ID_ERROR {
|
||||
if ret != libc::RTP_ID_ERROR {
|
||||
p.pid = ret;
|
||||
Ok((p, ours))
|
||||
} else {
|
||||
|
@ -1,298 +0,0 @@
|
||||
#![allow(non_camel_case_types, unused)]
|
||||
|
||||
use libc::{self, c_int, size_t, c_char, BOOL, RTP_DESC, RTP_ID, TASK_ID};
|
||||
|
||||
|
||||
// Copied directly from rtpLibCommon.h, rtpLib.h, signal.h and taskLibCommon.h (for task options)
|
||||
|
||||
// **** definitions for rtpLibCommon.h ****
|
||||
|
||||
pub const RTP_GLOBAL_SYMBOLS : c_int = 0x01; // register global symbols for RTP
|
||||
pub const RTP_LOCAL_SYMBOLS : c_int = 0x02; // idem for local symbols
|
||||
pub const RTP_ALL_SYMBOLS : c_int = (RTP_GLOBAL_SYMBOLS | RTP_LOCAL_SYMBOLS);
|
||||
pub const RTP_DEBUG : c_int = 0x10; // set RTP in debug mode when created
|
||||
pub const RTP_BUFFER_VAL_OFF : c_int = 0x20; // disable buffer validation for all
|
||||
// system calls issued from the RTP
|
||||
pub const RTP_LOADED_WAIT : c_int = 0x40; // Wait until the RTP is loaded
|
||||
pub const RTP_CPU_AFFINITY_NONE : c_int = 0x80; // Remove any CPU affinity (SMP)
|
||||
|
||||
// Error Status codes
|
||||
|
||||
pub const M_rtpLib : c_int = 178 << 16;
|
||||
|
||||
pub const S_rtpLib_INVALID_FILE : c_int = (M_rtpLib | 1);
|
||||
pub const S_rtpLib_INVALID_OPTION : c_int = (M_rtpLib | 2);
|
||||
pub const S_rtpLib_ACCESS_DENIED : c_int = (M_rtpLib | 3);
|
||||
pub const S_rtpLib_INVALID_RTP_ID : c_int = (M_rtpLib | 4);
|
||||
pub const S_rtpLib_NO_SYMBOL_TABLE : c_int = (M_rtpLib | 5);
|
||||
pub const S_rtpLib_INVALID_SEGMENT_START_ADDRESS : c_int = (M_rtpLib | 6);
|
||||
pub const S_rtpLib_INVALID_SYMBOL_REGISTR_POLICY : c_int = (M_rtpLib | 7);
|
||||
pub const S_rtpLib_INSTANTIATE_FAILED : c_int = (M_rtpLib | 8);
|
||||
pub const S_rtpLib_INVALID_TASK_OPTION : c_int = (M_rtpLib | 9);
|
||||
pub const S_rtpLib_RTP_NAME_LENGTH_EXCEEDED : c_int = (M_rtpLib | 10); // rtpInfoGet
|
||||
|
||||
pub const VX_RTP_NAME_LENGTH : c_int = 255; // max name length for diplay
|
||||
|
||||
|
||||
// The 'status' field (32 bit integer) of a RTP holds the RTP state and status.
|
||||
//
|
||||
// NOTE: RTP_STATE_GET() : read the RTP state(s)
|
||||
// RTP_STATE_PUT() : write the RTP state(s)
|
||||
// RTP_STATE_SET() : set a RTP state
|
||||
// RTP_STATE_UNSET() : unset a RTP state
|
||||
//
|
||||
// RTP_STATUS_GET() : read the RTP status
|
||||
// RTP_STATUS_PUT() : write the RTP status
|
||||
// RTP_STATUS_SET() : set a RTP status
|
||||
// RTP_STATUS_UNSET() : unset a RTP status
|
||||
//
|
||||
// The PUT/SET/UNSET macros are available only in the kernel headers.
|
||||
|
||||
|
||||
// RTP states
|
||||
|
||||
pub const RTP_STATE_CREATE : c_int = 0x0001; // RrtpStructTP is under construction
|
||||
pub const RTP_STATE_NORMAL : c_int = 0x0002; // RrtpStructTP is ready
|
||||
pub const RTP_STATE_DELETE : c_int = 0x0004; // RrtpStructTP is being deleted
|
||||
|
||||
pub const RTP_STATUS_STOP : c_int = 0x0100; // RTP hrtpStructas recieved stopped signal
|
||||
pub const RTP_STATUS_ELECTED_DELETER : c_int = 0x0200; // RTP drtpStructelete has started
|
||||
|
||||
pub const RTP_STATE_MASK : c_int = (RTP_STATE_CREATE | RTP_STATE_NORMAL |
|
||||
RTP_STATE_DELETE);
|
||||
pub const RTP_STATUS_MASK : c_int = (RTP_STATUS_STOP | RTP_STATUS_ELECTED_DELETER);
|
||||
|
||||
pub fn RTP_STATE_GET (value : c_int) -> c_int {
|
||||
value & RTP_STATE_MASK
|
||||
}
|
||||
pub fn RTP_STATUS_GET (value : c_int) -> c_int {
|
||||
value & RTP_STATUS_MASK
|
||||
}
|
||||
|
||||
// Indicates that the RTP_ID returned is not valid.
|
||||
|
||||
// RTP_ID_ERROR is supposed to be set to -1, but you can't set
|
||||
// an unsigned value to a negative without casting, and you
|
||||
// can't cast unless the size of the integer types are the same,
|
||||
// but the size of RTP_ID may differ between kernel and user space.
|
||||
// Bitwise or-ing min and max should get the same result.
|
||||
pub const RTP_ID_ERROR : RTP_ID = RTP_ID::min_value() | RTP_ID::max_value();
|
||||
|
||||
// IS_RTP_ C macros
|
||||
|
||||
pub fn IS_RTP_STATE_NORMAL (value : c_int) -> bool {
|
||||
(RTP_STATE_GET(value) & RTP_STATE_NORMAL) == RTP_STATE_NORMAL
|
||||
}
|
||||
pub fn IS_RTP_STATE_CREATE (value : c_int) -> bool {
|
||||
(RTP_STATE_GET(value) & RTP_STATE_CREATE) == RTP_STATE_CREATE
|
||||
}
|
||||
pub fn IS_RTP_STATE_DELETE (value : c_int) -> bool {
|
||||
(RTP_STATE_GET(value) & RTP_STATE_DELETE) == RTP_STATE_DELETE
|
||||
}
|
||||
pub fn IS_RTP_STATUS_STOP (value : c_int) -> bool {
|
||||
(RTP_STATUS_GET(value) & RTP_STATUS_STOP ) == RTP_STATUS_STOP
|
||||
}
|
||||
pub fn IS_RTP_STATUS_ELECTED_DELETER (value : c_int) -> bool {
|
||||
(RTP_STATUS_GET(value) & RTP_STATUS_ELECTED_DELETER) == RTP_STATUS_ELECTED_DELETER
|
||||
}
|
||||
|
||||
// **** end of definitions for rtpLibCommon.h ****
|
||||
|
||||
|
||||
|
||||
|
||||
// **** definitions for rtpLib.h ****
|
||||
|
||||
pub fn rtpExit(exitCode : c_int) -> ! {
|
||||
unsafe{ libc::exit (exitCode) }
|
||||
}
|
||||
|
||||
/* rtpLib.h in the kernel
|
||||
pub const RTP_DEL_VIA_TASK_DELETE : c_int = 0x1; // rtpDelete() via taskDestroy()
|
||||
pub const RTP_DEL_FORCE : c_int = 0x2; // Forceful rtpDelete()
|
||||
pub const RTP_ID_ANY : RTP_ID = 0; // used for when a kernel task
|
||||
// wants to wait for the next
|
||||
// RTP to finish
|
||||
|
||||
|
||||
// Function pointers
|
||||
|
||||
pub type RTP_PRE_CREATE_HOOK = size_t;
|
||||
pub type RTP_POST_CREATE_HOOK = size_t;
|
||||
pub type RTP_INIT_COMPLETE_HOOK = size_t;
|
||||
pub type RTP_DELETE_HOOK = size_t;
|
||||
*/
|
||||
|
||||
// **** end of definitions for rtpLib.h ****
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// **** definitions for signal.h ****
|
||||
pub fn rtpKill(rtpId : RTP_ID, signo : c_int) -> c_int {
|
||||
unsafe{ libc::kill(rtpId as c_int, signo) }
|
||||
}
|
||||
|
||||
pub fn rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : size_t) -> c_int {
|
||||
unsafe{ libc::sigqueue(rtpId as c_int, signo, value) }
|
||||
}
|
||||
|
||||
pub fn _rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : *mut size_t, code : c_int) -> c_int {
|
||||
unsafe{ libc::_sigqueue(rtpId, signo, value, code) }
|
||||
}
|
||||
|
||||
pub fn taskRaise(signo : c_int) -> c_int {
|
||||
unsafe{ libc::taskKill(libc::taskIdSelf(), signo) }
|
||||
}
|
||||
pub fn rtpRaise(signo : c_int) -> c_int {
|
||||
unsafe{ libc::raise(signo) }
|
||||
}
|
||||
|
||||
// **** end of definitions for signal.h ****
|
||||
|
||||
|
||||
|
||||
// **** definitions for taskLibCommon.h ****
|
||||
pub const VX_PRIVATE_ENV : c_int = 0x0080; // 1 = private environment variables
|
||||
pub const VX_NO_STACK_FILL : c_int = 0x0100; // 1 = avoid stack fill of 0xee
|
||||
pub const VX_PRIVATE_UMASK : c_int = 0x0400; // 1 = private file creation mode mask
|
||||
pub const VX_TASK_NOACTIVATE : c_int = 0x2000; // taskOpen() does not taskActivate()
|
||||
pub const VX_NO_STACK_PROTECT : c_int = 0x4000; // no over/underflow stack protection,
|
||||
// stack space remains executable
|
||||
|
||||
// define for all valid user task options
|
||||
|
||||
pub const VX_USR_TASK_OPTIONS_BASE: c_int = (VX_PRIVATE_ENV |
|
||||
VX_NO_STACK_FILL |
|
||||
VX_TASK_NOACTIVATE |
|
||||
VX_NO_STACK_PROTECT |
|
||||
VX_PRIVATE_UMASK);
|
||||
|
||||
// **** end of definitions for taskLibCommon.h ****
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
// functions in rtpLibCommon.h
|
||||
|
||||
// forward declarations
|
||||
pub fn rtpSpawn (
|
||||
pubrtpFileName : *const c_char,
|
||||
argv : *const *const c_char,
|
||||
envp : *const *const c_char,
|
||||
priority : c_int,
|
||||
uStackSize : size_t,
|
||||
options : c_int,
|
||||
taskOptions : c_int,
|
||||
) -> RTP_ID;
|
||||
|
||||
pub fn rtpInfoGet (
|
||||
rtpId : RTP_ID,
|
||||
rtpStruct : *mut RTP_DESC,
|
||||
) -> c_int;
|
||||
|
||||
/* functions in rtpLib.h for kernel
|
||||
|
||||
|
||||
// function declarations
|
||||
|
||||
pub fn rtpDelete (
|
||||
id : RTP_ID,
|
||||
options : c_int,
|
||||
status : c_int,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpDeleteForce (
|
||||
rtpId : RTP_ID
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpShow (
|
||||
rtpNameOrId : *mut c_char,
|
||||
level : c_int,
|
||||
) -> BOOL;
|
||||
|
||||
// RTP signals are always present when RTPs are included. The public RTP
|
||||
// signal APIs are declared here.
|
||||
|
||||
|
||||
pub fn rtpKill (
|
||||
rtpId : RTP_ID,
|
||||
signo : c_int,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpSigqueue (
|
||||
rtpId : RTP_ID,
|
||||
signo : c_int,
|
||||
value : size_t, // Actual type is const union sigval value,
|
||||
// which is a union of int and void *
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpTaskKill (
|
||||
tid : TASK_ID,
|
||||
signo : c_int,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpTaskSigqueue (
|
||||
tid : TASK_ID,
|
||||
signo : c_int,
|
||||
value : const size_t, // Actual type is const union sigval,
|
||||
// which is a union of int and void *
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpWait (
|
||||
rtpWaitId : RTP_ID,
|
||||
timeout : libc::alloc_jemalloc_Vx_ticks_t,
|
||||
pRtpId : *mut RTP_ID,
|
||||
pStatus : *mut c_int,
|
||||
) -> c_int;
|
||||
|
||||
// Other public functions
|
||||
|
||||
|
||||
pub fn rtpPreCreateHookAdd (
|
||||
hook : RTP_PRE_CREATE_HOOK,
|
||||
addToHead : BOOL,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpPreCreateHookDelete (
|
||||
hook : RTP_POST_CREATE_HOOK,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpPostCreateHookAdd (
|
||||
hook : RTP_POST_CREATE_HOOK,
|
||||
addToHead : BOOL,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpPostCreateHookDelete (
|
||||
hook : RTP_POST_CREATE_HOOK,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpInitCompleteHookAdd (
|
||||
hook : RTP_INIT_COMPLETE_HOOK,
|
||||
addToHead : BOOL,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpInitCompleteHookDelete (
|
||||
hook : RTP_INIT_COMPLETE_HOOK,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpDeleteHookAdd (
|
||||
hook : RTP_DELETE_HOOK,
|
||||
addToHead : BOOL,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpDeleteHookDelete (
|
||||
hook : RTP_DELETE_HOOK,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpMemShow (
|
||||
rtpNameOrId : *mut c_char,
|
||||
level : c_int,
|
||||
) -> c_int;
|
||||
|
||||
pub fn rtpHookShow (
|
||||
|
||||
);
|
||||
*/
|
||||
}
|
@ -19,6 +19,5 @@ syntax_pos = { path = "../libsyntax_pos" }
|
||||
errors = { path = "../librustc_errors", package = "rustc_errors" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
rustc_lexer = { path = "../librustc_lexer" }
|
||||
rustc_macros = { path = "../librustc_macros" }
|
||||
rustc_target = { path = "../librustc_target" }
|
||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
||||
|
@ -1017,10 +1017,6 @@ impl<'a> ExtCtxt<'a> {
|
||||
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
|
||||
self.parse_sess.span_diagnostic.span_err_with_code(sp, msg, code);
|
||||
}
|
||||
pub fn mut_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
|
||||
-> DiagnosticBuilder<'a> {
|
||||
self.parse_sess.span_diagnostic.mut_span_err(sp, msg)
|
||||
}
|
||||
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.parse_sess.span_diagnostic.span_warn(sp, msg);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::config::StripUnconfigured;
|
||||
use crate::ext::base::*;
|
||||
use crate::ext::proc_macro::{collect_derives, MarkAttrs};
|
||||
use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind};
|
||||
use crate::ext::tt::macro_rules::annotate_err_with_kind;
|
||||
use crate::ext::mbe::macro_rules::annotate_err_with_kind;
|
||||
use crate::ext::placeholders::{placeholder, PlaceholderExpander};
|
||||
use crate::feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err};
|
||||
use crate::mut_visit::*;
|
||||
@ -115,8 +115,8 @@ macro_rules! ast_fragments {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MacResult for crate::ext::tt::macro_rules::ParserAnyMacro<'a> {
|
||||
$(fn $make_ast(self: Box<crate::ext::tt::macro_rules::ParserAnyMacro<'a>>)
|
||||
impl<'a> MacResult for crate::ext::mbe::macro_rules::ParserAnyMacro<'a> {
|
||||
$(fn $make_ast(self: Box<crate::ext::mbe::macro_rules::ParserAnyMacro<'a>>)
|
||||
-> Option<$AstTy> {
|
||||
Some(self.make(AstFragmentKind::$Kind).$make_ast())
|
||||
})*
|
||||
@ -384,7 +384,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
let attr = attr::find_by_name(item.attrs(), sym::derive)
|
||||
.expect("`derive` attribute should exist");
|
||||
let span = attr.span;
|
||||
let mut err = self.cx.mut_span_err(span,
|
||||
let mut err = self.cx.struct_span_err(span,
|
||||
"`derive` may only be applied to structs, enums and unions");
|
||||
if let ast::AttrStyle::Inner = attr.style {
|
||||
let trait_list = derives.iter()
|
||||
|
166
src/libsyntax/ext/mbe.rs
Normal file
166
src/libsyntax/ext/mbe.rs
Normal file
@ -0,0 +1,166 @@
|
||||
//! This module implements declarative macros: old `macro_rules` and the newer
|
||||
//! `macro`. Declarative macros are also known as "macro by example", and that's
|
||||
//! why we call this module `mbe`. For external documentation, prefer the
|
||||
//! official terminology: "declarative macros".
|
||||
|
||||
crate mod transcribe;
|
||||
crate mod macro_check;
|
||||
crate mod macro_parser;
|
||||
crate mod macro_rules;
|
||||
crate mod quoted;
|
||||
|
||||
use crate::ast;
|
||||
use crate::parse::token::{self, Token, TokenKind};
|
||||
use crate::tokenstream::{DelimSpan};
|
||||
|
||||
use syntax_pos::{BytePos, Span};
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note
|
||||
/// that the delimiter itself might be `NoDelim`.
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)]
|
||||
struct Delimited {
|
||||
delim: token::DelimToken,
|
||||
tts: Vec<TokenTree>,
|
||||
}
|
||||
|
||||
impl Delimited {
|
||||
/// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter.
|
||||
fn open_tt(&self, span: Span) -> TokenTree {
|
||||
let open_span = if span.is_dummy() {
|
||||
span
|
||||
} else {
|
||||
span.with_hi(span.lo() + BytePos(self.delim.len() as u32))
|
||||
};
|
||||
TokenTree::token(token::OpenDelim(self.delim), open_span)
|
||||
}
|
||||
|
||||
/// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter.
|
||||
fn close_tt(&self, span: Span) -> TokenTree {
|
||||
let close_span = if span.is_dummy() {
|
||||
span
|
||||
} else {
|
||||
span.with_lo(span.hi() - BytePos(self.delim.len() as u32))
|
||||
};
|
||||
TokenTree::token(token::CloseDelim(self.delim), close_span)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)]
|
||||
struct SequenceRepetition {
|
||||
/// The sequence of token trees
|
||||
tts: Vec<TokenTree>,
|
||||
/// The optional separator
|
||||
separator: Option<Token>,
|
||||
/// Whether the sequence can be repeated zero (*), or one or more times (+)
|
||||
kleene: KleeneToken,
|
||||
/// The number of `Match`s that appear in the sequence (and subsequences)
|
||||
num_captures: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)]
|
||||
struct KleeneToken {
|
||||
span: Span,
|
||||
op: KleeneOp,
|
||||
}
|
||||
|
||||
impl KleeneToken {
|
||||
fn new(op: KleeneOp, span: Span) -> KleeneToken {
|
||||
KleeneToken { span, op }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
|
||||
/// for token sequences.
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
||||
enum KleeneOp {
|
||||
/// Kleene star (`*`) for zero or more repetitions
|
||||
ZeroOrMore,
|
||||
/// Kleene plus (`+`) for one or more repetitions
|
||||
OneOrMore,
|
||||
/// Kleene optional (`?`) for zero or one reptitions
|
||||
ZeroOrOne,
|
||||
}
|
||||
|
||||
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)`
|
||||
/// are "first-class" token trees. Useful for parsing macros.
|
||||
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
enum TokenTree {
|
||||
Token(Token),
|
||||
Delimited(DelimSpan, Lrc<Delimited>),
|
||||
/// A kleene-style repetition sequence
|
||||
Sequence(DelimSpan, Lrc<SequenceRepetition>),
|
||||
/// e.g., `$var`
|
||||
MetaVar(Span, ast::Ident),
|
||||
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
|
||||
MetaVarDecl(
|
||||
Span,
|
||||
ast::Ident, /* name to bind */
|
||||
ast::Ident, /* kind of nonterminal */
|
||||
),
|
||||
}
|
||||
|
||||
impl TokenTree {
|
||||
/// Return the number of tokens in the tree.
|
||||
fn len(&self) -> usize {
|
||||
match *self {
|
||||
TokenTree::Delimited(_, ref delimed) => match delimed.delim {
|
||||
token::NoDelim => delimed.tts.len(),
|
||||
_ => delimed.tts.len() + 2,
|
||||
},
|
||||
TokenTree::Sequence(_, ref seq) => seq.tts.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given token tree is delimited.
|
||||
fn is_delimited(&self) -> bool {
|
||||
match *self {
|
||||
TokenTree::Delimited(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given token tree is a token of the given kind.
|
||||
fn is_token(&self, expected_kind: &TokenKind) -> bool {
|
||||
match self {
|
||||
TokenTree::Token(Token { kind: actual_kind, .. }) => actual_kind == expected_kind,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences.
|
||||
fn get_tt(&self, index: usize) -> TokenTree {
|
||||
match (self, index) {
|
||||
(&TokenTree::Delimited(_, ref delimed), _) if delimed.delim == token::NoDelim => {
|
||||
delimed.tts[index].clone()
|
||||
}
|
||||
(&TokenTree::Delimited(span, ref delimed), _) => {
|
||||
if index == 0 {
|
||||
return delimed.open_tt(span.open);
|
||||
}
|
||||
if index == delimed.tts.len() + 1 {
|
||||
return delimed.close_tt(span.close);
|
||||
}
|
||||
delimed.tts[index - 1].clone()
|
||||
}
|
||||
(&TokenTree::Sequence(_, ref seq), _) => seq.tts[index].clone(),
|
||||
_ => panic!("Cannot expand a token tree"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the `TokenTree`'s span.
|
||||
fn span(&self) -> Span {
|
||||
match *self {
|
||||
TokenTree::Token(Token { span, .. })
|
||||
| TokenTree::MetaVar(span, _)
|
||||
| TokenTree::MetaVarDecl(span, _, _) => span,
|
||||
TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(),
|
||||
}
|
||||
}
|
||||
|
||||
fn token(kind: TokenKind, span: Span) -> TokenTree {
|
||||
TokenTree::Token(Token::new(kind, span))
|
||||
}
|
||||
}
|
@ -106,7 +106,7 @@
|
||||
//! bound.
|
||||
use crate::ast::NodeId;
|
||||
use crate::early_buffered_lints::BufferedEarlyLintId;
|
||||
use crate::ext::tt::quoted::{KleeneToken, TokenTree};
|
||||
use crate::ext::mbe::{KleeneToken, TokenTree};
|
||||
use crate::parse::token::TokenKind;
|
||||
use crate::parse::token::{DelimToken, Token};
|
||||
use crate::parse::ParseSess;
|
||||
@ -196,7 +196,7 @@ struct MacroState<'a> {
|
||||
/// - `node_id` is used to emit lints
|
||||
/// - `span` is used when no spans are available
|
||||
/// - `lhses` and `rhses` should have the same length and represent the macro definition
|
||||
pub fn check_meta_variables(
|
||||
pub(super) fn check_meta_variables(
|
||||
sess: &ParseSess,
|
||||
node_id: NodeId,
|
||||
span: Span,
|
@ -70,12 +70,12 @@
|
||||
//! eof: [a $( a )* a b ·]
|
||||
//! ```
|
||||
|
||||
pub use NamedMatch::*;
|
||||
pub use ParseResult::*;
|
||||
crate use NamedMatch::*;
|
||||
crate use ParseResult::*;
|
||||
use TokenTreeOrTokenTreeSlice::*;
|
||||
|
||||
use crate::ast::{Ident, Name};
|
||||
use crate::ext::tt::quoted::{self, TokenTree};
|
||||
use crate::ext::mbe::{self, TokenTree};
|
||||
use crate::parse::{Directory, ParseSess};
|
||||
use crate::parse::parser::{Parser, PathStyle};
|
||||
use crate::parse::token::{self, DocComment, Nonterminal, Token};
|
||||
@ -195,7 +195,7 @@ struct MatcherPos<'root, 'tt> {
|
||||
// `None`.
|
||||
|
||||
/// The KleeneOp of this sequence if we are in a repetition.
|
||||
seq_op: Option<quoted::KleeneOp>,
|
||||
seq_op: Option<mbe::KleeneOp>,
|
||||
|
||||
/// The separator if we are in a repetition.
|
||||
sep: Option<Token>,
|
||||
@ -267,7 +267,7 @@ impl<'root, 'tt> DerefMut for MatcherPosHandle<'root, 'tt> {
|
||||
}
|
||||
|
||||
/// Represents the possible results of an attempted parse.
|
||||
pub enum ParseResult<T> {
|
||||
crate enum ParseResult<T> {
|
||||
/// Parsed successfully.
|
||||
Success(T),
|
||||
/// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected
|
||||
@ -279,10 +279,10 @@ pub enum ParseResult<T> {
|
||||
|
||||
/// A `ParseResult` where the `Success` variant contains a mapping of `Ident`s to `NamedMatch`es.
|
||||
/// This represents the mapping of metavars to the token trees they bind to.
|
||||
pub type NamedParseResult = ParseResult<FxHashMap<Ident, NamedMatch>>;
|
||||
crate type NamedParseResult = ParseResult<FxHashMap<Ident, NamedMatch>>;
|
||||
|
||||
/// Count how many metavars are named in the given matcher `ms`.
|
||||
pub fn count_names(ms: &[TokenTree]) -> usize {
|
||||
pub(super) fn count_names(ms: &[TokenTree]) -> usize {
|
||||
ms.iter().fold(0, |count, elt| {
|
||||
count + match *elt {
|
||||
TokenTree::Sequence(_, ref seq) => seq.num_captures,
|
||||
@ -352,7 +352,7 @@ fn initial_matcher_pos<'root, 'tt>(ms: &'tt [TokenTree], open: Span) -> MatcherP
|
||||
/// only on the nesting depth of `ast::TTSeq`s in the originating
|
||||
/// token tree it was derived from.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NamedMatch {
|
||||
crate enum NamedMatch {
|
||||
MatchedSeq(Lrc<NamedMatchVec>, DelimSpan),
|
||||
MatchedNonterminal(Lrc<Nonterminal>),
|
||||
}
|
||||
@ -415,7 +415,7 @@ fn nameize<I: Iterator<Item = NamedMatch>>(
|
||||
|
||||
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
|
||||
/// other tokens, this is "unexpected token...".
|
||||
pub fn parse_failure_msg(tok: &Token) -> String {
|
||||
crate fn parse_failure_msg(tok: &Token) -> String {
|
||||
match tok.kind {
|
||||
token::Eof => "unexpected end of macro invocation".to_string(),
|
||||
_ => format!(
|
||||
@ -532,7 +532,7 @@ fn inner_parse_loop<'root, 'tt>(
|
||||
}
|
||||
// We don't need a separator. Move the "dot" back to the beginning of the matcher
|
||||
// and try to match again UNLESS we are only allowed to have _one_ repetition.
|
||||
else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) {
|
||||
else if item.seq_op != Some(mbe::KleeneOp::ZeroOrOne) {
|
||||
item.match_cur = item.match_lo;
|
||||
item.idx = 0;
|
||||
cur_items.push(item);
|
||||
@ -555,8 +555,8 @@ fn inner_parse_loop<'root, 'tt>(
|
||||
// implicitly disallowing OneOrMore from having 0 matches here. Thus, that will
|
||||
// result in a "no rules expected token" error by virtue of this matcher not
|
||||
// working.
|
||||
if seq.kleene.op == quoted::KleeneOp::ZeroOrMore
|
||||
|| seq.kleene.op == quoted::KleeneOp::ZeroOrOne
|
||||
if seq.kleene.op == mbe::KleeneOp::ZeroOrMore
|
||||
|| seq.kleene.op == mbe::KleeneOp::ZeroOrOne
|
||||
{
|
||||
let mut new_item = item.clone();
|
||||
new_item.match_cur += seq.num_captures;
|
||||
@ -648,7 +648,7 @@ fn inner_parse_loop<'root, 'tt>(
|
||||
/// - `directory`: Information about the file locations (needed for the black-box parser)
|
||||
/// - `recurse_into_modules`: Whether or not to recurse into modules (needed for the black-box
|
||||
/// parser)
|
||||
pub fn parse(
|
||||
pub(super) fn parse(
|
||||
sess: &ParseSess,
|
||||
tts: TokenStream,
|
||||
ms: &[TokenTree],
|
@ -4,12 +4,12 @@ use crate::edition::Edition;
|
||||
use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
|
||||
use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind};
|
||||
use crate::ext::expand::{AstFragment, AstFragmentKind};
|
||||
use crate::ext::tt::macro_check;
|
||||
use crate::ext::tt::macro_parser::{parse, parse_failure_msg};
|
||||
use crate::ext::tt::macro_parser::{Error, Failure, Success};
|
||||
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq};
|
||||
use crate::ext::tt::quoted;
|
||||
use crate::ext::tt::transcribe::transcribe;
|
||||
use crate::ext::mbe;
|
||||
use crate::ext::mbe::macro_check;
|
||||
use crate::ext::mbe::macro_parser::{parse, parse_failure_msg};
|
||||
use crate::ext::mbe::macro_parser::{Error, Failure, Success};
|
||||
use crate::ext::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedParseResult};
|
||||
use crate::ext::mbe::transcribe::transcribe;
|
||||
use crate::feature_gate::Features;
|
||||
use crate::parse::parser::Parser;
|
||||
use crate::parse::token::TokenKind::*;
|
||||
@ -35,7 +35,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
|
||||
`ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \
|
||||
`literal`, `path`, `meta`, `tt`, `item` and `vis`";
|
||||
|
||||
pub struct ParserAnyMacro<'a> {
|
||||
crate struct ParserAnyMacro<'a> {
|
||||
parser: Parser<'a>,
|
||||
|
||||
/// Span of the expansion site of the macro this parser is for
|
||||
@ -45,7 +45,11 @@ pub struct ParserAnyMacro<'a> {
|
||||
arm_span: Span,
|
||||
}
|
||||
|
||||
pub fn annotate_err_with_kind(err: &mut DiagnosticBuilder<'_>, kind: AstFragmentKind, span: Span) {
|
||||
crate fn annotate_err_with_kind(
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
kind: AstFragmentKind,
|
||||
span: Span,
|
||||
) {
|
||||
match kind {
|
||||
AstFragmentKind::Ty => {
|
||||
err.span_label(span, "this macro call doesn't expand to a type");
|
||||
@ -58,7 +62,7 @@ pub fn annotate_err_with_kind(err: &mut DiagnosticBuilder<'_>, kind: AstFragment
|
||||
}
|
||||
|
||||
impl<'a> ParserAnyMacro<'a> {
|
||||
pub fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
|
||||
crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
|
||||
let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self;
|
||||
let fragment = panictry!(parser.parse_ast_fragment(kind, true).map_err(|mut e| {
|
||||
if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") {
|
||||
@ -131,8 +135,8 @@ struct MacroRulesMacroExpander {
|
||||
name: ast::Ident,
|
||||
span: Span,
|
||||
transparency: Transparency,
|
||||
lhses: Vec<quoted::TokenTree>,
|
||||
rhses: Vec<quoted::TokenTree>,
|
||||
lhses: Vec<mbe::TokenTree>,
|
||||
rhses: Vec<mbe::TokenTree>,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
@ -165,8 +169,8 @@ fn generic_extension<'cx>(
|
||||
name: ast::Ident,
|
||||
transparency: Transparency,
|
||||
arg: TokenStream,
|
||||
lhses: &[quoted::TokenTree],
|
||||
rhses: &[quoted::TokenTree],
|
||||
lhses: &[mbe::TokenTree],
|
||||
rhses: &[mbe::TokenTree],
|
||||
) -> Box<dyn MacResult + 'cx> {
|
||||
if cx.trace_macros() {
|
||||
trace_macros_note(cx, sp, format!("expanding `{}! {{ {} }}`", name, arg));
|
||||
@ -178,7 +182,7 @@ fn generic_extension<'cx>(
|
||||
for (i, lhs) in lhses.iter().enumerate() {
|
||||
// try each arm's matchers
|
||||
let lhs_tt = match *lhs {
|
||||
quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
_ => cx.span_bug(sp, "malformed macro lhs"),
|
||||
};
|
||||
|
||||
@ -186,7 +190,7 @@ fn generic_extension<'cx>(
|
||||
Success(named_matches) => {
|
||||
let rhs = match rhses[i] {
|
||||
// ignore delimiters
|
||||
quoted::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
|
||||
mbe::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
|
||||
_ => cx.span_bug(sp, "malformed macro rhs"),
|
||||
};
|
||||
let arm_span = rhses[i].span();
|
||||
@ -254,7 +258,7 @@ fn generic_extension<'cx>(
|
||||
for lhs in lhses {
|
||||
// try each arm's matchers
|
||||
let lhs_tt = match *lhs {
|
||||
quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
_ => continue,
|
||||
};
|
||||
match TokenTree::parse(cx, lhs_tt, arg.clone()) {
|
||||
@ -284,8 +288,8 @@ fn generic_extension<'cx>(
|
||||
//
|
||||
// Holy self-referential!
|
||||
|
||||
/// Converts a `macro_rules!` invocation into a syntax extension.
|
||||
pub fn compile(
|
||||
/// Converts a macro item into a syntax extension.
|
||||
pub fn compile_declarative_macro(
|
||||
sess: &ParseSess,
|
||||
features: &Features,
|
||||
def: &ast::Item,
|
||||
@ -308,32 +312,32 @@ pub fn compile(
|
||||
// ...quasiquoting this would be nice.
|
||||
// These spans won't matter, anyways
|
||||
let argument_gram = vec![
|
||||
quoted::TokenTree::Sequence(
|
||||
mbe::TokenTree::Sequence(
|
||||
DelimSpan::dummy(),
|
||||
Lrc::new(quoted::SequenceRepetition {
|
||||
Lrc::new(mbe::SequenceRepetition {
|
||||
tts: vec![
|
||||
quoted::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
|
||||
quoted::TokenTree::token(token::FatArrow, def.span),
|
||||
quoted::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
|
||||
mbe::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
|
||||
mbe::TokenTree::token(token::FatArrow, def.span),
|
||||
mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
|
||||
],
|
||||
separator: Some(Token::new(
|
||||
if body.legacy { token::Semi } else { token::Comma },
|
||||
def.span,
|
||||
)),
|
||||
kleene: quoted::KleeneToken::new(quoted::KleeneOp::OneOrMore, def.span),
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
|
||||
num_captures: 2,
|
||||
}),
|
||||
),
|
||||
// to phase into semicolon-termination instead of semicolon-separation
|
||||
quoted::TokenTree::Sequence(
|
||||
mbe::TokenTree::Sequence(
|
||||
DelimSpan::dummy(),
|
||||
Lrc::new(quoted::SequenceRepetition {
|
||||
tts: vec![quoted::TokenTree::token(
|
||||
Lrc::new(mbe::SequenceRepetition {
|
||||
tts: vec![mbe::TokenTree::token(
|
||||
if body.legacy { token::Semi } else { token::Comma },
|
||||
def.span,
|
||||
)],
|
||||
separator: None,
|
||||
kleene: quoted::KleeneToken::new(quoted::KleeneOp::ZeroOrMore, def.span),
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span),
|
||||
num_captures: 0,
|
||||
}),
|
||||
),
|
||||
@ -363,7 +367,7 @@ pub fn compile(
|
||||
.map(|m| {
|
||||
if let MatchedNonterminal(ref nt) = *m {
|
||||
if let NtTT(ref tt) = **nt {
|
||||
let tt = quoted::parse(
|
||||
let tt = mbe::quoted::parse(
|
||||
tt.clone().into(),
|
||||
true,
|
||||
sess,
|
||||
@ -380,7 +384,7 @@ pub fn compile(
|
||||
}
|
||||
sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
|
||||
})
|
||||
.collect::<Vec<quoted::TokenTree>>(),
|
||||
.collect::<Vec<mbe::TokenTree>>(),
|
||||
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
|
||||
};
|
||||
|
||||
@ -390,7 +394,7 @@ pub fn compile(
|
||||
.map(|m| {
|
||||
if let MatchedNonterminal(ref nt) = *m {
|
||||
if let NtTT(ref tt) = **nt {
|
||||
return quoted::parse(
|
||||
return mbe::quoted::parse(
|
||||
tt.clone().into(),
|
||||
false,
|
||||
sess,
|
||||
@ -405,7 +409,7 @@ pub fn compile(
|
||||
}
|
||||
sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
|
||||
})
|
||||
.collect::<Vec<quoted::TokenTree>>(),
|
||||
.collect::<Vec<mbe::TokenTree>>(),
|
||||
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
|
||||
};
|
||||
|
||||
@ -450,11 +454,11 @@ fn check_lhs_nt_follows(
|
||||
sess: &ParseSess,
|
||||
features: &Features,
|
||||
attrs: &[ast::Attribute],
|
||||
lhs: "ed::TokenTree,
|
||||
lhs: &mbe::TokenTree,
|
||||
) -> bool {
|
||||
// lhs is going to be like TokenTree::Delimited(...), where the
|
||||
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
|
||||
if let quoted::TokenTree::Delimited(_, ref tts) = *lhs {
|
||||
if let mbe::TokenTree::Delimited(_, ref tts) = *lhs {
|
||||
check_matcher(sess, features, attrs, &tts.tts)
|
||||
} else {
|
||||
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
|
||||
@ -467,8 +471,8 @@ fn check_lhs_nt_follows(
|
||||
|
||||
/// Checks that the lhs contains no repetition which could match an empty token
|
||||
/// tree, because then the matcher would hang indefinitely.
|
||||
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
|
||||
use quoted::TokenTree;
|
||||
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
|
||||
use mbe::TokenTree;
|
||||
for tt in tts {
|
||||
match *tt {
|
||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (),
|
||||
@ -482,8 +486,8 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
|
||||
&& seq.tts.iter().all(|seq_tt| match *seq_tt {
|
||||
TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis,
|
||||
TokenTree::Sequence(_, ref sub_seq) => {
|
||||
sub_seq.kleene.op == quoted::KleeneOp::ZeroOrMore
|
||||
|| sub_seq.kleene.op == quoted::KleeneOp::ZeroOrOne
|
||||
sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
|
||||
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
@ -502,9 +506,9 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn check_rhs(sess: &ParseSess, rhs: "ed::TokenTree) -> bool {
|
||||
fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
|
||||
match *rhs {
|
||||
quoted::TokenTree::Delimited(..) => return true,
|
||||
mbe::TokenTree::Delimited(..) => return true,
|
||||
_ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited"),
|
||||
}
|
||||
false
|
||||
@ -514,7 +518,7 @@ fn check_matcher(
|
||||
sess: &ParseSess,
|
||||
features: &Features,
|
||||
attrs: &[ast::Attribute],
|
||||
matcher: &[quoted::TokenTree],
|
||||
matcher: &[mbe::TokenTree],
|
||||
) -> bool {
|
||||
let first_sets = FirstSets::new(matcher);
|
||||
let empty_suffix = TokenSet::empty();
|
||||
@ -546,8 +550,8 @@ struct FirstSets {
|
||||
}
|
||||
|
||||
impl FirstSets {
|
||||
fn new(tts: &[quoted::TokenTree]) -> FirstSets {
|
||||
use quoted::TokenTree;
|
||||
fn new(tts: &[mbe::TokenTree]) -> FirstSets {
|
||||
use mbe::TokenTree;
|
||||
|
||||
let mut sets = FirstSets { first: FxHashMap::default() };
|
||||
build_recur(&mut sets, tts);
|
||||
@ -594,8 +598,8 @@ impl FirstSets {
|
||||
|
||||
// Reverse scan: Sequence comes before `first`.
|
||||
if subfirst.maybe_empty
|
||||
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
|
||||
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
|
||||
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|
||||
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
|
||||
{
|
||||
// If sequence is potentially empty, then
|
||||
// union them (preserving first emptiness).
|
||||
@ -615,8 +619,8 @@ impl FirstSets {
|
||||
|
||||
// walks forward over `tts` until all potential FIRST tokens are
|
||||
// identified.
|
||||
fn first(&self, tts: &[quoted::TokenTree]) -> TokenSet {
|
||||
use quoted::TokenTree;
|
||||
fn first(&self, tts: &[mbe::TokenTree]) -> TokenSet {
|
||||
use mbe::TokenTree;
|
||||
|
||||
let mut first = TokenSet::empty();
|
||||
for tt in tts.iter() {
|
||||
@ -652,8 +656,8 @@ impl FirstSets {
|
||||
assert!(first.maybe_empty);
|
||||
first.add_all(subfirst);
|
||||
if subfirst.maybe_empty
|
||||
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
|
||||
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
|
||||
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|
||||
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
|
||||
{
|
||||
// Continue scanning for more first
|
||||
// tokens, but also make sure we
|
||||
@ -674,7 +678,7 @@ impl FirstSets {
|
||||
}
|
||||
}
|
||||
|
||||
// A set of `quoted::TokenTree`s, which may include `TokenTree::Match`s
|
||||
// A set of `mbe::TokenTree`s, which may include `TokenTree::Match`s
|
||||
// (for macro-by-example syntactic variables). It also carries the
|
||||
// `maybe_empty` flag; that is true if and only if the matcher can
|
||||
// match an empty token sequence.
|
||||
@ -686,7 +690,7 @@ impl FirstSets {
|
||||
// (Notably, we must allow for *-op to occur zero times.)
|
||||
#[derive(Clone, Debug)]
|
||||
struct TokenSet {
|
||||
tokens: Vec<quoted::TokenTree>,
|
||||
tokens: Vec<mbe::TokenTree>,
|
||||
maybe_empty: bool,
|
||||
}
|
||||
|
||||
@ -698,13 +702,13 @@ impl TokenSet {
|
||||
|
||||
// Returns the set `{ tok }` for the single-token (and thus
|
||||
// non-empty) sequence [tok].
|
||||
fn singleton(tok: quoted::TokenTree) -> Self {
|
||||
fn singleton(tok: mbe::TokenTree) -> Self {
|
||||
TokenSet { tokens: vec![tok], maybe_empty: false }
|
||||
}
|
||||
|
||||
// Changes self to be the set `{ tok }`.
|
||||
// Since `tok` is always present, marks self as non-empty.
|
||||
fn replace_with(&mut self, tok: quoted::TokenTree) {
|
||||
fn replace_with(&mut self, tok: mbe::TokenTree) {
|
||||
self.tokens.clear();
|
||||
self.tokens.push(tok);
|
||||
self.maybe_empty = false;
|
||||
@ -719,7 +723,7 @@ impl TokenSet {
|
||||
}
|
||||
|
||||
// Adds `tok` to the set for `self`, marking sequence as non-empy.
|
||||
fn add_one(&mut self, tok: quoted::TokenTree) {
|
||||
fn add_one(&mut self, tok: mbe::TokenTree) {
|
||||
if !self.tokens.contains(&tok) {
|
||||
self.tokens.push(tok);
|
||||
}
|
||||
@ -727,7 +731,7 @@ impl TokenSet {
|
||||
}
|
||||
|
||||
// Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.)
|
||||
fn add_one_maybe(&mut self, tok: quoted::TokenTree) {
|
||||
fn add_one_maybe(&mut self, tok: mbe::TokenTree) {
|
||||
if !self.tokens.contains(&tok) {
|
||||
self.tokens.push(tok);
|
||||
}
|
||||
@ -768,10 +772,10 @@ fn check_matcher_core(
|
||||
features: &Features,
|
||||
attrs: &[ast::Attribute],
|
||||
first_sets: &FirstSets,
|
||||
matcher: &[quoted::TokenTree],
|
||||
matcher: &[mbe::TokenTree],
|
||||
follow: &TokenSet,
|
||||
) -> TokenSet {
|
||||
use quoted::TokenTree;
|
||||
use mbe::TokenTree;
|
||||
|
||||
let mut last = TokenSet::empty();
|
||||
|
||||
@ -946,8 +950,8 @@ fn check_matcher_core(
|
||||
last
|
||||
}
|
||||
|
||||
fn token_can_be_followed_by_any(tok: "ed::TokenTree) -> bool {
|
||||
if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
|
||||
fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
|
||||
if let mbe::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
|
||||
frag_can_be_followed_by_any(frag_spec.name)
|
||||
} else {
|
||||
// (Non NT's can always be followed by anthing in matchers.)
|
||||
@ -993,8 +997,8 @@ enum IsInFollow {
|
||||
/// break macros that were relying on that binary operator as a
|
||||
/// separator.
|
||||
// when changing this do not forget to update doc/book/macros.md!
|
||||
fn is_in_follow(tok: "ed::TokenTree, frag: Symbol) -> IsInFollow {
|
||||
use quoted::TokenTree;
|
||||
fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow {
|
||||
use mbe::TokenTree;
|
||||
|
||||
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
|
||||
// closing a token tree can never be matched by any fragment;
|
||||
@ -1112,10 +1116,10 @@ fn has_legal_fragment_specifier(
|
||||
sess: &ParseSess,
|
||||
features: &Features,
|
||||
attrs: &[ast::Attribute],
|
||||
tok: "ed::TokenTree,
|
||||
tok: &mbe::TokenTree,
|
||||
) -> Result<(), String> {
|
||||
debug!("has_legal_fragment_specifier({:?})", tok);
|
||||
if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
|
||||
if let mbe::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
|
||||
let frag_span = tok.span();
|
||||
if !is_legal_fragment_specifier(sess, features, attrs, frag_spec.name, frag_span) {
|
||||
return Err(frag_spec.to_string());
|
||||
@ -1156,14 +1160,27 @@ fn is_legal_fragment_specifier(
|
||||
}
|
||||
}
|
||||
|
||||
fn quoted_tt_to_string(tt: "ed::TokenTree) -> String {
|
||||
fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
|
||||
match *tt {
|
||||
quoted::TokenTree::Token(ref token) => crate::print::pprust::token_to_string(&token),
|
||||
quoted::TokenTree::MetaVar(_, name) => format!("${}", name),
|
||||
quoted::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind),
|
||||
mbe::TokenTree::Token(ref token) => crate::print::pprust::token_to_string(&token),
|
||||
mbe::TokenTree::MetaVar(_, name) => format!("${}", name),
|
||||
mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind),
|
||||
_ => panic!(
|
||||
"unexpected quoted::TokenTree::{{Sequence or Delimited}} \
|
||||
"unexpected mbe::TokenTree::{{Sequence or Delimited}} \
|
||||
in follow set checker"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenTree {
|
||||
/// Use this token tree as a matcher to parse given tts.
|
||||
fn parse(cx: &ExtCtxt<'_>, mtch: &[mbe::TokenTree], tts: TokenStream)
|
||||
-> NamedParseResult {
|
||||
// `None` is because we're not interpolating
|
||||
let directory = Directory {
|
||||
path: Cow::from(cx.current_expansion.module.directory.as_path()),
|
||||
ownership: cx.current_expansion.directory_ownership,
|
||||
};
|
||||
parse(cx.parse_sess(), tts, mtch, Some(directory), true)
|
||||
}
|
||||
}
|
@ -1,179 +1,19 @@
|
||||
use crate::ast;
|
||||
use crate::ast::NodeId;
|
||||
use crate::ext::tt::macro_parser;
|
||||
use crate::ext::mbe::macro_parser;
|
||||
use crate::ext::mbe::{TokenTree, KleeneOp, KleeneToken, SequenceRepetition, Delimited};
|
||||
use crate::feature_gate::Features;
|
||||
use crate::parse::token::{self, Token, TokenKind};
|
||||
use crate::parse::token::{self, Token};
|
||||
use crate::parse::ParseSess;
|
||||
use crate::print::pprust;
|
||||
use crate::symbol::kw;
|
||||
use crate::tokenstream::{self, DelimSpan};
|
||||
use crate::tokenstream;
|
||||
|
||||
use syntax_pos::{edition::Edition, BytePos, Span};
|
||||
use syntax_pos::{edition::Edition, Span};
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use std::iter::Peekable;
|
||||
|
||||
/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note
|
||||
/// that the delimiter itself might be `NoDelim`.
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)]
|
||||
pub struct Delimited {
|
||||
pub delim: token::DelimToken,
|
||||
pub tts: Vec<TokenTree>,
|
||||
}
|
||||
|
||||
impl Delimited {
|
||||
/// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter.
|
||||
pub fn open_tt(&self, span: Span) -> TokenTree {
|
||||
let open_span = if span.is_dummy() {
|
||||
span
|
||||
} else {
|
||||
span.with_hi(span.lo() + BytePos(self.delim.len() as u32))
|
||||
};
|
||||
TokenTree::token(token::OpenDelim(self.delim), open_span)
|
||||
}
|
||||
|
||||
/// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter.
|
||||
pub fn close_tt(&self, span: Span) -> TokenTree {
|
||||
let close_span = if span.is_dummy() {
|
||||
span
|
||||
} else {
|
||||
span.with_lo(span.hi() - BytePos(self.delim.len() as u32))
|
||||
};
|
||||
TokenTree::token(token::CloseDelim(self.delim), close_span)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)]
|
||||
pub struct SequenceRepetition {
|
||||
/// The sequence of token trees
|
||||
pub tts: Vec<TokenTree>,
|
||||
/// The optional separator
|
||||
pub separator: Option<Token>,
|
||||
/// Whether the sequence can be repeated zero (*), or one or more times (+)
|
||||
pub kleene: KleeneToken,
|
||||
/// The number of `Match`s that appear in the sequence (and subsequences)
|
||||
pub num_captures: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)]
|
||||
pub struct KleeneToken {
|
||||
pub span: Span,
|
||||
pub op: KleeneOp,
|
||||
}
|
||||
|
||||
impl KleeneToken {
|
||||
pub fn new(op: KleeneOp, span: Span) -> KleeneToken {
|
||||
KleeneToken { span, op }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
|
||||
/// for token sequences.
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
||||
pub enum KleeneOp {
|
||||
/// Kleene star (`*`) for zero or more repetitions
|
||||
ZeroOrMore,
|
||||
/// Kleene plus (`+`) for one or more repetitions
|
||||
OneOrMore,
|
||||
/// Kleene optional (`?`) for zero or one reptitions
|
||||
ZeroOrOne,
|
||||
}
|
||||
|
||||
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)`
|
||||
/// are "first-class" token trees. Useful for parsing macros.
|
||||
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub enum TokenTree {
|
||||
Token(Token),
|
||||
Delimited(DelimSpan, Lrc<Delimited>),
|
||||
/// A kleene-style repetition sequence
|
||||
Sequence(DelimSpan, Lrc<SequenceRepetition>),
|
||||
/// e.g., `$var`
|
||||
MetaVar(Span, ast::Ident),
|
||||
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
|
||||
MetaVarDecl(
|
||||
Span,
|
||||
ast::Ident, /* name to bind */
|
||||
ast::Ident, /* kind of nonterminal */
|
||||
),
|
||||
}
|
||||
|
||||
impl TokenTree {
|
||||
/// Return the number of tokens in the tree.
|
||||
pub fn len(&self) -> usize {
|
||||
match *self {
|
||||
TokenTree::Delimited(_, ref delimed) => match delimed.delim {
|
||||
token::NoDelim => delimed.tts.len(),
|
||||
_ => delimed.tts.len() + 2,
|
||||
},
|
||||
TokenTree::Sequence(_, ref seq) => seq.tts.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given token tree contains no other tokens. This is vacuously true for
|
||||
/// single tokens or metavar/decls, but may be false for delimited trees or sequences.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match *self {
|
||||
TokenTree::Delimited(_, ref delimed) => match delimed.delim {
|
||||
token::NoDelim => delimed.tts.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
TokenTree::Sequence(_, ref seq) => seq.tts.is_empty(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given token tree is delimited.
|
||||
pub fn is_delimited(&self) -> bool {
|
||||
match *self {
|
||||
TokenTree::Delimited(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given token tree is a token of the given kind.
|
||||
pub fn is_token(&self, expected_kind: &TokenKind) -> bool {
|
||||
match self {
|
||||
TokenTree::Token(Token { kind: actual_kind, .. }) => actual_kind == expected_kind,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences.
|
||||
pub fn get_tt(&self, index: usize) -> TokenTree {
|
||||
match (self, index) {
|
||||
(&TokenTree::Delimited(_, ref delimed), _) if delimed.delim == token::NoDelim => {
|
||||
delimed.tts[index].clone()
|
||||
}
|
||||
(&TokenTree::Delimited(span, ref delimed), _) => {
|
||||
if index == 0 {
|
||||
return delimed.open_tt(span.open);
|
||||
}
|
||||
if index == delimed.tts.len() + 1 {
|
||||
return delimed.close_tt(span.close);
|
||||
}
|
||||
delimed.tts[index - 1].clone()
|
||||
}
|
||||
(&TokenTree::Sequence(_, ref seq), _) => seq.tts[index].clone(),
|
||||
_ => panic!("Cannot expand a token tree"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the `TokenTree`'s span.
|
||||
pub fn span(&self) -> Span {
|
||||
match *self {
|
||||
TokenTree::Token(Token { span, .. })
|
||||
| TokenTree::MetaVar(span, _)
|
||||
| TokenTree::MetaVarDecl(span, _, _) => span,
|
||||
TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn token(kind: TokenKind, span: Span) -> TokenTree {
|
||||
TokenTree::Token(Token::new(kind, span))
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a `tokenstream::TokenStream` and returns a `Vec<self::TokenTree>`. Specifically, this
|
||||
/// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a
|
||||
/// collection of `TokenTree` for use in parsing a macro.
|
||||
@ -195,7 +35,7 @@ impl TokenTree {
|
||||
/// # Returns
|
||||
///
|
||||
/// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
|
||||
pub fn parse(
|
||||
pub(super) fn parse(
|
||||
input: tokenstream::TokenStream,
|
||||
expect_matchers: bool,
|
||||
sess: &ParseSess,
|
@ -1,7 +1,7 @@
|
||||
use crate::ast::{Ident, Mac};
|
||||
use crate::ext::base::ExtCtxt;
|
||||
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
|
||||
use crate::ext::tt::quoted;
|
||||
use crate::ext::mbe;
|
||||
use crate::ext::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
|
||||
use crate::mut_visit::{self, MutVisitor};
|
||||
use crate::parse::token::{self, NtTT, Token};
|
||||
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
|
||||
@ -38,22 +38,22 @@ impl Marker {
|
||||
|
||||
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
|
||||
enum Frame {
|
||||
Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
|
||||
Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
|
||||
Delimited { forest: Lrc<mbe::Delimited>, idx: usize, span: DelimSpan },
|
||||
Sequence { forest: Lrc<mbe::SequenceRepetition>, idx: usize, sep: Option<Token> },
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
/// Construct a new frame around the delimited set of tokens.
|
||||
fn new(tts: Vec<quoted::TokenTree>) -> Frame {
|
||||
let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts });
|
||||
fn new(tts: Vec<mbe::TokenTree>) -> Frame {
|
||||
let forest = Lrc::new(mbe::Delimited { delim: token::NoDelim, tts });
|
||||
Frame::Delimited { forest, idx: 0, span: DelimSpan::dummy() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Frame {
|
||||
type Item = quoted::TokenTree;
|
||||
type Item = mbe::TokenTree;
|
||||
|
||||
fn next(&mut self) -> Option<quoted::TokenTree> {
|
||||
fn next(&mut self) -> Option<mbe::TokenTree> {
|
||||
match *self {
|
||||
Frame::Delimited { ref forest, ref mut idx, .. } => {
|
||||
*idx += 1;
|
||||
@ -90,7 +90,7 @@ impl Iterator for Frame {
|
||||
pub(super) fn transcribe(
|
||||
cx: &ExtCtxt<'_>,
|
||||
interp: &FxHashMap<Ident, NamedMatch>,
|
||||
src: Vec<quoted::TokenTree>,
|
||||
src: Vec<mbe::TokenTree>,
|
||||
transparency: Transparency,
|
||||
) -> TokenStream {
|
||||
// Nothing for us to transcribe...
|
||||
@ -178,7 +178,7 @@ pub(super) fn transcribe(
|
||||
// We are descending into a sequence. We first make sure that the matchers in the RHS
|
||||
// and the matches in `interp` have the same shape. Otherwise, either the caller or the
|
||||
// macro writer has made a mistake.
|
||||
seq @ quoted::TokenTree::Sequence(..) => {
|
||||
seq @ mbe::TokenTree::Sequence(..) => {
|
||||
match lockstep_iter_size(&seq, interp, &repeats) {
|
||||
LockstepIterSize::Unconstrained => {
|
||||
cx.span_fatal(
|
||||
@ -199,7 +199,7 @@ pub(super) fn transcribe(
|
||||
LockstepIterSize::Constraint(len, _) => {
|
||||
// We do this to avoid an extra clone above. We know that this is a
|
||||
// sequence already.
|
||||
let (sp, seq) = if let quoted::TokenTree::Sequence(sp, seq) = seq {
|
||||
let (sp, seq) = if let mbe::TokenTree::Sequence(sp, seq) = seq {
|
||||
(sp, seq)
|
||||
} else {
|
||||
unreachable!()
|
||||
@ -207,7 +207,7 @@ pub(super) fn transcribe(
|
||||
|
||||
// Is the repetition empty?
|
||||
if len == 0 {
|
||||
if seq.kleene.op == quoted::KleeneOp::OneOrMore {
|
||||
if seq.kleene.op == mbe::KleeneOp::OneOrMore {
|
||||
// FIXME: this really ought to be caught at macro definition
|
||||
// time... It happens when the Kleene operator in the matcher and
|
||||
// the body for the same meta-variable do not match.
|
||||
@ -232,7 +232,7 @@ pub(super) fn transcribe(
|
||||
}
|
||||
|
||||
// Replace the meta-var with the matched token tree from the invocation.
|
||||
quoted::TokenTree::MetaVar(mut sp, mut ident) => {
|
||||
mbe::TokenTree::MetaVar(mut sp, mut ident) => {
|
||||
// Find the matched nonterminal from the macro invocation, and use it to replace
|
||||
// the meta-var.
|
||||
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
|
||||
@ -269,7 +269,7 @@ pub(super) fn transcribe(
|
||||
// We will produce all of the results of the inside of the `Delimited` and then we will
|
||||
// jump back out of the Delimited, pop the result_stack and add the new results back to
|
||||
// the previous results (from outside the Delimited).
|
||||
quoted::TokenTree::Delimited(mut span, delimited) => {
|
||||
mbe::TokenTree::Delimited(mut span, delimited) => {
|
||||
marker.visit_delim_span(&mut span);
|
||||
stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
|
||||
result_stack.push(mem::take(&mut result));
|
||||
@ -277,14 +277,14 @@ pub(super) fn transcribe(
|
||||
|
||||
// Nothing much to do here. Just push the token to the result, being careful to
|
||||
// preserve syntax context.
|
||||
quoted::TokenTree::Token(token) => {
|
||||
mbe::TokenTree::Token(token) => {
|
||||
let mut tt = TokenTree::Token(token);
|
||||
marker.visit_tt(&mut tt);
|
||||
result.push(tt.into());
|
||||
}
|
||||
|
||||
// There should be no meta-var declarations in the invocation of a macro.
|
||||
quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
|
||||
mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -368,11 +368,11 @@ impl LockstepIterSize {
|
||||
/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
|
||||
/// multiple nested matcher sequences.
|
||||
fn lockstep_iter_size(
|
||||
tree: "ed::TokenTree,
|
||||
tree: &mbe::TokenTree,
|
||||
interpolations: &FxHashMap<Ident, NamedMatch>,
|
||||
repeats: &[(usize, usize)],
|
||||
) -> LockstepIterSize {
|
||||
use quoted::TokenTree;
|
||||
use mbe::TokenTree;
|
||||
match *tree {
|
||||
TokenTree::Delimited(_, ref delimed) => {
|
||||
delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
|
@ -162,19 +162,14 @@ pub mod ext {
|
||||
mod proc_macro_server;
|
||||
|
||||
pub use syntax_pos::hygiene;
|
||||
pub use mbe::macro_rules::compile_declarative_macro;
|
||||
pub mod allocator;
|
||||
pub mod base;
|
||||
pub mod build;
|
||||
pub mod expand;
|
||||
pub mod proc_macro;
|
||||
|
||||
pub mod tt {
|
||||
pub mod transcribe;
|
||||
pub mod macro_check;
|
||||
pub mod macro_parser;
|
||||
pub mod macro_rules;
|
||||
pub mod quoted;
|
||||
}
|
||||
crate mod mbe;
|
||||
}
|
||||
|
||||
pub mod early_buffered_lints;
|
||||
|
@ -24,7 +24,7 @@ use crate::symbol::{kw, sym};
|
||||
use std::mem;
|
||||
use log::debug;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, StashKey};
|
||||
|
||||
/// Whether the type alias or associated type is a concrete type or an opaque type.
|
||||
#[derive(Debug)]
|
||||
@ -1477,10 +1477,23 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty = $expr` with
|
||||
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
|
||||
///
|
||||
/// When `m` is `"const"`, `$ident` may also be `"_"`.
|
||||
fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
|
||||
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
|
||||
self.expect(&token::Colon)?;
|
||||
let ty = self.parse_ty()?;
|
||||
|
||||
// Parse the type of a `const` or `static mut?` item.
|
||||
// That is, the `":" $ty` fragment.
|
||||
let ty = if self.token == token::Eq {
|
||||
self.recover_missing_const_type(id, m)
|
||||
} else {
|
||||
// Not `=` so expect `":"" $ty` as usual.
|
||||
self.expect(&token::Colon)?;
|
||||
self.parse_ty()?
|
||||
};
|
||||
|
||||
self.expect(&token::Eq)?;
|
||||
let e = self.parse_expr()?;
|
||||
self.expect(&token::Semi)?;
|
||||
@ -1491,6 +1504,34 @@ impl<'a> Parser<'a> {
|
||||
Ok((id, item, None))
|
||||
}
|
||||
|
||||
/// We were supposed to parse `:` but instead, we're already at `=`.
|
||||
/// This means that the type is missing.
|
||||
fn recover_missing_const_type(&mut self, id: Ident, m: Option<Mutability>) -> P<Ty> {
|
||||
// Construct the error and stash it away with the hope
|
||||
// that typeck will later enrich the error with a type.
|
||||
let kind = match m {
|
||||
Some(Mutability::Mutable) => "static mut",
|
||||
Some(Mutability::Immutable) => "static",
|
||||
None => "const",
|
||||
};
|
||||
let mut err = self.struct_span_err(id.span, &format!("missing type for `{}` item", kind));
|
||||
err.span_suggestion(
|
||||
id.span,
|
||||
"provide a type for the item",
|
||||
format!("{}: <type>", id),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
err.stash(id.span, StashKey::ItemNoType);
|
||||
|
||||
// The user intended that the type be inferred,
|
||||
// so treat this as if the user wrote e.g. `const A: _ = expr;`.
|
||||
P(Ty {
|
||||
node: TyKind::Infer,
|
||||
span: id.span,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses `type Foo = Bar;` or returns `None`
|
||||
/// without modifying the parser state.
|
||||
fn eat_type(&mut self) -> Option<PResult<'a, (Ident, AliasKind, Generics)>> {
|
||||
|
@ -13,9 +13,6 @@
|
||||
//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking
|
||||
//! ownership of the original.
|
||||
|
||||
use crate::ext::base;
|
||||
use crate::ext::tt::{macro_parser, quoted};
|
||||
use crate::parse::Directory;
|
||||
use crate::parse::token::{self, DelimToken, Token, TokenKind};
|
||||
use crate::print::pprust;
|
||||
|
||||
@ -26,7 +23,6 @@ use rustc_data_structures::sync::Lrc;
|
||||
use rustc_serialize::{Decoder, Decodable, Encoder, Encodable};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
#[cfg(test)]
|
||||
@ -63,17 +59,6 @@ where
|
||||
{}
|
||||
|
||||
impl TokenTree {
|
||||
/// Use this token tree as a matcher to parse given tts.
|
||||
pub fn parse(cx: &base::ExtCtxt<'_>, mtch: &[quoted::TokenTree], tts: TokenStream)
|
||||
-> macro_parser::NamedParseResult {
|
||||
// `None` is because we're not interpolating
|
||||
let directory = Directory {
|
||||
path: Cow::from(cx.current_expansion.module.directory.as_path()),
|
||||
ownership: cx.current_expansion.directory_ownership,
|
||||
};
|
||||
macro_parser::parse(cx.parse_sess(), tts, mtch, Some(directory), true)
|
||||
}
|
||||
|
||||
/// Checks if this TokenTree is equal to the other, regardless of span information.
|
||||
pub fn eq_unspanned(&self, other: &TokenTree) -> bool {
|
||||
match (self, other) {
|
||||
|
@ -18,4 +18,3 @@ rustc_target = { path = "../librustc_target" }
|
||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
rustc_lexer = { path = "../librustc_lexer" }
|
||||
|
@ -295,7 +295,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
.filter(|fmt| fmt.precision_span.is_some())
|
||||
.count();
|
||||
if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
|
||||
e = self.ecx.mut_span_err(
|
||||
e = self.ecx.struct_span_err(
|
||||
sp,
|
||||
&format!(
|
||||
"{} positional argument{} in format string, but {}",
|
||||
@ -336,7 +336,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
sp = MultiSpan::from_span(self.fmtsp);
|
||||
}
|
||||
|
||||
e = self.ecx.mut_span_err(sp,
|
||||
e = self.ecx.struct_span_err(sp,
|
||||
&format!("invalid reference to positional {} ({})",
|
||||
arg_list,
|
||||
self.describe_num_args()));
|
||||
|
@ -62,6 +62,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
|
||||
};
|
||||
|
||||
interface::run_compiler(config, |compiler| {
|
||||
compiler.compile().ok();
|
||||
// This runs all the passes prior to linking, too.
|
||||
compiler.link().ok();
|
||||
});
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ enum Fruit {
|
||||
Apple(String, String),
|
||||
Pear(u32),
|
||||
Orange((String, String)),
|
||||
Banana(()),
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let x = Fruit::Apple(String::new(), String::new());
|
||||
match x {
|
||||
@ -12,5 +12,6 @@ fn main() {
|
||||
Fruit::Apple(a, b, c) => {}, //~ ERROR E0023
|
||||
Fruit::Pear(1, 2) => {}, //~ ERROR E0023
|
||||
Fruit::Orange(a, b) => {}, //~ ERROR E0023
|
||||
Fruit::Banana() => {}, //~ ERROR E0023
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,19 @@ help: missing parenthesis
|
||||
LL | Fruit::Orange((a, b)) => {},
|
||||
| ^ ^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 1 field
|
||||
--> $DIR/E0023.rs:15:9
|
||||
|
|
||||
LL | Banana(()),
|
||||
| ---------- tuple variant defined here
|
||||
...
|
||||
LL | Fruit::Banana() => {},
|
||||
| ^^^^^^^^^^^^^^^ expected 1 field, found 0
|
||||
help: missing parenthesis
|
||||
|
|
||||
LL | Fruit::Banana(()) => {},
|
||||
| ^ ^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0023`.
|
||||
|
23
src/test/ui/impl-trait/auto-trait.rs
Normal file
23
src/test/ui/impl-trait/auto-trait.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// Tests that type alias impls traits do not leak auto-traits for
|
||||
// the purposes of coherence checking
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
trait OpaqueTrait { }
|
||||
impl<T> OpaqueTrait for T { }
|
||||
type OpaqueType = impl OpaqueTrait;
|
||||
fn mk_opaque() -> OpaqueType { () }
|
||||
|
||||
#[derive(Debug)]
|
||||
struct D<T>(T);
|
||||
|
||||
trait AnotherTrait { }
|
||||
impl<T: Send> AnotherTrait for T { }
|
||||
|
||||
// This is in error, because we cannot assume that `OpaqueType: !Send`.
|
||||
// (We treat opaque types as "foreign types" that could grow more impls
|
||||
// in the future.)
|
||||
impl AnotherTrait for D<OpaqueType> {
|
||||
//~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
|
||||
}
|
||||
|
||||
fn main() {}
|
12
src/test/ui/impl-trait/auto-trait.stderr
Normal file
12
src/test/ui/impl-trait/auto-trait.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`:
|
||||
--> $DIR/auto-trait.rs:19:1
|
||||
|
|
||||
LL | impl<T: Send> AnotherTrait for T { }
|
||||
| -------------------------------- first implementation here
|
||||
...
|
||||
LL | impl AnotherTrait for D<OpaqueType> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
22
src/test/ui/impl-trait/negative-reasoning.rs
Normal file
22
src/test/ui/impl-trait/negative-reasoning.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Tests that we cannot assume that an opaque type does *not* implement some
|
||||
// other trait
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
trait OpaqueTrait { }
|
||||
impl<T> OpaqueTrait for T { }
|
||||
type OpaqueType = impl OpaqueTrait;
|
||||
fn mk_opaque() -> OpaqueType { () }
|
||||
|
||||
#[derive(Debug)]
|
||||
struct D<T>(T);
|
||||
|
||||
trait AnotherTrait { }
|
||||
impl<T: std::fmt::Debug> AnotherTrait for T { }
|
||||
|
||||
|
||||
// This is in error, because we cannot assume that `OpaqueType: !Debug`
|
||||
impl AnotherTrait for D<OpaqueType> {
|
||||
//~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
|
||||
}
|
||||
|
||||
fn main() {}
|
14
src/test/ui/impl-trait/negative-reasoning.stderr
Normal file
14
src/test/ui/impl-trait/negative-reasoning.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`:
|
||||
--> $DIR/negative-reasoning.rs:18:1
|
||||
|
|
||||
LL | impl<T: std::fmt::Debug> AnotherTrait for T { }
|
||||
| ------------------------------------------- first implementation here
|
||||
...
|
||||
LL | impl AnotherTrait for D<OpaqueType> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
|
||||
|
|
||||
= note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `OpaqueType` in future versions
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
46
src/test/ui/suggestions/const-no-type.rs
Normal file
46
src/test/ui/suggestions/const-no-type.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// In the cases below, the type is missing from the `const` and `static` items.
|
||||
//
|
||||
// Here, we test that we:
|
||||
//
|
||||
// a) Perform parser recovery.
|
||||
//
|
||||
// b) Emit a diagnostic with the actual inferred type to RHS of `=` as the suggestion.
|
||||
|
||||
fn main() {}
|
||||
|
||||
// These will not reach typeck:
|
||||
|
||||
#[cfg(FALSE)]
|
||||
const C2 = 42;
|
||||
//~^ ERROR missing type for `const` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION C2: <type>
|
||||
|
||||
#[cfg(FALSE)]
|
||||
static S2 = "abc";
|
||||
//~^ ERROR missing type for `static` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION S2: <type>
|
||||
|
||||
#[cfg(FALSE)]
|
||||
static mut SM2 = "abc";
|
||||
//~^ ERROR missing type for `static mut` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION SM2: <type>
|
||||
|
||||
// These will, so the diagnostics should be stolen by typeck:
|
||||
|
||||
const C = 42;
|
||||
//~^ ERROR missing type for `const` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION C: i32
|
||||
|
||||
static S = Vec::<String>::new();
|
||||
//~^ ERROR missing type for `static` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION S: std::vec::Vec<std::string::String>
|
||||
|
||||
static mut SM = "abc";
|
||||
//~^ ERROR missing type for `static mut` item
|
||||
//~| HELP provide a type for the item
|
||||
//~| SUGGESTION &'static str
|
38
src/test/ui/suggestions/const-no-type.stderr
Normal file
38
src/test/ui/suggestions/const-no-type.stderr
Normal file
@ -0,0 +1,38 @@
|
||||
error: missing type for `const` item
|
||||
--> $DIR/const-no-type.rs:33:7
|
||||
|
|
||||
LL | const C = 42;
|
||||
| ^ help: provide a type for the item: `C: i32`
|
||||
|
||||
error: missing type for `static` item
|
||||
--> $DIR/const-no-type.rs:38:8
|
||||
|
|
||||
LL | static S = Vec::<String>::new();
|
||||
| ^ help: provide a type for the item: `S: std::vec::Vec<std::string::String>`
|
||||
|
||||
error: missing type for `static mut` item
|
||||
--> $DIR/const-no-type.rs:43:12
|
||||
|
|
||||
LL | static mut SM = "abc";
|
||||
| ^^ help: provide a type for the item: `SM: &'static str`
|
||||
|
||||
error: missing type for `const` item
|
||||
--> $DIR/const-no-type.rs:14:7
|
||||
|
|
||||
LL | const C2 = 42;
|
||||
| ^^ help: provide a type for the item: `C2: <type>`
|
||||
|
||||
error: missing type for `static` item
|
||||
--> $DIR/const-no-type.rs:20:8
|
||||
|
|
||||
LL | static S2 = "abc";
|
||||
| ^^ help: provide a type for the item: `S2: <type>`
|
||||
|
||||
error: missing type for `static mut` item
|
||||
--> $DIR/const-no-type.rs:26:12
|
||||
|
|
||||
LL | static mut SM2 = "abc";
|
||||
| ^^^ help: provide a type for the item: `SM2: <type>`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
@ -0,0 +1,2 @@
|
||||
pub trait ForeignTrait {}
|
||||
pub struct ForeignType<T>(pub T);
|
17
src/test/ui/type-alias-impl-trait/coherence.rs
Normal file
17
src/test/ui/type-alias-impl-trait/coherence.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// aux-build:foreign-crate.rs
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
extern crate foreign_crate;
|
||||
|
||||
trait LocalTrait {}
|
||||
impl<T> LocalTrait for foreign_crate::ForeignType<T> {}
|
||||
|
||||
type AliasOfForeignType<T> = impl LocalTrait;
|
||||
fn use_alias<T>(val: T) -> AliasOfForeignType<T> {
|
||||
foreign_crate::ForeignType(val)
|
||||
}
|
||||
|
||||
impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
|
||||
//~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates
|
||||
|
||||
fn main() {}
|
9
src/test/ui/type-alias-impl-trait/coherence.stderr
Normal file
9
src/test/ui/type-alias-impl-trait/coherence.stderr
Normal file
@ -0,0 +1,9 @@
|
||||
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
|
||||
--> $DIR/coherence.rs:14:6
|
||||
|
|
||||
LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
|
||||
| ^ unconstrained type parameter
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0207`.
|
@ -0,0 +1,21 @@
|
||||
// check-pass
|
||||
// Regression test for issue #63677 - ensure that
|
||||
// coherence checking can properly handle 'impl trait'
|
||||
// in type aliases
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
pub trait Trait {}
|
||||
pub struct S1<T>(T);
|
||||
pub struct S2<T>(T);
|
||||
|
||||
pub type T1 = impl Trait;
|
||||
pub type T2 = S1<T1>;
|
||||
pub type T3 = S2<T2>;
|
||||
|
||||
impl<T> Trait for S1<T> {}
|
||||
impl<T: Trait> S2<T> {}
|
||||
impl T3 {}
|
||||
|
||||
pub fn use_t1() -> T1 { S1(()) }
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user