<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

include!("../relm-state/src/macros.rs");
<commit_msg>Temporary fix to publish the crate<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/// Connect events to sending a message.
///
/// ## Rules
/// 1. Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// 3. Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// 4. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        connect_stream!($widget, $event($($args),*), $other_component.stream(), $msg);
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        connect_stream!(return $relm.stream(), $widget, $event($($args),*), $msg);
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        let stream = $src_component.stream().clone();
        connect_stream!(stream@$message, $dst_component.stream(), $msg);
    };
}

/// Connect events to sending a message.
/// Similar to `connect!` but wants a stream instead of a component.
///
/// ## Rules
/// 1. Send `$msg` to `$other_stream` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect_stream {
    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    (return $stream:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_stream:expr, $msg:expr) => {
        let stream = $other_stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_stream:ident @ $message:pat, $dst_stream:expr, $msg:expr) => {
        let stream = $dst_stream.clone();
        $src_stream.observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}

/// Connect an asynchronous method call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = $relm.stream().clone();
        $object.$async_method($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $object.$async_method($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });
    }};
}

/// Connect an asynchronous function call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async_func {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = $relm.stream().clone();
        $class::$async_func($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $class::$async_func($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

    }};
}

/// Like `connect_async!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_full {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = $relm.stream().clone();
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
        cancellable
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}

/// Like `connect_async_func!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_func_full {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async!($async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = $relm.stream().clone();
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
        cancellable
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use itertools::Itertools;
use std::collections::{hash_map, HashMap};
use std::io;
use std::path::PathBuf;
use syntax::source_map::FileLoader;
use syntax::source_map::SourceMap;
use syntax_pos::{self, BytePos, Span};

/// A span within a file. Also differs from rustc's Span in that it's not interned, which allows us
/// to make use of it after the compiler has finished running.
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
struct LocalSpan {
    lo: BytePos,
    hi: BytePos,
}

pub(crate) trait SpanT {
    fn lo(&self) -> BytePos;
    fn hi(&self) -> BytePos;
}

impl SpanT for Span {
    fn lo(&self) -> BytePos {
        Span::lo(*self)
    }
    fn hi(&self) -> BytePos {
        Span::hi(*self)
    }
}

impl SpanT for LocalSpan {
    fn lo(&self) -> BytePos {
        self.lo
    }
    fn hi(&self) -> BytePos {
        self.hi
    }
}

pub(crate) struct SourceChunk<'a> {
    source: &'a str,
    start_pos: BytePos,
}

impl<'a> SourceChunk<'a> {
    pub(crate) fn new(source: &'a str, start_pos: BytePos) -> SourceChunk<'a> {
        SourceChunk { source, start_pos }
    }

    fn get_snippet(&self, lo: BytePos, hi: BytePos) -> &'a str {
        use syntax_pos::Pos;
        &self.source[(lo - self.start_pos).to_usize()..(hi - self.start_pos).to_usize()]
    }

    fn to_end_from(&self, from: BytePos) -> &'a str {
        use syntax_pos::Pos;
        &self.source[(from - self.start_pos).to_usize()..]
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct CodeSubstitution<S> {
    // The span to be replaced.
    pub(crate) span: S,
    // New code to replace what's at span.
    pub(crate) new_code: String,
    // Whether parenthesis are needed around the substitution.
    pub(crate) needs_parenthesis: bool,
    // Whether `new_code` is a single token tree. e.g. an already parenthesised expression. If it
    // is, then we needn't add parenthesis even if the precedence suggests that we do. This is
    // necessary because our precedence rules are based on HIR which has no way to represent
    // parenthesis.
    code_is_single_tree: bool,
}

impl<S> CodeSubstitution<S> {
    pub(crate) fn new(span: S, new_code: String) -> CodeSubstitution<S> {
        let single_tree = code_is_single_tree(&new_code);
        CodeSubstitution {
            span,
            new_code,
            needs_parenthesis: false,
            code_is_single_tree: single_tree,
        }
    }

    pub(crate) fn needs_parenthesis(mut self, needs_parenthesis: bool) -> CodeSubstitution<S> {
        self.needs_parenthesis = needs_parenthesis;
        self
    }
}

impl CodeSubstitution<Span> {
    pub(crate) fn apply_with_source_map(
        substitutions: &[CodeSubstitution<Span>],
        source_map: &SourceMap,
        base_span: Span,
    ) -> String {
        let base_source = source_map.span_to_snippet(base_span).unwrap();
        apply_substitutions(
            substitutions,
            SourceChunk::new(&base_source, base_span.lo()),
        )
    }

    fn into_file_local_substitution(self, file_start: BytePos) -> CodeSubstitution<LocalSpan> {
        CodeSubstitution {
            span: LocalSpan {
                lo: self.span.lo() - file_start,
                hi: self.span.hi() - file_start,
            },
            new_code: self.new_code,
            needs_parenthesis: self.needs_parenthesis,
            code_is_single_tree: self.code_is_single_tree,
        }
    }
}

// Take the code represented by base_span and apply all the substitutions, returning the
// resulting code. All spans for the supplied substitutions must be within base_span and must be
// non-overlapping.
pub(crate) fn apply_substitutions<'a, S: SpanT + Sized>(
    substitutions: &[CodeSubstitution<S>],
    source_chunk: SourceChunk<'a>,
) -> String {
    let mut output = String::new();
    let mut span_lo = source_chunk.start_pos;
    for substitution in substitutions {
        output.push_str(source_chunk.get_snippet(span_lo, substitution.span.lo()));
        let should_add_parenthesis =
            substitution.needs_parenthesis && !substitution.code_is_single_tree;
        if should_add_parenthesis {
            output.push('(');
        }
        output.push_str(&substitution.new_code);
        if should_add_parenthesis {
            output.push(')');
        }
        span_lo = substitution.span.hi();
        // Macro invocations consume a ; that follows them. Check if the code we're replacing
        // ends with a ;. If it does and the new code doesn't then insert one. This may need to
        // be smarter, but hopefully this will do.
        let code_being_replaced =
            source_chunk.get_snippet(substitution.span.lo(), substitution.span.hi());
        if code_being_replaced.ends_with(';') && !substitution.new_code.ends_with(';') {
            output.push(';');
        }
    }
    output.push_str(source_chunk.to_end_from(span_lo));
    output
}

/// Returns whether the supplied code is a single tokentree - e.g. a parenthesised expression.
fn code_is_single_tree(code: &str) -> bool {
    use syntax::parse::{self, ParseSess};
    use syntax::source_map::FilePathMapping;
    use syntax::tokenstream::TokenTree;

    let session = ParseSess::new(FilePathMapping::empty());
    let ts = parse::parse_stream_from_source_str(
        syntax_pos::FileName::anon_source_code(code),
        code.to_owned(),
        &session,
        None,
    );
    let mut count = 0;
    for tree in ts.into_trees() {
        match tree {
            TokenTree::Delimited(..) => {
                count += 1;
            }
            _ => return false,
        }
    }
    count <= 1
}

// TODO: We may want to warn if we somehow end up with overlapping matches that aren't duplicates.
fn remove_duplicate_or_overlapping_matches(matches: &mut Vec<CodeSubstitution<LocalSpan>>) {
    let mut max_hi = BytePos(0);
    matches.retain(|subst| {
        let retain = max_hi <= subst.span.lo;
        max_hi = ::std::cmp::max(max_hi, subst.span.hi);
        retain
    });
}

#[derive(Debug, Default)]
pub struct FileRelativeSubstitutions {
    // Substitutions keyed by filename. Each vector of substitutions must be sorted.
    substitutions_by_filename: HashMap<PathBuf, Vec<CodeSubstitution<LocalSpan>>>,
}

impl FileRelativeSubstitutions {
    pub(crate) fn new(
        substitutions: Vec<CodeSubstitution<Span>>,
        source_map: &SourceMap,
    ) -> FileRelativeSubstitutions {
        let mut by_file: HashMap<PathBuf, Vec<CodeSubstitution<LocalSpan>>> = HashMap::new();
        let substitutions_grouped_by_file = substitutions
            .into_iter()
            .group_by(|subst| source_map.span_to_filename(subst.span));
        for (filename, file_substitutions) in &substitutions_grouped_by_file {
            if let syntax_pos::FileName::Real(ref path) = filename {
                let file_relative_for_file = by_file
                    .entry(path.to_path_buf())
                    .or_insert_with(Default::default);
                let source_file = source_map.get_source_file(&filename).unwrap();
                for subst in file_substitutions {
                    file_relative_for_file
                        .push(subst.into_file_local_substitution(source_file.start_pos));
                }
                file_relative_for_file.sort();
                remove_duplicate_or_overlapping_matches(file_relative_for_file);
            }
        }
        FileRelativeSubstitutions {
            substitutions_by_filename: by_file,
        }
    }

    pub fn merge(&mut self, other: FileRelativeSubstitutions) {
        for (filename, substitutions) in other.substitutions_by_filename {
            match self.substitutions_by_filename.entry(filename) {
                hash_map::Entry::Vacant(entry) => {
                    entry.insert(substitutions);
                }
                hash_map::Entry::Occupied(mut entry) => {
                    let merged = entry.get_mut();
                    merged.extend(substitutions);
                    merged.sort();
                    remove_duplicate_or_overlapping_matches(merged);
                }
            }
        }
    }

    pub fn updated_files(
        &self,
        file_loader: &dyn FileLoader,
    ) -> io::Result<HashMap<PathBuf, String>> {
        let mut updated_files = HashMap::new();
        for (filename, substitutions) in &self.substitutions_by_filename {
            let source = file_loader.read_file(&filename)?;
            let output = apply_substitutions(
                substitutions,
                SourceChunk::new(&source, syntax_pos::BytePos(0)),
            );
            updated_files.insert(filename.clone(), output);
        }
        Ok(updated_files)
    }
}
<commit_msg>Improve panic message when we end up with bad spans<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use itertools::Itertools;
use std::collections::{hash_map, HashMap};
use std::fmt::Debug;
use std::io;
use std::path::PathBuf;
use syntax::source_map::FileLoader;
use syntax::source_map::SourceMap;
use syntax_pos::{self, BytePos, Span};

/// A span within a file. Also differs from rustc's Span in that it's not interned, which allows us
/// to make use of it after the compiler has finished running.
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
struct LocalSpan {
    lo: BytePos,
    hi: BytePos,
}

pub(crate) trait SpanT {
    fn lo(&self) -> BytePos;
    fn hi(&self) -> BytePos;
}

impl SpanT for Span {
    fn lo(&self) -> BytePos {
        Span::lo(*self)
    }
    fn hi(&self) -> BytePos {
        Span::hi(*self)
    }
}

impl SpanT for LocalSpan {
    fn lo(&self) -> BytePos {
        self.lo
    }
    fn hi(&self) -> BytePos {
        self.hi
    }
}

#[derive(Debug)]
pub(crate) struct SourceChunk<'a> {
    source: &'a str,
    start_pos: BytePos,
}

impl<'a> SourceChunk<'a> {
    pub(crate) fn new(source: &'a str, start_pos: BytePos) -> SourceChunk<'a> {
        SourceChunk { source, start_pos }
    }

    fn get_snippet(&self, lo: BytePos, hi: BytePos) -> &'a str {
        use syntax_pos::Pos;
        &self.source[(lo - self.start_pos).to_usize()..(hi - self.start_pos).to_usize()]
    }

    fn to_end_from(&self, from: BytePos) -> &'a str {
        use syntax_pos::Pos;
        &self.source[(from - self.start_pos).to_usize()..]
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct CodeSubstitution<S> {
    // The span to be replaced.
    pub(crate) span: S,
    // New code to replace what's at span.
    pub(crate) new_code: String,
    // Whether parenthesis are needed around the substitution.
    pub(crate) needs_parenthesis: bool,
    // Whether `new_code` is a single token tree. e.g. an already parenthesised expression. If it
    // is, then we needn't add parenthesis even if the precedence suggests that we do. This is
    // necessary because our precedence rules are based on HIR which has no way to represent
    // parenthesis.
    code_is_single_tree: bool,
}

impl<S> CodeSubstitution<S> {
    pub(crate) fn new(span: S, new_code: String) -> CodeSubstitution<S> {
        let single_tree = code_is_single_tree(&new_code);
        CodeSubstitution {
            span,
            new_code,
            needs_parenthesis: false,
            code_is_single_tree: single_tree,
        }
    }

    pub(crate) fn needs_parenthesis(mut self, needs_parenthesis: bool) -> CodeSubstitution<S> {
        self.needs_parenthesis = needs_parenthesis;
        self
    }
}

impl CodeSubstitution<Span> {
    pub(crate) fn apply_with_source_map(
        substitutions: &[CodeSubstitution<Span>],
        source_map: &SourceMap,
        base_span: Span,
    ) -> String {
        let base_source = source_map.span_to_snippet(base_span).unwrap();
        apply_substitutions(
            substitutions,
            SourceChunk::new(&base_source, base_span.lo()),
        )
    }

    fn into_file_local_substitution(self, file_start: BytePos) -> CodeSubstitution<LocalSpan> {
        CodeSubstitution {
            span: LocalSpan {
                lo: self.span.lo() - file_start,
                hi: self.span.hi() - file_start,
            },
            new_code: self.new_code,
            needs_parenthesis: self.needs_parenthesis,
            code_is_single_tree: self.code_is_single_tree,
        }
    }
}

// Take the code represented by base_span and apply all the substitutions, returning the
// resulting code. All spans for the supplied substitutions must be within base_span and must be
// non-overlapping.
pub(crate) fn apply_substitutions<'a, S: SpanT + Sized + Debug>(
    substitutions: &[CodeSubstitution<S>],
    source_chunk: SourceChunk<'a>,
) -> String {
    let mut output = String::new();
    let mut span_lo = source_chunk.start_pos;
    for substitution in substitutions {
        if substitution.span.lo() < span_lo {
            panic!(
                "Bad substitutions: {:#?}\nFor source: {:?}",
                substitutions, source_chunk
            );
        }
        output.push_str(source_chunk.get_snippet(span_lo, substitution.span.lo()));
        let should_add_parenthesis =
            substitution.needs_parenthesis && !substitution.code_is_single_tree;
        if should_add_parenthesis {
            output.push('(');
        }
        output.push_str(&substitution.new_code);
        if should_add_parenthesis {
            output.push(')');
        }
        span_lo = substitution.span.hi();
        // Macro invocations consume a ; that follows them. Check if the code we're replacing
        // ends with a ;. If it does and the new code doesn't then insert one. This may need to
        // be smarter, but hopefully this will do.
        let code_being_replaced =
            source_chunk.get_snippet(substitution.span.lo(), substitution.span.hi());
        if code_being_replaced.ends_with(';') && !substitution.new_code.ends_with(';') {
            output.push(';');
        }
    }
    output.push_str(source_chunk.to_end_from(span_lo));
    output
}

/// Returns whether the supplied code is a single tokentree - e.g. a parenthesised expression.
fn code_is_single_tree(code: &str) -> bool {
    use syntax::parse::{self, ParseSess};
    use syntax::source_map::FilePathMapping;
    use syntax::tokenstream::TokenTree;

    let session = ParseSess::new(FilePathMapping::empty());
    let ts = parse::parse_stream_from_source_str(
        syntax_pos::FileName::anon_source_code(code),
        code.to_owned(),
        &session,
        None,
    );
    let mut count = 0;
    for tree in ts.into_trees() {
        match tree {
            TokenTree::Delimited(..) => {
                count += 1;
            }
            _ => return false,
        }
    }
    count <= 1
}

// TODO: We may want to warn if we somehow end up with overlapping matches that aren't duplicates.
fn remove_duplicate_or_overlapping_matches(matches: &mut Vec<CodeSubstitution<LocalSpan>>) {
    let mut max_hi = BytePos(0);
    matches.retain(|subst| {
        let retain = max_hi <= subst.span.lo;
        max_hi = ::std::cmp::max(max_hi, subst.span.hi);
        retain
    });
}

#[derive(Debug, Default)]
pub struct FileRelativeSubstitutions {
    // Substitutions keyed by filename. Each vector of substitutions must be sorted.
    substitutions_by_filename: HashMap<PathBuf, Vec<CodeSubstitution<LocalSpan>>>,
}

impl FileRelativeSubstitutions {
    pub(crate) fn new(
        substitutions: Vec<CodeSubstitution<Span>>,
        source_map: &SourceMap,
    ) -> FileRelativeSubstitutions {
        let mut by_file: HashMap<PathBuf, Vec<CodeSubstitution<LocalSpan>>> = HashMap::new();
        let substitutions_grouped_by_file = substitutions
            .into_iter()
            .group_by(|subst| source_map.span_to_filename(subst.span));
        for (filename, file_substitutions) in &substitutions_grouped_by_file {
            if let syntax_pos::FileName::Real(ref path) = filename {
                let file_relative_for_file = by_file
                    .entry(path.to_path_buf())
                    .or_insert_with(Default::default);
                let source_file = source_map.get_source_file(&filename).unwrap();
                for subst in file_substitutions {
                    file_relative_for_file
                        .push(subst.into_file_local_substitution(source_file.start_pos));
                }
                file_relative_for_file.sort();
                remove_duplicate_or_overlapping_matches(file_relative_for_file);
            }
        }
        FileRelativeSubstitutions {
            substitutions_by_filename: by_file,
        }
    }

    pub fn merge(&mut self, other: FileRelativeSubstitutions) {
        for (filename, substitutions) in other.substitutions_by_filename {
            match self.substitutions_by_filename.entry(filename) {
                hash_map::Entry::Vacant(entry) => {
                    entry.insert(substitutions);
                }
                hash_map::Entry::Occupied(mut entry) => {
                    let merged = entry.get_mut();
                    merged.extend(substitutions);
                    merged.sort();
                    remove_duplicate_or_overlapping_matches(merged);
                }
            }
        }
    }

    pub fn updated_files(
        &self,
        file_loader: &dyn FileLoader,
    ) -> io::Result<HashMap<PathBuf, String>> {
        let mut updated_files = HashMap::new();
        for (filename, substitutions) in &self.substitutions_by_filename {
            let source = file_loader.read_file(&filename)?;
            let output = apply_substitutions(
                substitutions,
                SourceChunk::new(&source, syntax_pos::BytePos(0)),
            );
            updated_files.insert(filename.clone(), output);
        }
        Ok(updated_files)
    }
}
<|endoftext|>


<commit_before>//! The HTTP request method
use std::fmt;
use std::str::FromStr;
use std::convert::AsRef;

use error::Error;
use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
                   Extension};

/// The Request Method (VERB)
///
/// Currently includes 8 variants representing the 8 methods defined in
/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
/// and an Extension variant for all extensions.
///
/// It may make sense to grow this to include all variants currently
/// registered with IANA, if they are at all common to use.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Method {
    /// OPTIONS
    Options,
    /// GET
    Get,
    /// POST
    Post,
    /// PUT
    Put,
    /// DELETE
    Delete,
    /// HEAD
    Head,
    /// TRACE
    Trace,
    /// CONNECT
    Connect,
    /// PATCH
    Patch,
    /// Method extensions. An example would be `let m = Extension("FOO".to_string())`.
    Extension(String)
}

impl AsRef<str> for Method {
    fn as_ref(&self) -> &str {
        match *self {
            Options => "OPTIONS",
            Get => "GET",
            Post => "POST",
            Put => "PUT",
            Delete => "DELETE",
            Head => "HEAD",
            Trace => "TRACE",
            Connect => "CONNECT",
            Patch => "PATCH",
            Extension(ref s) => s.as_ref()
        }
    }
}

impl Method {
    /// Whether a method is considered "safe", meaning the request is
    /// essentially read-only.
    ///
    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
    /// for more words.
    pub fn safe(&self) -> bool {
        match *self {
            Get | Head | Options | Trace => true,
            _ => false
        }
    }

    /// Whether a method is considered "idempotent", meaning the request has
    /// the same result if executed multiple times.
    ///
    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
    /// more words.
    pub fn idempotent(&self) -> bool {
        if self.safe() {
            true
        } else {
            match *self {
                Put | Delete => true,
                _ => false
            }
        }
    }
}

impl FromStr for Method {
    type Err = Error;
    fn from_str(s: &str) -> Result<Method, Error> {
        if s == "" {
            Err(Error::Method)
        } else {
            Ok(match s {
                "OPTIONS" => Options,
                "GET" => Get,
                "POST" => Post,
                "PUT" => Put,
                "DELETE" => Delete,
                "HEAD" => Head,
                "TRACE" => Trace,
                "CONNECT" => Connect,
                "PATCH" => Patch,
                _ => Extension(s.to_owned())
            })
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(match *self {
            Options => "OPTIONS",
            Get => "GET",
            Post => "POST",
            Put => "PUT",
            Delete => "DELETE",
            Head => "HEAD",
            Trace => "TRACE",
            Connect => "CONNECT",
            Patch => "PATCH",
            Extension(ref s) => s.as_ref()
        })
    }
}

impl Default for Method {
    fn default() -> Method {
        Method::Get
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::str::FromStr;
    use error::Error;
    use super::Method;
    use super::Method::{Get, Post, Put, Extension};

    #[test]
    fn test_safe() {
        assert_eq!(true, Get.safe());
        assert_eq!(false, Post.safe());
    }

    #[test]
    fn test_idempotent() {
        assert_eq!(true, Get.idempotent());
        assert_eq!(true, Put.idempotent());
        assert_eq!(false, Post.idempotent());
    }

    #[test]
    fn test_from_str() {
        assert_eq!(Get, FromStr::from_str("GET").unwrap());
        assert_eq!(Extension("MOVE".to_owned()),
                   FromStr::from_str("MOVE").unwrap());
        let x: Result<Method, _> = FromStr::from_str("");
        if let Err(Error::Method) = x {
        } else {
            panic!("An empty method is invalid!")
        }
    }

    #[test]
    fn test_fmt() {
        assert_eq!("GET".to_owned(), format!("{}", Get));
        assert_eq!("MOVE".to_owned(),
                   format!("{}", Extension("MOVE".to_owned())));
    }

    #[test]
    fn test_hashable() {
        let mut counter: HashMap<Method,usize> = HashMap::new();
        counter.insert(Get, 1);
        assert_eq!(Some(&1), counter.get(&Get));
    }

    #[test]
    fn test_as_str() {
        assert_eq!(Get.as_ref(), "GET");
        assert_eq!(Post.as_ref(), "POST");
        assert_eq!(Put.as_ref(), "PUT");
        assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE");
    }
}
<commit_msg>perf(method): improve logic of parsing a Method<commit_after>//! The HTTP request method
use std::fmt;
use std::str::FromStr;
use std::convert::AsRef;

use error::Error;
use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
                   Extension};

/// The Request Method (VERB)
///
/// Currently includes 8 variants representing the 8 methods defined in
/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
/// and an Extension variant for all extensions.
///
/// It may make sense to grow this to include all variants currently
/// registered with IANA, if they are at all common to use.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Method {
    /// OPTIONS
    Options,
    /// GET
    Get,
    /// POST
    Post,
    /// PUT
    Put,
    /// DELETE
    Delete,
    /// HEAD
    Head,
    /// TRACE
    Trace,
    /// CONNECT
    Connect,
    /// PATCH
    Patch,
    /// Method extensions. An example would be `let m = Extension("FOO".to_string())`.
    Extension(String)
}

impl AsRef<str> for Method {
    fn as_ref(&self) -> &str {
        match *self {
            Options => "OPTIONS",
            Get => "GET",
            Post => "POST",
            Put => "PUT",
            Delete => "DELETE",
            Head => "HEAD",
            Trace => "TRACE",
            Connect => "CONNECT",
            Patch => "PATCH",
            Extension(ref s) => s.as_ref()
        }
    }
}

impl Method {
    /// Whether a method is considered "safe", meaning the request is
    /// essentially read-only.
    ///
    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
    /// for more words.
    pub fn safe(&self) -> bool {
        match *self {
            Get | Head | Options | Trace => true,
            _ => false
        }
    }

    /// Whether a method is considered "idempotent", meaning the request has
    /// the same result if executed multiple times.
    ///
    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
    /// more words.
    pub fn idempotent(&self) -> bool {
        if self.safe() {
            true
        } else {
            match *self {
                Put | Delete => true,
                _ => false
            }
        }
    }
}

macro_rules! from_str {
    ($s:ident, { $($n:pat => { $($text:pat => $var:ident,)* },)* }) => ({
        let s = $s;
        match s.len() {
            $(
            $n => match s {
                $(
                $text => return Ok($var),
                )*
                _ => {},
            },
            )*
            0 => return Err(::Error::Method),
            _ => {},
        }
        Ok(Extension(s.to_owned()))
    })
}

impl FromStr for Method {
    type Err = Error;
    fn from_str(s: &str) -> Result<Method, Error> {
        from_str!(s, {
            3 => {
                "GET" => Get,
                "PUT" => Put,
            },
            4 => {
                "HEAD" => Head,
                "POST" => Post,
            },
            5 => {
                "PATCH" => Patch,
                "TRACE" => Trace,
            },
            6 => {
                "DELETE" => Delete,
            },
            7 => {
                "OPTIONS" => Options,
                "CONNECT" => Connect,
            },
        })
    }
}

impl fmt::Display for Method {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(match *self {
            Options => "OPTIONS",
            Get => "GET",
            Post => "POST",
            Put => "PUT",
            Delete => "DELETE",
            Head => "HEAD",
            Trace => "TRACE",
            Connect => "CONNECT",
            Patch => "PATCH",
            Extension(ref s) => s.as_ref()
        })
    }
}

impl Default for Method {
    fn default() -> Method {
        Method::Get
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::str::FromStr;
    use error::Error;
    use super::Method;
    use super::Method::{Get, Post, Put, Extension};

    #[test]
    fn test_safe() {
        assert_eq!(true, Get.safe());
        assert_eq!(false, Post.safe());
    }

    #[test]
    fn test_idempotent() {
        assert_eq!(true, Get.idempotent());
        assert_eq!(true, Put.idempotent());
        assert_eq!(false, Post.idempotent());
    }

    #[test]
    fn test_from_str() {
        assert_eq!(Get, FromStr::from_str("GET").unwrap());
        assert_eq!(Extension("MOVE".to_owned()),
                   FromStr::from_str("MOVE").unwrap());
        let x: Result<Method, _> = FromStr::from_str("");
        if let Err(Error::Method) = x {
        } else {
            panic!("An empty method is invalid!")
        }
    }

    #[test]
    fn test_fmt() {
        assert_eq!("GET".to_owned(), format!("{}", Get));
        assert_eq!("MOVE".to_owned(),
                   format!("{}", Extension("MOVE".to_owned())));
    }

    #[test]
    fn test_hashable() {
        let mut counter: HashMap<Method,usize> = HashMap::new();
        counter.insert(Get, 1);
        assert_eq!(Some(&1), counter.get(&Get));
    }

    #[test]
    fn test_as_str() {
        assert_eq!(Get.as_ref(), "GET");
        assert_eq!(Post.as_ref(), "POST");
        assert_eq!(Put.as_ref(), "PUT");
        assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE");
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};

fn main() {
    assert_eq!(env::args_os().len(), 2);
    let test = PathBuf::from(env::args_os().nth(1).unwrap());
    let dst = Path::new("/data/local/tmp").join(test.file_name().unwrap());

    let status = Command::new("adb")
        .arg("wait-for-device")
        .status()
        .expect("failed to run rumprun-bake");
    assert!(status.success());

    let status = Command::new("adb")
        .arg("push")
        .arg(&test)
        .arg(&dst)
        .status()
        .expect("failed to run rumprun-bake");
    assert!(status.success());

    let output = Command::new("adb")
        .arg("shell")
        .arg(&dst)
        .output()
        .expect("failed to run rumprun-bake");
    assert!(status.success());

    println!("status: {}\nstdout ---\n{}\nstderr ---\n{}",
             output.status,
             String::from_utf8_lossy(&output.stdout),
             String::from_utf8_lossy(&output.stderr));

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut lines = stdout.lines().filter(|l| l.starts_with("PASSED "));
    if !lines.any(|l| l.contains(" tests")) {
        panic!("failed to find successful test run");
    }
}
<commit_msg>Auto merge of #1234 - gnzlbg:android_runtest, r=gnzlbg<commit_after>use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};

fn main() {
    let args = env::args_os()
        .skip(1)
        .filter(|arg| arg != "--quiet")
        .collect::<Vec<_>>();
    assert_eq!(args.len(), 1);
    let test = PathBuf::from(&args[0]);
    let dst = Path::new("/data/local/tmp").join(test.file_name().unwrap());

    let status = Command::new("adb")
        .arg("wait-for-device")
        .status()
        .expect("failed to run: adb wait-for-device");
    assert!(status.success());

    let status = Command::new("adb")
        .arg("push")
        .arg(&test)
        .arg(&dst)
        .status()
        .expect("failed to run: adb pushr");
    assert!(status.success());

    let output = Command::new("adb")
        .arg("shell")
        .arg(&dst)
        .output()
        .expect("failed to run: adb shell");
    assert!(status.success());

    println!("status: {}\nstdout ---\n{}\nstderr ---\n{}",
             output.status,
             String::from_utf8_lossy(&output.stdout),
             String::from_utf8_lossy(&output.stderr));

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut lines = stdout.lines().filter(|l| l.starts_with("PASSED "));
    if !lines.any(|l| l.contains(" tests")) {
        panic!("failed to find successful test run");
    }
}
<|endoftext|>


<commit_before>//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
use hir::Semantics;
use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
use syntax::ast::{self, IsString};

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

const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
("CARGO","Path to the cargo binary performing the build"),
("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"),
("CARGO_PKG_VERSION","The full version of your package"),
("CARGO_PKG_VERSION_MAJOR","The major version of your package"),
("CARGO_PKG_VERSION_MINOR","The minor version of your package"),
("CARGO_PKG_VERSION_PATCH","The patch version of your package"),
("CARGO_PKG_VERSION_PRE","The pre-release version of your package"),
("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"),
("CARGO_PKG_NAME","The name of your package"),
("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"),
("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"),
        ("CARGO_PKG_REPOSITORY",
"The repository from the manifest of your package"),
("CARGO_PKG_LICENSE","The license from the manifest of your package"),
("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"),
("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"),
("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"),
("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"),
("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"),
("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code")
];

pub(crate) fn complete_cargo_env_vars(
    acc: &mut Completions,
    ctx: &CompletionContext<'_>,
    expanded: &ast::String,
) -> Option<()> {
    guard_env_macro(expanded, &ctx.sema)?;
    let range = expanded.text_range_between_quotes()?;

    CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
        let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
        item.detail(*detail);
        item.add_to(acc);
    });

    Some(())
}

fn guard_env_macro(
    string: &ast::String,
    semantics: &Semantics<'_, RootDatabase>,
) -> Option<()> {
    let call = macro_call_for_string_token(string)?;
    let name = call.path()?.segment()?.name_ref()?;
    let makro = semantics.resolve_macro_call(&call)?;
    let db = semantics.db;

    match name.text().as_str() {
        "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_edit, completion_list};

    fn check(macro_name: &str) {
        check_edit(
            "CARGO_BIN_NAME",
            &format!(
                r#"
            #[rustc_builtin_macro]
            macro_rules! {} {{
                ($var:literal) => {{ 0 }}
            }}

            fn main() {{
                let foo = {}!("CAR$0");
            }}
        "#,
                macro_name, macro_name
            ),
            &format!(
                r#"
            #[rustc_builtin_macro]
            macro_rules! {} {{
                ($var:literal) => {{ 0 }}
            }}

            fn main() {{
                let foo = {}!("CARGO_BIN_NAME");
            }}
        "#,
                macro_name, macro_name
            ),
        );
    }
    #[test]
    fn completes_env_variable_in_env() {
        check("env")
    }

    #[test]
    fn completes_env_variable_in_option_env() {
        check("option_env");
    }

    #[test]
    fn doesnt_complete_in_random_strings() {
        let fixture = r#"
            fn main() {
                let foo = "CA$0";
            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
    }

    #[test]
    fn doesnt_complete_in_random_macro() {
        let fixture = r#"
            macro_rules! bar {
                ($($arg:tt)*) => { 0 }
            }

            fn main() {
                let foo = bar!("CA$0");

            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
    }

    #[test]
    fn doesnt_complete_for_shadowed_macro() {
        let fixture = r#"
            macro_rules! env {
                ($var:literal) => { 0 }
            }

            fn main() {
                let foo = env!("CA$0");
            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions)
    }
}
<commit_msg>Fix formatting for cargo vars list<commit_after>//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
use hir::Semantics;
use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
use syntax::ast::{self, IsString};

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

const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
    ("CARGO","Path to the cargo binary performing the build"),
    ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"),
    ("CARGO_PKG_VERSION","The full version of your package"),
    ("CARGO_PKG_VERSION_MAJOR","The major version of your package"),
    ("CARGO_PKG_VERSION_MINOR","The minor version of your package"),
    ("CARGO_PKG_VERSION_PATCH","The patch version of your package"),
    ("CARGO_PKG_VERSION_PRE","The pre-release version of your package"),
    ("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"),
    ("CARGO_PKG_NAME","The name of your package"),
    ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"),
    ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"),
    ("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"),
    ("CARGO_PKG_LICENSE","The license from the manifest of your package"),
    ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"),
    ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"),
    ("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"),
    ("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"),
    ("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"),
    ("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code")
];

pub(crate) fn complete_cargo_env_vars(
    acc: &mut Completions,
    ctx: &CompletionContext<'_>,
    expanded: &ast::String,
) -> Option<()> {
    guard_env_macro(expanded, &ctx.sema)?;
    let range = expanded.text_range_between_quotes()?;

    CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
        let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
        item.detail(*detail);
        item.add_to(acc);
    });

    Some(())
}

fn guard_env_macro(
    string: &ast::String,
    semantics: &Semantics<'_, RootDatabase>,
) -> Option<()> {
    let call = macro_call_for_string_token(string)?;
    let name = call.path()?.segment()?.name_ref()?;
    let makro = semantics.resolve_macro_call(&call)?;
    let db = semantics.db;

    match name.text().as_str() {
        "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_edit, completion_list};

    fn check(macro_name: &str) {
        check_edit(
            "CARGO_BIN_NAME",
            &format!(
                r#"
            #[rustc_builtin_macro]
            macro_rules! {} {{
                ($var:literal) => {{ 0 }}
            }}

            fn main() {{
                let foo = {}!("CAR$0");
            }}
        "#,
                macro_name, macro_name
            ),
            &format!(
                r#"
            #[rustc_builtin_macro]
            macro_rules! {} {{
                ($var:literal) => {{ 0 }}
            }}

            fn main() {{
                let foo = {}!("CARGO_BIN_NAME");
            }}
        "#,
                macro_name, macro_name
            ),
        );
    }
    #[test]
    fn completes_env_variable_in_env() {
        check("env")
    }

    #[test]
    fn completes_env_variable_in_option_env() {
        check("option_env");
    }

    #[test]
    fn doesnt_complete_in_random_strings() {
        let fixture = r#"
            fn main() {
                let foo = "CA$0";
            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
    }

    #[test]
    fn doesnt_complete_in_random_macro() {
        let fixture = r#"
            macro_rules! bar {
                ($($arg:tt)*) => { 0 }
            }

            fn main() {
                let foo = bar!("CA$0");

            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
    }

    #[test]
    fn doesnt_complete_for_shadowed_macro() {
        let fixture = r#"
            macro_rules! env {
                ($var:literal) => { 0 }
            }

            fn main() {
                let foo = env!("CA$0");
            }
        "#;

        let completions = completion_list(fixture);
        assert!(completions.is_empty(), "Completions weren't empty: {}", completions)
    }
}
<|endoftext|>


<commit_before>// -*- rust -*-

import front.creader;
import front.parser;
import front.token;
import front.eval;
import middle.trans;
import middle.resolve;
import middle.capture;
import middle.ty;
import middle.typeck;
import middle.typestate_check;
import util.common;

import std.map.mk_hashmap;
import std.option;
import std.option.some;
import std.option.none;
import std._str;
import std._vec;
import std.io;

import std.GetOpts;
import std.GetOpts.optopt;
import std.GetOpts.optmulti;
import std.GetOpts.optflag;
import std.GetOpts.opt_present;

fn default_environment(session.session sess,
                       str argv0,
                       str input) -> eval.env {

    auto libc = "libc.so";
    alt (sess.get_targ_cfg().os) {
        case (session.os_win32) { libc = "msvcrt.dll"; }
        case (session.os_macos) { libc = "libc.dylib"; }
        case (session.os_linux) { libc = "libc.so.6"; }
    }

    ret
        vec(
            // Target bindings.
            tup("target_os", eval.val_str(std.os.target_os())),
            tup("target_arch", eval.val_str("x86")),
            tup("target_libc", eval.val_str(libc)),

            // Build bindings.
            tup("build_compiler", eval.val_str(argv0)),
            tup("build_input", eval.val_str(input))
            );
}

fn parse_input(session.session sess,
                      parser.parser p,
                      str input) -> @front.ast.crate {
    if (_str.ends_with(input, ".rc")) {
        ret parser.parse_crate_from_crate_file(p);
    } else if (_str.ends_with(input, ".rs")) {
        ret parser.parse_crate_from_source_file(p);
    }
    sess.err("unknown input file type: " + input);
    fail;
}

fn compile_input(session.session sess,
                        eval.env env,
                        str input, str output,
                        bool shared,
                        bool optimize,
                        bool verify,
                        bool save_temps,
                        trans.output_type ot,
                        vec[str] library_search_paths) {
    auto def = tup(0, 0);
    auto p = parser.new_parser(sess, env, def, input, 0u);
    auto crate = parse_input(sess, p, input);
    if (ot == trans.output_type_none) {ret;}

    crate = creader.read_crates(sess, crate, library_search_paths);
    crate = resolve.resolve_crate(sess, crate);
    capture.check_for_captures(sess, crate);

    auto ty_cx = ty.mk_ctxt(sess);
    auto typeck_result = typeck.check_crate(ty_cx, crate);
    crate = typeck_result._0;
    auto type_cache = typeck_result._1;
    // FIXME: uncomment once typestate_check works
    // crate = typestate_check.check_crate(crate);
    trans.trans_crate(sess, crate, ty_cx, type_cache, output, shared,
                      optimize, verify, save_temps, ot);
}

fn pretty_print_input(session.session sess,
                             eval.env env,
                             str input) {
    auto def = tup(0, 0);
    auto p = front.parser.new_parser(sess, env, def, input, 0u);
    auto crate = front.parser.parse_crate_from_source_file(p);
    pretty.pprust.print_file(crate.node.module, input, std.io.stdout());
}

fn warn_wrong_compiler() {
    io.stdout().write_str("This is the rust 'self-hosted' compiler.
The one written in rust.
It is currently incomplete.
You may want rustboot instead, the compiler next door.\n");
}

fn usage(session.session sess, str argv0) {
    io.stdout().write_str(#fmt("usage: %s [options] <input>\n", argv0) + "
options:

    -o <filename>      write output to <filename>
    --nowarn           suppress wrong-compiler warning
    --glue             generate glue.bc file
    --shared           compile a shared-library crate
    --pretty           pretty-print the input instead of compiling
    --ls               list the symbols defined by a crate file
    -L <path>          add a directory to the library search path
    --noverify         suppress LLVM verification step (slight speedup)
    --parse-only       parse only; do not compile, assemble, or link
    -O                 optimize
    -S                 compile only; do not assemble or link
    -c                 compile and assemble, but do not link
    --save-temps       write intermediate files in addition to normal output
    -h                 display this message\n\n");
}

fn get_os() -> session.os {
    auto s = std.os.target_os();
    if (_str.eq(s, "win32")) { ret session.os_win32; }
    if (_str.eq(s, "macos")) { ret session.os_macos; }
    if (_str.eq(s, "linux")) { ret session.os_linux; }
}

fn main(vec[str] args) {

    // FIXME: don't hard-wire this.
    auto target_cfg = rec(os = get_os(),
                          arch = session.arch_x86,
                          int_type = common.ty_i32,
                          uint_type = common.ty_u32,
                          float_type = common.ty_f64 );

    auto crate_cache = common.new_int_hash[session.crate_metadata]();
    auto target_crate_num = 0;
    let vec[@front.ast.meta_item] md = vec();
    auto sess = session.session(target_crate_num, target_cfg, crate_cache,
                                md, front.codemap.new_codemap());

    auto opts = vec(optflag("nowarn"), optflag("h"), optflag("glue"),
                    optflag("pretty"), optflag("ls"), optflag("parse-only"),
                    optflag("O"), optflag("shared"), optmulti("L"),
                    optflag("S"), optflag("c"), optopt("o"),
                    optflag("save-temps"), optflag("noverify"));
    auto binary = _vec.shift[str](args);
    auto match;
    alt (GetOpts.getopts(args, opts)) {
        case (GetOpts.failure(?f)) { sess.err(GetOpts.fail_str(f)); fail; }
        case (GetOpts.success(?m)) { match = m; }
    }
    if (!opt_present(match, "nowarn")) {
        warn_wrong_compiler();
    }
    if (opt_present(match, "h")) {
        usage(sess, binary);
        ret;
    }

    auto pretty = opt_present(match, "pretty");
    auto ls = opt_present(match, "ls");
    auto glue = opt_present(match, "glue");
    auto shared = opt_present(match, "shared");
    auto output_file = GetOpts.opt_maybe_str(match, "o");
    auto library_search_paths = GetOpts.opt_strs(match, "L");
    auto ot = trans.output_type_bitcode;
    if (opt_present(match, "parse-only")) {
        ot = trans.output_type_none;
    } else if (opt_present(match, "S")) {
        ot = trans.output_type_assembly;
    } else if (opt_present(match, "c")) {
        ot = trans.output_type_object;
    }
    auto verify = !opt_present(match, "noverify");
    auto save_temps = opt_present(match, "save-temps");
    // FIXME: Maybe we should support -O0, -O1, -Os, etc
    auto optimize = opt_present(match, "O");
    auto n_inputs = _vec.len[str](match.free);

    if (glue) {
        if (n_inputs > 0u) {
            sess.err("No input files allowed with --glue.");
        }
        auto out = option.from_maybe[str]("glue.bc", output_file);
        middle.trans.make_common_glue(out, optimize, verify, save_temps, ot);
        ret;
    }

    if (n_inputs == 0u) {
        sess.err("No input filename given.");
    } else if (n_inputs > 1u) {
        sess.err("Multiple input filenames provided.");
    }

    auto ifile = match.free.(0);
    auto env = default_environment(sess, args.(0), ifile);
    if (pretty) {
        pretty_print_input(sess, env, ifile);
    } else if (ls) {
        front.creader.list_file_metadata(ifile, std.io.stdout());
    } else {
        alt (output_file) {
            case (none[str]) {
                let vec[str] parts = _str.split(ifile, '.' as u8);
                _vec.pop[str](parts);
                parts += vec(".bc");
                auto ofile = _str.concat(parts);
                compile_input(sess, env, ifile, ofile, shared,
                              optimize, verify, save_temps, ot,
                              library_search_paths);
            }
            case (some[str](?ofile)) {
                compile_input(sess, env, ifile, ofile, shared,
                              optimize, verify, save_temps, ot,
                              library_search_paths);
            }
        }
    }
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Enable typestate_check<commit_after>// -*- rust -*-

import front.creader;
import front.parser;
import front.token;
import front.eval;
import middle.trans;
import middle.resolve;
import middle.capture;
import middle.ty;
import middle.typeck;
import middle.typestate_check;
import util.common;

import std.map.mk_hashmap;
import std.option;
import std.option.some;
import std.option.none;
import std._str;
import std._vec;
import std.io;

import std.GetOpts;
import std.GetOpts.optopt;
import std.GetOpts.optmulti;
import std.GetOpts.optflag;
import std.GetOpts.opt_present;

fn default_environment(session.session sess,
                       str argv0,
                       str input) -> eval.env {

    auto libc = "libc.so";
    alt (sess.get_targ_cfg().os) {
        case (session.os_win32) { libc = "msvcrt.dll"; }
        case (session.os_macos) { libc = "libc.dylib"; }
        case (session.os_linux) { libc = "libc.so.6"; }
    }

    ret
        vec(
            // Target bindings.
            tup("target_os", eval.val_str(std.os.target_os())),
            tup("target_arch", eval.val_str("x86")),
            tup("target_libc", eval.val_str(libc)),

            // Build bindings.
            tup("build_compiler", eval.val_str(argv0)),
            tup("build_input", eval.val_str(input))
            );
}

fn parse_input(session.session sess,
                      parser.parser p,
                      str input) -> @front.ast.crate {
    if (_str.ends_with(input, ".rc")) {
        ret parser.parse_crate_from_crate_file(p);
    } else if (_str.ends_with(input, ".rs")) {
        ret parser.parse_crate_from_source_file(p);
    }
    sess.err("unknown input file type: " + input);
    fail;
}

fn compile_input(session.session sess,
                        eval.env env,
                        str input, str output,
                        bool shared,
                        bool optimize,
                        bool verify,
                        bool save_temps,
                        trans.output_type ot,
                        vec[str] library_search_paths) {
    auto def = tup(0, 0);
    auto p = parser.new_parser(sess, env, def, input, 0u);
    auto crate = parse_input(sess, p, input);
    if (ot == trans.output_type_none) {ret;}

    crate = creader.read_crates(sess, crate, library_search_paths);
    crate = resolve.resolve_crate(sess, crate);
    capture.check_for_captures(sess, crate);

    auto ty_cx = ty.mk_ctxt(sess);
    auto typeck_result = typeck.check_crate(ty_cx, crate);
    crate = typeck_result._0;
    auto type_cache = typeck_result._1;
    crate = typestate_check.check_crate(crate);
    trans.trans_crate(sess, crate, ty_cx, type_cache, output, shared,
                      optimize, verify, save_temps, ot);
}

fn pretty_print_input(session.session sess,
                             eval.env env,
                             str input) {
    auto def = tup(0, 0);
    auto p = front.parser.new_parser(sess, env, def, input, 0u);
    auto crate = front.parser.parse_crate_from_source_file(p);
    pretty.pprust.print_file(crate.node.module, input, std.io.stdout());
}

fn warn_wrong_compiler() {
    io.stdout().write_str("This is the rust 'self-hosted' compiler.
The one written in rust.
It is currently incomplete.
You may want rustboot instead, the compiler next door.\n");
}

fn usage(session.session sess, str argv0) {
    io.stdout().write_str(#fmt("usage: %s [options] <input>\n", argv0) + "
options:

    -o <filename>      write output to <filename>
    --nowarn           suppress wrong-compiler warning
    --glue             generate glue.bc file
    --shared           compile a shared-library crate
    --pretty           pretty-print the input instead of compiling
    --ls               list the symbols defined by a crate file
    -L <path>          add a directory to the library search path
    --noverify         suppress LLVM verification step (slight speedup)
    --parse-only       parse only; do not compile, assemble, or link
    -O                 optimize
    -S                 compile only; do not assemble or link
    -c                 compile and assemble, but do not link
    --save-temps       write intermediate files in addition to normal output
    -h                 display this message\n\n");
}

fn get_os() -> session.os {
    auto s = std.os.target_os();
    if (_str.eq(s, "win32")) { ret session.os_win32; }
    if (_str.eq(s, "macos")) { ret session.os_macos; }
    if (_str.eq(s, "linux")) { ret session.os_linux; }
}

fn main(vec[str] args) {

    // FIXME: don't hard-wire this.
    auto target_cfg = rec(os = get_os(),
                          arch = session.arch_x86,
                          int_type = common.ty_i32,
                          uint_type = common.ty_u32,
                          float_type = common.ty_f64 );

    auto crate_cache = common.new_int_hash[session.crate_metadata]();
    auto target_crate_num = 0;
    let vec[@front.ast.meta_item] md = vec();
    auto sess = session.session(target_crate_num, target_cfg, crate_cache,
                                md, front.codemap.new_codemap());

    auto opts = vec(optflag("nowarn"), optflag("h"), optflag("glue"),
                    optflag("pretty"), optflag("ls"), optflag("parse-only"),
                    optflag("O"), optflag("shared"), optmulti("L"),
                    optflag("S"), optflag("c"), optopt("o"),
                    optflag("save-temps"), optflag("noverify"));
    auto binary = _vec.shift[str](args);
    auto match;
    alt (GetOpts.getopts(args, opts)) {
        case (GetOpts.failure(?f)) { sess.err(GetOpts.fail_str(f)); fail; }
        case (GetOpts.success(?m)) { match = m; }
    }
    if (!opt_present(match, "nowarn")) {
        warn_wrong_compiler();
    }
    if (opt_present(match, "h")) {
        usage(sess, binary);
        ret;
    }

    auto pretty = opt_present(match, "pretty");
    auto ls = opt_present(match, "ls");
    auto glue = opt_present(match, "glue");
    auto shared = opt_present(match, "shared");
    auto output_file = GetOpts.opt_maybe_str(match, "o");
    auto library_search_paths = GetOpts.opt_strs(match, "L");
    auto ot = trans.output_type_bitcode;
    if (opt_present(match, "parse-only")) {
        ot = trans.output_type_none;
    } else if (opt_present(match, "S")) {
        ot = trans.output_type_assembly;
    } else if (opt_present(match, "c")) {
        ot = trans.output_type_object;
    }
    auto verify = !opt_present(match, "noverify");
    auto save_temps = opt_present(match, "save-temps");
    // FIXME: Maybe we should support -O0, -O1, -Os, etc
    auto optimize = opt_present(match, "O");
    auto n_inputs = _vec.len[str](match.free);

    if (glue) {
        if (n_inputs > 0u) {
            sess.err("No input files allowed with --glue.");
        }
        auto out = option.from_maybe[str]("glue.bc", output_file);
        middle.trans.make_common_glue(out, optimize, verify, save_temps, ot);
        ret;
    }

    if (n_inputs == 0u) {
        sess.err("No input filename given.");
    } else if (n_inputs > 1u) {
        sess.err("Multiple input filenames provided.");
    }

    auto ifile = match.free.(0);
    auto env = default_environment(sess, args.(0), ifile);
    if (pretty) {
        pretty_print_input(sess, env, ifile);
    } else if (ls) {
        front.creader.list_file_metadata(ifile, std.io.stdout());
    } else {
        alt (output_file) {
            case (none[str]) {
                let vec[str] parts = _str.split(ifile, '.' as u8);
                _vec.pop[str](parts);
                parts += vec(".bc");
                auto ofile = _str.concat(parts);
                compile_input(sess, env, ifile, ofile, shared,
                              optimize, verify, save_temps, ot,
                              library_search_paths);
            }
            case (some[str](?ofile)) {
                compile_input(sess, env, ifile, ofile, shared,
                              optimize, verify, save_temps, ot,
                              library_search_paths);
            }
        }
    }
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>