<commit_before>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::CrateId;
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::SourceRootConfig,
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than on impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        let mut has_fs_changes = false;

        let change = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in changed_files {
                if file.is_created_or_deleted() {
                    if let Some(path) = vfs.file_path(file.file_id).as_path() {
                        fs_changes.push((path.to_path_buf(), file.change_kind));
                        has_fs_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    match String::from_utf8(bytes).ok() {
                        Some(text) => {
                            let (text, line_endings) = LineEndings::normalize(text);
                            line_endings_map.insert(file.file_id, line_endings);
                            Some(Arc::new(text))
                        }
                        None => None,
                    }
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_fs_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            change
        };

        self.analysis_host.apply_change(change);
        self.maybe_refresh(&fs_changes);
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }
    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self.req_queue.outgoing.complete(response.id.clone());
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }
    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }
    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation()
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<commit_msg>Fix Cargo.toml change detection<commit_after>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::CrateId;
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::SourceRootConfig,
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than on impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        let mut has_fs_changes = false;

        let change = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in changed_files {
                if let Some(path) = vfs.file_path(file.file_id).as_path() {
                    fs_changes.push((path.to_path_buf(), file.change_kind));
                    if file.is_created_or_deleted() {
                        has_fs_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    match String::from_utf8(bytes).ok() {
                        Some(text) => {
                            let (text, line_endings) = LineEndings::normalize(text);
                            line_endings_map.insert(file.file_id, line_endings);
                            Some(Arc::new(text))
                        }
                        None => None,
                    }
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_fs_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            change
        };

        self.analysis_host.apply_change(change);
        self.maybe_refresh(&fs_changes);
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }
    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self.req_queue.outgoing.complete(response.id.clone());
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }
    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }
    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation()
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<|endoftext|>


<commit_before>use rustc_hash::{FxHashSet};
use ra_editor::find_node_at_offset;
use ra_syntax::{
    algo::visit::{visitor, Visitor},
    SourceFileNode, AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*,
};

use crate::{
    db::RootDatabase,
    completion::CompletionItem,
    descriptors::{
        module::{ModuleDescriptor},
        function::FnScopes,
        Path,
    },
    Cancelable
};

pub(super) fn completions(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    module: &ModuleDescriptor,
    file: &SourceFileNode,
    name_ref: ast::NameRef,
) -> Cancelable<()> {
    let kind = match classify_name_ref(name_ref) {
        Some(it) => it,
        None => return Ok(()),
    };

    match kind {
        NameRefKind::LocalRef { enclosing_fn } => {
            if let Some(fn_def) = enclosing_fn {
                let scopes = FnScopes::new(fn_def);
                complete_fn(name_ref, &scopes, acc);
                complete_expr_keywords(&file, fn_def, name_ref, acc);
                complete_expr_snippets(acc);
            }

            let module_scope = module.scope(db)?;
            acc.extend(
                module_scope
                    .items
                    .iter()
                    .filter(|(_name, res)| {
                        // Don't expose this item
                        match res.import_name {
                            None => true,
                            Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()),
                        }
                    })
                    .map(|(name, _res)| CompletionItem {
                        label: name.to_string(),
                        lookup: None,
                        snippet: None,
                    }),
            );
        }
        NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
        NameRefKind::BareIdentInMod => {
            let name_range = name_ref.syntax().range();
            let top_node = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.range() == name_range)
                .last()
                .unwrap();
            match top_node.parent().map(|it| it.kind()) {
                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
                _ => (),
            }
        }
    }
    Ok(())
}

enum NameRefKind<'a> {
    /// NameRef is a part of single-segment path, for example, a refernece to a
    /// local variable.
    LocalRef {
        enclosing_fn: Option<ast::FnDef<'a>>,
    },
    /// NameRef is the last segment in some path
    Path(Path),
    /// NameRef is bare identifier at the module's root.
    /// Used for keyword completion
    BareIdentInMod,
}

fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
    let name_range = name_ref.syntax().range();
    let top_node = name_ref
        .syntax()
        .ancestors()
        .take_while(|it| it.range() == name_range)
        .last()
        .unwrap();
    match top_node.parent().map(|it| it.kind()) {
        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
        _ => (),
    }

    let parent = name_ref.syntax().parent()?;
    if let Some(segment) = ast::PathSegment::cast(parent) {
        let path = segment.parent_path();
        if let Some(path) = Path::from_ast(path) {
            if !path.is_ident() {
                return Some(NameRefKind::Path(path));
            }
        }
        if path.qualifier().is_none() {
            let enclosing_fn = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
                .find_map(ast::FnDef::cast);
            return Some(NameRefKind::LocalRef { enclosing_fn });
        }
    }
    None
}

fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
    let mut shadowed = FxHashSet::default();
    acc.extend(
        scopes
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| scopes.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .map(|entry| CompletionItem {
                label: entry.name().to_string(),
                lookup: None,
                snippet: None,
            }),
    );
    if scopes.self_param.is_some() {
        acc.push(CompletionItem {
            label: "self".to_string(),
            lookup: None,
            snippet: None,
        })
    }
}

fn complete_path(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    module: &ModuleDescriptor,
    mut path: Path,
) -> Cancelable<()> {
    if path.segments.is_empty() {
        return Ok(());
    }
    path.segments.pop();
    let target_module = match module.resolve_path(path) {
        None => return Ok(()),
        Some(it) => it,
    };
    let module_scope = target_module.scope(db)?;
    let completions = module_scope
        .items
        .iter()
        .map(|(name, _res)| CompletionItem {
            label: name.to_string(),
            lookup: None,
            snippet: None,
        });
    acc.extend(completions);
    Ok(())
}

fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "Test function".to_string(),
        lookup: Some("tfn".to_string()),
        snippet: Some(
            "#[test]\n\
            fn ${1:feature}() {\n\
               $0\n\
            }"
            .to_string()
        ),
    });
    acc.push(CompletionItem {
        label: "pub(crate)".to_string(),
        lookup: None,
        snippet: Some("pub(crate) $0".to_string()),
    })
}

fn complete_expr_keywords(
    file: &SourceFileNode,
    fn_def: ast::FnDef,
    name_ref: ast::NameRef,
    acc: &mut Vec<CompletionItem>,
) {
    acc.push(keyword("if", "if $0 {}"));
    acc.push(keyword("match", "match $0 {}"));
    acc.push(keyword("while", "while $0 {}"));
    acc.push(keyword("loop", "loop {$0}"));

    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                acc.push(keyword("else", "else {$0}"));
                acc.push(keyword("else if", "else if $0 {}"));
            }
        }
    }
    if is_in_loop_body(name_ref) {
        acc.push(keyword("continue", "continue"));
        acc.push(keyword("break", "break"));
    }
    acc.extend(complete_return(fn_def, name_ref));
}

fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
    for node in name_ref.syntax().ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if name_ref
                .syntax()
                .range()
                .is_subrange(&body.syntax().range())
            {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
    //     .next()
    //     .and_then(|it| it.syntax().parent())
    //     .and_then(ast::Block::cast)
    //     .is_some();

    // if is_last_in_block {
    //     return None;
    // }

    let is_stmt = match name_ref
        .syntax()
        .ancestors()
        .filter_map(ast::ExprStmt::cast)
        .next()
    {
        None => false,
        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
    };
    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

fn keyword(kw: &str, snip: &str) -> CompletionItem {
    CompletionItem {
        label: kw.to_string(),
        lookup: None,
        snippet: Some(snip.to_string()),
    }
}

fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "pd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
    });
    acc.push(CompletionItem {
        label: "ppd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
    });
}
<commit_msg>Fix formatting<commit_after>use rustc_hash::{FxHashSet};
use ra_editor::find_node_at_offset;
use ra_syntax::{
    algo::visit::{visitor, Visitor},
    SourceFileNode, AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*,
};

use crate::{
    db::RootDatabase,
    completion::CompletionItem,
    descriptors::{
        module::{ModuleDescriptor},
        function::FnScopes,
        Path,
    },
    Cancelable
};

pub(super) fn completions(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    module: &ModuleDescriptor,
    file: &SourceFileNode,
    name_ref: ast::NameRef,
) -> Cancelable<()> {
    let kind = match classify_name_ref(name_ref) {
        Some(it) => it,
        None => return Ok(()),
    };

    match kind {
        NameRefKind::LocalRef { enclosing_fn } => {
            if let Some(fn_def) = enclosing_fn {
                let scopes = FnScopes::new(fn_def);
                complete_fn(name_ref, &scopes, acc);
                complete_expr_keywords(&file, fn_def, name_ref, acc);
                complete_expr_snippets(acc);
            }

            let module_scope = module.scope(db)?;
            acc.extend(
                module_scope
                    .items
                    .iter()
                    .filter(|(_name, res)| {
                        // Don't expose this item
                        match res.import_name {
                            None => true,
                            Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()),
                        }
                    })
                    .map(|(name, _res)| CompletionItem {
                        label: name.to_string(),
                        lookup: None,
                        snippet: None,
                    }),
            );
        }
        NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
        NameRefKind::BareIdentInMod => {
            let name_range = name_ref.syntax().range();
            let top_node = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.range() == name_range)
                .last()
                .unwrap();
            match top_node.parent().map(|it| it.kind()) {
                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
                _ => (),
            }
        }
    }
    Ok(())
}

enum NameRefKind<'a> {
    /// NameRef is a part of single-segment path, for example, a refernece to a
    /// local variable.
    LocalRef {
        enclosing_fn: Option<ast::FnDef<'a>>,
    },
    /// NameRef is the last segment in some path
    Path(Path),
    /// NameRef is bare identifier at the module's root.
    /// Used for keyword completion
    BareIdentInMod,
}

fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
    let name_range = name_ref.syntax().range();
    let top_node = name_ref
        .syntax()
        .ancestors()
        .take_while(|it| it.range() == name_range)
        .last()
        .unwrap();
    match top_node.parent().map(|it| it.kind()) {
        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
        _ => (),
    }

    let parent = name_ref.syntax().parent()?;
    if let Some(segment) = ast::PathSegment::cast(parent) {
        let path = segment.parent_path();
        if let Some(path) = Path::from_ast(path) {
            if !path.is_ident() {
                return Some(NameRefKind::Path(path));
            }
        }
        if path.qualifier().is_none() {
            let enclosing_fn = name_ref
                .syntax()
                .ancestors()
                .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
                .find_map(ast::FnDef::cast);
            return Some(NameRefKind::LocalRef { enclosing_fn });
        }
    }
    None
}

fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
    let mut shadowed = FxHashSet::default();
    acc.extend(
        scopes
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| scopes.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .map(|entry| CompletionItem {
                label: entry.name().to_string(),
                lookup: None,
                snippet: None,
            }),
    );
    if scopes.self_param.is_some() {
        acc.push(CompletionItem {
            label: "self".to_string(),
            lookup: None,
            snippet: None,
        })
    }
}

fn complete_path(
    acc: &mut Vec<CompletionItem>,
    db: &RootDatabase,
    module: &ModuleDescriptor,
    mut path: Path,
) -> Cancelable<()> {
    if path.segments.is_empty() {
        return Ok(());
    }
    path.segments.pop();
    let target_module = match module.resolve_path(path) {
        None => return Ok(()),
        Some(it) => it,
    };
    let module_scope = target_module.scope(db)?;
    let completions = module_scope
        .items
        .iter()
        .map(|(name, _res)| CompletionItem {
            label: name.to_string(),
            lookup: None,
            snippet: None,
        });
    acc.extend(completions);
    Ok(())
}

fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "Test function".to_string(),
        lookup: Some("tfn".to_string()),
        snippet: Some(
            "#[test]\n\
             fn ${1:feature}() {\n\
             $0\n\
             }"
            .to_string(),
        ),
    });
    acc.push(CompletionItem {
        label: "pub(crate)".to_string(),
        lookup: None,
        snippet: Some("pub(crate) $0".to_string()),
    })
}

fn complete_expr_keywords(
    file: &SourceFileNode,
    fn_def: ast::FnDef,
    name_ref: ast::NameRef,
    acc: &mut Vec<CompletionItem>,
) {
    acc.push(keyword("if", "if $0 {}"));
    acc.push(keyword("match", "match $0 {}"));
    acc.push(keyword("while", "while $0 {}"));
    acc.push(keyword("loop", "loop {$0}"));

    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                acc.push(keyword("else", "else {$0}"));
                acc.push(keyword("else if", "else if $0 {}"));
            }
        }
    }
    if is_in_loop_body(name_ref) {
        acc.push(keyword("continue", "continue"));
        acc.push(keyword("break", "break"));
    }
    acc.extend(complete_return(fn_def, name_ref));
}

fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
    for node in name_ref.syntax().ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if name_ref
                .syntax()
                .range()
                .is_subrange(&body.syntax().range())
            {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
    //     .next()
    //     .and_then(|it| it.syntax().parent())
    //     .and_then(ast::Block::cast)
    //     .is_some();

    // if is_last_in_block {
    //     return None;
    // }

    let is_stmt = match name_ref
        .syntax()
        .ancestors()
        .filter_map(ast::ExprStmt::cast)
        .next()
    {
        None => false,
        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
    };
    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

fn keyword(kw: &str, snip: &str) -> CompletionItem {
    CompletionItem {
        label: kw.to_string(),
        lookup: None,
        snippet: Some(snip.to_string()),
    }
}

fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
    acc.push(CompletionItem {
        label: "pd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
    });
    acc.push(CompletionItem {
        label: "ppd".to_string(),
        lookup: None,
        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
    });
}
<|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::WHITESPACE,
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

}
<commit_msg>add failing test test_introduce_var_block_expr_second_to_last<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::WHITESPACE,
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

    #[test]
    fn test_introduce_var_block_expr_second_to_last() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>{ let x = 0; x }<|>
    something_else();
}",
            "
fn foo() {
    let <|>var_name = { let x = 0; x };
    var_name
    something_else();
}",
        );
    }
}
<|endoftext|>


<commit_before>
use serialize::json::ToJson;

use errors::{Error, ErrorRefExt, ValidationError};
use framework::media::Media;
use server::Response;
use server_backend::status::BadRequest;

pub fn validation_error_formatter(err: &Box<Error>, _media: &Media) -> Option<Response> {
    match err.downcast::<ValidationError>() {
        Some(err) => {
             // TODO respond with media
            Some(Response::from_json(BadRequest, &err.reason.to_json()))
        },
        None => None
    }
}<commit_msg>feat: trivial usage of `media.format` in default `validation_error_formatter`<commit_after>
use serialize::json::ToJson;

use errors::{Error, ErrorRefExt, ValidationError};
use framework::media;
use framework::media::Media;
use server::Response;
use server_backend::status::BadRequest;

pub fn validation_error_formatter(err: &Box<Error>, media: &Media) -> Option<Response> {
    match err.downcast::<ValidationError>() {
        Some(err) => {
            match media.format {
                media::JsonFormat => Some(Response::from_json(BadRequest, &err.reason.to_json())),
                // TODO: Make formatter for a mere mortals
                _ => Some(Response::from_string(BadRequest, err.reason.to_json().to_pretty_str()))
            }
        },
        None => None
    }
}<|endoftext|>


<commit_before>// Copyright 2014-2015 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.

//! Scoped thread-local storage
//!
//! This module provides the ability to generate *scoped* thread-local
//! variables. In this sense, scoped indicates that thread local storage
//! actually stores a reference to a value, and this reference is only placed
//! in storage for a scoped amount of time.
//!
//! There are no restrictions on what types can be placed into a scoped
//! variable, but all scoped variables are initialized to the equivalent of
//! null. Scoped thread local storage is useful when a value is present for a known
//! period of time and it is not required to relinquish ownership of the
//! contents.
//!
//! # Example
//!
//! ```
//! scoped_thread_local!(static FOO: uint);
//!
//! // Initially each scoped slot is empty.
//! assert!(!FOO.is_set());
//!
//! // When inserting a value, the value is only in place for the duration
//! // of the closure specified.
//! FOO.set(&1, || {
//!     FOO.with(|slot| {
//!         assert_eq!(*slot, 1);
//!     });
//! });
//! ```

#![unstable(feature = "std_misc",
            reason = "scoped TLS has yet to have wide enough use to fully consider \
                      stabilizing its interface")]

use prelude::v1::*;

// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)]
pub mod __impl {
    pub use super::imp::KeyInner;
    pub use sys_common::thread_local::INIT as OS_INIT;
}

/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
///
/// Keys are statically allocated and can contain a reference to an instance of
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }

/// Declare a new scoped thread local storage key.
///
/// This macro declares a `static` item on which methods are used to get and
/// set the value stored within.
#[macro_export]
macro_rules! scoped_thread_local {
    (static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(static $name: $t);
    );
    (pub static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(pub static $name: $t);
    );
}

#[macro_export]
#[doc(hidden)]
macro_rules! __scoped_thread_local_inner {
    (static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows,
                           target_os = "android",
                           target_os = "ios",
                           target_arch = "aarch64")),
                   thread_local)]
        static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    (pub static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows,
                           target_os = "android",
                           target_os = "ios",
                           target_arch = "aarch64")),
                   thread_local)]
        pub static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    ($t:ty) => ({
        use std::thread_local::scoped::Key as __Key;

        #[cfg(not(any(windows, target_os = "android", target_os = "ios", target_arch = "aarch64")))]
        const _INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::__impl::KeyInner {
                inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
            }
        };

        #[cfg(any(windows, target_os = "android", target_os = "ios", target_arch = "aarch64"))]
        const _INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::__impl::KeyInner {
                inner: ::std::thread_local::scoped::__impl::OS_INIT,
                marker: ::std::marker::InvariantType,
            }
        };

        _INIT
    })
}

impl<T> Key<T> {
    /// Insert a value into this scoped thread local storage slot for a
    /// duration of a closure.
    ///
    /// While `cb` is running, the value `t` will be returned by `get` unless
    /// this function is called recursively inside of `cb`.
    ///
    /// Upon return, this function will restore the previous value, if any
    /// was available.
    ///
    /// # Example
    ///
    /// ```
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.set(&100, || {
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    ///
    ///     // set can be called recursively
    ///     FOO.set(&101, || {
    ///         // ...
    ///     });
    ///
    ///     // Recursive calls restore the previous value.
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    /// });
    /// ```
    pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
        F: FnOnce() -> R,
    {
        struct Reset<'a, T: 'a> {
            key: &'a __impl::KeyInner<T>,
            val: *mut T,
        }
        #[unsafe_destructor]
        impl<'a, T> Drop for Reset<'a, T> {
            fn drop(&mut self) {
                unsafe { self.key.set(self.val) }
            }
        }

        let prev = unsafe {
            let prev = self.inner.get();
            self.inner.set(t as *const T as *mut T);
            prev
        };

        let _reset = Reset { key: &self.inner, val: prev };
        cb()
    }

    /// Get a value out of this scoped variable.
    ///
    /// This function takes a closure which receives the value of this
    /// variable.
    ///
    /// # Panics
    ///
    /// This function will panic if `set` has not previously been called.
    ///
    /// # Example
    ///
    /// ```no_run
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.with(|slot| {
    ///     // work with `slot`
    /// });
    /// ```
    pub fn with<R, F>(&'static self, cb: F) -> R where
        F: FnOnce(&T) -> R
    {
        unsafe {
            let ptr = self.inner.get();
            assert!(!ptr.is_null(), "cannot access a scoped thread local \
                                     variable without calling `set` first");
            cb(&*ptr)
        }
    }

    /// Test whether this TLS key has been `set` for the current thread.
    pub fn is_set(&'static self) -> bool {
        unsafe { !self.inner.get().is_null() }
    }
}

#[cfg(not(any(windows, target_os = "android", target_os = "ios", target_arch = "aarch64")))]
mod imp {
    use std::cell::UnsafeCell;

    #[doc(hidden)]
    pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> }

    unsafe impl<T> ::marker::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { *self.inner.get() }
    }
}

#[cfg(any(windows, target_os = "android", target_os = "ios", target_arch = "aarch64"))]
mod imp {
    use marker;
    use sys_common::thread_local::StaticKey as OsStaticKey;

    #[doc(hidden)]
    pub struct KeyInner<T> {
        pub inner: OsStaticKey,
        pub marker: marker::InvariantType<T>,
    }

    unsafe impl<T> ::marker::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
    }
}

#[cfg(test)]
mod tests {
    use cell::Cell;
    use prelude::v1::*;

    scoped_thread_local!(static FOO: uint);

    #[test]
    fn smoke() {
        scoped_thread_local!(static BAR: uint);

        assert!(!BAR.is_set());
        BAR.set(&1, || {
            assert!(BAR.is_set());
            BAR.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!BAR.is_set());
    }

    #[test]
    fn cell_allowed() {
        scoped_thread_local!(static BAR: Cell<uint>);

        BAR.set(&Cell::new(1), || {
            BAR.with(|slot| {
                assert_eq!(slot.get(), 1);
            });
        });
    }

    #[test]
    fn scope_item_allowed() {
        assert!(!FOO.is_set());
        FOO.set(&1, || {
            assert!(FOO.is_set());
            FOO.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!FOO.is_set());
    }
}
<commit_msg>rollup merge of #22177: semarie/openbsd-notls<commit_after>// Copyright 2014-2015 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.

//! Scoped thread-local storage
//!
//! This module provides the ability to generate *scoped* thread-local
//! variables. In this sense, scoped indicates that thread local storage
//! actually stores a reference to a value, and this reference is only placed
//! in storage for a scoped amount of time.
//!
//! There are no restrictions on what types can be placed into a scoped
//! variable, but all scoped variables are initialized to the equivalent of
//! null. Scoped thread local storage is useful when a value is present for a known
//! period of time and it is not required to relinquish ownership of the
//! contents.
//!
//! # Example
//!
//! ```
//! scoped_thread_local!(static FOO: uint);
//!
//! // Initially each scoped slot is empty.
//! assert!(!FOO.is_set());
//!
//! // When inserting a value, the value is only in place for the duration
//! // of the closure specified.
//! FOO.set(&1, || {
//!     FOO.with(|slot| {
//!         assert_eq!(*slot, 1);
//!     });
//! });
//! ```

#![unstable(feature = "std_misc",
            reason = "scoped TLS has yet to have wide enough use to fully consider \
                      stabilizing its interface")]

use prelude::v1::*;

// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)]
pub mod __impl {
    pub use super::imp::KeyInner;
    pub use sys_common::thread_local::INIT as OS_INIT;
}

/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
///
/// Keys are statically allocated and can contain a reference to an instance of
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }

/// Declare a new scoped thread local storage key.
///
/// This macro declares a `static` item on which methods are used to get and
/// set the value stored within.
#[macro_export]
macro_rules! scoped_thread_local {
    (static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(static $name: $t);
    );
    (pub static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(pub static $name: $t);
    );
}

#[macro_export]
#[doc(hidden)]
macro_rules! __scoped_thread_local_inner {
    (static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows,
                           target_os = "android",
                           target_os = "ios",
                           target_os = "openbsd",
                           target_arch = "aarch64")),
                   thread_local)]
        static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    (pub static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows,
                           target_os = "android",
                           target_os = "ios",
                           target_os = "openbsd",
                           target_arch = "aarch64")),
                   thread_local)]
        pub static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    ($t:ty) => ({
        use std::thread_local::scoped::Key as __Key;

        #[cfg(not(any(windows,
                      target_os = "android",
                      target_os = "ios",
                      target_os = "openbsd",
                      target_arch = "aarch64")))]
        const _INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::__impl::KeyInner {
                inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
            }
        };

        #[cfg(any(windows,
                  target_os = "android",
                  target_os = "ios",
                  target_os = "openbsd",
                  target_arch = "aarch64"))]
        const _INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::__impl::KeyInner {
                inner: ::std::thread_local::scoped::__impl::OS_INIT,
                marker: ::std::marker::InvariantType,
            }
        };

        _INIT
    })
}

impl<T> Key<T> {
    /// Insert a value into this scoped thread local storage slot for a
    /// duration of a closure.
    ///
    /// While `cb` is running, the value `t` will be returned by `get` unless
    /// this function is called recursively inside of `cb`.
    ///
    /// Upon return, this function will restore the previous value, if any
    /// was available.
    ///
    /// # Example
    ///
    /// ```
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.set(&100, || {
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    ///
    ///     // set can be called recursively
    ///     FOO.set(&101, || {
    ///         // ...
    ///     });
    ///
    ///     // Recursive calls restore the previous value.
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    /// });
    /// ```
    pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
        F: FnOnce() -> R,
    {
        struct Reset<'a, T: 'a> {
            key: &'a __impl::KeyInner<T>,
            val: *mut T,
        }
        #[unsafe_destructor]
        impl<'a, T> Drop for Reset<'a, T> {
            fn drop(&mut self) {
                unsafe { self.key.set(self.val) }
            }
        }

        let prev = unsafe {
            let prev = self.inner.get();
            self.inner.set(t as *const T as *mut T);
            prev
        };

        let _reset = Reset { key: &self.inner, val: prev };
        cb()
    }

    /// Get a value out of this scoped variable.
    ///
    /// This function takes a closure which receives the value of this
    /// variable.
    ///
    /// # Panics
    ///
    /// This function will panic if `set` has not previously been called.
    ///
    /// # Example
    ///
    /// ```no_run
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.with(|slot| {
    ///     // work with `slot`
    /// });
    /// ```
    pub fn with<R, F>(&'static self, cb: F) -> R where
        F: FnOnce(&T) -> R
    {
        unsafe {
            let ptr = self.inner.get();
            assert!(!ptr.is_null(), "cannot access a scoped thread local \
                                     variable without calling `set` first");
            cb(&*ptr)
        }
    }

    /// Test whether this TLS key has been `set` for the current thread.
    pub fn is_set(&'static self) -> bool {
        unsafe { !self.inner.get().is_null() }
    }
}

#[cfg(not(any(windows,
              target_os = "android",
              target_os = "ios",
              target_os = "openbsd",
              target_arch = "aarch64")))]
mod imp {
    use std::cell::UnsafeCell;

    #[doc(hidden)]
    pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> }

    unsafe impl<T> ::marker::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { *self.inner.get() }
    }
}

#[cfg(any(windows,
          target_os = "android",
          target_os = "ios",
          target_os = "openbsd",
          target_arch = "aarch64"))]
mod imp {
    use marker;
    use sys_common::thread_local::StaticKey as OsStaticKey;

    #[doc(hidden)]
    pub struct KeyInner<T> {
        pub inner: OsStaticKey,
        pub marker: marker::InvariantType<T>,
    }

    unsafe impl<T> ::marker::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
    }
}

#[cfg(test)]
mod tests {
    use cell::Cell;
    use prelude::v1::*;

    scoped_thread_local!(static FOO: uint);

    #[test]
    fn smoke() {
        scoped_thread_local!(static BAR: uint);

        assert!(!BAR.is_set());
        BAR.set(&1, || {
            assert!(BAR.is_set());
            BAR.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!BAR.is_set());
    }

    #[test]
    fn cell_allowed() {
        scoped_thread_local!(static BAR: Cell<uint>);

        BAR.set(&Cell::new(1), || {
            BAR.with(|slot| {
                assert_eq!(slot.get(), 1);
            });
        });
    }

    #[test]
    fn scope_item_allowed() {
        assert!(!FOO.is_set());
        FOO.set(&1, || {
            assert!(FOO.is_set());
            FOO.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!FOO.is_set());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

#![feature(catch_panic)]

extern crate rustfmt;
extern crate diff;
extern crate regex;
extern crate term;

use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, BufRead, BufReader};
use std::thread;
use rustfmt::*;
use rustfmt::config::Config;
use rustfmt::rustfmt_diff::*;

static DIFF_CONTEXT_SIZE: usize = 3;

fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
    let path = dir_entry.ok().expect("Couldn't get DirEntry.").path();

    path.to_str().expect("Couldn't stringify path.").to_owned()
}

// Integration tests. The files in the tests/source are formatted and compared
// to their equivalent in tests/target. The target file and config can be
// overriden by annotations in the source file. The input and output must match
// exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at
// least report.
#[test]
fn system_tests() {
    // Get all files in the tests/source directory
    let files = fs::read_dir("tests/source").ok().expect("Couldn't read source dir.");
    // turn a DirEntry into a String that represents the relative path to the file
    let files = files.map(get_path_string);

    let (count, fails) = check_files(files);

    // Display results
    println!("Ran {} system tests.", count);
    assert!(fails == 0, "{} system tests failed", fails);
}

// Idempotence tests. Files in tests/target are checked to be unaltered by
// rustfmt.
#[test]
fn idempotence_tests() {
    // Get all files in the tests/target directory
    let files = fs::read_dir("tests/target").ok().expect("Couldn't read target dir.");
    let files = files.chain(fs::read_dir("tests").ok().expect("Couldn't read tests dir."));
    let files = files.chain(fs::read_dir("src/bin").ok().expect("Couldn't read src dir."));
    // turn a DirEntry into a String that represents the relative path to the file
    let files = files.map(get_path_string);
    // hack because there's no `IntoIterator` impl for `[T; N]`
    let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());

    let (count, fails) = check_files(files);

    // Display results
    println!("Ran {} idempotent tests.", count);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files<I>(files: I) -> (u32, u32)
    where I: Iterator<Item = String>
{
    let mut count = 0;
    let mut fails = 0;

    for file_name in files.filter(|f| f.ends_with(".rs")) {
        println!("Testing '{}'...", file_name);
        if let Err(msg) = idempotent_check(file_name) {
            print_mismatches(msg);
            fails += 1;
        }
        count += 1;
    }

    (count, fails)
}

fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
    let mut t = term::stdout().unwrap();

    for (file_name, diff) in result {
        print_diff(diff, |line_num| format!("\nMismatch at {}:{}:", file_name, line_num));
    }

    assert!(t.reset().unwrap());
}

// Ick, just needed to get a &'static to handle_result.
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;

pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, Vec<Mismatch>>> {
    let sig_comments = read_significant_comments(&filename);
    let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));
    let args = vec!["rustfmt".to_owned(), filename];

    for (key, val) in sig_comments {
        if key != "target" && key != "config" {
            config.override_value(&key, &val);
        }
    }

    // this thread is not used for concurrency, but rather to workaround the issue that the passed
    // function handle needs to have static lifetime. Instead of using a global RefCell, we use
    // panic to return a result in case of failure. This has the advantage of smoothing the road to
    // multithreaded rustfmt
    thread::catch_panic(move || {
        run(args, WriteMode::Return(HANDLE_RESULT), config);
    })
        .map_err(|any| *any.downcast().ok().expect("Downcast failed."))
}

// Reads test config file from comments and reads its contents.
fn get_config(config_file: Option<&str>) -> Box<Config> {
    let config_file_name = match config_file {
        None => return Box::new(Default::default()),
        Some(file_name) => {
            let mut full_path = "tests/config/".to_owned();
            full_path.push_str(&file_name);
            full_path
        }
    };

    let mut def_config_file = fs::File::open(config_file_name).ok().expect("Couldn't open config.");
    let mut def_config = String::new();
    def_config_file.read_to_string(&mut def_config).ok().expect("Couldn't read config.");

    Box::new(Config::from_toml(&def_config))
}

// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
    let file = fs::File::open(file_name).ok().expect(&format!("Couldn't read file {}.", file_name));
    let reader = BufReader::new(file);
    let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
    let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");

    // Matches lines containing significant comments or whitespace.
    let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
                         .ok()
                         .expect("Failed creating pattern 2.");

    reader.lines()
        .map(|line| line.ok().expect("Failed getting line."))
        .take_while(|line| line_regex.is_match(&line))
        .filter_map(|line| {
            regex.captures_iter(&line)
                .next()
                .map(|capture| {
                    (capture.at(1).expect("Couldn't unwrap capture.").to_owned(),
                     capture.at(2).expect("Couldn't unwrap capture.").to_owned())
                })
        })
        .collect()
}

// Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>) {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        // FIXME: reading significant comments again. Is there a way we can just
        // pass the target to this function?
        let sig_comments = read_significant_comments(&file_name);

        // If file is in tests/source, compare to file with same name in tests/target.
        let target = get_target(&file_name, sig_comments.get("target").map(|x| &(*x)[..]));
        let mut f = fs::File::open(&target).ok().expect("Couldn't open target.");

        let mut text = String::new();
        // TODO: speedup by running through bytes iterator
        f.read_to_string(&mut text).ok().expect("Failed reading target.");
        if fmt_text != text {
            let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
            failures.insert(file_name, diff);
        }
    }

    if !failures.is_empty() {
        panic!(failures);
    }
}

// Map source file paths to their target paths.
fn get_target(file_name: &str, target: Option<&str>) -> String {
    if file_name.starts_with("tests/source/") {
        let base = target.unwrap_or(file_name.trim_left_matches("tests/source/"));

        format!("tests/target/{}", base)
    } else {
        file_name.to_owned()
    }
}
<commit_msg>Escape quotes in string literals<commit_after>// Copyright 2015 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.

#![feature(catch_panic)]

extern crate rustfmt;
extern crate diff;
extern crate regex;
extern crate term;

use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, BufRead, BufReader};
use std::thread;
use rustfmt::*;
use rustfmt::config::Config;
use rustfmt::rustfmt_diff::*;

static DIFF_CONTEXT_SIZE: usize = 3;

fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
    let path = dir_entry.ok().expect("Couldn\'t get DirEntry.").path();

    path.to_str().expect("Couldn\'t stringify path.").to_owned()
}

// Integration tests. The files in the tests/source are formatted and compared
// to their equivalent in tests/target. The target file and config can be
// overriden by annotations in the source file. The input and output must match
// exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at
// least report.
#[test]
fn system_tests() {
    // Get all files in the tests/source directory
    let files = fs::read_dir("tests/source").ok().expect("Couldn\'t read source dir.");
    // turn a DirEntry into a String that represents the relative path to the file
    let files = files.map(get_path_string);

    let (count, fails) = check_files(files);

    // Display results
    println!("Ran {} system tests.", count);
    assert!(fails == 0, "{} system tests failed", fails);
}

// Idempotence tests. Files in tests/target are checked to be unaltered by
// rustfmt.
#[test]
fn idempotence_tests() {
    // Get all files in the tests/target directory
    let files = fs::read_dir("tests/target").ok().expect("Couldn\'t read target dir.");
    let files = files.chain(fs::read_dir("tests").ok().expect("Couldn\'t read tests dir."));
    let files = files.chain(fs::read_dir("src/bin").ok().expect("Couldn\'t read src dir."));
    // turn a DirEntry into a String that represents the relative path to the file
    let files = files.map(get_path_string);
    // hack because there's no `IntoIterator` impl for `[T; N]`
    let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());

    let (count, fails) = check_files(files);

    // Display results
    println!("Ran {} idempotent tests.", count);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files<I>(files: I) -> (u32, u32)
    where I: Iterator<Item = String>
{
    let mut count = 0;
    let mut fails = 0;

    for file_name in files.filter(|f| f.ends_with(".rs")) {
        println!("Testing '{}'...", file_name);
        if let Err(msg) = idempotent_check(file_name) {
            print_mismatches(msg);
            fails += 1;
        }
        count += 1;
    }

    (count, fails)
}

fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
    let mut t = term::stdout().unwrap();

    for (file_name, diff) in result {
        print_diff(diff, |line_num| format!("\nMismatch at {}:{}:", file_name, line_num));
    }

    assert!(t.reset().unwrap());
}

// Ick, just needed to get a &'static to handle_result.
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;

pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, Vec<Mismatch>>> {
    let sig_comments = read_significant_comments(&filename);
    let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));
    let args = vec!["rustfmt".to_owned(), filename];

    for (key, val) in sig_comments {
        if key != "target" && key != "config" {
            config.override_value(&key, &val);
        }
    }

    // this thread is not used for concurrency, but rather to workaround the issue that the passed
    // function handle needs to have static lifetime. Instead of using a global RefCell, we use
    // panic to return a result in case of failure. This has the advantage of smoothing the road to
    // multithreaded rustfmt
    thread::catch_panic(move || {
        run(args, WriteMode::Return(HANDLE_RESULT), config);
    })
        .map_err(|any| *any.downcast().ok().expect("Downcast failed."))
}

// Reads test config file from comments and reads its contents.
fn get_config(config_file: Option<&str>) -> Box<Config> {
    let config_file_name = match config_file {
        None => return Box::new(Default::default()),
        Some(file_name) => {
            let mut full_path = "tests/config/".to_owned();
            full_path.push_str(&file_name);
            full_path
        }
    };

    let mut def_config_file = fs::File::open(config_file_name)
                                  .ok()
                                  .expect("Couldn\'t open config.");
    let mut def_config = String::new();
    def_config_file.read_to_string(&mut def_config).ok().expect("Couldn\'t read config.");

    Box::new(Config::from_toml(&def_config))
}

// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
    let file = fs::File::open(file_name)
                   .ok()
                   .expect(&format!("Couldn\'t read file {}.", file_name));
    let reader = BufReader::new(file);
    let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
    let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");

    // Matches lines containing significant comments or whitespace.
    let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
                         .ok()
                         .expect("Failed creating pattern 2.");

    reader.lines()
        .map(|line| line.ok().expect("Failed getting line."))
        .take_while(|line| line_regex.is_match(&line))
        .filter_map(|line| {
            regex.captures_iter(&line)
                .next()
                .map(|capture| {
                    (capture.at(1).expect("Couldn\'t unwrap capture.").to_owned(),
                     capture.at(2).expect("Couldn\'t unwrap capture.").to_owned())
                })
        })
        .collect()
}

// Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>) {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        // FIXME: reading significant comments again. Is there a way we can just
        // pass the target to this function?
        let sig_comments = read_significant_comments(&file_name);

        // If file is in tests/source, compare to file with same name in tests/target.
        let target = get_target(&file_name, sig_comments.get("target").map(|x| &(*x)[..]));
        let mut f = fs::File::open(&target).ok().expect("Couldn\'t open target.");

        let mut text = String::new();
        // TODO: speedup by running through bytes iterator
        f.read_to_string(&mut text).ok().expect("Failed reading target.");
        if fmt_text != text {
            let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
            failures.insert(file_name, diff);
        }
    }

    if !failures.is_empty() {
        panic!(failures);
    }
}

// Map source file paths to their target paths.
fn get_target(file_name: &str, target: Option<&str>) -> String {
    if file_name.starts_with("tests/source/") {
        let base = target.unwrap_or(file_name.trim_left_matches("tests/source/"));

        format!("tests/target/{}", base)
    } else {
        file_name.to_owned()
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
#![feature(test)]
extern crate "user32-sys" as user32;
extern crate test;
use user32::*;
use test::black_box as bb;
#[test]
fn functions() {
    bb(ActivateKeyboardLayout);
    // bb(AddClipboardFormatListener);
    bb(AdjustWindowRect);
    bb(AdjustWindowRectEx);
    bb(AllowSetForegroundWindow);
    bb(AnimateWindow);
    bb(AnyPopup);
    bb(ArrangeIconicWindows);
    bb(AttachThreadInput);
    bb(BeginPaint);
    bb(BlockInput);
    bb(BringWindowToTop);
    // bb(CalculatePopupWindowPosition);
    bb(CascadeWindows);
    bb(ChangeClipboardChain);
    bb(ChangeDisplaySettingsExW);
    bb(ChangeDisplaySettingsW);
    bb(ChildWindowFromPoint);
    bb(ChildWindowFromPointEx);
    bb(ClipCursor);
    bb(CloseClipboard);
    bb(CloseDesktop);
    bb(CloseWindow);
    bb(CloseWindowStation);
    bb(CountClipboardFormats);
    bb(CreateCaret);
    bb(CreateCursor);
    bb(CreateWindowExW);
    bb(DefWindowProcW);
    bb(DeferWindowPos);
    bb(DeleteMenu);
    bb(DeregisterShellHookWindow);
    bb(DestroyAcceleratorTable);
    bb(DestroyCaret);
    bb(DestroyCursor);
    bb(DestroyIcon);
    bb(DestroyMenu);
    bb(DestroyWindow);
    bb(DispatchMessageW);
    bb(EmptyClipboard);
    bb(EnableScrollBar);
    bb(EnableWindow);
    bb(EndPaint);
    bb(EndTask);
    bb(EnumClipboardFormats);
    bb(EnumDisplayDevicesW);
    bb(EnumDisplaySettingsExW);
    bb(EnumThreadWindows);
    bb(EnumWindows);
    bb(FillRect);
    bb(FindWindowA );
    bb(FindWindowExA);
    bb(FindWindowExW);
    bb(FindWindowW);
    bb(GetActiveWindow);
    bb(GetAncestor);
    bb(GetAsyncKeyState);
    bb(GetCaretBlinkTime);
    bb(GetCaretPos);
    bb(GetClassInfoExW);
    bb(GetClassInfoW);
    bb(GetClassLongA);
    bb(GetClassLongPtrA);
    bb(GetClassLongPtrW);
    bb(GetClassLongW);
    bb(GetClassWord);
    bb(GetClientRect);
    bb(GetClipCursor);
    bb(GetClipboardData);
    bb(GetClipboardFormatNameA);
    bb(GetClipboardFormatNameW);
    bb(GetClipboardOwner);
    bb(GetClipboardSequenceNumber);
    bb(GetClipboardViewer);
    bb(GetCursor);
    bb(GetCursorPos);
    bb(GetDC);
    bb(GetDesktopWindow);
    bb(GetFocus);
    bb(GetForegroundWindow);
    bb(GetKBCodePage);
    bb(GetKeyNameTextA);
    bb(GetKeyNameTextW);
    bb(GetKeyState);
    bb(GetKeyboardLayout);
    bb(GetKeyboardLayoutList);
    bb(GetKeyboardLayoutNameA);
    bb(GetKeyboardLayoutNameW);
    bb(GetKeyboardState);
    bb(GetKeyboardType);
    bb(GetMessageW);
    bb(GetOpenClipboardWindow);
    bb(GetParent);
    // bb(GetPhysicalCursorPos);
    bb(GetScrollPos);
    bb(GetScrollRange);
    bb(GetShellWindow);
    bb(GetSysColor);
    bb(GetSystemMetrics);
    bb(GetThreadDesktop);
    // bb(GetUpdatedClipboardFormats);
    bb(GetWindow);
    bb(GetWindowLongA);
    bb(GetWindowLongPtrA);
    bb(GetWindowLongPtrW);
    bb(GetWindowLongW);
    bb(GetWindowPlacement);
    bb(GetWindowRect);
    bb(GetWindowTextA);
    bb(GetWindowTextLengthA);
    bb(GetWindowTextLengthW);
    bb(GetWindowTextW);
    bb(HideCaret);
    bb(InvalidateRect);
    bb(IsClipboardFormatAvailable);
    bb(IsIconic);
    bb(IsWindow);
    bb(IsWindowEnabled);
    bb(IsWindowVisible);
    bb(LoadCursorFromFileW);
    bb(LoadCursorW);
    bb(LoadImageA);
    bb(LoadImageW);
    bb(MessageBoxA);
    bb(MessageBoxExA);
    bb(MessageBoxExW);
    bb(MessageBoxW);
    bb(OpenClipboard);
    bb(PeekMessageW);
    bb(PostMessageW);
    bb(PostQuitMessage);
    bb(RegisterClassExW);
    bb(RegisterClipboardFormatA);
    bb(RegisterClipboardFormatW);
    bb(ReleaseDC);
    bb(ScrollDC);
    bb(ScrollWindow);
    bb(ScrollWindowEx);
    bb(SendInput);
    bb(SendMessageA);
    bb(SendMessageTimeoutA);
    bb(SendMessageTimeoutW);
    bb(SendMessageW);
    bb(SendNotifyMessageA);
    bb(SendNotifyMessageW);
    bb(SetActiveWindow);
    bb(SetCaretBlinkTime);
    bb(SetCaretPos);
    bb(SetClassLongA);
    bb(SetClassLongPtrA);
    bb(SetClassLongPtrW);
    bb(SetClassLongW);
    bb(SetClassWord);
    bb(SetClipboardViewer);
    bb(SetCursor);
    bb(SetCursorPos);
    bb(SetFocus);
    bb(SetForegroundWindow);
    // bb(SetPhysicalCursorPos);
    bb(SetScrollPos);
    bb(SetScrollRange);
    bb(SetSystemCursor);
    bb(SetWindowLongA);
    bb(SetWindowLongPtrA);
    bb(SetWindowLongPtrW);
    bb(SetWindowLongW);
    bb(SetWindowPos);
    bb(SetWindowTextW);
    bb(ShowCaret);
    bb(ShowCursor);
    bb(ShowWindow);
    bb(ShowWindowAsync);
    bb(TranslateMessage);
    bb(UnregisterClassA);
    bb(UnregisterClassW);
    bb(UpdateWindow);
    bb(WaitMessage);
}
<commit_msg>Fix appveyor<commit_after>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
#![feature(test)]
extern crate "user32-sys" as user32;
extern crate test;
use user32::*;
use test::black_box as bb;
#[cfg(target_arch = "x86_64")]
#[test]
fn functions_x64() {
    bb(GetClassLongPtrA);
    bb(GetClassLongPtrW);
    bb(GetWindowLongPtrA);
    bb(GetWindowLongPtrW);
    bb(SetClassLongPtrA);
    bb(SetClassLongPtrW);
    bb(SetWindowLongPtrA);
    bb(SetWindowLongPtrW);
}
#[test]
fn functions() {
    bb(ActivateKeyboardLayout);
    // bb(AddClipboardFormatListener);
    bb(AdjustWindowRect);
    bb(AdjustWindowRectEx);
    bb(AllowSetForegroundWindow);
    bb(AnimateWindow);
    bb(AnyPopup);
    bb(ArrangeIconicWindows);
    bb(AttachThreadInput);
    bb(BeginPaint);
    bb(BlockInput);
    bb(BringWindowToTop);
    // bb(CalculatePopupWindowPosition);
    bb(CascadeWindows);
    bb(ChangeClipboardChain);
    bb(ChangeDisplaySettingsExW);
    bb(ChangeDisplaySettingsW);
    bb(ChildWindowFromPoint);
    bb(ChildWindowFromPointEx);
    bb(ClipCursor);
    bb(CloseClipboard);
    bb(CloseDesktop);
    bb(CloseWindow);
    bb(CloseWindowStation);
    bb(CountClipboardFormats);
    bb(CreateCaret);
    bb(CreateCursor);
    bb(CreateWindowExW);
    bb(DefWindowProcW);
    bb(DeferWindowPos);
    bb(DeleteMenu);
    bb(DeregisterShellHookWindow);
    bb(DestroyAcceleratorTable);
    bb(DestroyCaret);
    bb(DestroyCursor);
    bb(DestroyIcon);
    bb(DestroyMenu);
    bb(DestroyWindow);
    bb(DispatchMessageW);
    bb(EmptyClipboard);
    bb(EnableScrollBar);
    bb(EnableWindow);
    bb(EndPaint);
    bb(EndTask);
    bb(EnumClipboardFormats);
    bb(EnumDisplayDevicesW);
    bb(EnumDisplaySettingsExW);
    bb(EnumThreadWindows);
    bb(EnumWindows);
    bb(FillRect);
    bb(FindWindowA );
    bb(FindWindowExA);
    bb(FindWindowExW);
    bb(FindWindowW);
    bb(GetActiveWindow);
    bb(GetAncestor);
    bb(GetAsyncKeyState);
    bb(GetCaretBlinkTime);
    bb(GetCaretPos);
    bb(GetClassInfoExW);
    bb(GetClassInfoW);
    bb(GetClassLongA);
    bb(GetClassLongW);
    bb(GetClassWord);
    bb(GetClientRect);
    bb(GetClipCursor);
    bb(GetClipboardData);
    bb(GetClipboardFormatNameA);
    bb(GetClipboardFormatNameW);
    bb(GetClipboardOwner);
    bb(GetClipboardSequenceNumber);
    bb(GetClipboardViewer);
    bb(GetCursor);
    bb(GetCursorPos);
    bb(GetDC);
    bb(GetDesktopWindow);
    bb(GetFocus);
    bb(GetForegroundWindow);
    bb(GetKBCodePage);
    bb(GetKeyNameTextA);
    bb(GetKeyNameTextW);
    bb(GetKeyState);
    bb(GetKeyboardLayout);
    bb(GetKeyboardLayoutList);
    bb(GetKeyboardLayoutNameA);
    bb(GetKeyboardLayoutNameW);
    bb(GetKeyboardState);
    bb(GetKeyboardType);
    bb(GetMessageW);
    bb(GetOpenClipboardWindow);
    bb(GetParent);
    // bb(GetPhysicalCursorPos);
    bb(GetScrollPos);
    bb(GetScrollRange);
    bb(GetShellWindow);
    bb(GetSysColor);
    bb(GetSystemMetrics);
    bb(GetThreadDesktop);
    // bb(GetUpdatedClipboardFormats);
    bb(GetWindow);
    bb(GetWindowLongA);
    bb(GetWindowLongW);
    bb(GetWindowPlacement);
    bb(GetWindowRect);
    bb(GetWindowTextA);
    bb(GetWindowTextLengthA);
    bb(GetWindowTextLengthW);
    bb(GetWindowTextW);
    bb(HideCaret);
    bb(InvalidateRect);
    bb(IsClipboardFormatAvailable);
    bb(IsIconic);
    bb(IsWindow);
    bb(IsWindowEnabled);
    bb(IsWindowVisible);
    bb(LoadCursorFromFileW);
    bb(LoadCursorW);
    bb(LoadImageA);
    bb(LoadImageW);
    bb(MessageBoxA);
    bb(MessageBoxExA);
    bb(MessageBoxExW);
    bb(MessageBoxW);
    bb(OpenClipboard);
    bb(PeekMessageW);
    bb(PostMessageW);
    bb(PostQuitMessage);
    bb(RegisterClassExW);
    bb(RegisterClipboardFormatA);
    bb(RegisterClipboardFormatW);
    bb(ReleaseDC);
    bb(ScrollDC);
    bb(ScrollWindow);
    bb(ScrollWindowEx);
    bb(SendInput);
    bb(SendMessageA);
    bb(SendMessageTimeoutA);
    bb(SendMessageTimeoutW);
    bb(SendMessageW);
    bb(SendNotifyMessageA);
    bb(SendNotifyMessageW);
    bb(SetActiveWindow);
    bb(SetCaretBlinkTime);
    bb(SetCaretPos);
    bb(SetClassLongA);
    bb(SetClassLongW);
    bb(SetClassWord);
    bb(SetClipboardViewer);
    bb(SetCursor);
    bb(SetCursorPos);
    bb(SetFocus);
    bb(SetForegroundWindow);
    // bb(SetPhysicalCursorPos);
    bb(SetScrollPos);
    bb(SetScrollRange);
    bb(SetSystemCursor);
    bb(SetWindowLongA);
    bb(SetWindowLongW);
    bb(SetWindowPos);
    bb(SetWindowTextW);
    bb(ShowCaret);
    bb(ShowCursor);
    bb(ShowWindow);
    bb(ShowWindowAsync);
    bb(TranslateMessage);
    bb(UnregisterClassA);
    bb(UnregisterClassW);
    bb(UpdateWindow);
    bb(WaitMessage);
}
<|endoftext|>