<commit_before>
use winapi::um::d3d12;
use wio::com::ComPtr;

// Linear stack allocator for CPU descriptor heaps.
pub struct HeapLinear {
    handle_size: usize,
    num: usize,
    size: usize,
    start: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE,
    raw: ComPtr<d3d12::ID3D12DescriptorHeap>,
}

impl HeapLinear {
    pub fn new(
        device: &ComPtr<d3d12::ID3D12Device>,
        ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE,
        size: usize,
    ) -> Self {
        unimplemented!()
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        assert!(!self.full());

        let slot = self.num;
        self.num += 1;

        d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
            ptr: self.start.ptr + self.handle_size * slot,
        }
    }

    pub fn full(&self) -> bool {
        self.num < self.size
    }

    pub fn clear(&mut self) {
        self.num = 0;
    }
}

// Fixed-size (64) free-list allocator for CPU descriptors.
pub struct Heap {
    occupancy: u64,
    handle_size: usize,
    start: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE,
    raw: ComPtr<d3d12::ID3D12DescriptorHeap>,
}

impl Heap {
    pub fn new(device: &ComPtr<d3d12::ID3D12Device>, ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE) -> Self {
        unimplemented!()
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        // Find first free slot
        let slot = (0..64)
            .position(|i| self.occupancy & (1 << i) == 0)
            .expect("Descriptor heap is full");
        self.occupancy |= 1 << slot;

        d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
            ptr: self.start.ptr + self.handle_size * slot,
        }
    }

    pub fn full(&self) -> bool {
        self.occupancy == !0
    }
}

pub struct DescriptorCpuPool {
    device: ComPtr<d3d12::ID3D12Device>,
    ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE,
    heaps: Vec<Heap>,
    handle_size: usize,
}

impl DescriptorCpuPool {
    pub fn new(device: &ComPtr<d3d12::ID3D12Device>, ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE) -> Self {
        unimplemented!()
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        unimplemented!()
    }
}
<commit_msg>dx12: Implement CPU descriptor heap allocation<commit_after>
use std::collections::HashSet;
use std::ptr;
use winapi::Interface;
use winapi::um::d3d12;
use wio::com::ComPtr;

// Linear stack allocator for CPU descriptor heaps.
pub struct HeapLinear {
    handle_size: usize,
    num: usize,
    size: usize,
    start: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE,
    raw: ComPtr<d3d12::ID3D12DescriptorHeap>,
}

impl HeapLinear {
    pub fn new(
        device: &ComPtr<d3d12::ID3D12Device>,
        ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE,
        size: usize,
    ) -> Self {
        let desc = d3d12::D3D12_DESCRIPTOR_HEAP_DESC {
            Type: ty,
            NumDescriptors: size as u32,
            Flags: d3d12::D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
            NodeMask: 0,
        };

        let mut heap: *mut d3d12::ID3D12DescriptorHeap = ptr::null_mut();
        let handle_size = unsafe {
            device.CreateDescriptorHeap(
                &desc,
                &d3d12::ID3D12DescriptorHeap::uuidof(),
                &mut heap as *mut *mut _ as *mut *mut _,
            );
            device.GetDescriptorHandleIncrementSize(ty) as usize
        };

        let start = unsafe { (*heap).GetCPUDescriptorHandleForHeapStart() };

        HeapLinear {
            handle_size,
            num: 0,
            size,
            start,
            raw: unsafe { ComPtr::from_raw(heap) },
        }
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        assert!(!self.full());

        let slot = self.num;
        self.num += 1;

        d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
            ptr: self.start.ptr + self.handle_size * slot,
        }
    }

    pub fn full(&self) -> bool {
        self.num >= self.size
    }

    pub fn clear(&mut self) {
        self.num = 0;
    }
}

// Fixed-size (64) free-list allocator for CPU descriptors.
struct Heap {
    occupancy: u64,
    handle_size: usize,
    start: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE,
    raw: ComPtr<d3d12::ID3D12DescriptorHeap>,
}

impl Heap {
    pub fn new(device: &ComPtr<d3d12::ID3D12Device>, ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE) -> Self {
        let desc = d3d12::D3D12_DESCRIPTOR_HEAP_DESC {
            Type: ty,
            NumDescriptors: 64,
            Flags: d3d12::D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
            NodeMask: 0,
        };

        let mut heap: *mut d3d12::ID3D12DescriptorHeap = ptr::null_mut();
        let handle_size = unsafe {
            device.CreateDescriptorHeap(
                &desc,
                &d3d12::ID3D12DescriptorHeap::uuidof(),
                &mut heap as *mut *mut _ as *mut *mut _,
            );
            device.GetDescriptorHandleIncrementSize(ty) as usize
        };
        let start = unsafe { (*heap).GetCPUDescriptorHandleForHeapStart() };

        Heap {
            handle_size,
            occupancy: 0,
            start,
            raw: unsafe { ComPtr::from_raw(heap) },
        }
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        // Find first free slot
        let slot = (0..64)
            .position(|i| self.occupancy & (1 << i) == 0)
            .expect("Descriptor heap is full");
        self.occupancy |= 1 << slot;

        d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
            ptr: self.start.ptr + self.handle_size * slot,
        }
    }

    pub fn full(&self) -> bool {
        self.occupancy == !0
    }
}

pub struct DescriptorCpuPool {
    device: ComPtr<d3d12::ID3D12Device>,
    ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE,
    heaps: Vec<Heap>,
    free_list: HashSet<usize>,
}

impl DescriptorCpuPool {
    pub fn new(device: &ComPtr<d3d12::ID3D12Device>, ty: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE) -> Self {
        DescriptorCpuPool {
            device: device.clone(),
            ty,
            heaps: Vec::new(),
            free_list: HashSet::new(),
        }
    }

    pub fn alloc_handle(&mut self) -> d3d12::D3D12_CPU_DESCRIPTOR_HANDLE {
        let heap_id = self
            .free_list
            .iter()
            .cloned()
            .next()
            .unwrap_or_else(|| {
                // Allocate a new heap
                let id = self.heaps.len();
                self.heaps.push(Heap::new(&self.device, self.ty));
                self.free_list.insert(id);
                id
            });

        let heap = &mut self.heaps[heap_id];
        let handle = heap.alloc_handle();
        if heap.full() {
            self.free_list.remove(&heap_id);
        }

        handle
    }

    // TODO: free handles
}
<|endoftext|>


<commit_before>//!
//! This module defines the external interface for controlling one particular
//! instance of a mock server.
//!

use std::cell::RefCell;
use std::ffi::CString;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use log::*;
use pact_plugin_driver::plugin_manager::drop_plugin_access;
use pact_plugin_driver::plugin_models::{PluginDependency, PluginDependencyType};
use rustls::ServerConfig;
use serde::{Deserialize, Serialize};
use serde_json::json;

use pact_models::pact::{Pact, write_pact};
use pact_models::PactSpecification;
use pact_models::sync_pact::RequestResponsePact;
use pact_models::v4::http_parts::HttpRequest;

use crate::hyper_server;
use crate::matching::MatchResult;

/// Mock server configuration
#[derive(Debug, Default, Clone)]
pub struct MockServerConfig {
  /// If CORS Pre-Flight requests should be responded to
  pub cors_preflight: bool,
  /// Pact specification to use
  pub pact_specification: PactSpecification
}

/// Mock server scheme
#[derive(Debug, Clone)]
pub enum MockServerScheme {
  /// HTTP
  HTTP,
  /// HTTPS
  HTTPS
}

impl Default for MockServerScheme {
  fn default() -> Self {
    MockServerScheme::HTTP
  }
}

impl ToString for MockServerScheme {
  fn to_string(&self) -> String {
    match self {
      MockServerScheme::HTTP => "http".into(),
      MockServerScheme::HTTPS => "https".into()
    }
  }
}

/// Metrics for the mock server
#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct MockServerMetrics {
  /// Total requests
  pub requests: usize
}

/// Struct to represent the "foreground" part of mock server
#[derive(Debug)]
pub struct MockServer {
  /// Mock server unique ID
  pub id: String,
  /// Scheme the mock server is using
  pub scheme: MockServerScheme,
  /// Port the mock server is running on
  pub port: Option<u16>,
  /// Address the mock server is bound to
  pub address: Option<String>,
  /// List of resources that need to be cleaned up when the mock server completes
  pub resources: Vec<CString>,
  /// Pact that this mock server is based on
  pub pact: Arc<Mutex<dyn Pact + Send + Sync>>,
  /// Receiver of match results
  matches: Arc<Mutex<Vec<MatchResult>>>,
  /// Shutdown signal
  shutdown_tx: RefCell<Option<futures::channel::oneshot::Sender<()>>>,
  /// Mock server config
  pub config: MockServerConfig,
  /// Metrics collected by the mock server
  pub metrics: MockServerMetrics,
  /// Pact spec version to use
  pub spec_version: PactSpecification
}

impl MockServer {
  /// Create a new mock server, consisting of its state (self) and its executable server future.
  pub async fn new(
    id: String,
    pact: Box<dyn Pact + Send + Sync>,
    addr: std::net::SocketAddr,
    config: MockServerConfig
  ) -> Result<(Arc<Mutex<MockServer>>, impl std::future::Future<Output = ()>), String> {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));

    let mock_server = Arc::new(Mutex::new(MockServer {
      id: id.clone(),
      port: None,
      address: None,
      scheme: MockServerScheme::HTTP,
      resources: vec![],
      pact: pact.thread_safe(),
      matches: matches.clone(),
      shutdown_tx: RefCell::new(Some(shutdown_tx)),
      config: config.clone(),
      metrics: MockServerMetrics::default(),
      spec_version: pact_specification(config.pact_specification, pact.specification_version())
    }));

    let (future, socket_addr) = hyper_server::create_and_bind(
      pact.thread_safe(),
      addr,
      async {
        shutdown_rx.await.ok();
      },
      matches,
      mock_server.clone(),
      &id
    )
      .await
      .map_err(|err| format!("Could not start server: {}", err))?;

    {
      let mut ms = mock_server.lock().unwrap();
      ms.deref_mut().port = Some(socket_addr.port());
      ms.deref_mut().address = Some(socket_addr.ip().to_string());

      debug!("Started mock server on {}:{}", socket_addr.ip(), socket_addr.port());
    }

    Ok((mock_server.clone(), future))
  }

  /// Create a new TLS mock server, consisting of its state (self) and its executable server future.
  pub async fn new_tls(
    id: String,
    pact: Box<dyn Pact>,
    addr: std::net::SocketAddr,
    tls: &ServerConfig,
    config: MockServerConfig
  ) -> Result<(Arc<Mutex<MockServer>>, impl std::future::Future<Output = ()>), String> {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));
    let mock_server = Arc::new(Mutex::new(MockServer {
      id: id.clone(),
      port: None,
      address: None,
      scheme: MockServerScheme::HTTPS,
      resources: vec![],
      pact: pact.thread_safe(),
      matches: matches.clone(),
      shutdown_tx: RefCell::new(Some(shutdown_tx)),
      config: config.clone(),
      metrics: MockServerMetrics::default(),
      spec_version: pact_specification(config.pact_specification, pact.specification_version())
    }));

    let (future, socket_addr) = hyper_server::create_and_bind_tls(
      pact.thread_safe(),
      addr,
      async {
        shutdown_rx.await.ok();
      },
      matches,
      tls.clone(),
      mock_server.clone()
    ).await.map_err(|err| format!("Could not start server: {}", err))?;

    {
      let mut ms = mock_server.lock().unwrap();
      ms.deref_mut().port = Some(socket_addr.port());
      ms.deref_mut().address = Some(socket_addr.ip().to_string());

      debug!("Started mock server on {}:{}", socket_addr.ip(), socket_addr.port());
    }

    Ok((mock_server.clone(), future))
  }

  /// Send the shutdown signal to the server
  pub fn shutdown(&mut self) -> Result<(), String> {
    // Need to check if any plugins need to be shutdown
    let pact = self.pact.lock().unwrap();
    for plugin in pact.plugin_data() {
      let dependency = PluginDependency {
        name: plugin.name,
        version: Some(plugin.version),
        dependency_type: PluginDependencyType::Plugin
      };
      drop_plugin_access(&dependency);
    }

    let shutdown_future = &mut *self.shutdown_tx.borrow_mut();
    match shutdown_future.take() {
      Some(sender) => {
        match sender.send(()) {
          Ok(()) => {
            debug!("Mock server {} shutdown - {:?}", self.id, self.metrics);
            Ok(())
          },
          Err(_) => Err("Problem sending shutdown signal to mock server".into())
        }
      },
      _ => Err("Mock server already shut down".into())
    }
  }

    /// Converts this mock server to a `Value` struct
    pub fn to_json(&self) -> serde_json::Value {
      let pact = self.pact.lock().unwrap();
      json!({
        "id" : self.id.clone(),
        "port" : self.port.unwrap_or_default() as u64,
        "address" : self.address.clone().unwrap_or_default(),
        "scheme" : self.scheme.to_string(),
        "provider" : pact.provider().name.clone(),
        "status" : if self.mismatches().is_empty() { "ok" } else { "error" },
        "metrics" : self.metrics
      })
    }

    /// Returns all collected matches
    pub fn matches(&self) -> Vec<MatchResult> {
        self.matches.lock().unwrap().clone()
    }

    /// Returns all the mismatches that have occurred with this mock server
    pub fn mismatches(&self) -> Vec<MatchResult> {
      let matches = self.matches();
      let mismatches = matches.iter()
        .filter(|m| !m.matched() && !m.cors_preflight())
        .map(|m| m.clone());
      let requests: Vec<HttpRequest> = matches.iter().map(|m| {
        match m {
          MatchResult::RequestMatch(request, _) => Some(request),
          MatchResult::RequestMismatch(request, _) => Some(request),
          MatchResult::RequestNotFound(_) => None,
          MatchResult::MissingRequest(_) => None
        }
      }).filter(|o| o.is_some()).map(|o| o.unwrap().clone()).collect();

      let pact = self.pact.lock().unwrap();
      let interactions = pact.interactions();
      let missing = interactions.iter()
        .map(|i| i.as_v4_http().unwrap().request)
        .filter(|req| !requests.contains(req))
        .map(|req| MatchResult::MissingRequest(req.clone()));
      mismatches.chain(missing).collect()
    }

  /// Mock server writes its pact out to the provided directory
  pub fn write_pact(&self, output_path: &Option<String>, overwrite: bool) -> anyhow::Result<()> {
    trace!("write_pact: output_path = {:?}, overwrite = {}", output_path, overwrite);
    let mut pact = self.pact.lock().unwrap();
    pact.add_md_version("mockserver", option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"));
    let pact_file_name = pact.default_file_name();
    let filename = match *output_path {
      Some(ref path) => {
        let mut path = PathBuf::from(path);
        path.push(pact_file_name);
        path
      },
      None => PathBuf::from(pact_file_name)
    };

    info!("Writing pact out to '{}'", filename.display());
    let specification = self.spec_version;
    match write_pact(pact.boxed(), filename.as_path(), specification, overwrite) {
      Ok(_) => Ok(()),
      Err(err) => {
        warn!("Failed to write pact to file - {}", err);
        Err(err)
      }
    }
  }

    /// Returns the URL of the mock server
    pub fn url(&self) -> String {
      let addr = self.address.clone().unwrap_or_else(|| "127.0.0.1".to_string());
      match self.port {
        Some(port) => format!("{}://{}:{}", self.scheme.to_string(),
          if addr == "0.0.0.0" { "127.0.0.1" } else { addr.as_str() }, port),
        None => "error(port is not set)".to_string()
      }
    }
}

fn pact_specification(spec1: PactSpecification, spec2: PactSpecification) -> PactSpecification {
  match spec1 {
    PactSpecification::Unknown => spec2,
    _ => spec1
  }
}

impl Clone for MockServer {
  /// Make a clone all of the MockServer fields.
  /// Note that the clone of the original server cannot be shut down directly.
  fn clone(&self) -> MockServer {
    MockServer {
      id: self.id.clone(),
      port: self.port,
      address: self.address.clone(),
      scheme: self.scheme.clone(),
      resources: vec![],
      pact: self.pact.clone(),
      matches: self.matches.clone(),
      shutdown_tx: RefCell::new(None),
      config: self.config.clone(),
      metrics: self.metrics.clone(),
      spec_version: self.spec_version
    }
  }
}

impl Default for MockServer {
  fn default() -> Self {
    MockServer {
      id: "".to_string(),
      scheme: Default::default(),
      port: None,
      address: None,
      resources: vec![],
      pact: Arc::new(Mutex::new(RequestResponsePact::default())),
      matches: Arc::new(Mutex::new(vec![])),
      shutdown_tx: RefCell::new(None),
      config: Default::default(),
      metrics: Default::default(),
      spec_version: Default::default()
    }
  }
}
<commit_msg>feat(mock server): default pact spec to V3 if unknown<commit_after>//!
//! This module defines the external interface for controlling one particular
//! instance of a mock server.
//!

use std::cell::RefCell;
use std::ffi::CString;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use log::*;
use pact_plugin_driver::plugin_manager::drop_plugin_access;
use pact_plugin_driver::plugin_models::{PluginDependency, PluginDependencyType};
use rustls::ServerConfig;
use serde::{Deserialize, Serialize};
use serde_json::json;

use pact_models::pact::{Pact, write_pact};
use pact_models::PactSpecification;
use pact_models::sync_pact::RequestResponsePact;
use pact_models::v4::http_parts::HttpRequest;

use crate::hyper_server;
use crate::matching::MatchResult;

/// Mock server configuration
#[derive(Debug, Default, Clone)]
pub struct MockServerConfig {
  /// If CORS Pre-Flight requests should be responded to
  pub cors_preflight: bool,
  /// Pact specification to use
  pub pact_specification: PactSpecification
}

/// Mock server scheme
#[derive(Debug, Clone)]
pub enum MockServerScheme {
  /// HTTP
  HTTP,
  /// HTTPS
  HTTPS
}

impl Default for MockServerScheme {
  fn default() -> Self {
    MockServerScheme::HTTP
  }
}

impl ToString for MockServerScheme {
  fn to_string(&self) -> String {
    match self {
      MockServerScheme::HTTP => "http".into(),
      MockServerScheme::HTTPS => "https".into()
    }
  }
}

/// Metrics for the mock server
#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct MockServerMetrics {
  /// Total requests
  pub requests: usize
}

/// Struct to represent the "foreground" part of mock server
#[derive(Debug)]
pub struct MockServer {
  /// Mock server unique ID
  pub id: String,
  /// Scheme the mock server is using
  pub scheme: MockServerScheme,
  /// Port the mock server is running on
  pub port: Option<u16>,
  /// Address the mock server is bound to
  pub address: Option<String>,
  /// List of resources that need to be cleaned up when the mock server completes
  pub resources: Vec<CString>,
  /// Pact that this mock server is based on
  pub pact: Arc<Mutex<dyn Pact + Send + Sync>>,
  /// Receiver of match results
  matches: Arc<Mutex<Vec<MatchResult>>>,
  /// Shutdown signal
  shutdown_tx: RefCell<Option<futures::channel::oneshot::Sender<()>>>,
  /// Mock server config
  pub config: MockServerConfig,
  /// Metrics collected by the mock server
  pub metrics: MockServerMetrics,
  /// Pact spec version to use
  pub spec_version: PactSpecification
}

impl MockServer {
  /// Create a new mock server, consisting of its state (self) and its executable server future.
  pub async fn new(
    id: String,
    pact: Box<dyn Pact + Send + Sync>,
    addr: std::net::SocketAddr,
    config: MockServerConfig
  ) -> Result<(Arc<Mutex<MockServer>>, impl std::future::Future<Output = ()>), String> {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));

    let mock_server = Arc::new(Mutex::new(MockServer {
      id: id.clone(),
      port: None,
      address: None,
      scheme: MockServerScheme::HTTP,
      resources: vec![],
      pact: pact.thread_safe(),
      matches: matches.clone(),
      shutdown_tx: RefCell::new(Some(shutdown_tx)),
      config: config.clone(),
      metrics: MockServerMetrics::default(),
      spec_version: pact_specification(config.pact_specification, pact.specification_version())
    }));

    let (future, socket_addr) = hyper_server::create_and_bind(
      pact.thread_safe(),
      addr,
      async {
        shutdown_rx.await.ok();
      },
      matches,
      mock_server.clone(),
      &id
    )
      .await
      .map_err(|err| format!("Could not start server: {}", err))?;

    {
      let mut ms = mock_server.lock().unwrap();
      ms.deref_mut().port = Some(socket_addr.port());
      ms.deref_mut().address = Some(socket_addr.ip().to_string());

      debug!("Started mock server on {}:{}", socket_addr.ip(), socket_addr.port());
    }

    Ok((mock_server.clone(), future))
  }

  /// Create a new TLS mock server, consisting of its state (self) and its executable server future.
  pub async fn new_tls(
    id: String,
    pact: Box<dyn Pact>,
    addr: std::net::SocketAddr,
    tls: &ServerConfig,
    config: MockServerConfig
  ) -> Result<(Arc<Mutex<MockServer>>, impl std::future::Future<Output = ()>), String> {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));
    let mock_server = Arc::new(Mutex::new(MockServer {
      id: id.clone(),
      port: None,
      address: None,
      scheme: MockServerScheme::HTTPS,
      resources: vec![],
      pact: pact.thread_safe(),
      matches: matches.clone(),
      shutdown_tx: RefCell::new(Some(shutdown_tx)),
      config: config.clone(),
      metrics: MockServerMetrics::default(),
      spec_version: pact_specification(config.pact_specification, pact.specification_version())
    }));

    let (future, socket_addr) = hyper_server::create_and_bind_tls(
      pact.thread_safe(),
      addr,
      async {
        shutdown_rx.await.ok();
      },
      matches,
      tls.clone(),
      mock_server.clone()
    ).await.map_err(|err| format!("Could not start server: {}", err))?;

    {
      let mut ms = mock_server.lock().unwrap();
      ms.deref_mut().port = Some(socket_addr.port());
      ms.deref_mut().address = Some(socket_addr.ip().to_string());

      debug!("Started mock server on {}:{}", socket_addr.ip(), socket_addr.port());
    }

    Ok((mock_server.clone(), future))
  }

  /// Send the shutdown signal to the server
  pub fn shutdown(&mut self) -> Result<(), String> {
    // Need to check if any plugins need to be shutdown
    let pact = self.pact.lock().unwrap();
    for plugin in pact.plugin_data() {
      let dependency = PluginDependency {
        name: plugin.name,
        version: Some(plugin.version),
        dependency_type: PluginDependencyType::Plugin
      };
      drop_plugin_access(&dependency);
    }

    let shutdown_future = &mut *self.shutdown_tx.borrow_mut();
    match shutdown_future.take() {
      Some(sender) => {
        match sender.send(()) {
          Ok(()) => {
            debug!("Mock server {} shutdown - {:?}", self.id, self.metrics);
            Ok(())
          },
          Err(_) => Err("Problem sending shutdown signal to mock server".into())
        }
      },
      _ => Err("Mock server already shut down".into())
    }
  }

    /// Converts this mock server to a `Value` struct
    pub fn to_json(&self) -> serde_json::Value {
      let pact = self.pact.lock().unwrap();
      json!({
        "id" : self.id.clone(),
        "port" : self.port.unwrap_or_default() as u64,
        "address" : self.address.clone().unwrap_or_default(),
        "scheme" : self.scheme.to_string(),
        "provider" : pact.provider().name.clone(),
        "status" : if self.mismatches().is_empty() { "ok" } else { "error" },
        "metrics" : self.metrics
      })
    }

    /// Returns all collected matches
    pub fn matches(&self) -> Vec<MatchResult> {
        self.matches.lock().unwrap().clone()
    }

    /// Returns all the mismatches that have occurred with this mock server
    pub fn mismatches(&self) -> Vec<MatchResult> {
      let matches = self.matches();
      let mismatches = matches.iter()
        .filter(|m| !m.matched() && !m.cors_preflight())
        .map(|m| m.clone());
      let requests: Vec<HttpRequest> = matches.iter().map(|m| {
        match m {
          MatchResult::RequestMatch(request, _) => Some(request),
          MatchResult::RequestMismatch(request, _) => Some(request),
          MatchResult::RequestNotFound(_) => None,
          MatchResult::MissingRequest(_) => None
        }
      }).filter(|o| o.is_some()).map(|o| o.unwrap().clone()).collect();

      let pact = self.pact.lock().unwrap();
      let interactions = pact.interactions();
      let missing = interactions.iter()
        .map(|i| i.as_v4_http().unwrap().request)
        .filter(|req| !requests.contains(req))
        .map(|req| MatchResult::MissingRequest(req.clone()));
      mismatches.chain(missing).collect()
    }

  /// Mock server writes its pact out to the provided directory
  pub fn write_pact(&self, output_path: &Option<String>, overwrite: bool) -> anyhow::Result<()> {
    trace!("write_pact: output_path = {:?}, overwrite = {}", output_path, overwrite);
    let mut pact = self.pact.lock().unwrap();
    pact.add_md_version("mockserver", option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"));
    let pact_file_name = pact.default_file_name();
    let filename = match *output_path {
      Some(ref path) => {
        let mut path = PathBuf::from(path);
        path.push(pact_file_name);
        path
      },
      None => PathBuf::from(pact_file_name)
    };

    info!("Writing pact out to '{}'", filename.display());
    let specification = match self.spec_version {
      PactSpecification::Unknown => PactSpecification::V3,
      _ => self.spec_version
    };
    match write_pact(pact.boxed(), filename.as_path(), specification, overwrite) {
      Ok(_) => Ok(()),
      Err(err) => {
        warn!("Failed to write pact to file - {}", err);
        Err(err)
      }
    }
  }

    /// Returns the URL of the mock server
    pub fn url(&self) -> String {
      let addr = self.address.clone().unwrap_or_else(|| "127.0.0.1".to_string());
      match self.port {
        Some(port) => format!("{}://{}:{}", self.scheme.to_string(),
          if addr == "0.0.0.0" { "127.0.0.1" } else { addr.as_str() }, port),
        None => "error(port is not set)".to_string()
      }
    }
}

fn pact_specification(spec1: PactSpecification, spec2: PactSpecification) -> PactSpecification {
  match spec1 {
    PactSpecification::Unknown => spec2,
    _ => spec1
  }
}

impl Clone for MockServer {
  /// Make a clone all of the MockServer fields.
  /// Note that the clone of the original server cannot be shut down directly.
  fn clone(&self) -> MockServer {
    MockServer {
      id: self.id.clone(),
      port: self.port,
      address: self.address.clone(),
      scheme: self.scheme.clone(),
      resources: vec![],
      pact: self.pact.clone(),
      matches: self.matches.clone(),
      shutdown_tx: RefCell::new(None),
      config: self.config.clone(),
      metrics: self.metrics.clone(),
      spec_version: self.spec_version
    }
  }
}

impl Default for MockServer {
  fn default() -> Self {
    MockServer {
      id: "".to_string(),
      scheme: Default::default(),
      port: None,
      address: None,
      resources: vec![],
      pact: Arc::new(Mutex::new(RequestResponsePact::default())),
      matches: Arc::new(Mutex::new(vec![])),
      shutdown_tx: RefCell::new(None),
      config: Default::default(),
      metrics: Default::default(),
      spec_version: Default::default()
    }
  }
}
<|endoftext|>


<commit_before>use relative_path::{RelativePath, RelativePathBuf};
use hir::{ModuleSource, source_binder};
use ra_db::{SourceDatabase};
use ra_syntax::{
    AstNode, SyntaxNode, SourceFile,
    ast,
    algo::find_node_at_offset,
};

use crate::{
    db::RootDatabase,
    FilePosition,
    FileRange,
    FileId,
    NavigationTarget,
    FileSystemEdit,
    SourceChange,
    SourceFileEdit,
    TextRange,
};

#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
    declaration: NavigationTarget,
    references: Vec<FileRange>,
}

impl ReferenceSearchResult {
    pub fn declaration(&self) -> &NavigationTarget {
        &self.declaration
    }

    pub fn references(&self) -> &[FileRange] {
        &self.references
    }

    /// Total number of references
    /// At least 1 since all valid references should
    /// Have a declaration
    pub fn len(&self) -> usize {
        self.references.len() + 1
    }
}

// allow turning ReferenceSearchResult into an iterator
// over FileRanges
impl IntoIterator for ReferenceSearchResult {
    type Item = FileRange;
    type IntoIter = ::std::vec::IntoIter<FileRange>;

    fn into_iter(mut self) -> Self::IntoIter {
        let mut v = Vec::with_capacity(self.len());
        v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() });
        v.append(&mut self.references);
        v.into_iter()
    }
}

pub(crate) fn find_all_refs(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<ReferenceSearchResult> {
    let file = db.parse(position.file_id);
    let (binding, descr) = find_binding(db, &file, position)?;
    let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);

    let references = descr
        .scopes(db)
        .find_all_refs(binding)
        .into_iter()
        .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
        .collect::<Vec<_>>();

    return Some(ReferenceSearchResult { declaration, references });

    fn find_binding<'a>(
        db: &RootDatabase,
        source_file: &'a SourceFile,
        position: FilePosition,
    ) -> Option<(&'a ast::BindPat, hir::Function)> {
        let syntax = source_file.syntax();
        if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
            let descr =
                source_binder::function_from_child_node(db, position.file_id, binding.syntax())?;
            return Some((binding, descr));
        };
        let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
        let descr =
            source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?;
        let scope = descr.scopes(db);
        let resolved = scope.resolve_local_name(name_ref)?;
        let resolved = resolved.ptr().to_node(source_file);
        let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?;
        Some((binding, descr))
    }
}

pub(crate) fn rename(
    db: &RootDatabase,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let source_file = db.parse(position.file_id);
    let syntax = source_file.syntax();

    if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) {
        rename_mod(db, ast_name, ast_module, position, new_name)
    } else {
        rename_reference(db, position, new_name)
    }
}

fn find_name_and_module_at_offset(
    syntax: &SyntaxNode,
    position: FilePosition,
) -> Option<(&ast::Name, &ast::Module)> {
    let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset);
    let ast_name_parent = ast::Module::cast(ast_name?.syntax().parent()?);

    if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) {
        return Some((name, ast_module));
    }
    None
}

fn source_edit_from_fileid_range(
    file_id: FileId,
    range: TextRange,
    new_name: &str,
) -> SourceFileEdit {
    SourceFileEdit {
        file_id,
        edit: {
            let mut builder = ra_text_edit::TextEditBuilder::default();
            builder.replace(range, new_name.into());
            builder.finish()
        },
    }
}

fn rename_mod(
    db: &RootDatabase,
    ast_name: &ast::Name,
    ast_module: &ast::Module,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let mut source_file_edits = Vec::new();
    let mut file_system_edits = Vec::new();
    if let Some(module) = source_binder::module_from_declaration(db, position.file_id, &ast_module)
    {
        let (file_id, module_source) = module.definition_source(db);
        let file_id = file_id.as_original_file();
        match module_source {
            ModuleSource::SourceFile(..) => {
                let mod_path: RelativePathBuf = db.file_relative_path(file_id);
                // mod is defined in path/to/dir/mod.rs
                let dst_path = if mod_path.file_stem() == Some("mod") {
                    mod_path
                        .parent()
                        .and_then(|p| p.parent())
                        .or_else(|| Some(RelativePath::new("")))
                        .map(|p| p.join(new_name).join("mod.rs"))
                } else {
                    Some(mod_path.with_file_name(new_name).with_extension("rs"))
                };
                if let Some(path) = dst_path {
                    let move_file = FileSystemEdit::MoveFile {
                        src: file_id,
                        dst_source_root: db.file_source_root(position.file_id),
                        dst_path: path,
                    };
                    file_system_edits.push(move_file);
                }
            }
            ModuleSource::Module(..) => {}
        }
    }

    let edit = SourceFileEdit {
        file_id: position.file_id,
        edit: {
            let mut builder = ra_text_edit::TextEditBuilder::default();
            builder.replace(ast_name.syntax().range(), new_name.into());
            builder.finish()
        },
    };
    source_file_edits.push(edit);

    Some(SourceChange {
        label: "rename".to_string(),
        source_file_edits,
        file_system_edits,
        cursor_position: None,
    })
}

fn rename_reference(
    db: &RootDatabase,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let refs = find_all_refs(db, position)?;

    let edit = refs
        .into_iter()
        .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name))
        .collect::<Vec<_>>();

    if edit.is_empty() {
        return None;
    }

    Some(SourceChange {
        label: "rename".to_string(),
        source_file_edits: edit,
        file_system_edits: Vec::new(),
        cursor_position: None,
    })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot_matches;
    use test_utils::assert_eq_text;
    use crate::{
        mock_analysis::single_file_with_position,
        mock_analysis::analysis_and_position,
        FileId
};

    #[test]
    fn test_rename_for_local() {
        test_rename(
            r#"
    fn main() {
        let mut i = 1;
        let j = 1;
        i = i<|> + j;

        {
            i = 0;
        }

        i = 5;
    }"#,
            "k",
            r#"
    fn main() {
        let mut k = 1;
        let j = 1;
        k = k + j;

        {
            k = 0;
        }

        k = 5;
    }"#,
        );
    }

    #[test]
    fn test_rename_for_param_inside() {
        test_rename(
            r#"
    fn foo(i : u32) -> u32 {
        i<|>
    }"#,
            "j",
            r#"
    fn foo(j : u32) -> u32 {
        j
    }"#,
        );
    }

    #[test]
    fn test_rename_refs_for_fn_param() {
        test_rename(
            r#"
    fn foo(i<|> : u32) -> u32 {
        i
    }"#,
            "new_name",
            r#"
    fn foo(new_name : u32) -> u32 {
        new_name
    }"#,
        );
    }

    #[test]
    fn test_rename_for_mut_param() {
        test_rename(
            r#"
    fn foo(mut i<|> : u32) -> u32 {
        i
    }"#,
            "new_name",
            r#"
    fn foo(mut new_name : u32) -> u32 {
        new_name
    }"#,
        );
    }

    #[test]
    fn test_rename_mod() {
        let (analysis, position) = analysis_and_position(
            "
            //- /lib.rs
            mod bar;

            //- /bar.rs
            mod foo<|>;

            //- /bar/foo.rs
            // emtpy
            ",
        );
        let new_name = "foo2";
        let source_change = analysis.rename(position, new_name).unwrap();
        assert_debug_snapshot_matches!("rename_mod", &source_change);
    }

    #[test]
    fn test_rename_mod_in_dir() {
        let (analysis, position) = analysis_and_position(
            "
            //- /lib.rs
            mod fo<|>o;
            //- /foo/mod.rs
            // emtpy
            ",
        );
        let new_name = "foo2";
        let source_change = analysis.rename(position, new_name).unwrap();
        assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change);
    }

    fn test_rename(text: &str, new_name: &str, expected: &str) {
        let (analysis, position) = single_file_with_position(text);
        let source_change = analysis.rename(position, new_name).unwrap();
        let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
        let mut file_id: Option<FileId> = None;
        if let Some(change) = source_change {
            for edit in change.source_file_edits {
                file_id = Some(edit.file_id);
                for atom in edit.edit.as_atoms() {
                    text_edit_builder.replace(atom.delete, atom.insert.clone());
                }
            }
        }
        let result = text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()));
        assert_eq_text!(expected, &*result);
    }
}
<commit_msg>Remove leading ::<commit_after>use relative_path::{RelativePath, RelativePathBuf};
use hir::{ModuleSource, source_binder};
use ra_db::{SourceDatabase};
use ra_syntax::{
    AstNode, SyntaxNode, SourceFile,
    ast,
    algo::find_node_at_offset,
};

use crate::{
    db::RootDatabase,
    FilePosition,
    FileRange,
    FileId,
    NavigationTarget,
    FileSystemEdit,
    SourceChange,
    SourceFileEdit,
    TextRange,
};

#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
    declaration: NavigationTarget,
    references: Vec<FileRange>,
}

impl ReferenceSearchResult {
    pub fn declaration(&self) -> &NavigationTarget {
        &self.declaration
    }

    pub fn references(&self) -> &[FileRange] {
        &self.references
    }

    /// Total number of references
    /// At least 1 since all valid references should
    /// Have a declaration
    pub fn len(&self) -> usize {
        self.references.len() + 1
    }
}

// allow turning ReferenceSearchResult into an iterator
// over FileRanges
impl IntoIterator for ReferenceSearchResult {
    type Item = FileRange;
    type IntoIter = std::vec::IntoIter<FileRange>;

    fn into_iter(mut self) -> Self::IntoIter {
        let mut v = Vec::with_capacity(self.len());
        v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() });
        v.append(&mut self.references);
        v.into_iter()
    }
}

pub(crate) fn find_all_refs(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<ReferenceSearchResult> {
    let file = db.parse(position.file_id);
    let (binding, descr) = find_binding(db, &file, position)?;
    let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);

    let references = descr
        .scopes(db)
        .find_all_refs(binding)
        .into_iter()
        .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
        .collect::<Vec<_>>();

    return Some(ReferenceSearchResult { declaration, references });

    fn find_binding<'a>(
        db: &RootDatabase,
        source_file: &'a SourceFile,
        position: FilePosition,
    ) -> Option<(&'a ast::BindPat, hir::Function)> {
        let syntax = source_file.syntax();
        if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
            let descr =
                source_binder::function_from_child_node(db, position.file_id, binding.syntax())?;
            return Some((binding, descr));
        };
        let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
        let descr =
            source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?;
        let scope = descr.scopes(db);
        let resolved = scope.resolve_local_name(name_ref)?;
        let resolved = resolved.ptr().to_node(source_file);
        let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?;
        Some((binding, descr))
    }
}

pub(crate) fn rename(
    db: &RootDatabase,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let source_file = db.parse(position.file_id);
    let syntax = source_file.syntax();

    if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) {
        rename_mod(db, ast_name, ast_module, position, new_name)
    } else {
        rename_reference(db, position, new_name)
    }
}

fn find_name_and_module_at_offset(
    syntax: &SyntaxNode,
    position: FilePosition,
) -> Option<(&ast::Name, &ast::Module)> {
    let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset);
    let ast_name_parent = ast::Module::cast(ast_name?.syntax().parent()?);

    if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) {
        return Some((name, ast_module));
    }
    None
}

fn source_edit_from_fileid_range(
    file_id: FileId,
    range: TextRange,
    new_name: &str,
) -> SourceFileEdit {
    SourceFileEdit {
        file_id,
        edit: {
            let mut builder = ra_text_edit::TextEditBuilder::default();
            builder.replace(range, new_name.into());
            builder.finish()
        },
    }
}

fn rename_mod(
    db: &RootDatabase,
    ast_name: &ast::Name,
    ast_module: &ast::Module,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let mut source_file_edits = Vec::new();
    let mut file_system_edits = Vec::new();
    if let Some(module) = source_binder::module_from_declaration(db, position.file_id, &ast_module)
    {
        let (file_id, module_source) = module.definition_source(db);
        let file_id = file_id.as_original_file();
        match module_source {
            ModuleSource::SourceFile(..) => {
                let mod_path: RelativePathBuf = db.file_relative_path(file_id);
                // mod is defined in path/to/dir/mod.rs
                let dst_path = if mod_path.file_stem() == Some("mod") {
                    mod_path
                        .parent()
                        .and_then(|p| p.parent())
                        .or_else(|| Some(RelativePath::new("")))
                        .map(|p| p.join(new_name).join("mod.rs"))
                } else {
                    Some(mod_path.with_file_name(new_name).with_extension("rs"))
                };
                if let Some(path) = dst_path {
                    let move_file = FileSystemEdit::MoveFile {
                        src: file_id,
                        dst_source_root: db.file_source_root(position.file_id),
                        dst_path: path,
                    };
                    file_system_edits.push(move_file);
                }
            }
            ModuleSource::Module(..) => {}
        }
    }

    let edit = SourceFileEdit {
        file_id: position.file_id,
        edit: {
            let mut builder = ra_text_edit::TextEditBuilder::default();
            builder.replace(ast_name.syntax().range(), new_name.into());
            builder.finish()
        },
    };
    source_file_edits.push(edit);

    Some(SourceChange {
        label: "rename".to_string(),
        source_file_edits,
        file_system_edits,
        cursor_position: None,
    })
}

fn rename_reference(
    db: &RootDatabase,
    position: FilePosition,
    new_name: &str,
) -> Option<SourceChange> {
    let refs = find_all_refs(db, position)?;

    let edit = refs
        .into_iter()
        .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name))
        .collect::<Vec<_>>();

    if edit.is_empty() {
        return None;
    }

    Some(SourceChange {
        label: "rename".to_string(),
        source_file_edits: edit,
        file_system_edits: Vec::new(),
        cursor_position: None,
    })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot_matches;
    use test_utils::assert_eq_text;
    use crate::{
        mock_analysis::single_file_with_position,
        mock_analysis::analysis_and_position,
        FileId
};

    #[test]
    fn test_rename_for_local() {
        test_rename(
            r#"
    fn main() {
        let mut i = 1;
        let j = 1;
        i = i<|> + j;

        {
            i = 0;
        }

        i = 5;
    }"#,
            "k",
            r#"
    fn main() {
        let mut k = 1;
        let j = 1;
        k = k + j;

        {
            k = 0;
        }

        k = 5;
    }"#,
        );
    }

    #[test]
    fn test_rename_for_param_inside() {
        test_rename(
            r#"
    fn foo(i : u32) -> u32 {
        i<|>
    }"#,
            "j",
            r#"
    fn foo(j : u32) -> u32 {
        j
    }"#,
        );
    }

    #[test]
    fn test_rename_refs_for_fn_param() {
        test_rename(
            r#"
    fn foo(i<|> : u32) -> u32 {
        i
    }"#,
            "new_name",
            r#"
    fn foo(new_name : u32) -> u32 {
        new_name
    }"#,
        );
    }

    #[test]
    fn test_rename_for_mut_param() {
        test_rename(
            r#"
    fn foo(mut i<|> : u32) -> u32 {
        i
    }"#,
            "new_name",
            r#"
    fn foo(mut new_name : u32) -> u32 {
        new_name
    }"#,
        );
    }

    #[test]
    fn test_rename_mod() {
        let (analysis, position) = analysis_and_position(
            "
            //- /lib.rs
            mod bar;

            //- /bar.rs
            mod foo<|>;

            //- /bar/foo.rs
            // emtpy
            ",
        );
        let new_name = "foo2";
        let source_change = analysis.rename(position, new_name).unwrap();
        assert_debug_snapshot_matches!("rename_mod", &source_change);
    }

    #[test]
    fn test_rename_mod_in_dir() {
        let (analysis, position) = analysis_and_position(
            "
            //- /lib.rs
            mod fo<|>o;
            //- /foo/mod.rs
            // emtpy
            ",
        );
        let new_name = "foo2";
        let source_change = analysis.rename(position, new_name).unwrap();
        assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change);
    }

    fn test_rename(text: &str, new_name: &str, expected: &str) {
        let (analysis, position) = single_file_with_position(text);
        let source_change = analysis.rename(position, new_name).unwrap();
        let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
        let mut file_id: Option<FileId> = None;
        if let Some(change) = source_change {
            for edit in change.source_file_edits {
                file_id = Some(edit.file_id);
                for atom in edit.edit.as_atoms() {
                    text_edit_builder.replace(atom.delete, atom.insert.clone());
                }
            }
        }
        let result = text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()));
        assert_eq_text!(expected, &*result);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
use eutil::Downcast;
use frame::{ServoCefFrame, ServoCefFrameExtensions};
use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
use interfaces::{cef_request_context_t};
use servo::Browser;
use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t, cef_window_handle_t};
use window;
use wrappers::CefWrap;

use compositing::windowing::{WindowNavigateMsg, WindowEvent};
use glutin_app;
use libc::c_int;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell, BorrowState};
use std::ptr;
use std::sync::atomic::{AtomicIsize, Ordering};

thread_local!(pub static ID_COUNTER: AtomicIsize = AtomicIsize::new(0));
thread_local!(pub static BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()));

pub enum ServoBrowser {
    Invalid,
    OnScreen(Browser),
    OffScreen(Browser),
}

impl ServoBrowser {
    fn handle_event(&mut self, event: WindowEvent) {
        match *self {
            ServoBrowser::OnScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::OffScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::Invalid => {}
        }
    }

    pub fn get_title_for_main_frame(&self) {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::OffScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::Invalid => {}
        }
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::OffScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::Invalid => 1.0,
        }
    }
}

cef_class_impl! {
    ServoCefBrowser : CefBrowser, cef_browser_t {
        fn get_host(&this,) -> *mut cef_browser_host_t {{
            this.downcast().host.clone()
        }}

        fn go_back(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
        }}

        fn go_forward(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
        }}

        // Returns the main (top-level) frame for the browser window.
        fn get_main_frame(&this,) -> *mut cef_frame_t {{
            this.downcast().frame.clone()
        }}

        fn get_identifier(&this,) -> c_int {{
            this.downcast().id as c_int
        }}
    }
}

pub struct ServoCefBrowser {
    /// A reference to the browser's primary frame.
    pub frame: CefFrame,
    /// A reference to the browser's host.
    pub host: CefBrowserHost,
    /// A reference to the browser client.
    pub client: CefClient,
    /// Whether the on-created callback has fired yet.
    pub callback_executed: Cell<bool>,
    /// the display system window handle: only to be used with host.get_window_handle()
    window_handle: cef_window_handle_t,

    id: isize,
    servo_browser: RefCell<ServoBrowser>,
    message_queue: RefCell<Vec<WindowEvent>>,
}

impl ServoCefBrowser {
    pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
        let frame = ServoCefFrame::new().as_cef_interface();
        let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();
        let mut window_handle: cef_window_handle_t = 0;

        let servo_browser = if window_info.windowless_rendering_enabled == 0 {
            let glutin_window = glutin_app::create_window();
            let servo_browser = Browser::new(Some(glutin_window.clone()));
            window_handle = glutin_window.platform_window() as cef_window_handle_t;
            ServoBrowser::OnScreen(servo_browser)
        } else {
            ServoBrowser::Invalid
        };

        let id = ID_COUNTER.with(|counter| {
            counter.fetch_add(1, Ordering::SeqCst)
        });

        ServoCefBrowser {
            frame: frame,
            host: host,
            client: client,
            callback_executed: Cell::new(false),
            servo_browser: RefCell::new(servo_browser),
            message_queue: RefCell::new(vec!()),
            id: id,
            window_handle: window_handle,
        }
    }
}

pub trait ServoCefBrowserExtensions {
    fn init(&self, window_info: &cef_window_info_t);
    fn send_window_event(&self, event: WindowEvent);
    fn get_title_for_main_frame(&self);
    fn pinch_zoom_level(&self) -> f32;
}

impl ServoCefBrowserExtensions for CefBrowser {
    fn init(&self, window_info: &cef_window_info_t) {
        if window_info.windowless_rendering_enabled != 0 {
            let window = window::Window::new();
            let servo_browser = Browser::new(Some(window.clone()));
            window.set_browser(self.clone());
            *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
        }

        self.downcast().host.set_browser((*self).clone());
        self.downcast().frame.set_browser((*self).clone());
        if window_info.parent_window != 0 {
            self.downcast().host.initialize_compositing();
        }
    }

    fn send_window_event(&self, event: WindowEvent) {
        self.downcast().message_queue.borrow_mut().push(event);

        loop {
            match self.downcast().servo_browser.borrow_state() {
                BorrowState::Unused => {
                    let event = match self.downcast().message_queue.borrow_mut().pop() {
                        None => return,
                        Some(event) => event,
                    };
                    self.downcast().servo_browser.borrow_mut().handle_event(event);
                }
                _ => {
                    // We're trying to send an event while processing another one. This will
                    // cause general badness, so queue up that event instead of immediately
                    // processing it.
                    break
                }
            }
        }
    }

    fn get_title_for_main_frame(&self) {
        self.downcast().servo_browser.borrow().get_title_for_main_frame()
    }

    fn pinch_zoom_level(&self) -> f32 {
        self.downcast().servo_browser.borrow().pinch_zoom_level()
    }
}

pub fn update() {
    BROWSERS.with(|browsers| {
        for browser in browsers.borrow().iter() {
            if browser.downcast().callback_executed.get() == false {
                browser_callback_after_created(browser.clone());
            }
            browser.send_window_event(WindowEvent::Idle);
        }
    });
}

pub fn close(browser: CefBrowser) {
    BROWSERS.with(|browsers| {
        let mut browsers = browsers.borrow_mut();
        browsers.iter()
                .position(|&ref n| n.downcast().id == browser.downcast().id)
                .map(|e| browsers.remove(e));
    });
}

pub fn get_window(browser: &CefBrowser) -> cef_window_handle_t {
    browser.downcast().window_handle
}

pub fn browser_callback_after_created(browser: CefBrowser) {
    if browser.downcast().client.is_null_cef_object() {
        return
    }
    let client = browser.downcast().client.clone();
    let life_span_handler = client.get_life_span_handler();
    if life_span_handler.is_not_null_cef_object() {
        life_span_handler.on_after_created(browser.clone());
    }
    browser.downcast().callback_executed.set(true);
}

fn browser_host_create(window_info: &cef_window_info_t,
                       client: CefClient,
                       url: *const cef_string_t,
                       callback_executed: bool)
                       -> CefBrowser {
    let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
    browser.init(window_info);
    if callback_executed {
        browser_callback_after_created(browser.clone());
    }
    if url != ptr::null() {
       unsafe { browser.downcast().frame.load_url(CefWrap::to_rust(url)); }
    }
    BROWSERS.with(|browsers| {
        browsers.borrow_mut().push(browser.clone());
    });
    browser
}

cef_static_method_impls! {
    fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
                                       client: *mut cef_client_t,
                                       url: *const cef_string_t,
                                       _browser_settings: *const cef_browser_settings_t,
                                       _request_context: *mut cef_request_context_t)
                                       -> c_int {{
        let client: CefClient = client;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, url, false);
        1i32
    }}
    fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t,
                                            client: *mut cef_client_t,
                                            url: *const cef_string_t,
                                            _browser_settings: *const cef_browser_settings_t,
                                            _request_context: *mut cef_request_context_t)
                                            -> *mut cef_browser_t {{
        let client: CefClient = client;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, url, true)
    }}
}
<commit_msg>try fixing macos embedding build<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
use eutil::Downcast;
use frame::{ServoCefFrame, ServoCefFrameExtensions};
use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
use interfaces::{cef_request_context_t};
use servo::Browser;
use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t, cef_window_handle_t};
use window;
use wrappers::CefWrap;

use compositing::windowing::{WindowNavigateMsg, WindowEvent};
use glutin_app;
use libc::c_int;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell, BorrowState};
use std::ptr;
use std::sync::atomic::{AtomicIsize, Ordering};

thread_local!(pub static ID_COUNTER: AtomicIsize = AtomicIsize::new(0));
thread_local!(pub static BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()));

pub enum ServoBrowser {
    Invalid,
    OnScreen(Browser),
    OffScreen(Browser),
}

impl ServoBrowser {
    fn handle_event(&mut self, event: WindowEvent) {
        match *self {
            ServoBrowser::OnScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::OffScreen(ref mut browser) => { browser.handle_event(event); }
            ServoBrowser::Invalid => {}
        }
    }

    pub fn get_title_for_main_frame(&self) {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::OffScreen(ref browser) => browser.get_title_for_main_frame(),
            ServoBrowser::Invalid => {}
        }
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        match *self {
            ServoBrowser::OnScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::OffScreen(ref browser) => browser.pinch_zoom_level(),
            ServoBrowser::Invalid => 1.0,
        }
    }
}

cef_class_impl! {
    ServoCefBrowser : CefBrowser, cef_browser_t {
        fn get_host(&this,) -> *mut cef_browser_host_t {{
            this.downcast().host.clone()
        }}

        fn go_back(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
        }}

        fn go_forward(&this,) -> () {{
            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
        }}

        // Returns the main (top-level) frame for the browser window.
        fn get_main_frame(&this,) -> *mut cef_frame_t {{
            this.downcast().frame.clone()
        }}

        fn get_identifier(&this,) -> c_int {{
            this.downcast().id as c_int
        }}
    }
}

pub struct ServoCefBrowser {
    /// A reference to the browser's primary frame.
    pub frame: CefFrame,
    /// A reference to the browser's host.
    pub host: CefBrowserHost,
    /// A reference to the browser client.
    pub client: CefClient,
    /// Whether the on-created callback has fired yet.
    pub callback_executed: Cell<bool>,
    /// the display system window handle: only to be used with host.get_window_handle()
    window_handle: cef_window_handle_t,

    id: isize,
    servo_browser: RefCell<ServoBrowser>,
    message_queue: RefCell<Vec<WindowEvent>>,
}

impl ServoCefBrowser {
    pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
        let frame = ServoCefFrame::new().as_cef_interface();
        let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();
        let mut window_handle: cef_window_handle_t = get_null_window_handle();

        let servo_browser = if window_info.windowless_rendering_enabled == 0 {
            let glutin_window = glutin_app::create_window();
            let servo_browser = Browser::new(Some(glutin_window.clone()));
            window_handle = glutin_window.platform_window() as cef_window_handle_t;
            ServoBrowser::OnScreen(servo_browser)
        } else {
            ServoBrowser::Invalid
        };

        let id = ID_COUNTER.with(|counter| {
            counter.fetch_add(1, Ordering::SeqCst)
        });

        ServoCefBrowser {
            frame: frame,
            host: host,
            client: client,
            callback_executed: Cell::new(false),
            servo_browser: RefCell::new(servo_browser),
            message_queue: RefCell::new(vec!()),
            id: id,
            window_handle: window_handle,
        }
    }
}

pub trait ServoCefBrowserExtensions {
    fn init(&self, window_info: &cef_window_info_t);
    fn send_window_event(&self, event: WindowEvent);
    fn get_title_for_main_frame(&self);
    fn pinch_zoom_level(&self) -> f32;
}

impl ServoCefBrowserExtensions for CefBrowser {
    fn init(&self, window_info: &cef_window_info_t) {
        if window_info.windowless_rendering_enabled != 0 {
            let window = window::Window::new();
            let servo_browser = Browser::new(Some(window.clone()));
            window.set_browser(self.clone());
            *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
        }

        self.downcast().host.set_browser((*self).clone());
        self.downcast().frame.set_browser((*self).clone());
        if window_info.windowless_rendering_enabled == 0 {
            self.downcast().host.initialize_compositing();
        }
    }

    fn send_window_event(&self, event: WindowEvent) {
        self.downcast().message_queue.borrow_mut().push(event);

        loop {
            match self.downcast().servo_browser.borrow_state() {
                BorrowState::Unused => {
                    let event = match self.downcast().message_queue.borrow_mut().pop() {
                        None => return,
                        Some(event) => event,
                    };
                    self.downcast().servo_browser.borrow_mut().handle_event(event);
                }
                _ => {
                    // We're trying to send an event while processing another one. This will
                    // cause general badness, so queue up that event instead of immediately
                    // processing it.
                    break
                }
            }
        }
    }

    fn get_title_for_main_frame(&self) {
        self.downcast().servo_browser.borrow().get_title_for_main_frame()
    }

    fn pinch_zoom_level(&self) -> f32 {
        self.downcast().servo_browser.borrow().pinch_zoom_level()
    }
}

#[cfg(target_os="macos")]
pub fn get_null_window_handle() -> cef_window_handle_t {
    ptr::null_mut()
}
#[cfg(target_os="linux")]
pub fn get_null_window_handle() -> cef_window_handle_t {
    0
}

pub fn update() {
    BROWSERS.with(|browsers| {
        for browser in browsers.borrow().iter() {
            if browser.downcast().callback_executed.get() == false {
                browser_callback_after_created(browser.clone());
            }
            browser.send_window_event(WindowEvent::Idle);
        }
    });
}

pub fn close(browser: CefBrowser) {
    BROWSERS.with(|browsers| {
        let mut browsers = browsers.borrow_mut();
        browsers.iter()
                .position(|&ref n| n.downcast().id == browser.downcast().id)
                .map(|e| browsers.remove(e));
    });
}

pub fn get_window(browser: &CefBrowser) -> cef_window_handle_t {
    browser.downcast().window_handle
}

pub fn browser_callback_after_created(browser: CefBrowser) {
    if browser.downcast().client.is_null_cef_object() {
        return
    }
    let client = browser.downcast().client.clone();
    let life_span_handler = client.get_life_span_handler();
    if life_span_handler.is_not_null_cef_object() {
        life_span_handler.on_after_created(browser.clone());
    }
    browser.downcast().callback_executed.set(true);
}

fn browser_host_create(window_info: &cef_window_info_t,
                       client: CefClient,
                       url: *const cef_string_t,
                       callback_executed: bool)
                       -> CefBrowser {
    let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
    browser.init(window_info);
    if callback_executed {
        browser_callback_after_created(browser.clone());
    }
    if url != ptr::null() {
       unsafe { browser.downcast().frame.load_url(CefWrap::to_rust(url)); }
    }
    BROWSERS.with(|browsers| {
        browsers.borrow_mut().push(browser.clone());
    });
    browser
}

cef_static_method_impls! {
    fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
                                       client: *mut cef_client_t,
                                       url: *const cef_string_t,
                                       _browser_settings: *const cef_browser_settings_t,
                                       _request_context: *mut cef_request_context_t)
                                       -> c_int {{
        let client: CefClient = client;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, url, false);
        1i32
    }}
    fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t,
                                            client: *mut cef_client_t,
                                            url: *const cef_string_t,
                                            _browser_settings: *const cef_browser_settings_t,
                                            _request_context: *mut cef_request_context_t)
                                            -> *mut cef_browser_t {{
        let client: CefClient = client;
        let _browser_settings: &cef_browser_settings_t = _browser_settings;
        let _request_context: CefRequestContext = _request_context;
        browser_host_create(window_info, client, url, true)
    }}
}
<|endoftext|>


<commit_before>use crate::context::LintContext;
use crate::rustc_middle::ty::TypeFoldable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `noop_method_call` lint detects specific calls to noop methods
    /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// struct Foo;
    /// let foo = &Foo;
    /// let clone: &Foo = foo.clone();
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// Some method calls are noops meaning that they do nothing. Usually such methods
    /// are the result of blanket implementations that happen to create some method invocations
    /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
    /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
    /// as references are copy. This lint detects these calls and warns the user about them.
    pub NOOP_METHOD_CALL,
    Warn,
    "detects the use of well-known noop methods"
}

declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);

impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // We only care about method calls
        if let ExprKind::MethodCall(call, _, elements, _) = expr.kind {
            // Get the `DefId` only when dealing with an `AssocFn`
            if let Some((DefKind::AssocFn, did)) =
                cx.typeck_results().type_dependent_def(expr.hir_id)
            {
                // Check that we're dealing with a trait method
                if let Some(trait_id) = cx.tcx.trait_of_item(did) {
                    // Check we're dealing with one of the traits we care about
                    if ![sym::Clone, sym::Deref, sym::Borrow]
                        .iter()
                        .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id))
                    {
                        return;
                    }

                    let substs = cx.typeck_results().node_substs(expr.hir_id);
                    // We can't resolve on types that require monomorphization,
                    // so check that we don't need to perfom substitution
                    if !substs.needs_subst() {
                        let param_env = cx.tcx.param_env(trait_id);
                        // Resolve the trait method instance
                        if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) {
                            // Check that it implements the noop diagnostic
                            for (s, peel_ref) in [
                                (sym::noop_method_borrow, true),
                                (sym::noop_method_clone, false),
                                (sym::noop_method_deref, true),
                            ]
                            .iter()
                            {
                                if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
                                    let method = &call.ident.name;
                                    let receiver = &elements[0];
                                    let receiver_ty = cx.typeck_results().expr_ty(receiver);
                                    let receiver_ty = match receiver_ty.kind() {
                                        // Remove one borrow from the receiver as all the trait methods
                                        // we care about here have a `&self` receiver.
                                        ty::Ref(_, ty, _) if *peel_ref => ty,
                                        _ => receiver_ty,
                                    };
                                    let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
                                    if receiver_ty != expr_ty {
                                        return;
                                    }
                                    let expr_span = expr.span;
                                    let note = format!(
                                        "the type `{:?}` which `{}` is being called on is the same as \
                                        the type returned from `{}`, so the method call does not do \
                                        anything and can be removed",
                                        receiver_ty, method, method,
                                    );

                                    let span = expr_span.with_lo(receiver.span.hi());
                                    cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
                                        let method = &call.ident.name;
                                        let message = format!("call to `.{}()` on a reference in this situation does nothing", &method);
                                        lint.build(&message)
                                            .span_label(span, "unnecessary method call")
                                            .note(&note)
                                            .emit()
                                     });
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Clean up code rightward drift<commit_after>use crate::context::LintContext;
use crate::rustc_middle::ty::TypeFoldable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `noop_method_call` lint detects specific calls to noop methods
    /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// struct Foo;
    /// let foo = &Foo;
    /// let clone: &Foo = foo.clone();
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// Some method calls are noops meaning that they do nothing. Usually such methods
    /// are the result of blanket implementations that happen to create some method invocations
    /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
    /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
    /// as references are copy. This lint detects these calls and warns the user about them.
    pub NOOP_METHOD_CALL,
    Warn,
    "detects the use of well-known noop methods"
}

declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);

impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // We only care about method calls.
        let (call, elements) = match expr.kind {
            ExprKind::MethodCall(call, _, elements, _) => (call, elements),
            _ => return,
        };
        // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
        // traits and ignore any other method call.
        let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
            // Verify we are dealing with a method/associated function.
            Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
                // Check that we're dealing with a trait method for one of the traits we care about.
                Some(trait_id)
                    if [sym::Clone, sym::Deref, sym::Borrow]
                        .iter()
                        .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) =>
                {
                    (trait_id, did)
                }
                _ => return,
            },
            _ => return,
        };
        let substs = cx.typeck_results().node_substs(expr.hir_id);
        if substs.needs_subst() {
            // We can't resolve on types that require monomorphization, so we don't handle them if
            // we need to perfom substitution.
            return;
        }
        let param_env = cx.tcx.param_env(trait_id);
        // Resolve the trait method instance.
        let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
            Ok(Some(i)) => i,
            _ => return,
        };
        // (Re)check that it implements the noop diagnostic.
        for (s, peel_ref) in [
            (sym::noop_method_borrow, true),
            (sym::noop_method_clone, false),
            (sym::noop_method_deref, true),
        ]
        .iter()
        {
            if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
                let method = &call.ident.name;
                let receiver = &elements[0];
                let receiver_ty = cx.typeck_results().expr_ty(receiver);
                let receiver_ty = match receiver_ty.kind() {
                    // Remove one borrow from the receiver if appropriate to positively verify that
                    // the receiver `&self` type and the return type are the same, depending on the
                    // involved trait being checked.
                    ty::Ref(_, ty, _) if *peel_ref => ty,
                    // When it comes to `Clone` we need to check the `receiver_ty` directly.
                    // FIXME: we must come up with a better strategy for this.
                    _ => receiver_ty,
                };
                let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
                if receiver_ty != expr_ty {
                    // This lint will only trigger if the receiver type and resulting expression \
                    // type are the same, implying that the method call is unnecessary.
                    return;
                }
                let expr_span = expr.span;
                let note = format!(
                    "the type `{:?}` which `{}` is being called on is the same as \
                     the type returned from `{}`, so the method call does not do \
                     anything and can be removed",
                    receiver_ty, method, method,
                );

                let span = expr_span.with_lo(receiver.span.hi());
                cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
                    let method = &call.ident.name;
                    let message = format!(
                        "call to `.{}()` on a reference in this situation does nothing",
                        &method,
                    );
                    lint.build(&message)
                        .span_label(span, "unnecessary method call")
                        .note(&note)
                        .emit()
                });
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::bindings::codegen::Bindings::HTMLDetailsElementBinding;
use dom::bindings::codegen::Bindings::HTMLDetailsElementBinding::HTMLDetailsElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use std::cell::Cell;
use task::Task;
use task_source::TaskSource;

#[dom_struct]
pub struct HTMLDetailsElement {
    htmlelement: HTMLElement,
    toggle_counter: Cell<u32>
}

impl HTMLDetailsElement {
    fn new_inherited(local_name: LocalName,
                     prefix: Option<Prefix>,
                     document: &Document) -> HTMLDetailsElement {
        HTMLDetailsElement {
            htmlelement:
                HTMLElement::new_inherited(local_name, prefix, document),
            toggle_counter: Cell::new(0)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLDetailsElement> {
        Node::reflect_node(box HTMLDetailsElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLDetailsElementBinding::Wrap)
    }

    pub fn check_toggle_count(&self, number: u32) -> bool {
        number == self.toggle_counter.get()
    }
}

impl HTMLDetailsElementMethods for HTMLDetailsElement {
    // https://html.spec.whatwg.org/multipage/#dom-details-open
    make_bool_getter!(Open, "open");

    // https://html.spec.whatwg.org/multipage/#dom-details-open
    make_bool_setter!(SetOpen, "open");
}

impl VirtualMethods for HTMLDetailsElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);

        if attr.local_name() == &local_name!("open") {
            let counter = self.toggle_counter.get() + 1;
            self.toggle_counter.set(counter);

            let window = window_from_node(self);
            let task_source = window.dom_manipulation_task_source();
            let details = Trusted::new(self);
            let task = box DetailsNotificationTask {
                element: details,
                toggle_number: counter
            };
            let _ = task_source.queue(task, window.upcast());
        }
    }
}

pub struct DetailsNotificationTask {
    element: Trusted<HTMLDetailsElement>,
    toggle_number: u32
}

impl Task for DetailsNotificationTask {
    fn run(self: Box<Self>) {
        let target = self.element.root();
        if target.check_toggle_count(self.toggle_number) {
            target.upcast::<EventTarget>().fire_event(atom!("toggle"));
        }
    }
}
<commit_msg>Use task! for the details notification steps<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::bindings::codegen::Bindings::HTMLDetailsElementBinding;
use dom::bindings::codegen::Bindings::HTMLDetailsElementBinding::HTMLDetailsElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use std::cell::Cell;
use task_source::TaskSource;

#[dom_struct]
pub struct HTMLDetailsElement {
    htmlelement: HTMLElement,
    toggle_counter: Cell<u32>
}

impl HTMLDetailsElement {
    fn new_inherited(local_name: LocalName,
                     prefix: Option<Prefix>,
                     document: &Document) -> HTMLDetailsElement {
        HTMLDetailsElement {
            htmlelement:
                HTMLElement::new_inherited(local_name, prefix, document),
            toggle_counter: Cell::new(0)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLDetailsElement> {
        Node::reflect_node(box HTMLDetailsElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLDetailsElementBinding::Wrap)
    }
}

impl HTMLDetailsElementMethods for HTMLDetailsElement {
    // https://html.spec.whatwg.org/multipage/#dom-details-open
    make_bool_getter!(Open, "open");

    // https://html.spec.whatwg.org/multipage/#dom-details-open
    make_bool_setter!(SetOpen, "open");
}

impl VirtualMethods for HTMLDetailsElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);

        if attr.local_name() == &local_name!("open") {
            let counter = self.toggle_counter.get() + 1;
            self.toggle_counter.set(counter);

            let window = window_from_node(self);
            let this = Trusted::new(self);
            // FIXME(nox): Why are errors silenced here?
            let _ = window.dom_manipulation_task_source().queue(
                box task!(details_notification_task_steps: move || {
                    let this = this.root();
                    if counter == this.toggle_counter.get() {
                        this.upcast::<EventTarget>().fire_event(atom!("toggle"));
                    }
                }),
                window.upcast(),
            );
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>docs: typo fix<commit_after><|endoftext|>