<commit_before>//! Interface to a mock server provided by a plugin

use std::{env, thread};
use std::fmt::Write;
use std::path::PathBuf;

use anyhow::anyhow;
use itertools::Itertools;
use pact_models::pact::{Pact, write_pact};
use pact_models::PactSpecification;
use pact_plugin_driver::catalogue_manager::CatalogueEntry;
use pact_plugin_driver::mock_server::{MockServerConfig, MockServerDetails};
use pact_plugin_driver::plugin_manager::{shutdown_mock_server, start_mock_server};
use tokio::runtime::Handle;
use tracing::{debug, info};
use url::Url;

use pact_matching::metrics::{MetricEvent, send_metrics, send_metrics_async};
use pact_mock_server::matching::MatchResult;
use pact_mock_server::mock_server::MockServerMetrics;

use crate::mock_server::ValidatingMockServer;
use crate::util::panic_or_print_error;

/// Mock server that has been provided by a plugin
pub struct PluginMockServer {
  /// Details of the running mock server
  pub mock_server_details: MockServerDetails,
  /// Pact that is used to configure the mock server
  pub pact: Box<dyn Pact + Send + Sync>,
  /// Path to write any pact files to
  pub output_path: Option<PathBuf>,
  /// Catalogue entry for the transport
  pub catalogue_entry: CatalogueEntry,
}

impl PluginMockServer {
  /// Start a new plugin mock server. This will send the start mock server request to the plugin
  /// that provides the mock server. A new Tokio reactor will be started.
  pub fn start(
    pact: Box<dyn Pact + Send + Sync>,
    output_path: Option<PathBuf>,
    catalogue_entry: &CatalogueEntry
  ) -> anyhow::Result<Box<dyn ValidatingMockServer>> {
    let runtime = tokio::runtime::Builder::new_current_thread()
      .enable_all()
      .build()?;
    runtime.block_on(async { PluginMockServer::start_async(pact, output_path, catalogue_entry).await })
  }

  /// Start a new plugin mock server (async version). This will send the start mock server request
  /// to the plugin that provides the mock server.
  pub async fn start_async(
    pact: Box<dyn Pact + Send + Sync>,
    output_path: Option<PathBuf>,
    catalogue_entry: &CatalogueEntry
  ) -> anyhow::Result<Box<dyn ValidatingMockServer>> {
    let result = start_mock_server(catalogue_entry, pact.boxed(), MockServerConfig {
      output_path: output_path.clone(),
      host_interface: None,
      port: 0,
      tls: false
    }).await?;
    Ok(Box::new(PluginMockServer {
      mock_server_details: result,
      pact: pact.boxed(),
      output_path: output_path.clone(),
      catalogue_entry: catalogue_entry.clone()
    }))
  }

  /// Helper to shutdown the mock server and get the results
  pub(crate) fn drop_helper(&self) -> anyhow::Result<()> {
    let handle = Handle::try_current()
      .or_else(|_| tokio::runtime::Builder::new_current_thread()
      .enable_all()
      .build()
      .map(|runtime| runtime.handle().clone()))?;

    let interactions = self.pact.interactions().len();
    let mock_server_details = self.mock_server_details.clone();
    let result = thread::spawn(move || {
      handle.block_on(async {
        send_metrics_async(MetricEvent::ConsumerTestRun {
          interactions,
          test_framework: "pact_consumer".to_string(),
          app_name: "pact_consumer".to_string(),
          app_version: env!("CARGO_PKG_VERSION").to_string()
        }).await;
        shutdown_mock_server(&mock_server_details).await
      })
    }).join();
    match result {
      Err(_) => Err(anyhow!("Failed to shutdown the mock server: could not start a new thread")),
      Ok(result) => match result {
        Ok(results) => {
          let results = results.iter()
            .filter(|r| !(r.error.is_empty() && r.mismatches.is_empty()))
            .collect_vec();
          if results.is_empty() {
            self.write_pact()
          } else {
            let mut message = format!("plugin mock server failed verification:\n");
            for (index, result) in results.iter().enumerate() {
              if result.error.is_empty() {
                let _ = writeln!(&mut message, "    {}) {} - the following mismatches occurred:", index + 1, result.path);
                for (mismatch, details) in result.mismatches.iter().enumerate() {
                  let _ = writeln!(&mut message, "        {}.{}) [{}] {}", index + 1, mismatch + 1, details.path, details.mismatch);
                }
              } else {
                let _ = writeln!(&mut message, "    {}) {}: {}", index + 1, result.path, result.error);
              }
            }
            Err(anyhow!(message))
          }
        }
        Err(err) => Err(anyhow!(err))
      }
    }
  }

  fn write_pact(&self) -> anyhow::Result<()> {
    let output_dir = self.output_path.as_ref().map(|dir| dir.to_string_lossy().to_string())
      .unwrap_or_else(|| {
        let val = env::var("PACT_OUTPUT_DIR");
        debug!("env:PACT_OUTPUT_DIR = {:?}", val);
        val.unwrap_or_else(|_| "target/pacts".to_owned())
      });
    let overwrite = env::var("PACT_OVERWRITE")
      .unwrap_or_else(|_| "false".to_owned()) == "true";
    debug!("env:PACT_OVERWRITE = {:?}", overwrite);

    let mut v4_pact = self.pact.as_v4_pact();
    let pact = if let Ok(ref mut pact) = v4_pact {
      for interaction in &mut pact.interactions {
        let catalogue_entry = &self.catalogue_entry;
        interaction.set_transport(Some(catalogue_entry.key.clone()));
      }
      pact.boxed()
    } else {
      self.pact.boxed()
    };

    let pact_file_name = pact.default_file_name();
    let mut filename = PathBuf::from(output_dir);
    filename.push(pact_file_name);

    info!("Writing pact out to '{}'", filename.display());
    write_pact(pact, filename.as_path(), PactSpecification::V4, overwrite)
  }
}

impl ValidatingMockServer for PluginMockServer {
  fn url(&self) -> Url {
    self.mock_server_details.base_url.parse()
      .expect("Failed to parse the URL from the plugin mock server")
  }

  fn path(&self, path: &str) -> Url {
    self.url().join(path).expect("Could not join the path to the base URL")
  }

  // TODO: need a mechanism for plugin mock servers to provide the current status
  fn status(&self) -> Vec<MatchResult> {
    vec![]
  }

  // TODO: need a mechanism for plugin mock servers to provide metrics
  fn metrics(&self) -> MockServerMetrics {
    MockServerMetrics::default()
  }
}

impl Drop for PluginMockServer {
  fn drop(&mut self) {
    let result = self.drop_helper();
    if let Err(msg) = result {
      panic_or_print_error(msg.to_string().as_str());
    }
  }
}
<commit_msg>chore: cleanup some compiler warnings<commit_after>//! Interface to a mock server provided by a plugin

use std::{env, thread};
use std::fmt::Write;
use std::path::PathBuf;

use anyhow::anyhow;
use itertools::Itertools;
use pact_models::pact::{Pact, write_pact};
use pact_models::PactSpecification;
use pact_plugin_driver::catalogue_manager::CatalogueEntry;
use pact_plugin_driver::mock_server::{MockServerConfig, MockServerDetails};
use pact_plugin_driver::plugin_manager::{shutdown_mock_server, start_mock_server};
use tokio::runtime::Handle;
use tracing::{debug, info};
use url::Url;

use pact_matching::metrics::{MetricEvent, send_metrics_async};
use pact_mock_server::matching::MatchResult;
use pact_mock_server::mock_server::MockServerMetrics;

use crate::mock_server::ValidatingMockServer;
use crate::util::panic_or_print_error;

/// Mock server that has been provided by a plugin
pub struct PluginMockServer {
  /// Details of the running mock server
  pub mock_server_details: MockServerDetails,
  /// Pact that is used to configure the mock server
  pub pact: Box<dyn Pact + Send + Sync>,
  /// Path to write any pact files to
  pub output_path: Option<PathBuf>,
  /// Catalogue entry for the transport
  pub catalogue_entry: CatalogueEntry,
}

impl PluginMockServer {
  /// Start a new plugin mock server. This will send the start mock server request to the plugin
  /// that provides the mock server. A new Tokio reactor will be started.
  pub fn start(
    pact: Box<dyn Pact + Send + Sync>,
    output_path: Option<PathBuf>,
    catalogue_entry: &CatalogueEntry
  ) -> anyhow::Result<Box<dyn ValidatingMockServer>> {
    let runtime = tokio::runtime::Builder::new_current_thread()
      .enable_all()
      .build()?;
    runtime.block_on(async { PluginMockServer::start_async(pact, output_path, catalogue_entry).await })
  }

  /// Start a new plugin mock server (async version). This will send the start mock server request
  /// to the plugin that provides the mock server.
  pub async fn start_async(
    pact: Box<dyn Pact + Send + Sync>,
    output_path: Option<PathBuf>,
    catalogue_entry: &CatalogueEntry
  ) -> anyhow::Result<Box<dyn ValidatingMockServer>> {
    let result = start_mock_server(catalogue_entry, pact.boxed(), MockServerConfig {
      output_path: output_path.clone(),
      host_interface: None,
      port: 0,
      tls: false
    }).await?;
    Ok(Box::new(PluginMockServer {
      mock_server_details: result,
      pact: pact.boxed(),
      output_path: output_path.clone(),
      catalogue_entry: catalogue_entry.clone()
    }))
  }

  /// Helper to shutdown the mock server and get the results
  pub(crate) fn drop_helper(&self) -> anyhow::Result<()> {
    let handle = Handle::try_current()
      .or_else(|_| tokio::runtime::Builder::new_current_thread()
      .enable_all()
      .build()
      .map(|runtime| runtime.handle().clone()))?;

    let interactions = self.pact.interactions().len();
    let mock_server_details = self.mock_server_details.clone();
    let result = thread::spawn(move || {
      handle.block_on(async {
        send_metrics_async(MetricEvent::ConsumerTestRun {
          interactions,
          test_framework: "pact_consumer".to_string(),
          app_name: "pact_consumer".to_string(),
          app_version: env!("CARGO_PKG_VERSION").to_string()
        }).await;
        shutdown_mock_server(&mock_server_details).await
      })
    }).join();
    match result {
      Err(_) => Err(anyhow!("Failed to shutdown the mock server: could not start a new thread")),
      Ok(result) => match result {
        Ok(results) => {
          let results = results.iter()
            .filter(|r| !(r.error.is_empty() && r.mismatches.is_empty()))
            .collect_vec();
          if results.is_empty() {
            self.write_pact()
          } else {
            let mut message = format!("plugin mock server failed verification:\n");
            for (index, result) in results.iter().enumerate() {
              if result.error.is_empty() {
                let _ = writeln!(&mut message, "    {}) {} - the following mismatches occurred:", index + 1, result.path);
                for (mismatch, details) in result.mismatches.iter().enumerate() {
                  let _ = writeln!(&mut message, "        {}.{}) [{}] {}", index + 1, mismatch + 1, details.path, details.mismatch);
                }
              } else {
                let _ = writeln!(&mut message, "    {}) {}: {}", index + 1, result.path, result.error);
              }
            }
            Err(anyhow!(message))
          }
        }
        Err(err) => Err(anyhow!(err))
      }
    }
  }

  fn write_pact(&self) -> anyhow::Result<()> {
    let output_dir = self.output_path.as_ref().map(|dir| dir.to_string_lossy().to_string())
      .unwrap_or_else(|| {
        let val = env::var("PACT_OUTPUT_DIR");
        debug!("env:PACT_OUTPUT_DIR = {:?}", val);
        val.unwrap_or_else(|_| "target/pacts".to_owned())
      });
    let overwrite = env::var("PACT_OVERWRITE")
      .unwrap_or_else(|_| "false".to_owned()) == "true";
    debug!("env:PACT_OVERWRITE = {:?}", overwrite);

    let mut v4_pact = self.pact.as_v4_pact();
    let pact = if let Ok(ref mut pact) = v4_pact {
      for interaction in &mut pact.interactions {
        let catalogue_entry = &self.catalogue_entry;
        interaction.set_transport(Some(catalogue_entry.key.clone()));
      }
      pact.boxed()
    } else {
      self.pact.boxed()
    };

    let pact_file_name = pact.default_file_name();
    let mut filename = PathBuf::from(output_dir);
    filename.push(pact_file_name);

    info!("Writing pact out to '{}'", filename.display());
    write_pact(pact, filename.as_path(), PactSpecification::V4, overwrite)
  }
}

impl ValidatingMockServer for PluginMockServer {
  fn url(&self) -> Url {
    self.mock_server_details.base_url.parse()
      .expect("Failed to parse the URL from the plugin mock server")
  }

  fn path(&self, path: &str) -> Url {
    self.url().join(path).expect("Could not join the path to the base URL")
  }

  // TODO: need a mechanism for plugin mock servers to provide the current status
  fn status(&self) -> Vec<MatchResult> {
    vec![]
  }

  // TODO: need a mechanism for plugin mock servers to provide metrics
  fn metrics(&self) -> MockServerMetrics {
    MockServerMetrics::default()
  }
}

impl Drop for PluginMockServer {
  fn drop(&mut self) {
    let result = self.drop_helper();
    if let Err(msg) = result {
      panic_or_print_error(msg.to_string().as_str());
    }
  }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io::Read;
use std::io::Write;
use std::process;

use tempfile;

use protobuf::descriptor;
use protobuf::descriptor::FileDescriptorSet;
use protobuf::reflect::MessageDescriptor;
use protobuf::rustproto;
use protobuf::text_format::merge_from_str;
use protobuf::text_format::print_to_string;
use protobuf::Message;

fn parse_using_rust_protobuf(text: &str, message_descriptor: &MessageDescriptor) -> Box<dyn Message> {
    let mut message = message_descriptor.new_instance();

    merge_from_str(&mut *message, text).expect(&format!("merge_from_str: {:?}", text));

    message
}

fn parse_using_protoc(text: &str, message_descriptor: &MessageDescriptor) -> Box<dyn Message> {
    let temp_dir = tempfile::Builder::new()
        .prefix(message_descriptor.name())
        .tempdir()
        .expect("temp dir");

    let mut fds = FileDescriptorSet::new();
    fds.file = vec![
        descriptor::file_descriptor_proto().clone(),
        rustproto::file_descriptor_proto().clone(),
        message_descriptor.file_descriptor_proto().clone(),
    ].into();

    let mut temp_file = temp_dir.path().to_owned();
    temp_file.push("fds");

    fs::write(&temp_file, fds.write_to_bytes().expect("seriailze")).expect("write");

    // TODO: use protoc crate
    let mut protoc = process::Command::new("protoc")
        .args(&[
            &format!(
                "--descriptor_set_in={}",
                temp_file.to_str().expect("to_str")
            ),
            &format!("--encode={}", message_descriptor.full_name()),
            message_descriptor.file_descriptor_proto().get_name(),
        ]).stdin(process::Stdio::piped())
        .stdout(process::Stdio::piped())
        .stderr(process::Stdio::inherit())
        .spawn()
        .expect("protoc");

    let mut stdin = protoc.stdin.take().expect("stdin");
    stdin.write_all(text.as_bytes()).expect("write to stdin");
    drop(stdin);

    let mut encoded = Vec::new();
    protoc
        .stdout
        .take()
        .expect("stdout")
        .read_to_end(&mut encoded)
        .expect("read_to_end");

    let exit_status = protoc.wait().expect("wait");
    assert!(
        exit_status.success(),
        "exit status: {:?} when parsing with protoc: {:?}",
        exit_status,
        text
    );

    let mut expected = message_descriptor.new_instance();
    expected
        .merge_from_bytes(&encoded)
        .expect("merge_from_bytes");

    expected
}

fn print_using_protoc(message: &dyn Message) -> String {
    let message_descriptor = message.descriptor();

    // TODO: copy-paste of parse_using_protoc

    let temp_dir = tempfile::Builder::new()
        .prefix(message_descriptor.name())
        .tempdir()
        .expect("temp dir");

    let mut fds = FileDescriptorSet::new();
    fds.file = vec![
        descriptor::file_descriptor_proto().clone(),
        rustproto::file_descriptor_proto().clone(),
        message_descriptor.file_descriptor_proto().clone(),
    ].into();

    let mut temp_file = temp_dir.path().to_owned();
    temp_file.push("fds");

    fs::write(&temp_file, fds.write_to_bytes().expect("seriailze")).expect("write");

    // TODO: use protoc crate
    let mut protoc = process::Command::new("protoc")
        .args(&[
            &format!(
                "--descriptor_set_in={}",
                temp_file.to_str().expect("to_str")
            ),
            &format!("--decode={}", message_descriptor.full_name()),
            message_descriptor.file_descriptor_proto().get_name(),
        ]).stdin(process::Stdio::piped())
        .stdout(process::Stdio::piped())
        .stderr(process::Stdio::inherit())
        .spawn()
        .expect("protoc");

    let mut stdin = protoc.stdin.take().expect("stdin");
    stdin
        .write_all(&message.write_to_bytes().expect("serialize"))
        .expect("write to stdin");
    drop(stdin);

    let mut decoded = String::new();
    protoc
        .stdout
        .take()
        .expect("stdout")
        .read_to_string(&mut decoded)
        .expect("read_to_end");

    let exit_status = protoc.wait().expect("wait");
    assert!(
        exit_status.success(),
        "protoc exit status: {:?} while printing: {:?}",
        exit_status,
        message
    );

    decoded
}

pub fn test_text_format_str_descriptor(text: &str, message_descriptor: &MessageDescriptor) {
    let message = parse_using_rust_protobuf(text, message_descriptor);
    let expected = parse_using_protoc(text, message_descriptor);

    assert!(
        message_descriptor.eq(&*expected, &*message),
        "{:?} != {:?}",
        expected,
        message
    );

    // print using protoc and parse using rust-protobuf
    let printed_using_protoc = print_using_protoc(&*message);
    let pp = parse_using_rust_protobuf(&printed_using_protoc, message_descriptor);

    assert!(
        message_descriptor.eq(&*expected, &*pp),
        "{:?} != {:?}",
        expected,
        message
    );
}

pub fn test_text_format_str_message(expected: &str, message: &dyn Message) {
    assert_eq!(expected, &*print_to_string(message));

    test_text_format_str_descriptor(expected, message.descriptor());
}

pub fn test_text_format_message(message: &dyn Message) {
    let descriptor = message.descriptor();

    let printed_with_rust_protobuf = print_to_string(message);
    let printed_with_protoc = print_using_protoc(message);

    let from_protoc = parse_using_rust_protobuf(&printed_with_protoc, descriptor);
    let from_protobuf = parse_using_protoc(&printed_with_rust_protobuf, descriptor);

    assert!(
        message.reflect_eq(&*from_protoc),
        "{:?} != {:?}",
        message,
        from_protoc
    );
    assert!(
        message.reflect_eq(&*from_protobuf),
        "{:?} != {:?}",
        message,
        from_protobuf
    );
}
<commit_msg>Return a Result from parse_using_rust_protobuf (test util) instead of panicking<commit_after>use std::fs;
use std::io::Read;
use std::io::Write;
use std::process;
use std::error::Error;

use tempfile;

use protobuf::descriptor;
use protobuf::descriptor::FileDescriptorSet;
use protobuf::reflect::MessageDescriptor;
use protobuf::rustproto;
use protobuf::text_format::merge_from_str;
use protobuf::text_format::print_to_string;
use protobuf::Message;

pub fn parse_using_rust_protobuf(
    text: &str,
    message_descriptor: &MessageDescriptor
) -> Result<Box<dyn Message>, Box<dyn Error>>  {
    let mut message = message_descriptor.new_instance();

    merge_from_str(&mut *message, text)?;

    Ok(message)
}

fn parse_using_protoc(text: &str, message_descriptor: &MessageDescriptor) -> Box<dyn Message> {
    let temp_dir = tempfile::Builder::new()
        .prefix(message_descriptor.name())
        .tempdir()
        .expect("temp dir");

    let mut fds = FileDescriptorSet::new();
    fds.file = vec![
        descriptor::file_descriptor_proto().clone(),
        rustproto::file_descriptor_proto().clone(),
        message_descriptor.file_descriptor_proto().clone(),
    ].into();

    let mut temp_file = temp_dir.path().to_owned();
    temp_file.push("fds");

    fs::write(&temp_file, fds.write_to_bytes().expect("seriailze")).expect("write");

    // TODO: use protoc crate
    let mut protoc = process::Command::new("protoc")
        .args(&[
            &format!(
                "--descriptor_set_in={}",
                temp_file.to_str().expect("to_str")
            ),
            &format!("--encode={}", message_descriptor.full_name()),
            message_descriptor.file_descriptor_proto().get_name(),
        ]).stdin(process::Stdio::piped())
        .stdout(process::Stdio::piped())
        .stderr(process::Stdio::inherit())
        .spawn()
        .expect("protoc");

    let mut stdin = protoc.stdin.take().expect("stdin");
    stdin.write_all(text.as_bytes()).expect("write to stdin");
    drop(stdin);

    let mut encoded = Vec::new();
    protoc
        .stdout
        .take()
        .expect("stdout")
        .read_to_end(&mut encoded)
        .expect("read_to_end");

    let exit_status = protoc.wait().expect("wait");
    assert!(
        exit_status.success(),
        "exit status: {:?} when parsing with protoc: {:?}",
        exit_status,
        text
    );

    let mut expected = message_descriptor.new_instance();
    expected
        .merge_from_bytes(&encoded)
        .expect("merge_from_bytes");

    expected
}

fn print_using_protoc(message: &dyn Message) -> String {
    let message_descriptor = message.descriptor();

    // TODO: copy-paste of parse_using_protoc

    let temp_dir = tempfile::Builder::new()
        .prefix(message_descriptor.name())
        .tempdir()
        .expect("temp dir");

    let mut fds = FileDescriptorSet::new();
    fds.file = vec![
        descriptor::file_descriptor_proto().clone(),
        rustproto::file_descriptor_proto().clone(),
        message_descriptor.file_descriptor_proto().clone(),
    ].into();

    let mut temp_file = temp_dir.path().to_owned();
    temp_file.push("fds");

    fs::write(&temp_file, fds.write_to_bytes().expect("seriailze")).expect("write");

    // TODO: use protoc crate
    let mut protoc = process::Command::new("protoc")
        .args(&[
            &format!(
                "--descriptor_set_in={}",
                temp_file.to_str().expect("to_str")
            ),
            &format!("--decode={}", message_descriptor.full_name()),
            message_descriptor.file_descriptor_proto().get_name(),
        ]).stdin(process::Stdio::piped())
        .stdout(process::Stdio::piped())
        .stderr(process::Stdio::inherit())
        .spawn()
        .expect("protoc");

    let mut stdin = protoc.stdin.take().expect("stdin");
    stdin
        .write_all(&message.write_to_bytes().expect("serialize"))
        .expect("write to stdin");
    drop(stdin);

    let mut decoded = String::new();
    protoc
        .stdout
        .take()
        .expect("stdout")
        .read_to_string(&mut decoded)
        .expect("read_to_end");

    let exit_status = protoc.wait().expect("wait");
    assert!(
        exit_status.success(),
        "protoc exit status: {:?} while printing: {:?}",
        exit_status,
        message
    );

    decoded
}

pub fn test_text_format_str_descriptor(text: &str, message_descriptor: &MessageDescriptor) {
    let message = parse_using_rust_protobuf(text, message_descriptor)
        .expect(format!("parse_using_rust_protobuf: {:?}", &text).as_str());
    let expected = parse_using_protoc(text, message_descriptor);

    assert!(
        message_descriptor.eq(&*expected, &*message),
        "{:?} != {:?}",
        expected,
        message
    );

    // print using protoc and parse using rust-protobuf
    let printed_using_protoc = print_using_protoc(&*message);
    let pp = parse_using_rust_protobuf(&printed_using_protoc, message_descriptor)
        .expect(format!("parse_using_rust_protobuf: {:?}", &printed_using_protoc).as_str());

    assert!(
        message_descriptor.eq(&*expected, &*pp),
        "{:?} != {:?}",
        expected,
        message
    );
}

pub fn test_text_format_str_message(expected: &str, message: &dyn Message) {
    assert_eq!(expected, &*print_to_string(message));

    test_text_format_str_descriptor(expected, message.descriptor());
}

pub fn test_text_format_message(message: &dyn Message) {
    let descriptor = message.descriptor();

    let printed_with_rust_protobuf = print_to_string(message);
    let printed_with_protoc = print_using_protoc(message);

    let from_protoc = parse_using_rust_protobuf(&printed_with_protoc, descriptor)
        .expect(format!("parse_using_rust_protobuf: {:?}", &printed_with_protoc).as_str());
    let from_protobuf = parse_using_protoc(&printed_with_rust_protobuf, descriptor);

    assert!(
        message.reflect_eq(&*from_protoc),
        "{:?} != {:?}",
        message,
        from_protoc
    );
    assert!(
        message.reflect_eq(&*from_protobuf),
        "{:?} != {:?}",
        message,
        from_protobuf
    );
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
    match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq,
};
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for the creation of uninitialized `Vec<T>` by calling `set_len()`
    /// immediately after `with_capacity()` or `reserve()`.
    ///
    /// ### Why is this bad?
    /// It creates `Vec<T>` that contains uninitialized data, which leads to an
    /// undefined behavior with most safe operations.
    /// Notably, using uninitialized `Vec<u8>` with generic `Read` is unsound.
    ///
    /// ### Example
    /// ```rust,ignore
    /// let mut vec: Vec<u8> = Vec::with_capacity(1000);
    /// unsafe { vec.set_len(1000); }
    /// reader.read(&mut vec); // undefined behavior!
    /// ```
    /// Use an initialized buffer:
    /// ```rust,ignore
    /// let mut vec: Vec<u8> = vec![0; 1000];
    /// reader.read(&mut vec);
    /// ```
    /// Or, wrap the content in `MaybeUninit`:
    /// ```rust,ignore
    /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
    /// unsafe { vec.set_len(1000); }
    /// ```
    pub UNINIT_VEC,
    correctness,
    "Vec with uninitialized data"
}

declare_lint_pass!(UninitVec => [UNINIT_VEC]);

impl<'tcx> LateLintPass<'tcx> for UninitVec {
    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
        for w in block.stmts.windows(2) {
            if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind {
                handle_uninit_vec_pair(cx, &w[0], expr);
            }
        }

        if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) {
            handle_uninit_vec_pair(cx, stmt, expr);
        }
    }
}

fn handle_uninit_vec_pair(
    cx: &LateContext<'tcx>,
    maybe_with_capacity_or_reserve: &'tcx Stmt<'tcx>,
    maybe_set_len: &'tcx Expr<'tcx>,
) {
    if_chain! {
        if let Some(vec) = extract_with_capacity_or_reserve_target(cx, maybe_with_capacity_or_reserve);
        if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len);
        if vec.eq_expr(cx, set_len_self);
        if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind();
        if let ty::Adt(_, substs) = vec_ty.kind();
        // Check T of Vec<T>
        if !is_uninit_value_valid_for_ty(cx, substs.type_at(0));
        then {
            span_lint_and_then(
                cx,
                UNINIT_VEC,
                vec![call_span, maybe_with_capacity_or_reserve.span],
                "calling `set_len()` immediately after reserving a buffer creates uninitialized values",
                |diag| {
                    diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
                },
            );
        }
    }
}

#[derive(Clone, Copy, Debug)]
enum LocalOrExpr<'tcx> {
    Local(HirId),
    Expr(&'tcx Expr<'tcx>),
}

impl<'tcx> LocalOrExpr<'tcx> {
    fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
        match self {
            LocalOrExpr::Local(hir_id) => path_to_local_id(expr, hir_id),
            LocalOrExpr::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
        }
    }
}

/// Returns the target vec of `Vec::with_capacity()` or `Vec::reserve()`
fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stmt<'_>) -> Option<LocalOrExpr<'tcx>> {
    match stmt.kind {
        StmtKind::Local(local) => {
            // let mut x = Vec::with_capacity()
            if_chain! {
                if let Some(init_expr) = local.init;
                if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind;
                if is_with_capacity(cx, init_expr);
                then {
                    Some(LocalOrExpr::Local(hir_id))
                } else {
                    None
                }
            }
        },
        StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
            match expr.kind {
                ExprKind::Assign(lhs, rhs, _span) if is_with_capacity(cx, rhs) => {
                    // self.vec = Vec::with_capacity()
                    Some(LocalOrExpr::Expr(lhs))
                },
                ExprKind::MethodCall(path, _, [self_expr, _], _) => {
                    // self.vec.reserve()
                    if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::vec_type)
                        && path.ident.name.as_str() == "reserve"
                    {
                        Some(LocalOrExpr::Expr(self_expr))
                    } else {
                        None
                    }
                },
                _ => None,
            }
        },
        StmtKind::Item(_) => None,
    }
}

fn is_with_capacity(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool {
    if_chain! {
        if let ExprKind::Call(path_expr, _) = &expr.kind;
        if let ExprKind::Path(qpath) = &path_expr.kind;
        if let Res::Def(_, def_id) = cx.qpath_res(qpath, path_expr.hir_id);
        then {
            match_def_path(cx, def_id, &paths::VEC_WITH_CAPACITY)
        } else {
            false
        }
    }
}

/// Returns self if the expression is `Vec::set_len()`
fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> {
    // peel unsafe blocks in `unsafe { vec.set_len() }`
    let expr = peel_hir_expr_while(expr, |e| {
        if let ExprKind::Block(block, _) = e.kind {
            // Extract the first statement/expression
            match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) {
                (None, Some(expr)) => Some(expr),
                (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr),
                _ => None,
            }
        } else {
            None
        }
    });
    match expr.kind {
        ExprKind::MethodCall(_, _, [vec_expr, _], _) => {
            cx.typeck_results().type_dependent_def_id(expr.hir_id).and_then(|id| {
                if match_def_path(cx, id, &paths::VEC_SET_LEN) {
                    Some((vec_expr, expr.span))
                } else {
                    None
                }
            })
        },
        _ => None,
    }
}
<commit_msg>Add #allow attribute to suppress FP #7698<commit_after>use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
    match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq,
};
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for the creation of uninitialized `Vec<T>` by calling `set_len()`
    /// immediately after `with_capacity()` or `reserve()`.
    ///
    /// ### Why is this bad?
    /// It creates `Vec<T>` that contains uninitialized data, which leads to an
    /// undefined behavior with most safe operations.
    /// Notably, using uninitialized `Vec<u8>` with generic `Read` is unsound.
    ///
    /// ### Example
    /// ```rust,ignore
    /// let mut vec: Vec<u8> = Vec::with_capacity(1000);
    /// unsafe { vec.set_len(1000); }
    /// reader.read(&mut vec); // undefined behavior!
    /// ```
    /// Use an initialized buffer:
    /// ```rust,ignore
    /// let mut vec: Vec<u8> = vec![0; 1000];
    /// reader.read(&mut vec);
    /// ```
    /// Or, wrap the content in `MaybeUninit`:
    /// ```rust,ignore
    /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
    /// unsafe { vec.set_len(1000); }
    /// ```
    pub UNINIT_VEC,
    correctness,
    "Vec with uninitialized data"
}

declare_lint_pass!(UninitVec => [UNINIT_VEC]);

impl<'tcx> LateLintPass<'tcx> for UninitVec {
    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
        for w in block.stmts.windows(2) {
            if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind {
                handle_uninit_vec_pair(cx, &w[0], expr);
            }
        }

        if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) {
            handle_uninit_vec_pair(cx, stmt, expr);
        }
    }
}

fn handle_uninit_vec_pair(
    cx: &LateContext<'tcx>,
    maybe_with_capacity_or_reserve: &'tcx Stmt<'tcx>,
    maybe_set_len: &'tcx Expr<'tcx>,
) {
    if_chain! {
        if let Some(vec) = extract_with_capacity_or_reserve_target(cx, maybe_with_capacity_or_reserve);
        if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len);
        if vec.eq_expr(cx, set_len_self);
        if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind();
        if let ty::Adt(_, substs) = vec_ty.kind();
        // Check T of Vec<T>
        if !is_uninit_value_valid_for_ty(cx, substs.type_at(0));
        then {
            // FIXME: false positive #7698
            #[allow(clippy::collapsible_span_lint_calls)]
            span_lint_and_then(
                cx,
                UNINIT_VEC,
                vec![call_span, maybe_with_capacity_or_reserve.span],
                "calling `set_len()` immediately after reserving a buffer creates uninitialized values",
                |diag| {
                    diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
                },
            );
        }
    }
}

#[derive(Clone, Copy, Debug)]
enum LocalOrExpr<'tcx> {
    Local(HirId),
    Expr(&'tcx Expr<'tcx>),
}

impl<'tcx> LocalOrExpr<'tcx> {
    fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
        match self {
            LocalOrExpr::Local(hir_id) => path_to_local_id(expr, hir_id),
            LocalOrExpr::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
        }
    }
}

/// Returns the target vec of `Vec::with_capacity()` or `Vec::reserve()`
fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stmt<'_>) -> Option<LocalOrExpr<'tcx>> {
    match stmt.kind {
        StmtKind::Local(local) => {
            // let mut x = Vec::with_capacity()
            if_chain! {
                if let Some(init_expr) = local.init;
                if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind;
                if is_with_capacity(cx, init_expr);
                then {
                    Some(LocalOrExpr::Local(hir_id))
                } else {
                    None
                }
            }
        },
        StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
            match expr.kind {
                ExprKind::Assign(lhs, rhs, _span) if is_with_capacity(cx, rhs) => {
                    // self.vec = Vec::with_capacity()
                    Some(LocalOrExpr::Expr(lhs))
                },
                ExprKind::MethodCall(path, _, [self_expr, _], _) => {
                    // self.vec.reserve()
                    if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::vec_type)
                        && path.ident.name.as_str() == "reserve"
                    {
                        Some(LocalOrExpr::Expr(self_expr))
                    } else {
                        None
                    }
                },
                _ => None,
            }
        },
        StmtKind::Item(_) => None,
    }
}

fn is_with_capacity(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool {
    if_chain! {
        if let ExprKind::Call(path_expr, _) = &expr.kind;
        if let ExprKind::Path(qpath) = &path_expr.kind;
        if let Res::Def(_, def_id) = cx.qpath_res(qpath, path_expr.hir_id);
        then {
            match_def_path(cx, def_id, &paths::VEC_WITH_CAPACITY)
        } else {
            false
        }
    }
}

/// Returns self if the expression is `Vec::set_len()`
fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> {
    // peel unsafe blocks in `unsafe { vec.set_len() }`
    let expr = peel_hir_expr_while(expr, |e| {
        if let ExprKind::Block(block, _) = e.kind {
            // Extract the first statement/expression
            match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) {
                (None, Some(expr)) => Some(expr),
                (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr),
                _ => None,
            }
        } else {
            None
        }
    });
    match expr.kind {
        ExprKind::MethodCall(_, _, [vec_expr, _], _) => {
            cx.typeck_results().type_dependent_def_id(expr.hir_id).and_then(|id| {
                if match_def_path(cx, id, &paths::VEC_SET_LEN) {
                    Some((vec_expr, expr.span))
                } else {
                    None
                }
            })
        },
        _ => None,
    }
}
<|endoftext|>


<commit_before>use super::*;

fn build_app() -> App<'static> {
    build_app_with_name("myapp")
}

fn build_app_with_name(s: &'static str) -> App<'static> {
    App::new(s)
        .about("Tests completions")
        .arg(
            Arg::new("file")
                .value_hint(ValueHint::FilePath)
                .about("some input file"),
        )
        .subcommand(
            App::new("test").about("tests things").arg(
                Arg::new("case")
                    .long("case")
                    .takes_value(true)
                    .about("the case to test"),
            ),
        )
}

#[test]
fn zsh() {
    let mut app = build_app();
    common::<Zsh>(&mut app, "myapp", ZSH);
}

static ZSH: &str = r#"#compdef myapp

autoload -U is-at-least

_myapp() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
'::file -- some input file:_files' \
":: :_myapp_commands" \
"*::: :->myapp" \
&& ret=0
    case $state in
    (myapp)
        words=($line[2] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:myapp-command-$line[2]:"
        case $line[2] in
            (test)
_arguments "${_arguments_options[@]}" \
'--case=[the case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_myapp_commands] )) ||
_myapp_commands() {
    local commands; commands=(
        'test:tests things' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'myapp commands' commands "$@"
}
(( $+functions[_myapp__help_commands] )) ||
_myapp__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'myapp help commands' commands "$@"
}
(( $+functions[_myapp__test_commands] )) ||
_myapp__test_commands() {
    local commands; commands=(

    )
    _describe -t commands 'myapp test commands' commands "$@"
}

_myapp "$@""#;

#[test]
fn zsh_with_special_commands() {
    let mut app = build_app_special_commands();
    common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_CMDS);
}

fn build_app_special_commands() -> App<'static> {
    build_app_with_name("my_app")
        .subcommand(
            App::new("some_cmd").about("tests other things").arg(
                Arg::new("config")
                    .long("--config")
                    .takes_value(true)
                    .about("the other case to test"),
            ),
        )
        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
}

static ZSH_SPECIAL_CMDS: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
'::file -- some input file:_files' \
":: :_my_app_commands" \
"*::: :->my_app" \
&& ret=0
    case $state in
    (my_app)
        words=($line[2] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-command-$line[2]:"
        case $line[2] in
            (test)
_arguments "${_arguments_options[@]}" \
'--case=[the case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(some_cmd)
_arguments "${_arguments_options[@]}" \
'--config=[the other case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(some-cmd-with-hypens)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(
        'test:tests things' \
'some_cmd:tests other things' \
'some-cmd-with-hypens:' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'my_app commands' commands "$@"
}
(( $+functions[_my_app__help_commands] )) ||
_my_app__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app help commands' commands "$@"
}
(( $+functions[_my_app__some-cmd-with-hypens_commands] )) ||
_my_app__some-cmd-with-hypens_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app some-cmd-with-hypens commands' commands "$@"
}
(( $+functions[_my_app__some_cmd_commands] )) ||
_my_app__some_cmd_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app some_cmd commands' commands "$@"
}
(( $+functions[_my_app__test_commands] )) ||
_my_app__test_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app test commands' commands "$@"
}

_my_app "$@""#;

#[test]
fn zsh_with_special_help() {
    let mut app = build_app_special_help();
    common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
}

fn build_app_special_help() -> App<'static> {
    App::new("my_app")
        .arg(
            Arg::new("single-quotes")
                .long("single-quotes")
                .about("Can be 'always', 'auto', or 'never'"),
        )
        .arg(
            Arg::new("double-quotes")
                .long("double-quotes")
                .about("Can be \"always\", \"auto\", or \"never\""),
        )
        .arg(
            Arg::new("backticks")
                .long("backticks")
                .about("For more information see `echo test`"),
        )
        .arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
        .arg(
            Arg::new("brackets")
                .long("brackets")
                .about("List packages [filter]"),
        )
        .arg(
            Arg::new("expansions")
                .long("expansions")
                .about("Execute the shell command with $SHELL"),
        )
}

static ZSH_SPECIAL_HELP: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'--single-quotes[Can be '\''always'\'', '\''auto'\'', or '\''never'\'']' \
'--double-quotes[Can be "always", "auto", or "never"]' \
'--backticks[For more information see `echo test`]' \
'--backslash[Avoid '\''\\n'\'']' \
'--brackets[List packages \[filter\]]' \
'--expansions[Execute the shell command with $SHELL]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0

}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app commands' commands "$@"
}

_my_app "$@""#;

#[test]
fn zsh_with_nested_subcommands() {
    let mut app = build_app_nested_subcommands();
    common::<Zsh>(&mut app, "my_app", ZSH_NESTED_SUBCOMMANDS);
}

fn build_app_nested_subcommands() -> App<'static> {
    App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
}

static ZSH_NESTED_SUBCOMMANDS: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app_commands" \
"*::: :->first" \
&& ret=0
    case $state in
    (first)
        words=($line[1] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-command-$line[1]:"
        case $line[1] in
            (second)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app__second_commands" \
"*::: :->second" \
&& ret=0
case $state in
    (second)
        words=($line[1] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-second-command-$line[1]:"
        case $line[1] in
            (third)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(
        'second:' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'my_app commands' commands "$@"
}
(( $+functions[_my_app__help_commands] )) ||
_my_app__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app help commands' commands "$@"
}
(( $+functions[_my_app__second_commands] )) ||
_my_app__second_commands() {
    local commands; commands=(
        'third:' \
    )
    _describe -t commands 'my_app second commands' commands "$@"
}
(( $+functions[_my_app__second__third_commands] )) ||
_my_app__second__third_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app second third commands' commands "$@"
}

_my_app "$@""#;
<commit_msg>zsh: Test subcommand help text escaping<commit_after>use super::*;

fn build_app() -> App<'static> {
    build_app_with_name("myapp")
}

fn build_app_with_name(s: &'static str) -> App<'static> {
    App::new(s)
        .about("Tests completions")
        .arg(
            Arg::new("file")
                .value_hint(ValueHint::FilePath)
                .about("some input file"),
        )
        .subcommand(
            App::new("test").about("tests things").arg(
                Arg::new("case")
                    .long("case")
                    .takes_value(true)
                    .about("the case to test"),
            ),
        )
}

#[test]
fn zsh() {
    let mut app = build_app();
    common::<Zsh>(&mut app, "myapp", ZSH);
}

static ZSH: &str = r#"#compdef myapp

autoload -U is-at-least

_myapp() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
'::file -- some input file:_files' \
":: :_myapp_commands" \
"*::: :->myapp" \
&& ret=0
    case $state in
    (myapp)
        words=($line[2] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:myapp-command-$line[2]:"
        case $line[2] in
            (test)
_arguments "${_arguments_options[@]}" \
'--case=[the case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_myapp_commands] )) ||
_myapp_commands() {
    local commands; commands=(
        'test:tests things' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'myapp commands' commands "$@"
}
(( $+functions[_myapp__help_commands] )) ||
_myapp__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'myapp help commands' commands "$@"
}
(( $+functions[_myapp__test_commands] )) ||
_myapp__test_commands() {
    local commands; commands=(

    )
    _describe -t commands 'myapp test commands' commands "$@"
}

_myapp "$@""#;

#[test]
fn zsh_with_special_commands() {
    let mut app = build_app_special_commands();
    common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_CMDS);
}

fn build_app_special_commands() -> App<'static> {
    build_app_with_name("my_app")
        .subcommand(
            App::new("some_cmd").about("tests other things").arg(
                Arg::new("config")
                    .long("--config")
                    .takes_value(true)
                    .about("the other case to test"),
            ),
        )
        .subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
        .subcommand(
            App::new("some_cmd_with_special_characters")
                .about("This 'is' a \"special\" [character] string \\"),
        )
}

static ZSH_SPECIAL_CMDS: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
'::file -- some input file:_files' \
":: :_my_app_commands" \
"*::: :->my_app" \
&& ret=0
    case $state in
    (my_app)
        words=($line[2] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-command-$line[2]:"
        case $line[2] in
            (test)
_arguments "${_arguments_options[@]}" \
'--case=[the case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(some_cmd)
_arguments "${_arguments_options[@]}" \
'--config=[the other case to test]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(some-cmd-with-hypens)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(some_cmd_with_special_characters)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(
        'test:tests things' \
'some_cmd:tests other things' \
'some-cmd-with-hypens:' \
'some_cmd_with_special_characters:This '\''is'\'' a "special" \[character\] string \\' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'my_app commands' commands "$@"
}
(( $+functions[_my_app__help_commands] )) ||
_my_app__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app help commands' commands "$@"
}
(( $+functions[_my_app__some-cmd-with-hypens_commands] )) ||
_my_app__some-cmd-with-hypens_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app some-cmd-with-hypens commands' commands "$@"
}
(( $+functions[_my_app__some_cmd_commands] )) ||
_my_app__some_cmd_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app some_cmd commands' commands "$@"
}
(( $+functions[_my_app__some_cmd_with_special_characters_commands] )) ||
_my_app__some_cmd_with_special_characters_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app some_cmd_with_special_characters commands' commands "$@"
}
(( $+functions[_my_app__test_commands] )) ||
_my_app__test_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app test commands' commands "$@"
}

_my_app "$@""#;

#[test]
fn zsh_with_special_help() {
    let mut app = build_app_special_help();
    common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
}

fn build_app_special_help() -> App<'static> {
    App::new("my_app")
        .arg(
            Arg::new("single-quotes")
                .long("single-quotes")
                .about("Can be 'always', 'auto', or 'never'"),
        )
        .arg(
            Arg::new("double-quotes")
                .long("double-quotes")
                .about("Can be \"always\", \"auto\", or \"never\""),
        )
        .arg(
            Arg::new("backticks")
                .long("backticks")
                .about("For more information see `echo test`"),
        )
        .arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
        .arg(
            Arg::new("brackets")
                .long("brackets")
                .about("List packages [filter]"),
        )
        .arg(
            Arg::new("expansions")
                .long("expansions")
                .about("Execute the shell command with $SHELL"),
        )
}

static ZSH_SPECIAL_HELP: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'--single-quotes[Can be '\''always'\'', '\''auto'\'', or '\''never'\'']' \
'--double-quotes[Can be "always", "auto", or "never"]' \
'--backticks[For more information see `echo test`]' \
'--backslash[Avoid '\''\\n'\'']' \
'--brackets[List packages \[filter\]]' \
'--expansions[Execute the shell command with $SHELL]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0

}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app commands' commands "$@"
}

_my_app "$@""#;

#[test]
fn zsh_with_nested_subcommands() {
    let mut app = build_app_nested_subcommands();
    common::<Zsh>(&mut app, "my_app", ZSH_NESTED_SUBCOMMANDS);
}

fn build_app_nested_subcommands() -> App<'static> {
    App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
}

static ZSH_NESTED_SUBCOMMANDS: &str = r#"#compdef my_app

autoload -U is-at-least

_my_app() {
    typeset -A opt_args
    typeset -a _arguments_options
    local ret=1

    if is-at-least 5.2; then
        _arguments_options=(-s -S -C)
    else
        _arguments_options=(-s -C)
    fi

    local context curcontext="$curcontext" state line
    _arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app_commands" \
"*::: :->first" \
&& ret=0
    case $state in
    (first)
        words=($line[1] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-command-$line[1]:"
        case $line[1] in
            (second)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app__second_commands" \
"*::: :->second" \
&& ret=0
case $state in
    (second)
        words=($line[1] "${words[@]}")
        (( CURRENT += 1 ))
        curcontext="${curcontext%:*:*}:my_app-second-command-$line[1]:"
        case $line[1] in
            (third)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
        esac
    ;;
esac
}

(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
    local commands; commands=(
        'second:' \
'help:Prints this message or the help of the given subcommand(s)' \
    )
    _describe -t commands 'my_app commands' commands "$@"
}
(( $+functions[_my_app__help_commands] )) ||
_my_app__help_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app help commands' commands "$@"
}
(( $+functions[_my_app__second_commands] )) ||
_my_app__second_commands() {
    local commands; commands=(
        'third:' \
    )
    _describe -t commands 'my_app second commands' commands "$@"
}
(( $+functions[_my_app__second__third_commands] )) ||
_my_app__second__third_commands() {
    local commands; commands=(

    )
    _describe -t commands 'my_app second third commands' commands "$@"
}

_my_app "$@""#;
<|endoftext|>


<commit_before>use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    SyntaxKind::{self, ERROR, EOF, TOMBSTONE},
    TokenSource, ParseError, TokenSet,
    event::Event,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t dyn TokenSource,
    token_pos: usize,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t dyn TokenSource) -> Parser<'t> {
        Parser { token_source, token_pos: 0, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Returns the kinds of the current two tokens, if they are not separated
    /// by trivia.
    ///
    /// Useful for parsing things like `>>`.
    pub(crate) fn current2(&self) -> Option<(SyntaxKind, SyntaxKind)> {
        let c1 = self.token_source.token_kind(self.token_pos);
        let c2 = self.token_source.token_kind(self.token_pos + 1);
        if self.token_source.is_token_joint_to_next(self.token_pos) {
            Some((c1, c2))
        } else {
            None
        }
    }

    /// Returns the kinds of the current three tokens, if they are not separated
    /// by trivia.
    ///
    /// Useful for parsing things like `=>>`.
    pub(crate) fn current3(&self) -> Option<(SyntaxKind, SyntaxKind, SyntaxKind)> {
        let c1 = self.token_source.token_kind(self.token_pos);
        let c2 = self.token_source.token_kind(self.token_pos + 1);
        let c3 = self.token_source.token_kind(self.token_pos + 2);
        if self.token_source.is_token_joint_to_next(self.token_pos)
            && self.token_source.is_token_joint_to_next(self.token_pos + 1)
        {
            Some((c1, c2, c3))
        } else {
            None
        }
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);
        self.token_source.token_kind(self.token_pos + n)
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.current() == kind
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(self.token_pos, kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Advances the parser by one token unconditionally.
    pub(crate) fn bump(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // TODO: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Advances the parser by `n` tokens, remapping its kind.
    /// This is useful to create compound tokens from parts. For
    /// example, an `<<` token is two consecutive remapped `<` tokens
    pub(crate) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) {
        self.do_bump(kind, n);
    }

    /// Emit error with the `message`
    /// TODO: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(message.into());
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        self.bump();
        true
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::empty());
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        if self.at(SyntaxKind::L_CURLY) || self.at(SyntaxKind::R_CURLY) || self.at_ts(recovery) {
            self.error(message);
        } else {
            let m = self.start();
            self.error(message);
            self.bump();
            m.complete(self, ERROR);
        };
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        self.token_pos += usize::from(n_raw_tokens);
        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match p.events[idx] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }

    /// Contract a node `cm` and complete as `cm`'s `kind`.
    /// `cm` must be a child of `m` to work correctly.
    /// ```
    /// m--A        m--A
    /// +--cm--B -> +--B
    /// +--C        C
    ///
    /// [m: TOMBSTONE, A, cm: Start(k), B, Finish, C]
    /// [m: Start(k), A, cm: TOMBSTONE, B, Finish, C]
    /// ```
    pub(crate) fn contract_child(mut self, p: &mut Parser, cm: CompletedMarker) -> CompletedMarker {
        self.bomb.defuse();
        match p.events[self.pos as usize] {
            Event::Start { kind: ref mut slot, .. } => *slot = cm.kind(),
            _ => unreachable!(),
        };
        match p.events[cm.0 as usize] {
            Event::Start { kind: ref mut slot, .. } => *slot = TOMBSTONE,
            _ => unreachable!(),
        };
        CompletedMarker::new(self.pos, cm.kind())
    }
}

pub(crate) struct CompletedMarker(u32, SyntaxKind);

impl CompletedMarker {
    fn new(pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker(pos, kind)
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.0 as usize;
        match p.events[idx] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.0);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.1
    }
}
<commit_msg>Mark non-code block as text<commit_after>use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    SyntaxKind::{self, ERROR, EOF, TOMBSTONE},
    TokenSource, ParseError, TokenSet,
    event::Event,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t dyn TokenSource,
    token_pos: usize,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t dyn TokenSource) -> Parser<'t> {
        Parser { token_source, token_pos: 0, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Returns the kinds of the current two tokens, if they are not separated
    /// by trivia.
    ///
    /// Useful for parsing things like `>>`.
    pub(crate) fn current2(&self) -> Option<(SyntaxKind, SyntaxKind)> {
        let c1 = self.token_source.token_kind(self.token_pos);
        let c2 = self.token_source.token_kind(self.token_pos + 1);
        if self.token_source.is_token_joint_to_next(self.token_pos) {
            Some((c1, c2))
        } else {
            None
        }
    }

    /// Returns the kinds of the current three tokens, if they are not separated
    /// by trivia.
    ///
    /// Useful for parsing things like `=>>`.
    pub(crate) fn current3(&self) -> Option<(SyntaxKind, SyntaxKind, SyntaxKind)> {
        let c1 = self.token_source.token_kind(self.token_pos);
        let c2 = self.token_source.token_kind(self.token_pos + 1);
        let c3 = self.token_source.token_kind(self.token_pos + 2);
        if self.token_source.is_token_joint_to_next(self.token_pos)
            && self.token_source.is_token_joint_to_next(self.token_pos + 1)
        {
            Some((c1, c2, c3))
        } else {
            None
        }
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);
        self.token_source.token_kind(self.token_pos + n)
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.current() == kind
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(self.token_pos, kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Advances the parser by one token unconditionally.
    pub(crate) fn bump(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // TODO: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Advances the parser by `n` tokens, remapping its kind.
    /// This is useful to create compound tokens from parts. For
    /// example, an `<<` token is two consecutive remapped `<` tokens
    pub(crate) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) {
        self.do_bump(kind, n);
    }

    /// Emit error with the `message`
    /// TODO: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(message.into());
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        self.bump();
        true
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::empty());
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        if self.at(SyntaxKind::L_CURLY) || self.at(SyntaxKind::R_CURLY) || self.at_ts(recovery) {
            self.error(message);
        } else {
            let m = self.start();
            self.error(message);
            self.bump();
            m.complete(self, ERROR);
        };
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        self.token_pos += usize::from(n_raw_tokens);
        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match p.events[idx] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }

    /// Contract a node `cm` and complete as `cm`'s `kind`.
    /// `cm` must be a child of `m` to work correctly.
    /// ```text
    /// m--A        m--A
    /// +--cm--B -> +--B
    /// +--C        C
    ///
    /// [m: TOMBSTONE, A, cm: Start(k), B, Finish, C]
    /// [m: Start(k), A, cm: TOMBSTONE, B, Finish, C]
    /// ```
    pub(crate) fn contract_child(mut self, p: &mut Parser, cm: CompletedMarker) -> CompletedMarker {
        self.bomb.defuse();
        match p.events[self.pos as usize] {
            Event::Start { kind: ref mut slot, .. } => *slot = cm.kind(),
            _ => unreachable!(),
        };
        match p.events[cm.0 as usize] {
            Event::Start { kind: ref mut slot, .. } => *slot = TOMBSTONE,
            _ => unreachable!(),
        };
        CompletedMarker::new(self.pos, cm.kind())
    }
}

pub(crate) struct CompletedMarker(u32, SyntaxKind);

impl CompletedMarker {
    fn new(pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker(pos, kind)
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.0 as usize;
        match p.events[idx] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.0);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.1
    }
}
<|endoftext|>


<commit_before>use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::utils::Fallible;
use dom::bindings::codegen::CanvasRenderingContext2DBinding;
use dom::window::Window;
use js::jsapi::{JSContext, JSObject};
use dom::bindings::utils::{ErrorResult};

pub struct CanvasRenderingContext2D {
    reflector_: Reflector,
    window: @mut Window,
}

impl CanvasRenderingContext2D {
    pub fn new_inherited(window: @mut Window) -> CanvasRenderingContext2D {
        CanvasRenderingContext2D {
            reflector_: Reflector::new(),
            window: window,
        }
    }

    pub fn new(window: @mut Window) -> @mut CanvasRenderingContext2D {
        reflect_dom_object(@mut CanvasRenderingContext2D::new_inherited(window), window, CanvasRenderingContext2DBinding::Wrap)
    }

 pub fn Width(&self) -> u32 {
        0
    }

    pub fn SetWidth(&mut self, _width: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Height(&self) -> u32 {
        0
    }

    pub fn SetHeight(&mut self, _height: u32) -> ErrorResult {
        Ok(())
    }

}

impl CanvasRenderingContext2D {
    pub fn Constructor(window: @mut Window) -> Fallible<@mut CanvasRenderingContext2D> {
        Ok(CanvasRenderingContext2D::new(window))
    }
}

impl Reflectable for CanvasRenderingContext2D {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }

    fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {
        unreachable!();
    }

    fn GetParentObject(&self, _cx: *JSContext) -> Option<@mut Reflectable> {
        Some(self.window as @mut Reflectable)
    }

}
<commit_msg>Update canvasrenderingcontext2d.rs<commit_after>use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::utils::Fallible;
use dom::bindings::codegen::CanvasRenderingContext2DBinding;
use dom::window::Window;
use js::jsapi::{JSContext, JSObject};
use dom::bindings::utils::{ErrorResult};

pub struct CanvasRenderingContext2D {                         //Struct containing canvasrenderingcontext2d
    reflector_: Reflector,
    window: @mut Window,
}

impl CanvasRenderingContext2D {                               //canvas rendering context 2d interface
    pub fn new_inherited(window: @mut Window) -> CanvasRenderingContext2D {
        CanvasRenderingContext2D {
            reflector_: Reflector::new(),
            window: window,
        }
    }

    pub fn new(window: @mut Window) -> @mut CanvasRenderingContext2D {
        reflect_dom_object(@mut CanvasRenderingContext2D::new_inherited(window), window, CanvasRenderingContext2DBinding::Wrap)
    }

 pub fn Width(&self) -> u32 {                    //width of the canvas
        0
    }

    pub fn SetWidth(&mut self, _width: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Height(&self) -> u32 {                //height of the canvas
        0
    }

    pub fn SetHeight(&mut self, _height: u32) -> ErrorResult {
        Ok(())
    }

}

impl CanvasRenderingContext2D {
    pub fn Constructor(window: @mut Window) -> Fallible<@mut CanvasRenderingContext2D> {
        Ok(CanvasRenderingContext2D::new(window))
    }
}

impl Reflectable for CanvasRenderingContext2D {           //reflectable interface for CanvasRenderingContext2D
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }

    fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {
        unreachable!();
    }

    fn GetParentObject(&self, _cx: *JSContext) -> Option<@mut Reflectable> {
        Some(self.window as @mut Reflectable)
    }

}
<|endoftext|>


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

pub fn main() {
    let v: Vec<isize> = vec![0, 1, 2, 3, 4, 5];
    let s: String = "abcdef".to_string();
    v[3_usize];
    v[3];
    v[3u8];  //~ERROR : std::ops::Index<u8>` is not satisfied
    v[3i8];  //~ERROR : std::ops::Index<i8>` is not satisfied
    v[3u32]; //~ERROR : std::ops::Index<u32>` is not satisfied
    v[3i32]; //~ERROR : std::ops::Index<i32>` is not satisfied
    s.as_bytes()[3_usize];
    s.as_bytes()[3];
    s.as_bytes()[3u8];  //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3i8];  //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3u32]; //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3i32]; //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
}
<commit_msg>Update compile fail test error messages<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub fn main() {
    let v: Vec<isize> = vec![0, 1, 2, 3, 4, 5];
    let s: String = "abcdef".to_string();
    v[3_usize];
    v[3];
    v[3u8];  //~ERROR : std::slice::SliceIndex<[isize]>` is not satisfied
    v[3i8];  //~ERROR : std::slice::SliceIndex<[isize]>` is not satisfied
    v[3u32]; //~ERROR : std::slice::SliceIndex<[isize]>` is not satisfied
    v[3i32]; //~ERROR : std::slice::SliceIndex<[isize]>` is not satisfied
    s.as_bytes()[3_usize];
    s.as_bytes()[3];
    s.as_bytes()[3u8];  //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3i8];  //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3u32]; //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
    s.as_bytes()[3i32]; //~ERROR : std::slice::SliceIndex<[u8]>` is not satisfied
}
<|endoftext|>