<commit_before>use dom::base::{Attr, Comment, Doctype, DoctypeData, Element, ElementData, ElementKind};
use dom::base::{HTMLDivElement, HTMLHeadElement, HTMLImageElement, HTMLScriptElement};
use dom::base::{Node, NodeScope, Text, UnknownElement};
use dom::style::Stylesheet;
use geom::size::Size2D;
use gfx::geometry::px_to_au;
use parser::html_builder::CSSMessage;
use resource::resource_task::{Done, Load, Payload, ResourceTask};
use CSSExitMessage = parser::html_builder::Exit;
use CSSFileMessage = parser::html_builder::File;
use JSExitMessage = parser::html_builder::js_exit;
use JSFileMessage = parser::html_builder::js_file;
use JSMessage = parser::html_builder::js_message;

use comm::{Chan, Port};
use str::from_slice;
use unsafe::reinterpret_cast;
use std::net::url::Url;

type JSResult = ~[~[u8]];

struct HtmlParserResult {
    root: Node;
    style_port: comm::Port<Stylesheet>;
    js_port: comm::Port<JSResult>;
}

#[doc="Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

"]
fn css_link_listener(to_parent : comm::Chan<Stylesheet>, from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
          CSSFileMessage(url) => {
            let result_port = comm::Port();
            let result_chan = comm::Chan(result_port);
            // TODO: change copy to move once we have match move
            let url = copy url;
            task::spawn(|| {
                // TODO: change copy to move once we can move into closures
                let css_stream = css_lexer::spawn_css_lexer_task(copy url, resource_task);
                let mut css_rules = css_builder::build_stylesheet(css_stream);
                result_chan.send(css_rules);
            });

            vec::push(result_vec, result_port);
          }
          CSSExitMessage => {
            break;
          }
        }
    }

    let css_rules = vec::flat_map(result_vec, |result_port| { result_port.recv() });

    to_parent.send(css_rules);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
          JSFileMessage(url) => {
            let result_port = comm::Port();
            let result_chan = comm::Chan(result_port);
            // TODO: change copy to move once we have match move
            let url = copy url;
            do task::spawn || {
                let input_port = Port();
                // TODO: change copy to move once we can move into closures
                resource_task.send(Load(copy url, input_port.chan()));

                let mut buf = ~[];
                loop {
                    match input_port.recv() {
                      Payload(data) => {
                        buf += data;
                      }
                      Done(Ok(*)) => {
                        result_chan.send(buf);
                        break;
                      }
                      Done(Err(*)) => {
                        #error("error loading script %s", url.to_str());
                      }
                    }
                }
            }
            vec::push(result_vec, result_port);
          }
          JSExitMessage => {
            break;
          }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(js_scripts);
}

fn build_element_kind(tag_name: &str) -> ~ElementKind {
    if tag_name == "div" {
        ~HTMLDivElement
    } else if tag_name == "img" {
        ~HTMLImageElement({ mut size: Size2D(px_to_au(100), px_to_au(100)) })
    } else if tag_name == "script" {
        ~HTMLScriptElement
    } else if tag_name == "head" {
        ~HTMLHeadElement
    } else {
        ~UnknownElement
    }
}

fn parse_html(scope: NodeScope, url: Url, resource_task: ResourceTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Stylesheet>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Stylesheet>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @copy url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(reinterpret_cast(&root));
    parser.enable_scripting(true);
    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: &str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(from_slice(data)));
            unsafe { reinterpret_cast(&new_node) }
        },
        create_doctype: |doctype: &hubbub::Doctype| {
            debug!("create doctype");
            let name = from_slice(doctype.name);
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(from_slice(id))
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(from_slice(id))
            };
            let new_node = scope.new_node(Doctype(DoctypeData(name, public_id, system_id, doctype.force_quirks)));
            unsafe { reinterpret_cast(&new_node) }
        },
        create_element: |tag: &hubbub::Tag| {
            debug!("create element");
            let element_kind = build_element_kind(tag.name);
            let node = scope.new_node(Element(ElementData(from_slice(tag.name), element_kind)));
            for tag.attributes.each |attribute| {
                do scope.read(node) |node_contents| {
                    match *node_contents.kind {
                      Element(element) => {
                        element.attrs.push(~Attr(from_slice(attribute.name),
                                                 from_slice(attribute.value)));
                        match *element.kind {
                          HTMLImageElement(img) if attribute.name == "width" => {
                            match int::from_str(from_slice(attribute.value)) {
                              None => {} // Drop on the floor.
                              Some(s) => img.size.width = px_to_au(s)
                            }
                          }
                          HTMLImageElement(img) if attribute.name == "height" => {
                            match int::from_str(from_slice(attribute.value)) {
                              None => {} // Drop on the floor.
                              Some(s) => img.size.height = px_to_au(s)
                            }
                          }
                          HTMLDivElement | HTMLImageElement(*) | HTMLHeadElement |
                          HTMLScriptElement | UnknownElement => {} // Drop on the floor.
                        }
                      }

                      _ => fail ~"can't happen: unexpected node type"
                    }
                }
            }

            // Handle CSS style sheet links.
            do scope.read(node) |node_contents| {
                match *node_contents.kind {
                    Element(element) if element.tag_name == ~"link" => {
                        match (element.get_attr(~"rel"), element.get_attr(~"href")) {
                            (Some(rel), Some(href)) if rel == ~"stylesheet" => {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSFileMessage(make_url(href, Some(copy *url))));
                            }
                            _ => {}
                        }
                    }
                    _ => {}
                }
            }

            unsafe { reinterpret_cast(&node) }
        },
        create_text: |data| {
            debug!("create text");
            let new_node = scope.new_node(Text(from_slice(data)));
            unsafe { reinterpret_cast(&new_node) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent, child| unsafe {
            debug!("append child");
            scope.add_child(reinterpret_cast(&parent), reinterpret_cast(&child));
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |_node, _deep| {
            debug!("clone node");
            0u
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| unsafe {
            do scope.read(reinterpret_cast(&script)) |node_contents| {
                match *node_contents.kind {
                    Element(element) if element.tag_name == ~"script" => {
                        match element.get_attr(~"src") {
                            Some(src) => {
                                debug!("found script: %s", src);
                                let new_url = make_url(src, Some(copy *url));
                                js_chan.send(JSFileMessage(new_url));
                            }
                            None => {}
                        }
                    }
                    _ => {}
                }
            }
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSExitMessage);
    js_chan.send(JSExitMessage);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<commit_msg>Long lines<commit_after>use dom::base::{Attr, Comment, Doctype, DoctypeData, Element, ElementData, ElementKind};
use dom::base::{HTMLDivElement, HTMLHeadElement, HTMLImageElement, HTMLScriptElement};
use dom::base::{Node, NodeScope, Text, UnknownElement};
use dom::style::Stylesheet;
use geom::size::Size2D;
use gfx::geometry::px_to_au;
use parser::html_builder::CSSMessage;
use resource::resource_task::{Done, Load, Payload, ResourceTask};
use CSSExitMessage = parser::html_builder::Exit;
use CSSFileMessage = parser::html_builder::File;
use JSExitMessage = parser::html_builder::js_exit;
use JSFileMessage = parser::html_builder::js_file;
use JSMessage = parser::html_builder::js_message;

use comm::{Chan, Port};
use str::from_slice;
use unsafe::reinterpret_cast;
use std::net::url::Url;

type JSResult = ~[~[u8]];

struct HtmlParserResult {
    root: Node;
    style_port: comm::Port<Stylesheet>;
    js_port: comm::Port<JSResult>;
}

#[doc="Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

"]
fn css_link_listener(to_parent : comm::Chan<Stylesheet>, from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
          CSSFileMessage(url) => {
            let result_port = comm::Port();
            let result_chan = comm::Chan(result_port);
            // TODO: change copy to move once we have match move
            let url = copy url;
            task::spawn(|| {
                // TODO: change copy to move once we can move into closures
                let css_stream = css_lexer::spawn_css_lexer_task(copy url, resource_task);
                let mut css_rules = css_builder::build_stylesheet(css_stream);
                result_chan.send(css_rules);
            });

            vec::push(result_vec, result_port);
          }
          CSSExitMessage => {
            break;
          }
        }
    }

    let css_rules = vec::flat_map(result_vec, |result_port| { result_port.recv() });

    to_parent.send(css_rules);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
          JSFileMessage(url) => {
            let result_port = comm::Port();
            let result_chan = comm::Chan(result_port);
            // TODO: change copy to move once we have match move
            let url = copy url;
            do task::spawn || {
                let input_port = Port();
                // TODO: change copy to move once we can move into closures
                resource_task.send(Load(copy url, input_port.chan()));

                let mut buf = ~[];
                loop {
                    match input_port.recv() {
                      Payload(data) => {
                        buf += data;
                      }
                      Done(Ok(*)) => {
                        result_chan.send(buf);
                        break;
                      }
                      Done(Err(*)) => {
                        #error("error loading script %s", url.to_str());
                      }
                    }
                }
            }
            vec::push(result_vec, result_port);
          }
          JSExitMessage => {
            break;
          }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(js_scripts);
}

fn build_element_kind(tag_name: &str) -> ~ElementKind {
    if tag_name == "div" {
        ~HTMLDivElement
    } else if tag_name == "img" {
        ~HTMLImageElement({ mut size: Size2D(px_to_au(100), px_to_au(100)) })
    } else if tag_name == "script" {
        ~HTMLScriptElement
    } else if tag_name == "head" {
        ~HTMLHeadElement
    } else {
        ~UnknownElement
    }
}

fn parse_html(scope: NodeScope, url: Url, resource_task: ResourceTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Stylesheet>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Stylesheet>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @copy url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(reinterpret_cast(&root));
    parser.enable_scripting(true);
    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: &str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(from_slice(data)));
            unsafe { reinterpret_cast(&new_node) }
        },
        create_doctype: |doctype: &hubbub::Doctype| {
            debug!("create doctype");
            let name = from_slice(doctype.name);
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(from_slice(id))
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(from_slice(id))
            };
            let data = DoctypeData(name, public_id, system_id, doctype.force_quirks);
            let new_node = scope.new_node(Doctype(data));
            unsafe { reinterpret_cast(&new_node) }
        },
        create_element: |tag: &hubbub::Tag| {
            debug!("create element");
            let element_kind = build_element_kind(tag.name);
            let node = scope.new_node(Element(ElementData(from_slice(tag.name), element_kind)));
            for tag.attributes.each |attribute| {
                do scope.read(node) |node_contents| {
                    match *node_contents.kind {
                      Element(element) => {
                        element.attrs.push(~Attr(from_slice(attribute.name),
                                                 from_slice(attribute.value)));
                        match *element.kind {
                          HTMLImageElement(img) if attribute.name == "width" => {
                            match int::from_str(from_slice(attribute.value)) {
                              None => {} // Drop on the floor.
                              Some(s) => img.size.width = px_to_au(s)
                            }
                          }
                          HTMLImageElement(img) if attribute.name == "height" => {
                            match int::from_str(from_slice(attribute.value)) {
                              None => {} // Drop on the floor.
                              Some(s) => img.size.height = px_to_au(s)
                            }
                          }
                          HTMLDivElement | HTMLImageElement(*) | HTMLHeadElement |
                          HTMLScriptElement | UnknownElement => {} // Drop on the floor.
                        }
                      }

                      _ => fail ~"can't happen: unexpected node type"
                    }
                }
            }

            // Handle CSS style sheet links.
            do scope.read(node) |node_contents| {
                match *node_contents.kind {
                    Element(element) if element.tag_name == ~"link" => {
                        match (element.get_attr(~"rel"), element.get_attr(~"href")) {
                            (Some(rel), Some(href)) if rel == ~"stylesheet" => {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSFileMessage(make_url(href, Some(copy *url))));
                            }
                            _ => {}
                        }
                    }
                    _ => {}
                }
            }

            unsafe { reinterpret_cast(&node) }
        },
        create_text: |data| {
            debug!("create text");
            let new_node = scope.new_node(Text(from_slice(data)));
            unsafe { reinterpret_cast(&new_node) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent, child| unsafe {
            debug!("append child");
            scope.add_child(reinterpret_cast(&parent), reinterpret_cast(&child));
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |_node, _deep| {
            debug!("clone node");
            0u
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| unsafe {
            do scope.read(reinterpret_cast(&script)) |node_contents| {
                match *node_contents.kind {
                    Element(element) if element.tag_name == ~"script" => {
                        match element.get_attr(~"src") {
                            Some(src) => {
                                debug!("found script: %s", src);
                                let new_url = make_url(src, Some(copy *url));
                                js_chan.send(JSFileMessage(new_url));
                            }
                            None => {}
                        }
                    }
                    _ => {}
                }
            }
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSExitMessage);
    js_chan.send(JSExitMessage);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<|endoftext|>


<commit_before>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::dump_enabled;

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }

    pub fn visit_in_rpo_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reverse_postorder(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    def_id: DefId,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
    ) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, def_id, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
    ) -> Self {
        Self::new(tcx, body, def_id, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            def_id,
            dead_unwinds: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            def_id,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Apply the block transfer function, using the cached one if it exists.
            state.clone_from(&entry_sets[bb]);
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, def_id, &body, &results);
        if let Err(e) = res {
            warn!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let path = match attrs.output_path(A::NAME) {
        Some(path) => path,

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir);

            let item_name = ty::print::with_forced_impl_filename_line(|| {
                tcx.def_path(def_id).to_filename_friendly_no_crate()
            });
            path.push(format!("rustc.{}.{}.dot", item_name, A::NAME));
            path
        }

        None => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    debug!("printing dataflow results for {:?} to {}", def_id, path.display());
    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, def_id, results, style);
    let mut render_opts = vec![dot::RenderOption::Monospace];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    fs::write(&path, buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<commit_msg>Add `Engine::pass_name` to differentiate dataflow runs<commit_after>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::dump_enabled;

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }

    pub fn visit_in_rpo_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reverse_postorder(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    def_id: DefId,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    pass_name: Option<&'static str>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
    ) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, def_id, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
    ) -> Self {
        Self::new(tcx, body, def_id, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        def_id: DefId,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            def_id,
            dead_unwinds: None,
            pass_name: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
    ///
    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
    pub fn pass_name(mut self, name: &'static str) -> Self {
        self.pass_name = Some(name);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            def_id,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            pass_name,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Apply the block transfer function, using the cached one if it exists.
            state.clone_from(&entry_sets[bb]);
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, def_id, &body, &results, pass_name);
        if let Err(e) = res {
            warn!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
    pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let path = match attrs.output_path(A::NAME) {
        Some(path) => path,

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            // FIXME: Use some variant of `pretty::dump_path` for this
            let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir);

            let crate_name = tcx.crate_name(def_id.krate);
            let item_name = ty::print::with_forced_impl_filename_line(|| {
                tcx.def_path(def_id).to_filename_friendly_no_crate()
            });

            let pass_name = pass_name.map(|s| format!(".{}", s)).unwrap_or_default();

            path.push(format!("{}.{}.{}{}.dot", crate_name, item_name, A::NAME, pass_name));
            path
        }

        None => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    debug!("printing dataflow results for {:?} to {}", def_id, path.display());
    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, def_id, results, style);
    let mut render_opts = vec![dot::RenderOption::Monospace];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    fs::write(&path, buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<|endoftext|>


<commit_before>//! Completes lifetimes and labels.
//!
//! These completions work a bit differently in that they are only shown when what the user types
//! has a `'` preceding it, as our fake syntax tree is invalid otherwise(due to us not inserting a
//! lifetime but an ident for obvious reasons).
//! Due to this all the tests for lifetimes and labels live in this module for the time being as
//! there is no value in lifting these out into the outline module test since they will either not
//! show up for normal completions, or they won't show completions other than lifetimes depending
//! on the fixture input.
use hir::ScopeDef;

use crate::{completions::Completions, context::CompletionContext};

/// Completes lifetimes.
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.lifetime_allowed {
        return;
    }
    let lp_string;
    let param_lifetime = match (
        &ctx.lifetime_syntax,
        ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
    ) {
        (Some(lt), Some(lp)) if lp == lt.clone() => return,
        (Some(_), Some(lp)) => {
            lp_string = lp.to_string();
            Some(&*lp_string)
        }
        _ => None,
    };

    ctx.scope.process_all_names(&mut |name, res| {
        if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
            if param_lifetime != Some(&*name.to_string()) {
                acc.add_resolution(ctx, name, &res);
            }
        }
    });
    if param_lifetime.is_none() {
        acc.add_static_lifetime(ctx);
    }
}

/// Completes labels.
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_label_ref {
        return;
    }
    ctx.scope.process_all_names(&mut |name, res| {
        if let ScopeDef::Label(_) = res {
            acc.add_resolution(ctx, name, &res);
        }
    });
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::tests::{check_edit, completion_list};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn check_lifetime_edit() {
        check_edit(
            "'lifetime",
            r#"
fn func<'lifetime>(foo: &'li$0) {}
"#,
            r#"
fn func<'lifetime>(foo: &'lifetime) {}
"#,
        );
        cov_mark::check!(completes_if_lifetime_without_idents);
        check_edit(
            "'lifetime",
            r#"
fn func<'lifetime>(foo: &'$0) {}
"#,
            r#"
fn func<'lifetime>(foo: &'lifetime) {}
"#,
        );
    }

    #[test]
    fn complete_lifetime_in_ref() {
        check(
            r#"
fn foo<'lifetime>(foo: &'a$0 usize) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_ref_missing_ty() {
        check(
            r#"
fn foo<'lifetime>(foo: &'a$0) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }
    #[test]
    fn complete_lifetime_in_self_ref() {
        check(
            r#"
struct Foo;
impl<'impl> Foo {
    fn foo<'func>(&'a$0 self) {}
}
"#,
            expect![[r#"
                lt 'func
                lt 'impl
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_arg_list() {
        check(
            r#"
struct Foo<'lt>;
fn foo<'lifetime>(_: Foo<'a$0>) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_where_pred() {
        check(
            r#"
fn foo2<'lifetime, T>() where 'a$0 {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_ty_bound() {
        check(
            r#"
fn foo2<'lifetime, T>() where T: 'a$0 {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
        check(
            r#"
fn foo2<'lifetime, T>() where T: Trait<'a$0> {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn dont_complete_lifetime_in_assoc_ty_bound() {
        check(
            r#"
fn foo2<'lifetime, T>() where T: Trait<Item = 'a$0> {}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn complete_lifetime_in_param_list() {
        check(
            r#"
fn foo<'a$0>() {}
"#,
            expect![[r#""#]],
        );
        check(
            r#"
fn foo<'footime, 'lifetime: 'a$0>() {}
"#,
            expect![[r#"
                lt 'footime
            "#]],
        );
    }

    #[test]
    fn check_label_edit() {
        check_edit(
            "'label",
            r#"
fn foo() {
    'label: loop {
        break '$0
    }
}
"#,
            r#"
fn foo() {
    'label: loop {
        break 'label
    }
}
"#,
        );
    }

    #[test]
    fn complete_label_in_loop() {
        check(
            r#"
fn foo() {
    'foop: loop {
        break '$0
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
        check(
            r#"
fn foo() {
    'foop: loop {
        continue '$0
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_block_nested() {
        check(
            r#"
fn foo() {
    'foop: {
        'baap: {
            break '$0
        }
    }
}
"#,
            expect![[r#"
                lb 'baap
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_loop_with_value() {
        check(
            r#"
fn foo() {
    'foop: loop {
        break '$0 i32;
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_while_cond() {
        check(
            r#"
fn foo() {
    'outer: while { 'inner: loop { break '$0 } } {}
}
"#,
            expect![[r#"
                lb 'inner
                lb 'outer
            "#]],
        );
    }

    #[test]
    fn complete_label_in_for_iterable() {
        check(
            r#"
fn foo() {
    'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
}
"#,
            expect![[r#"
                lb 'inner
            "#]],
        );
    }
}
<commit_msg>Fix typo<commit_after>//! Completes lifetimes and labels.
//!
//! These completions work a bit differently in that they are only shown when what the user types
//! has a `'` preceding it, as our fake syntax tree is invalid otherwise (due to us not inserting
//! a lifetime but an ident for obvious reasons).
//! Due to this all the tests for lifetimes and labels live in this module for the time being as
//! there is no value in lifting these out into the outline module test since they will either not
//! show up for normal completions, or they won't show completions other than lifetimes depending
//! on the fixture input.
use hir::ScopeDef;

use crate::{completions::Completions, context::CompletionContext};

/// Completes lifetimes.
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.lifetime_allowed {
        return;
    }
    let lp_string;
    let param_lifetime = match (
        &ctx.lifetime_syntax,
        ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
    ) {
        (Some(lt), Some(lp)) if lp == lt.clone() => return,
        (Some(_), Some(lp)) => {
            lp_string = lp.to_string();
            Some(&*lp_string)
        }
        _ => None,
    };

    ctx.scope.process_all_names(&mut |name, res| {
        if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
            if param_lifetime != Some(&*name.to_string()) {
                acc.add_resolution(ctx, name, &res);
            }
        }
    });
    if param_lifetime.is_none() {
        acc.add_static_lifetime(ctx);
    }
}

/// Completes labels.
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_label_ref {
        return;
    }
    ctx.scope.process_all_names(&mut |name, res| {
        if let ScopeDef::Label(_) = res {
            acc.add_resolution(ctx, name, &res);
        }
    });
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::tests::{check_edit, completion_list};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn check_lifetime_edit() {
        check_edit(
            "'lifetime",
            r#"
fn func<'lifetime>(foo: &'li$0) {}
"#,
            r#"
fn func<'lifetime>(foo: &'lifetime) {}
"#,
        );
        cov_mark::check!(completes_if_lifetime_without_idents);
        check_edit(
            "'lifetime",
            r#"
fn func<'lifetime>(foo: &'$0) {}
"#,
            r#"
fn func<'lifetime>(foo: &'lifetime) {}
"#,
        );
    }

    #[test]
    fn complete_lifetime_in_ref() {
        check(
            r#"
fn foo<'lifetime>(foo: &'a$0 usize) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_ref_missing_ty() {
        check(
            r#"
fn foo<'lifetime>(foo: &'a$0) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }
    #[test]
    fn complete_lifetime_in_self_ref() {
        check(
            r#"
struct Foo;
impl<'impl> Foo {
    fn foo<'func>(&'a$0 self) {}
}
"#,
            expect![[r#"
                lt 'func
                lt 'impl
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_arg_list() {
        check(
            r#"
struct Foo<'lt>;
fn foo<'lifetime>(_: Foo<'a$0>) {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_where_pred() {
        check(
            r#"
fn foo2<'lifetime, T>() where 'a$0 {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn complete_lifetime_in_ty_bound() {
        check(
            r#"
fn foo2<'lifetime, T>() where T: 'a$0 {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
        check(
            r#"
fn foo2<'lifetime, T>() where T: Trait<'a$0> {}
"#,
            expect![[r#"
                lt 'lifetime
                lt 'static
            "#]],
        );
    }

    #[test]
    fn dont_complete_lifetime_in_assoc_ty_bound() {
        check(
            r#"
fn foo2<'lifetime, T>() where T: Trait<Item = 'a$0> {}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn complete_lifetime_in_param_list() {
        check(
            r#"
fn foo<'a$0>() {}
"#,
            expect![[r#""#]],
        );
        check(
            r#"
fn foo<'footime, 'lifetime: 'a$0>() {}
"#,
            expect![[r#"
                lt 'footime
            "#]],
        );
    }

    #[test]
    fn check_label_edit() {
        check_edit(
            "'label",
            r#"
fn foo() {
    'label: loop {
        break '$0
    }
}
"#,
            r#"
fn foo() {
    'label: loop {
        break 'label
    }
}
"#,
        );
    }

    #[test]
    fn complete_label_in_loop() {
        check(
            r#"
fn foo() {
    'foop: loop {
        break '$0
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
        check(
            r#"
fn foo() {
    'foop: loop {
        continue '$0
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_block_nested() {
        check(
            r#"
fn foo() {
    'foop: {
        'baap: {
            break '$0
        }
    }
}
"#,
            expect![[r#"
                lb 'baap
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_loop_with_value() {
        check(
            r#"
fn foo() {
    'foop: loop {
        break '$0 i32;
    }
}
"#,
            expect![[r#"
                lb 'foop
            "#]],
        );
    }

    #[test]
    fn complete_label_in_while_cond() {
        check(
            r#"
fn foo() {
    'outer: while { 'inner: loop { break '$0 } } {}
}
"#,
            expect![[r#"
                lb 'inner
                lb 'outer
            "#]],
        );
    }

    #[test]
    fn complete_label_in_for_iterable() {
        check(
            r#"
fn foo() {
    'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
}
"#,
            expect![[r#"
                lb 'inner
            "#]],
        );
    }
}
<|endoftext|>


<commit_before>use crate::{Assist, AssistId, AssistCtx};

use hir::Resolver;
use hir::db::HirDatabase;
use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
use ra_db::FilePosition;
use ra_fmt::{leading_indent, reindent};

use itertools::Itertools;

/// Given an `ast::ImplBlock`, resolves the target trait (the one being
/// implemented) to a `ast::TraitDef`.
pub(crate) fn resolve_target_trait_def(
    db: &impl HirDatabase,
    resolver: &Resolver,
    impl_block: &ast::ImplBlock,
) -> Option<TreeArc<ast::TraitDef>> {
    let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?;
    let hir_path = ast_path.path().and_then(hir::Path::from_ast)?;

    match resolver.resolve_path(db, &hir_path).take_types() {
        Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
        _ => None,
    }
}

pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
    let mut buf = String::new();

    for child in def.syntax().children() {
        if child.kind() == SyntaxKind::SEMI {
            buf.push_str(" { unimplemented!() }")
        } else {
            child.text().push_to(&mut buf);
        }
    }

    buf.trim_end().to_string()
}

pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let node = ctx.covering_node();
    let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
    let impl_item_list = impl_node.item_list()?;
    // Don't offer the assist when cursor is at the end, outside the block itself.
    if node.range().end() == impl_node.syntax().range().end() {
        return None;
    }

    let trait_def = {
        let db = ctx.db;
        // TODO: Can we get the position of cursor itself rather than supplied range?
        let range = ctx.frange;
        let position = FilePosition { file_id: range.file_id, offset: range.range.start() };
        let resolver = hir::source_binder::resolver_for_position(db, position);

        resolve_target_trait_def(db, &resolver, impl_node)?
    };

    let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
    let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };

    let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
    let impl_items = impl_item_list.impl_items();

    let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
    let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();

    let missing_fns: Vec<_> = trait_fns
        .into_iter()
        .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
        .collect();
    if missing_fns.is_empty() {
        return None;
    }

    ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| {
        let indent = {
            // FIXME: Find a way to get the indent already used in the file.
            // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
            const DEFAULT_INDENT: &str = "    ";
            let first_item = impl_item_list.impl_items().next();
            let first_item_indent = first_item.and_then(|i| leading_indent(i.syntax()));
            let impl_block_indent = || leading_indent(impl_node.syntax()).unwrap_or_default();

            first_item_indent
                .map(ToOwned::to_owned)
                .unwrap_or_else(|| impl_block_indent().to_owned() + DEFAULT_INDENT)
        };

        let mut func_bodies = missing_fns.into_iter().map(build_func_body);
        let func_bodies = func_bodies.join("\n");
        let func_bodies = String::from("\n") + &func_bodies;
        let func_bodies = reindent(&func_bodies, &indent) + "\n";

        let changed_range = {
            let last_whitespace = impl_item_list.syntax().children();
            let last_whitespace = last_whitespace.filter_map(ast::Whitespace::cast).last();
            let last_whitespace = last_whitespace.map(|w| w.syntax());

            let cursor_range = TextRange::from_to(node.range().end(), node.range().end());

            last_whitespace.map(|x| x.range()).unwrap_or(cursor_range)
        };

        let replaced_text_range = TextUnit::of_str(&func_bodies);

        edit.replace(changed_range, func_bodies);
        edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_add_missing_impl_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_empty_impl_block() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {<|>}",
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {
    fn foo(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_cursor_after_empty_impl_block() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {}<|>",
        )
    }
}
<commit_msg>Ignore unnamed trait fns and add more tests<commit_after>use crate::{Assist, AssistId, AssistCtx};

use hir::Resolver;
use hir::db::HirDatabase;
use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
use ra_db::FilePosition;
use ra_fmt::{leading_indent, reindent};

use itertools::Itertools;

/// Given an `ast::ImplBlock`, resolves the target trait (the one being
/// implemented) to a `ast::TraitDef`.
pub(crate) fn resolve_target_trait_def(
    db: &impl HirDatabase,
    resolver: &Resolver,
    impl_block: &ast::ImplBlock,
) -> Option<TreeArc<ast::TraitDef>> {
    let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?;
    let hir_path = ast_path.path().and_then(hir::Path::from_ast)?;

    match resolver.resolve_path(db, &hir_path).take_types() {
        Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
        _ => None,
    }
}

pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
    let mut buf = String::new();

    for child in def.syntax().children() {
        if child.kind() == SyntaxKind::SEMI {
            buf.push_str(" { unimplemented!() }")
        } else {
            child.text().push_to(&mut buf);
        }
    }

    buf.trim_end().to_string()
}

pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let node = ctx.covering_node();
    let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
    let impl_item_list = impl_node.item_list()?;
    // Don't offer the assist when cursor is at the end, outside the block itself.
    if node.range().end() == impl_node.syntax().range().end() {
        return None;
    }

    let trait_def = {
        let db = ctx.db;
        // TODO: Can we get the position of cursor itself rather than supplied range?
        let range = ctx.frange;
        let position = FilePosition { file_id: range.file_id, offset: range.range.start() };
        let resolver = hir::source_binder::resolver_for_position(db, position);

        resolve_target_trait_def(db, &resolver, impl_node)?
    };

    let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
    let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };

    let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
    let impl_items = impl_item_list.impl_items();

    let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
    let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();

    let missing_fns: Vec<_> = trait_fns
        .into_iter()
        .filter(|t| def_name(t).is_some())
        .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
        .collect();
    if missing_fns.is_empty() {
        return None;
    }

    ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| {
        let indent = {
            // FIXME: Find a way to get the indent already used in the file.
            // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
            const DEFAULT_INDENT: &str = "    ";
            let first_item = impl_item_list.impl_items().next();
            let first_item_indent = first_item.and_then(|i| leading_indent(i.syntax()));
            let impl_block_indent = || leading_indent(impl_node.syntax()).unwrap_or_default();

            first_item_indent
                .map(ToOwned::to_owned)
                .unwrap_or_else(|| impl_block_indent().to_owned() + DEFAULT_INDENT)
        };

        let func_bodies = missing_fns.into_iter().map(build_func_body).join("\n");
        let func_bodies = String::from("\n") + &func_bodies;
        let func_bodies = reindent(&func_bodies, &indent) + "\n";

        let changed_range = {
            let last_whitespace = impl_item_list.syntax().children();
            let last_whitespace = last_whitespace.filter_map(ast::Whitespace::cast).last();
            let last_whitespace = last_whitespace.map(|w| w.syntax());

            let cursor_range = TextRange::from_to(node.range().end(), node.range().end());

            last_whitespace.map(|x| x.range()).unwrap_or(cursor_range)
        };

        let replaced_text_range = TextUnit::of_str(&func_bodies);

        edit.replace(changed_range, func_bodies);
        edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_add_missing_impl_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_copied_overriden_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) -> u32 { 42 }<|>
}",
        );
    }

    #[test]
    fn test_empty_impl_block() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {<|>}",
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {
    fn foo(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_cursor_after_empty_impl_block() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {}<|>",
        )
    }

    #[test]
    fn test_empty_trait() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo;
struct S;
impl Foo for S { <|> }",
        )
    }

    #[test]
    fn test_ignore_unnamed_trait_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S { <|> }",
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S {
    fn valid(some: u32) -> bool { false }<|>
}",
        )
    }
}
<|endoftext|>


<commit_before>// xfail-test

// Broken due to arena API problems.

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod extra;
use extra::arena;

enum tree<'self> {
    nil,
    node(&'self tree<'self>, &'self tree<'self>, int),
}

fn item_check(t: &tree) -> int {
    match *t {
      nil => { return 0; }
      node(left, right, item) => {
        return item + item_check(left) - item_check(right);
      }
    }
}

fn bottom_up_tree<'r>(arena: &'r mut arena::Arena, item: int, depth: int)
                   -> &'r tree<'r> {
    if depth > 0 {
        return arena.alloc(
            || node(bottom_up_tree(arena, 2 * item - 1, depth - 1),
                    bottom_up_tree(arena, 2 * item, depth - 1),
                    item));
    }
    return arena.alloc(|| nil);
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"17"]
    } else if args.len() <= 1u {
        ~[~"", ~"8"]
    } else {
        args
    };

    let n = int::from_str(args[1]).get();
    let min_depth = 4;
    let mut max_depth;
    if min_depth + 2 > n {
        max_depth = min_depth + 2;
    } else {
        max_depth = n;
    }

    let mut stretch_arena = arena::Arena();
    let stretch_depth = max_depth + 1;
    let stretch_tree = bottom_up_tree(&mut stretch_arena, 0, stretch_depth);

    io::println(fmt!("stretch tree of depth %d\t check: %d",
                          stretch_depth,
                          item_check(stretch_tree)));

    let mut long_lived_arena = arena::Arena();
    let long_lived_tree = bottom_up_tree(&mut long_lived_arena, 0, max_depth);
    let mut depth = min_depth;
    while depth <= max_depth {
        let iterations = int::pow(2, (max_depth - depth + min_depth) as uint);
        let mut chk = 0;
        let mut i = 1;
        while i <= iterations {
            let mut temp_tree = bottom_up_tree(&mut long_lived_arena, i, depth);
            chk += item_check(temp_tree);
            temp_tree = bottom_up_tree(&mut long_lived_arena, -i, depth);
            chk += item_check(temp_tree);
            i += 1;
        }
        io::println(fmt!("%d\t trees of depth %d\t check: %d",
                         iterations * 2, depth,
                         chk));
        depth += 2;
    }
    io::println(fmt!("long lived trees of depth %d\t check: %d",
                     max_depth,
                     item_check(long_lived_tree)));
}
<commit_msg>Fixed os:: and int:: not being in scope, changed io::println to println<commit_after>// xfail-test

// Broken due to arena API problems.

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod extra;
use extra::arena;

enum tree<'self> {
    nil,
    node(&'self tree<'self>, &'self tree<'self>, int),
}

fn item_check(t: &tree) -> int {
    match *t {
      nil => { return 0; }
      node(left, right, item) => {
        return item + item_check(left) - item_check(right);
      }
    }
}

fn bottom_up_tree<'r>(arena: &'r mut arena::Arena, item: int, depth: int)
                   -> &'r tree<'r> {
    if depth > 0 {
        return arena.alloc(
            || node(bottom_up_tree(arena, 2 * item - 1, depth - 1),
                    bottom_up_tree(arena, 2 * item, depth - 1),
                    item));
    }
    return arena.alloc(|| nil);
}

fn main() {
    use std::os;
    use std::int;
    let args = std::os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"17"]
    } else if args.len() <= 1u {
        ~[~"", ~"8"]
    } else {
        args
    };

    let n = int::from_str(args[1]).get();
    let min_depth = 4;
    let mut max_depth;
    if min_depth + 2 > n {
        max_depth = min_depth + 2;
    } else {
        max_depth = n;
    }

    let mut stretch_arena = arena::Arena();
    let stretch_depth = max_depth + 1;
    let stretch_tree = bottom_up_tree(&mut stretch_arena, 0, stretch_depth);

    println(fmt!("stretch tree of depth %d\t check: %d",
                          stretch_depth,
                          item_check(stretch_tree)));

    let mut long_lived_arena = arena::Arena();
    let long_lived_tree = bottom_up_tree(&mut long_lived_arena, 0, max_depth);
    let mut depth = min_depth;
    while depth <= max_depth {
        let iterations = int::pow(2, (max_depth - depth + min_depth) as uint);
        let mut chk = 0;
        let mut i = 1;
        while i <= iterations {
            let mut temp_tree = bottom_up_tree(&mut long_lived_arena, i, depth);
            chk += item_check(temp_tree);
            temp_tree = bottom_up_tree(&mut long_lived_arena, -i, depth);
            chk += item_check(temp_tree);
            i += 1;
        }
        println(fmt!("%d\t trees of depth %d\t check: %d",
                         iterations * 2, depth,
                         chk));
        depth += 2;
    }
    println(fmt!("long lived tree of depth %d\t check: %d",
                     max_depth,
                     item_check(long_lived_tree)));
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-tidy-linelength
// ignore-android: FIXME(#10381)
// min-lldb-version: 310

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:print some
// gdb-check:$1 = {RUST$ENCODED$ENUM$0$None = {0x12345678}}

// gdb-command:print none
// gdb-check:$2 = {RUST$ENCODED$ENUM$0$None = {0x0}}

// gdb-command:print full
// gdb-check:$3 = {RUST$ENCODED$ENUM$1$Empty = {454545, 0x87654321, 9988}}

// gdb-command:print empty_gdb->discr
// gdb-check:$4 = (isize *) 0x0

// gdb-command:print droid
// gdb-check:$5 = {RUST$ENCODED$ENUM$2$Void = {id = 675675, range = 10000001, internals = 0x43218765}}

// gdb-command:print void_droid_gdb->internals
// gdb-check:$6 = (isize *) 0x0

// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print some
// lldb-check:[...]$0 = Some(&0x12345678)

// lldb-command:print none
// lldb-check:[...]$1 = None

// lldb-command:print full
// lldb-check:[...]$2 = Full(454545, &0x87654321, 9988)

// lldb-command:print empty
// lldb-check:[...]$3 = Empty

// lldb-command:print droid
// lldb-check:[...]$4 = Droid { id: 675675, range: 10000001, internals: &0x43218765 }

// lldb-command:print void_droid
// lldb-check:[...]$5 = Void

// lldb-command:print some_str
// lldb-check:[...]$6 = Some(&str { data_ptr: [...], length: 3 })

// lldb-command:print none_str
// lldb-check:[...]$7 = None

#![omit_gdb_pretty_printer_section]

// If a struct has exactly two variants, one of them is empty, and the other one
// contains a non-nullable pointer, then this value is used as the discriminator.
// The test cases in this file make sure that something readable is generated for
// this kind of types.
// If the non-empty variant contains a single non-nullable pointer than the whole
// item is represented as just a pointer and not wrapped in a struct.
// Unfortunately (for these test cases) the content of the non-discriminant fields
// in the null-case is not defined. So we just read the discriminator field in
// this case (by casting the value to a memory-equivalent struct).

enum MoreFields<'a> {
    Full(u32, &'a isize, i16),
    Empty
}

struct MoreFieldsRepr<'a> {
    a: u32,
    discr: &'a isize,
    b: i16
}

enum NamedFields<'a> {
    Droid { id: i32, range: i64, internals: &'a isize },
    Void
}

struct NamedFieldsRepr<'a> {
    id: i32,
    range: i64,
    internals: &'a isize
}

fn main() {

    let some_str: Option<&'static str> = Some("abc");
    let none_str: Option<&'static str> = None;

    let some: Option<&u32> = Some(unsafe { std::mem::transmute(0x12345678u) });
    let none: Option<&u32> = None;

    let full = MoreFields::Full(454545, unsafe { std::mem::transmute(0x87654321u) }, 9988);

    let empty = MoreFields::Empty;
    let empty_gdb: &MoreFieldsRepr = unsafe { std::mem::transmute(&MoreFields::Empty) };

    let droid = NamedFields::Droid {
        id: 675675,
        range: 10000001,
        internals: unsafe { std::mem::transmute(0x43218765u) }
    };

    let void_droid = NamedFields::Void;
    let void_droid_gdb: &NamedFieldsRepr = unsafe { std::mem::transmute(&NamedFields::Void) };

    zzz(); // #break
}

fn zzz() {()}
<commit_msg>debuginfo: Extend option-like-enum test case to contain nested discriminants.<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-tidy-linelength
// ignore-android: FIXME(#10381)
// min-lldb-version: 310

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:print some
// gdb-check:$1 = {RUST$ENCODED$ENUM$0$None = {0x12345678}}

// gdb-command:print none
// gdb-check:$2 = {RUST$ENCODED$ENUM$0$None = {0x0}}

// gdb-command:print full
// gdb-check:$3 = {RUST$ENCODED$ENUM$1$Empty = {454545, 0x87654321, 9988}}

// gdb-command:print empty_gdb->discr
// gdb-check:$4 = (isize *) 0x0

// gdb-command:print droid
// gdb-check:$5 = {RUST$ENCODED$ENUM$2$Void = {id = 675675, range = 10000001, internals = 0x43218765}}

// gdb-command:print void_droid_gdb->internals
// gdb-check:$6 = (isize *) 0x0

// gdb-command:print nested_non_zero_yep
// gdb-check:$7 = {RUST$ENCODED$ENUM$1$2$Nope = {10.5, {a = 10, b = 20, c = [...]}}}

// gdb-command:print nested_non_zero_nope
// gdb-check:$8 = {RUST$ENCODED$ENUM$1$2$Nope = {[...], {a = [...], b = [...], c = 0x0}}}

// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print some
// lldb-check:[...]$0 = Some(&0x12345678)

// lldb-command:print none
// lldb-check:[...]$1 = None

// lldb-command:print full
// lldb-check:[...]$2 = Full(454545, &0x87654321, 9988)

// lldb-command:print empty
// lldb-check:[...]$3 = Empty

// lldb-command:print droid
// lldb-check:[...]$4 = Droid { id: 675675, range: 10000001, internals: &0x43218765 }

// lldb-command:print void_droid
// lldb-check:[...]$5 = Void

// lldb-command:print some_str
// lldb-check:[...]$6 = Some(&str { data_ptr: [...], length: 3 })

// lldb-command:print none_str
// lldb-check:[...]$7 = None

// lldb-command:print nested_non_zero_yep
// lldb-check:[...]$8 = Yep(10.5, NestedNonZeroField { a: 10, b: 20, c: &[...] })

// lldb-command:print nested_non_zero_nope
// lldb-check:[...]$9 = Nope

#![omit_gdb_pretty_printer_section]

// If a struct has exactly two variants, one of them is empty, and the other one
// contains a non-nullable pointer, then this value is used as the discriminator.
// The test cases in this file make sure that something readable is generated for
// this kind of types.
// If the non-empty variant contains a single non-nullable pointer than the whole
// item is represented as just a pointer and not wrapped in a struct.
// Unfortunately (for these test cases) the content of the non-discriminant fields
// in the null-case is not defined. So we just read the discriminator field in
// this case (by casting the value to a memory-equivalent struct).

enum MoreFields<'a> {
    Full(u32, &'a isize, i16),
    Empty
}

struct MoreFieldsRepr<'a> {
    a: u32,
    discr: &'a isize,
    b: i16
}

enum NamedFields<'a> {
    Droid { id: i32, range: i64, internals: &'a isize },
    Void
}

struct NamedFieldsRepr<'a> {
    id: i32,
    range: i64,
    internals: &'a isize
}

struct NestedNonZeroField<'a> {
    a: u16,
    b: u32,
    c: &'a char,
}

enum NestedNonZero<'a> {
    Yep(f64, NestedNonZeroField<'a>),
    Nope
}

fn main() {

    let some_str: Option<&'static str> = Some("abc");
    let none_str: Option<&'static str> = None;

    let some: Option<&u32> = Some(unsafe { std::mem::transmute(0x12345678u) });
    let none: Option<&u32> = None;

    let full = MoreFields::Full(454545, unsafe { std::mem::transmute(0x87654321u) }, 9988);

    let empty = MoreFields::Empty;
    let empty_gdb: &MoreFieldsRepr = unsafe { std::mem::transmute(&MoreFields::Empty) };

    let droid = NamedFields::Droid {
        id: 675675,
        range: 10000001,
        internals: unsafe { std::mem::transmute(0x43218765u) }
    };

    let void_droid = NamedFields::Void;
    let void_droid_gdb: &NamedFieldsRepr = unsafe { std::mem::transmute(&NamedFields::Void) };

    let x = 'x';
    let nested_non_zero_yep = NestedNonZero::Yep(
        10.5,
        NestedNonZeroField {
            a: 10,
            b: 20,
            c: &x
        });

    let nested_non_zero_nope = NestedNonZero::Nope;

    zzz(); // #break
}

fn zzz() {()}
<|endoftext|>