<commit_before>use std::io::Write;
use std::marker::{Send, Sync};
use std::path::{Path, PathBuf};
use std::time::Duration;
use std::{fmt, io, thread};

use crate::directory::directory_lock::Lock;
use crate::directory::error::{DeleteError, LockError, OpenReadError, OpenWriteError};
use crate::directory::{FileHandle, FileSlice, WatchCallback, WatchHandle, WritePtr};

/// Retry the logic of acquiring locks is pretty simple.
/// We just retry `n` times after a given `duratio`, both
/// depending on the type of lock.
struct RetryPolicy {
    num_retries: usize,
    wait_in_ms: u64,
}

impl RetryPolicy {
    fn no_retry() -> RetryPolicy {
        RetryPolicy {
            num_retries: 0,
            wait_in_ms: 0,
        }
    }

    fn wait_and_retry(&mut self) -> bool {
        if self.num_retries == 0 {
            false
        } else {
            self.num_retries -= 1;
            let wait_duration = Duration::from_millis(self.wait_in_ms);
            thread::sleep(wait_duration);
            true
        }
    }
}

/// The `DirectoryLock` is an object that represents a file lock.
///
/// It is associated to a lock file, that gets deleted on `Drop.`
pub struct DirectoryLock(Box<dyn Send + Sync + 'static>);

struct DirectoryLockGuard {
    directory: Box<dyn Directory>,
    path: PathBuf,
}

impl<T: Send + Sync + 'static> From<Box<T>> for DirectoryLock {
    fn from(underlying: Box<T>) -> Self {
        DirectoryLock(underlying)
    }
}

impl Drop for DirectoryLockGuard {
    fn drop(&mut self) {
        if let Err(e) = self.directory.delete(&*self.path) {
            error!("Failed to remove the lock file. {:?}", e);
        }
    }
}

enum TryAcquireLockError {
    FileExists,
    IoError(io::Error),
}

fn try_acquire_lock(
    filepath: &Path,
    directory: &mut dyn Directory,
) -> Result<DirectoryLock, TryAcquireLockError> {
    let mut write = directory.open_write(filepath).map_err(|e| match e {
        OpenWriteError::FileAlreadyExists(_) => TryAcquireLockError::FileExists,
        OpenWriteError::IoError { io_error, .. } => TryAcquireLockError::IoError(io_error),
    })?;
    write.flush().map_err(TryAcquireLockError::IoError)?;
    Ok(DirectoryLock::from(Box::new(DirectoryLockGuard {
        directory: directory.box_clone(),
        path: filepath.to_owned(),
    })))
}

fn retry_policy(is_blocking: bool) -> RetryPolicy {
    if is_blocking {
        RetryPolicy {
            num_retries: 100,
            wait_in_ms: 100,
        }
    } else {
        RetryPolicy::no_retry()
    }
}

/// Write-once read many (WORM) abstraction for where
/// tantivy's data should be stored.
///
/// There are currently two implementations of `Directory`
///
/// - The [`MMapDirectory`](struct.MmapDirectory.html), this
/// should be your default choice.
/// - The [`RamDirectory`](struct.RamDirectory.html), which
/// should be used mostly for tests.
pub trait Directory: DirectoryClone + fmt::Debug + Send + Sync + 'static {
    /// Opens a file and returns a boxed `FileHandle`.
    ///
    /// Users of `Directory` should typically call `Directory::open_read(...)`,
    /// while `Directory` implementor should implement `get_file_handle()`.
    fn get_file_handle(&self, path: &Path) -> Result<Box<dyn FileHandle>, OpenReadError>;

    /// Once a virtual file is open, its data may not
    /// change.
    ///
    /// Specifically, subsequent writes or flushes should
    /// have no effect on the returned `FileSlice` object.
    ///
    /// You should only use this to read files create with [Directory::open_write].
    fn open_read(&self, path: &Path) -> Result<FileSlice, OpenReadError> {
        let file_handle = self.get_file_handle(path)?;
        Ok(FileSlice::new(file_handle))
    }

    /// Removes a file
    ///
    /// Removing a file will not affect an eventual
    /// existing FileSlice pointing to it.
    ///
    /// Removing a nonexistent file, yields a
    /// `DeleteError::DoesNotExist`.
    fn delete(&self, path: &Path) -> Result<(), DeleteError>;

    /// Returns true iff the file exists
    fn exists(&self, path: &Path) -> Result<bool, OpenReadError>;

    /// Opens a writer for the *virtual file* associated with
    /// a Path.
    ///
    /// Right after this call, for the span of the execution of the program
    /// the file should be created and any subsequent call to `open_read` for the
    /// same path should return a `FileSlice`.
    ///
    /// However, depending on the directory implementation,
    /// it might be required to call `sync_directory` to ensure
    /// that the file is durably created.
    /// (The semantics here are the same when dealing with
    /// a posix filesystem.)
    ///
    /// Write operations may be aggressively buffered.
    /// The client of this trait is responsible for calling flush
    /// to ensure that subsequent `read` operations
    /// will take into account preceding `write` operations.
    ///
    /// Flush operation should also be persistent.
    ///
    /// The user shall not rely on `Drop` triggering `flush`.
    /// Note that `RamDirectory` will panic! if `flush`
    /// was not called.
    ///
    /// The file may not previously exist.
    fn open_write(&self, path: &Path) -> Result<WritePtr, OpenWriteError>;

    /// Reads the full content file that has been written using
    /// atomic_write.
    ///
    /// This should only be used for small files.
    ///
    /// You should only use this to read files create with [Directory::atomic_write].
    fn atomic_read(&self, path: &Path) -> Result<Vec<u8>, OpenReadError>;

    /// Atomically replace the content of a file with data.
    ///
    /// This calls ensure that reads can never *observe*
    /// a partially written file.
    ///
    /// The file may or may not previously exist.
    fn atomic_write(&self, path: &Path, data: &[u8]) -> io::Result<()>;

    /// Sync the directory.
    ///
    /// This call is required to ensure that newly created files are
    /// effectively stored durably.
    fn sync_directory(&self) -> io::Result<()>;

    /// Acquire a lock in the given directory.
    ///
    /// The method is blocking or not depending on the `Lock` object.
    fn acquire_lock(&self, lock: &Lock) -> Result<DirectoryLock, LockError> {
        let mut box_directory = self.box_clone();
        let mut retry_policy = retry_policy(lock.is_blocking);
        loop {
            match try_acquire_lock(&lock.filepath, &mut *box_directory) {
                Ok(result) => {
                    return Ok(result);
                }
                Err(TryAcquireLockError::FileExists) => {
                    if !retry_policy.wait_and_retry() {
                        return Err(LockError::LockBusy);
                    }
                }
                Err(TryAcquireLockError::IoError(io_error)) => {
                    return Err(LockError::IoError(io_error));
                }
            }
        }
    }

    /// Registers a callback that will be called whenever a change on the `meta.json`
    /// using the `atomic_write` API is detected.
    ///
    /// The behavior when using `.watch()` on a file using [Directory::open_write] is, on the other
    /// hand, undefined.
    ///
    /// The file will be watched for the lifetime of the returned `WatchHandle`. The caller is
    /// required to keep it.
    /// It does not override previous callbacks. When the file is modified, all callback that are
    /// registered (and whose `WatchHandle` is still alive) are triggered.
    ///
    /// Internally, tantivy only uses this API to detect new commits to implement the
    /// `OnCommit` `ReloadPolicy`. Not implementing watch in a `Directory` only prevents the
    /// `OnCommit` `ReloadPolicy` to work properly.
    fn watch(&self, watch_callback: WatchCallback) -> crate::Result<WatchHandle>;
}

/// DirectoryClone
pub trait DirectoryClone {
    /// Clones the directory and boxes the clone
    fn box_clone(&self) -> Box<dyn Directory>;
}

impl<T> DirectoryClone for T
where T: 'static + Directory + Clone
{
    fn box_clone(&self) -> Box<dyn Directory> {
        Box::new(self.clone())
    }
}

impl Clone for Box<dyn Directory> {
    fn clone(&self) -> Self {
        self.box_clone()
    }
}

impl<T: Directory + 'static> From<T> for Box<dyn Directory> {
    fn from(t: T) -> Self {
        Box::new(t)
    }
}
<commit_msg>Fix link to RamDirectory and MMapDirectory in `Directory`'s documentation (#1295)<commit_after>use std::io::Write;
use std::marker::{Send, Sync};
use std::path::{Path, PathBuf};
use std::time::Duration;
use std::{fmt, io, thread};

use crate::directory::directory_lock::Lock;
use crate::directory::error::{DeleteError, LockError, OpenReadError, OpenWriteError};
use crate::directory::{FileHandle, FileSlice, WatchCallback, WatchHandle, WritePtr};

/// Retry the logic of acquiring locks is pretty simple.
/// We just retry `n` times after a given `duratio`, both
/// depending on the type of lock.
struct RetryPolicy {
    num_retries: usize,
    wait_in_ms: u64,
}

impl RetryPolicy {
    fn no_retry() -> RetryPolicy {
        RetryPolicy {
            num_retries: 0,
            wait_in_ms: 0,
        }
    }

    fn wait_and_retry(&mut self) -> bool {
        if self.num_retries == 0 {
            false
        } else {
            self.num_retries -= 1;
            let wait_duration = Duration::from_millis(self.wait_in_ms);
            thread::sleep(wait_duration);
            true
        }
    }
}

/// The `DirectoryLock` is an object that represents a file lock.
///
/// It is associated to a lock file, that gets deleted on `Drop.`
pub struct DirectoryLock(Box<dyn Send + Sync + 'static>);

struct DirectoryLockGuard {
    directory: Box<dyn Directory>,
    path: PathBuf,
}

impl<T: Send + Sync + 'static> From<Box<T>> for DirectoryLock {
    fn from(underlying: Box<T>) -> Self {
        DirectoryLock(underlying)
    }
}

impl Drop for DirectoryLockGuard {
    fn drop(&mut self) {
        if let Err(e) = self.directory.delete(&*self.path) {
            error!("Failed to remove the lock file. {:?}", e);
        }
    }
}

enum TryAcquireLockError {
    FileExists,
    IoError(io::Error),
}

fn try_acquire_lock(
    filepath: &Path,
    directory: &mut dyn Directory,
) -> Result<DirectoryLock, TryAcquireLockError> {
    let mut write = directory.open_write(filepath).map_err(|e| match e {
        OpenWriteError::FileAlreadyExists(_) => TryAcquireLockError::FileExists,
        OpenWriteError::IoError { io_error, .. } => TryAcquireLockError::IoError(io_error),
    })?;
    write.flush().map_err(TryAcquireLockError::IoError)?;
    Ok(DirectoryLock::from(Box::new(DirectoryLockGuard {
        directory: directory.box_clone(),
        path: filepath.to_owned(),
    })))
}

fn retry_policy(is_blocking: bool) -> RetryPolicy {
    if is_blocking {
        RetryPolicy {
            num_retries: 100,
            wait_in_ms: 100,
        }
    } else {
        RetryPolicy::no_retry()
    }
}

/// Write-once read many (WORM) abstraction for where
/// tantivy's data should be stored.
///
/// There are currently two implementations of `Directory`
///
/// - The [`MMapDirectory`][crate::directory::MmapDirectory], this
/// should be your default choice.
/// - The [`RamDirectory`][crate::directory::RamDirectory], which
/// should be used mostly for tests.
pub trait Directory: DirectoryClone + fmt::Debug + Send + Sync + 'static {
    /// Opens a file and returns a boxed `FileHandle`.
    ///
    /// Users of `Directory` should typically call `Directory::open_read(...)`,
    /// while `Directory` implementor should implement `get_file_handle()`.
    fn get_file_handle(&self, path: &Path) -> Result<Box<dyn FileHandle>, OpenReadError>;

    /// Once a virtual file is open, its data may not
    /// change.
    ///
    /// Specifically, subsequent writes or flushes should
    /// have no effect on the returned `FileSlice` object.
    ///
    /// You should only use this to read files create with [Directory::open_write].
    fn open_read(&self, path: &Path) -> Result<FileSlice, OpenReadError> {
        let file_handle = self.get_file_handle(path)?;
        Ok(FileSlice::new(file_handle))
    }

    /// Removes a file
    ///
    /// Removing a file will not affect an eventual
    /// existing FileSlice pointing to it.
    ///
    /// Removing a nonexistent file, yields a
    /// `DeleteError::DoesNotExist`.
    fn delete(&self, path: &Path) -> Result<(), DeleteError>;

    /// Returns true iff the file exists
    fn exists(&self, path: &Path) -> Result<bool, OpenReadError>;

    /// Opens a writer for the *virtual file* associated with
    /// a Path.
    ///
    /// Right after this call, for the span of the execution of the program
    /// the file should be created and any subsequent call to `open_read` for the
    /// same path should return a `FileSlice`.
    ///
    /// However, depending on the directory implementation,
    /// it might be required to call `sync_directory` to ensure
    /// that the file is durably created.
    /// (The semantics here are the same when dealing with
    /// a posix filesystem.)
    ///
    /// Write operations may be aggressively buffered.
    /// The client of this trait is responsible for calling flush
    /// to ensure that subsequent `read` operations
    /// will take into account preceding `write` operations.
    ///
    /// Flush operation should also be persistent.
    ///
    /// The user shall not rely on `Drop` triggering `flush`.
    /// Note that `RamDirectory` will panic! if `flush`
    /// was not called.
    ///
    /// The file may not previously exist.
    fn open_write(&self, path: &Path) -> Result<WritePtr, OpenWriteError>;

    /// Reads the full content file that has been written using
    /// atomic_write.
    ///
    /// This should only be used for small files.
    ///
    /// You should only use this to read files create with [Directory::atomic_write].
    fn atomic_read(&self, path: &Path) -> Result<Vec<u8>, OpenReadError>;

    /// Atomically replace the content of a file with data.
    ///
    /// This calls ensure that reads can never *observe*
    /// a partially written file.
    ///
    /// The file may or may not previously exist.
    fn atomic_write(&self, path: &Path, data: &[u8]) -> io::Result<()>;

    /// Sync the directory.
    ///
    /// This call is required to ensure that newly created files are
    /// effectively stored durably.
    fn sync_directory(&self) -> io::Result<()>;

    /// Acquire a lock in the given directory.
    ///
    /// The method is blocking or not depending on the `Lock` object.
    fn acquire_lock(&self, lock: &Lock) -> Result<DirectoryLock, LockError> {
        let mut box_directory = self.box_clone();
        let mut retry_policy = retry_policy(lock.is_blocking);
        loop {
            match try_acquire_lock(&lock.filepath, &mut *box_directory) {
                Ok(result) => {
                    return Ok(result);
                }
                Err(TryAcquireLockError::FileExists) => {
                    if !retry_policy.wait_and_retry() {
                        return Err(LockError::LockBusy);
                    }
                }
                Err(TryAcquireLockError::IoError(io_error)) => {
                    return Err(LockError::IoError(io_error));
                }
            }
        }
    }

    /// Registers a callback that will be called whenever a change on the `meta.json`
    /// using the `atomic_write` API is detected.
    ///
    /// The behavior when using `.watch()` on a file using [Directory::open_write] is, on the other
    /// hand, undefined.
    ///
    /// The file will be watched for the lifetime of the returned `WatchHandle`. The caller is
    /// required to keep it.
    /// It does not override previous callbacks. When the file is modified, all callback that are
    /// registered (and whose `WatchHandle` is still alive) are triggered.
    ///
    /// Internally, tantivy only uses this API to detect new commits to implement the
    /// `OnCommit` `ReloadPolicy`. Not implementing watch in a `Directory` only prevents the
    /// `OnCommit` `ReloadPolicy` to work properly.
    fn watch(&self, watch_callback: WatchCallback) -> crate::Result<WatchHandle>;
}

/// DirectoryClone
pub trait DirectoryClone {
    /// Clones the directory and boxes the clone
    fn box_clone(&self) -> Box<dyn Directory>;
}

impl<T> DirectoryClone for T
where T: 'static + Directory + Clone
{
    fn box_clone(&self) -> Box<dyn Directory> {
        Box::new(self.clone())
    }
}

impl Clone for Box<dyn Directory> {
    fn clone(&self) -> Self {
        self.box_clone()
    }
}

impl<T: Directory + 'static> From<T> for Box<dyn Directory> {
    fn from(t: T) -> Self {
        Box::new(t)
    }
}
<|endoftext|>


<commit_before>use super::write_file;
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use std::{fs, str};

const BINDING_CC_TEMPLATE: &'static str = include_str!("./templates/binding.cc");
const BINDING_GYP_TEMPLATE: &'static str = include_str!("./templates/binding.gyp");
const INDEX_JS_TEMPLATE: &'static str = include_str!("./templates/index.js");
const LIB_RS_TEMPLATE: &'static str = include_str!("./templates/lib.rs");
const BUILD_RS_TEMPLATE: &'static str = include_str!("./templates/build.rs");
const CARGO_TOML_TEMPLATE: &'static str = include_str!("./templates/cargo.toml");
const PACKAGE_JSON_TEMPLATE: &'static str = include_str!("./templates/package.json");
const PARSER_NAME_PLACEHOLDER: &'static str = "PARSER_NAME";
const CLI_VERSION_PLACEHOLDER: &'static str = "CLI_VERSION";
const CLI_VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn generate_binding_files(repo_path: &Path, language_name: &str) -> Result<()> {
    let bindings_dir = repo_path.join("bindings");

    let dashed_language_name = language_name.replace("_", "-");
    let dashed_language_name = dashed_language_name.as_str();

    // Generate rust bindings if needed.
    let rust_binding_dir = bindings_dir.join("rust");
    create_path(&rust_binding_dir, |path| create_dir(path))?;

    create_path(&rust_binding_dir.join("lib.rs").to_owned(), |path| {
        generate_file(path, LIB_RS_TEMPLATE, language_name)
    })?;

    create_path(&rust_binding_dir.join("build.rs").to_owned(), |path| {
        generate_file(path, BUILD_RS_TEMPLATE, language_name)
    })?;

    create_path(&repo_path.join("Cargo.toml").to_owned(), |path| {
        generate_file(path, CARGO_TOML_TEMPLATE, dashed_language_name)
    })?;

    // Generate node bindings
    let node_binding_dir = bindings_dir.join("node");
    create_path(&node_binding_dir, |path| create_dir(path))?;

    create_path(&node_binding_dir.join("index.js").to_owned(), |path| {
        generate_file(path, INDEX_JS_TEMPLATE, language_name)
    })?;

    create_path(&node_binding_dir.join("binding.cc").to_owned(), |path| {
        generate_file(path, BINDING_CC_TEMPLATE, language_name)
    })?;

    // Create binding.gyp, or update it with new binding path.
    let binding_gyp_path = repo_path.join("binding.gyp");
    create_path_else(
        &binding_gyp_path,
        |path| generate_file(path, BINDING_GYP_TEMPLATE, language_name),
        |path| {
            eprintln!("Updating binding.gyp with new binding path");
            let binding_gyp =
                fs::read_to_string(path).with_context(|| "Failed to read binding.gyp")?;
            let binding_gyp = binding_gyp.replace("src/binding.cc", "bindings/node/binding.cc");
            write_file(path, binding_gyp)
        },
    )?;

    // Create package.json, or update it with new binding path.
    let package_json_path = repo_path.join("package.json");
    create_path_else(
        &package_json_path,
        |path| generate_file(path, PACKAGE_JSON_TEMPLATE, dashed_language_name),
        |path| {
            let package_json_str =
                fs::read_to_string(path).with_context(|| "Failed to read package.json")?;
            let mut package_json =
                serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(
                    &package_json_str,
                )
                .with_context(|| "Failed to parse package.json")?;
            let package_json_main = package_json.get("main");
            let package_json_needs_update = package_json_main.map_or(true, |v| {
                let main_string = v.as_str();
                main_string == Some("index.js") || main_string == Some("./index.js")
            });
            if package_json_needs_update {
                eprintln!("Updating package.json with new binding path");

                package_json.insert(
                    "main".to_string(),
                    serde_json::Value::String("bindings/node".to_string()),
                );
                let mut package_json_str = serde_json::to_string_pretty(&package_json)?;
                package_json_str.push('\n');
                write_file(path, package_json_str)?;
            }
            Ok(())
        },
    )?;

    // Remove files from old node binding paths.
    let old_index_js_path = repo_path.join("index.js");
    let old_binding_cc_path = repo_path.join("src").join("binding.cc");
    if old_index_js_path.exists() {
        fs::remove_file(old_index_js_path).ok();
    }
    if old_binding_cc_path.exists() {
        fs::remove_file(old_binding_cc_path).ok();
    }

    Ok(())
}

fn generate_file(path: &Path, template: &str, language_name: &str) -> Result<()> {
    write_file(
        path,
        template
            .replace(PARSER_NAME_PLACEHOLDER, language_name)
            .replace(CLI_VERSION_PLACEHOLDER, CLI_VERSION),
    )
}

fn create_dir(path: &Path) -> Result<()> {
    fs::create_dir_all(&path)
        .with_context(|| format!("Failed to create {:?}", path.to_string_lossy()))
}

fn create_path<F>(path: &PathBuf, action: F) -> Result<bool>
where
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    }
    Ok(false)
}

fn create_path_else<T, F>(path: &PathBuf, action: T, else_action: F) -> Result<bool>
where
    T: Fn(&PathBuf) -> Result<()>,
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    } else {
        else_action(path)?;
    }
    Ok(false)
}
<commit_msg>Use double quoted patterns for more precise pattern matching in the binding.gyp files<commit_after>use super::write_file;
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use std::{fs, str};

const BINDING_CC_TEMPLATE: &'static str = include_str!("./templates/binding.cc");
const BINDING_GYP_TEMPLATE: &'static str = include_str!("./templates/binding.gyp");
const INDEX_JS_TEMPLATE: &'static str = include_str!("./templates/index.js");
const LIB_RS_TEMPLATE: &'static str = include_str!("./templates/lib.rs");
const BUILD_RS_TEMPLATE: &'static str = include_str!("./templates/build.rs");
const CARGO_TOML_TEMPLATE: &'static str = include_str!("./templates/cargo.toml");
const PACKAGE_JSON_TEMPLATE: &'static str = include_str!("./templates/package.json");
const PARSER_NAME_PLACEHOLDER: &'static str = "PARSER_NAME";
const CLI_VERSION_PLACEHOLDER: &'static str = "CLI_VERSION";
const CLI_VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn generate_binding_files(repo_path: &Path, language_name: &str) -> Result<()> {
    let bindings_dir = repo_path.join("bindings");

    let dashed_language_name = language_name.replace("_", "-");
    let dashed_language_name = dashed_language_name.as_str();

    // Generate rust bindings if needed.
    let rust_binding_dir = bindings_dir.join("rust");
    create_path(&rust_binding_dir, |path| create_dir(path))?;

    create_path(&rust_binding_dir.join("lib.rs").to_owned(), |path| {
        generate_file(path, LIB_RS_TEMPLATE, language_name)
    })?;

    create_path(&rust_binding_dir.join("build.rs").to_owned(), |path| {
        generate_file(path, BUILD_RS_TEMPLATE, language_name)
    })?;

    create_path(&repo_path.join("Cargo.toml").to_owned(), |path| {
        generate_file(path, CARGO_TOML_TEMPLATE, dashed_language_name)
    })?;

    // Generate node bindings
    let node_binding_dir = bindings_dir.join("node");
    create_path(&node_binding_dir, |path| create_dir(path))?;

    create_path(&node_binding_dir.join("index.js").to_owned(), |path| {
        generate_file(path, INDEX_JS_TEMPLATE, language_name)
    })?;

    create_path(&node_binding_dir.join("binding.cc").to_owned(), |path| {
        generate_file(path, BINDING_CC_TEMPLATE, language_name)
    })?;

    // Create binding.gyp, or update it with new binding path.
    let binding_gyp_path = repo_path.join("binding.gyp");
    create_path_else(
        &binding_gyp_path,
        |path| generate_file(path, BINDING_GYP_TEMPLATE, language_name),
        |path| {
            eprintln!("Updating binding.gyp with new binding path");
            let binding_gyp =
                fs::read_to_string(path).with_context(|| "Failed to read binding.gyp")?;
            let binding_gyp = binding_gyp.replace("\"src/binding.cc\"", "\"bindings/node/binding.cc\"");
            write_file(path, binding_gyp)
        },
    )?;

    // Create package.json, or update it with new binding path.
    let package_json_path = repo_path.join("package.json");
    create_path_else(
        &package_json_path,
        |path| generate_file(path, PACKAGE_JSON_TEMPLATE, dashed_language_name),
        |path| {
            let package_json_str =
                fs::read_to_string(path).with_context(|| "Failed to read package.json")?;
            let mut package_json =
                serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(
                    &package_json_str,
                )
                .with_context(|| "Failed to parse package.json")?;
            let package_json_main = package_json.get("main");
            let package_json_needs_update = package_json_main.map_or(true, |v| {
                let main_string = v.as_str();
                main_string == Some("index.js") || main_string == Some("./index.js")
            });
            if package_json_needs_update {
                eprintln!("Updating package.json with new binding path");

                package_json.insert(
                    "main".to_string(),
                    serde_json::Value::String("bindings/node".to_string()),
                );
                let mut package_json_str = serde_json::to_string_pretty(&package_json)?;
                package_json_str.push('\n');
                write_file(path, package_json_str)?;
            }
            Ok(())
        },
    )?;

    // Remove files from old node binding paths.
    let old_index_js_path = repo_path.join("index.js");
    let old_binding_cc_path = repo_path.join("src").join("binding.cc");
    if old_index_js_path.exists() {
        fs::remove_file(old_index_js_path).ok();
    }
    if old_binding_cc_path.exists() {
        fs::remove_file(old_binding_cc_path).ok();
    }

    Ok(())
}

fn generate_file(path: &Path, template: &str, language_name: &str) -> Result<()> {
    write_file(
        path,
        template
            .replace(PARSER_NAME_PLACEHOLDER, language_name)
            .replace(CLI_VERSION_PLACEHOLDER, CLI_VERSION),
    )
}

fn create_dir(path: &Path) -> Result<()> {
    fs::create_dir_all(&path)
        .with_context(|| format!("Failed to create {:?}", path.to_string_lossy()))
}

fn create_path<F>(path: &PathBuf, action: F) -> Result<bool>
where
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    }
    Ok(false)
}

fn create_path_else<T, F>(path: &PathBuf, action: T, else_action: F) -> Result<bool>
where
    T: Fn(&PathBuf) -> Result<()>,
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    } else {
        else_action(path)?;
    }
    Ok(false)
}
<|endoftext|>


<commit_before>//! Checks for needless boolean results of if-else expressions
//!
//! This lint is **warn** by default

use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_else_clause, is_expn_of};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for expressions of the form `if c { true } else {
    /// false }` (or vice versa) and suggests using the condition directly.
    ///
    /// **Why is this bad?** Redundant code.
    ///
    /// **Known problems:** Maybe false positives: Sometimes, the two branches are
    /// painstakingly documented (which we, of course, do not detect), so they *may*
    /// have some value. Even then, the documentation can be rewritten to match the
    /// shorter code.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if x {
    ///     false
    /// } else {
    ///     true
    /// }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// !x
    /// ```
    pub NEEDLESS_BOOL,
    complexity,
    "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for expressions of the form `x == true`,
    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
    /// suggest using the variable directly.
    ///
    /// **Why is this bad?** Unnecessary code.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if x == true {}
    /// if y == false {}
    /// ```
    /// use `x` directly:
    /// ```rust,ignore
    /// if x {}
    /// if !y {}
    /// ```
    pub BOOL_COMPARISON,
    complexity,
    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
}

declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);

impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        use self::Expression::{Bool, RetBool};
        if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
            let reduce = |ret, not| {
                let mut applicability = Applicability::MachineApplicable;
                let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
                let mut snip = if not { !snip } else { snip };

                if ret {
                    snip = snip.make_return();
                }

                if is_else_clause(cx.tcx, e) {
                    snip = snip.blockify();
                }

                span_lint_and_sugg(
                    cx,
                    NEEDLESS_BOOL,
                    e.span,
                    "this if-then-else expression returns a bool literal",
                    "you can reduce it to",
                    snip.to_string(),
                    applicability,
                );
            };
            if let ExprKind::Block(then_block, _) = then_block.kind {
                match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
                    (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
                        span_lint(
                            cx,
                            NEEDLESS_BOOL,
                            e.span,
                            "this if-then-else expression will always return true",
                        );
                    },
                    (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
                        span_lint(
                            cx,
                            NEEDLESS_BOOL,
                            e.span,
                            "this if-then-else expression will always return false",
                        );
                    },
                    (RetBool(true), RetBool(false)) => reduce(true, false),
                    (Bool(true), Bool(false)) => reduce(false, false),
                    (RetBool(false), RetBool(true)) => reduce(true, true),
                    (Bool(false), Bool(true)) => reduce(false, true),
                    _ => (),
                }
            } else {
                panic!("IfExpr `then` node is not an `ExprKind::Block`");
            }
        }
    }
}

declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);

impl<'tcx> LateLintPass<'tcx> for BoolComparison {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if e.span.from_expansion() {
            return;
        }

        if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
            let ignore_case = None::<(fn(_) -> _, &str)>;
            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
            match node {
                BinOpKind::Eq => {
                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
                    let false_case = Some((
                        |h: Sugg<'_>| !h,
                        "equality checks against false can be replaced by a negation",
                    ));
                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
                },
                BinOpKind::Ne => {
                    let true_case = Some((
                        |h: Sugg<'_>| !h,
                        "inequality checks against true can be replaced by a negation",
                    ));
                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
                },
                BinOpKind::Lt => check_comparison(
                    cx,
                    e,
                    ignore_case,
                    Some((|h| h, "greater than checks against false are unnecessary")),
                    Some((
                        |h: Sugg<'_>| !h,
                        "less than comparison against true can be replaced by a negation",
                    )),
                    ignore_case,
                    Some((
                        |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
                        "order comparisons between booleans can be simplified",
                    )),
                ),
                BinOpKind::Gt => check_comparison(
                    cx,
                    e,
                    Some((
                        |h: Sugg<'_>| !h,
                        "less than comparison against true can be replaced by a negation",
                    )),
                    ignore_case,
                    ignore_case,
                    Some((|h| h, "greater than checks against false are unnecessary")),
                    Some((
                        |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
                        "order comparisons between booleans can be simplified",
                    )),
                ),
                _ => (),
            }
        }
    }
}

struct ExpressionInfoWithSpan {
    one_side_is_unary_not: bool,
    left_span: Span,
    right_span: Span,
}

fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
    if let ExprKind::Unary(UnOp::Not, operand) = e.kind {
        return (true, operand.span);
    }
    (false, e.span)
}

fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
    let left = is_unary_not(left_side);
    let right = is_unary_not(right_side);

    ExpressionInfoWithSpan {
        one_side_is_unary_not: left.0 != right.0,
        left_span: left.1,
        right_span: right.1,
    }
}

fn check_comparison<'a, 'tcx>(
    cx: &LateContext<'tcx>,
    e: &'tcx Expr<'_>,
    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
) {
    use self::Expression::{Bool, Other};

    if let ExprKind::Binary(op, left_side, right_side) = e.kind {
        let (l_ty, r_ty) = (
            cx.typeck_results().expr_ty(left_side),
            cx.typeck_results().expr_ty(right_side),
        );
        if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() {
            return;
        }
        if l_ty.is_bool() && r_ty.is_bool() {
            let mut applicability = Applicability::MachineApplicable;

            if let BinOpKind::Eq = op.node {
                let expression_info = one_side_is_unary_not(left_side, right_side);
                if expression_info.one_side_is_unary_not {
                    span_lint_and_sugg(
                        cx,
                        BOOL_COMPARISON,
                        e.span,
                        "this comparison might be written more concisely",
                        "try simplifying it as shown",
                        format!(
                            "{} != {}",
                            snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
                            snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
                        ),
                        applicability,
                    );
                }
            }

            match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                }),
                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                }),
                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                }),
                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                }),
                (Other, Other) => no_literal.map_or((), |(h, m)| {
                    let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
                    let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
                    span_lint_and_sugg(
                        cx,
                        BOOL_COMPARISON,
                        e.span,
                        m,
                        "try simplifying it as shown",
                        h(left_side, right_side).to_string(),
                        applicability,
                    );
                }),
                _ => (),
            }
        }
    }
}

fn suggest_bool_comparison<'a, 'tcx>(
    cx: &LateContext<'tcx>,
    e: &'tcx Expr<'_>,
    expr: &Expr<'_>,
    mut applicability: Applicability,
    message: &str,
    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) {
    let hint = if expr.span.from_expansion() {
        if applicability != Applicability::Unspecified {
            applicability = Applicability::MaybeIncorrect;
        }
        Sugg::hir_with_macro_callsite(cx, expr, "..")
    } else {
        Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
    };
    span_lint_and_sugg(
        cx,
        BOOL_COMPARISON,
        e.span,
        message,
        "try simplifying it as shown",
        conv_hint(hint).to_string(),
        applicability,
    );
}

enum Expression {
    Bool(bool),
    RetBool(bool),
    Other,
}

fn fetch_bool_block(block: &Block<'_>) -> Expression {
    match (&*block.stmts, block.expr.as_ref()) {
        (&[], Some(e)) => fetch_bool_expr(&**e),
        (&[ref e], None) => {
            if let StmtKind::Semi(e) = e.kind {
                if let ExprKind::Ret(_) = e.kind {
                    fetch_bool_expr(e)
                } else {
                    Expression::Other
                }
            } else {
                Expression::Other
            }
        },
        _ => Expression::Other,
    }
}

fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
    match expr.kind {
        ExprKind::Block(block, _) => fetch_bool_block(block),
        ExprKind::Lit(ref lit_ptr) => {
            if let LitKind::Bool(value) = lit_ptr.node {
                Expression::Bool(value)
            } else {
                Expression::Other
            }
        },
        ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) {
            Expression::Bool(value) => Expression::RetBool(value),
            _ => Expression::Other,
        },
        _ => Expression::Other,
    }
}
<commit_msg>Allow needless_bool in macro<commit_after>//! Checks for needless boolean results of if-else expressions
//!
//! This lint is **warn** by default

use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_else_clause, is_expn_of};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for expressions of the form `if c { true } else {
    /// false }` (or vice versa) and suggests using the condition directly.
    ///
    /// **Why is this bad?** Redundant code.
    ///
    /// **Known problems:** Maybe false positives: Sometimes, the two branches are
    /// painstakingly documented (which we, of course, do not detect), so they *may*
    /// have some value. Even then, the documentation can be rewritten to match the
    /// shorter code.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if x {
    ///     false
    /// } else {
    ///     true
    /// }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// !x
    /// ```
    pub NEEDLESS_BOOL,
    complexity,
    "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for expressions of the form `x == true`,
    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
    /// suggest using the variable directly.
    ///
    /// **Why is this bad?** Unnecessary code.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if x == true {}
    /// if y == false {}
    /// ```
    /// use `x` directly:
    /// ```rust,ignore
    /// if x {}
    /// if !y {}
    /// ```
    pub BOOL_COMPARISON,
    complexity,
    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
}

declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);

impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        use self::Expression::{Bool, RetBool};
        if e.span.from_expansion() {
            return;
        }
        if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
            let reduce = |ret, not| {
                let mut applicability = Applicability::MachineApplicable;
                let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
                let mut snip = if not { !snip } else { snip };

                if ret {
                    snip = snip.make_return();
                }

                if is_else_clause(cx.tcx, e) {
                    snip = snip.blockify();
                }

                span_lint_and_sugg(
                    cx,
                    NEEDLESS_BOOL,
                    e.span,
                    "this if-then-else expression returns a bool literal",
                    "you can reduce it to",
                    snip.to_string(),
                    applicability,
                );
            };
            if let ExprKind::Block(then_block, _) = then_block.kind {
                match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
                    (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
                        span_lint(
                            cx,
                            NEEDLESS_BOOL,
                            e.span,
                            "this if-then-else expression will always return true",
                        );
                    },
                    (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
                        span_lint(
                            cx,
                            NEEDLESS_BOOL,
                            e.span,
                            "this if-then-else expression will always return false",
                        );
                    },
                    (RetBool(true), RetBool(false)) => reduce(true, false),
                    (Bool(true), Bool(false)) => reduce(false, false),
                    (RetBool(false), RetBool(true)) => reduce(true, true),
                    (Bool(false), Bool(true)) => reduce(false, true),
                    _ => (),
                }
            } else {
                panic!("IfExpr `then` node is not an `ExprKind::Block`");
            }
        }
    }
}

declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);

impl<'tcx> LateLintPass<'tcx> for BoolComparison {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if e.span.from_expansion() {
            return;
        }

        if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
            let ignore_case = None::<(fn(_) -> _, &str)>;
            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
            match node {
                BinOpKind::Eq => {
                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
                    let false_case = Some((
                        |h: Sugg<'_>| !h,
                        "equality checks against false can be replaced by a negation",
                    ));
                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
                },
                BinOpKind::Ne => {
                    let true_case = Some((
                        |h: Sugg<'_>| !h,
                        "inequality checks against true can be replaced by a negation",
                    ));
                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
                },
                BinOpKind::Lt => check_comparison(
                    cx,
                    e,
                    ignore_case,
                    Some((|h| h, "greater than checks against false are unnecessary")),
                    Some((
                        |h: Sugg<'_>| !h,
                        "less than comparison against true can be replaced by a negation",
                    )),
                    ignore_case,
                    Some((
                        |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
                        "order comparisons between booleans can be simplified",
                    )),
                ),
                BinOpKind::Gt => check_comparison(
                    cx,
                    e,
                    Some((
                        |h: Sugg<'_>| !h,
                        "less than comparison against true can be replaced by a negation",
                    )),
                    ignore_case,
                    ignore_case,
                    Some((|h| h, "greater than checks against false are unnecessary")),
                    Some((
                        |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
                        "order comparisons between booleans can be simplified",
                    )),
                ),
                _ => (),
            }
        }
    }
}

struct ExpressionInfoWithSpan {
    one_side_is_unary_not: bool,
    left_span: Span,
    right_span: Span,
}

fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
    if let ExprKind::Unary(UnOp::Not, operand) = e.kind {
        return (true, operand.span);
    }
    (false, e.span)
}

fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
    let left = is_unary_not(left_side);
    let right = is_unary_not(right_side);

    ExpressionInfoWithSpan {
        one_side_is_unary_not: left.0 != right.0,
        left_span: left.1,
        right_span: right.1,
    }
}

fn check_comparison<'a, 'tcx>(
    cx: &LateContext<'tcx>,
    e: &'tcx Expr<'_>,
    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
) {
    use self::Expression::{Bool, Other};

    if let ExprKind::Binary(op, left_side, right_side) = e.kind {
        let (l_ty, r_ty) = (
            cx.typeck_results().expr_ty(left_side),
            cx.typeck_results().expr_ty(right_side),
        );
        if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() {
            return;
        }
        if l_ty.is_bool() && r_ty.is_bool() {
            let mut applicability = Applicability::MachineApplicable;

            if let BinOpKind::Eq = op.node {
                let expression_info = one_side_is_unary_not(left_side, right_side);
                if expression_info.one_side_is_unary_not {
                    span_lint_and_sugg(
                        cx,
                        BOOL_COMPARISON,
                        e.span,
                        "this comparison might be written more concisely",
                        "try simplifying it as shown",
                        format!(
                            "{} != {}",
                            snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
                            snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
                        ),
                        applicability,
                    );
                }
            }

            match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                }),
                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                }),
                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                }),
                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
                    suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                }),
                (Other, Other) => no_literal.map_or((), |(h, m)| {
                    let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
                    let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
                    span_lint_and_sugg(
                        cx,
                        BOOL_COMPARISON,
                        e.span,
                        m,
                        "try simplifying it as shown",
                        h(left_side, right_side).to_string(),
                        applicability,
                    );
                }),
                _ => (),
            }
        }
    }
}

fn suggest_bool_comparison<'a, 'tcx>(
    cx: &LateContext<'tcx>,
    e: &'tcx Expr<'_>,
    expr: &Expr<'_>,
    mut applicability: Applicability,
    message: &str,
    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) {
    let hint = if expr.span.from_expansion() {
        if applicability != Applicability::Unspecified {
            applicability = Applicability::MaybeIncorrect;
        }
        Sugg::hir_with_macro_callsite(cx, expr, "..")
    } else {
        Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
    };
    span_lint_and_sugg(
        cx,
        BOOL_COMPARISON,
        e.span,
        message,
        "try simplifying it as shown",
        conv_hint(hint).to_string(),
        applicability,
    );
}

enum Expression {
    Bool(bool),
    RetBool(bool),
    Other,
}

fn fetch_bool_block(block: &Block<'_>) -> Expression {
    match (&*block.stmts, block.expr.as_ref()) {
        (&[], Some(e)) => fetch_bool_expr(&**e),
        (&[ref e], None) => {
            if let StmtKind::Semi(e) = e.kind {
                if let ExprKind::Ret(_) = e.kind {
                    fetch_bool_expr(e)
                } else {
                    Expression::Other
                }
            } else {
                Expression::Other
            }
        },
        _ => Expression::Other,
    }
}

fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
    match expr.kind {
        ExprKind::Block(block, _) => fetch_bool_block(block),
        ExprKind::Lit(ref lit_ptr) => {
            if let LitKind::Bool(value) = lit_ptr.node {
                Expression::Bool(value)
            } else {
                Expression::Other
            }
        },
        ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) {
            Expression::Bool(value) => Expression::RetBool(value),
            _ => Expression::Other,
        },
        _ => Expression::Other,
    }
}
<|endoftext|>


<commit_before>pub use super::*;

use crate::dataflow::generic::{Results, ResultsRefCursor};
use crate::dataflow::BitDenotation;
use crate::dataflow::MaybeBorrowedLocals;
use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use std::cell::RefCell;

#[derive(Copy, Clone)]
pub struct MaybeStorageLive<'a, 'tcx> {
    body: &'a Body<'tcx>,
}

impl<'a, 'tcx> MaybeStorageLive<'a, 'tcx> {
    pub fn new(body: &'a Body<'tcx>) -> Self {
        MaybeStorageLive { body }
    }

    pub fn body(&self) -> &Body<'tcx> {
        self.body
    }
}

impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str {
        "maybe_storage_live"
    }
    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
        // The resume argument is live on function entry (we don't care about
        // the `self` argument)
        for arg in self.body.args_iter().skip(1) {
            on_entry.insert(arg);
        }
    }

    fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];

        match stmt.kind {
            StatementKind::StorageLive(l) => trans.gen(l),
            StatementKind::StorageDead(l) => trans.kill(l),
            _ => (),
        }
    }

    fn terminator_effect(&self, _trans: &mut GenKillSet<Local>, _loc: Location) {
        // Terminators have no effect
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
    /// bottom = dead
    const BOTTOM_VALUE: bool = false;
}

type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;

/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct RequiresStorage<'mir, 'tcx> {
    body: ReadOnlyBodyAndCache<'mir, 'tcx>,
    borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
}

impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
    pub fn new(
        body: ReadOnlyBodyAndCache<'mir, 'tcx>,
        borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
    ) -> Self {
        RequiresStorage {
            body,
            borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
        }
    }

    pub fn body(&self) -> &Body<'tcx> {
        &self.body
    }
}

impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str {
        "requires_storage"
    }
    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
        // The resume argument is live on function entry (we don't care about
        // the `self` argument)
        for arg in self.body.args_iter().skip(1) {
            on_entry.insert(arg);
        }
    }

    fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];

        // If a place is borrowed in a statement, it needs storage for that statement.
        self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);

        // If a place is assigned to in a statement, it needs storage for that statement.
        match stmt.kind {
            StatementKind::StorageDead(l) => sets.kill(l),
            StatementKind::Assign(box (ref place, _))
            | StatementKind::SetDiscriminant { box ref place, .. } => {
                sets.gen(place.local);
            }
            StatementKind::InlineAsm(box InlineAsm { ref outputs, .. }) => {
                for place in &**outputs {
                    sets.gen(place.local);
                }
            }

            // Nothing to do for these. Match exhaustively so this fails to compile when new
            // variants are added.
            StatementKind::AscribeUserType(..)
            | StatementKind::FakeRead(..)
            | StatementKind::Nop
            | StatementKind::Retag(..)
            | StatementKind::StorageLive(..) => {}
        }
    }

    fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        // If we move from a place then only stops needing storage *after*
        // that statement.
        self.check_for_move(sets, loc);
    }

    fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        let terminator = self.body[loc.block].terminator();

        // If a place is borrowed in a terminator, it needs storage for that terminator.
        self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);

        if let TerminatorKind::Call { destination: Some((place, _)), .. } = terminator.kind {
            sets.gen(place.local);
        }
    }

    fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        // For call terminators the destination requires storage for the call
        // and after the call returns successfully, but not after a panic.
        // Since `propagate_call_unwind` doesn't exist, we have to kill the
        // destination here, and then gen it again in `propagate_call_return`.
        if let TerminatorKind::Call { destination: Some((ref place, _)), .. } =
            self.body[loc.block].terminator().kind
        {
            if let Some(local) = place.as_local() {
                sets.kill(local);
            }
        }
        self.check_for_move(sets, loc);
    }

    fn propagate_call_return(
        &self,
        in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        dest_place: &mir::Place<'tcx>,
    ) {
        in_out.insert(dest_place.local);
    }
}

impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
    /// Kill locals that are fully moved and have not been borrowed.
    fn check_for_move(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        let mut visitor = MoveVisitor { sets, borrowed_locals: &self.borrowed_locals };
        visitor.visit_location(self.body, loc);
    }
}

impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
    /// bottom = dead
    const BOTTOM_VALUE: bool = false;
}

struct MoveVisitor<'a, 'mir, 'tcx> {
    borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
    sets: &'a mut GenKillSet<Local>,
}

impl<'a, 'mir: 'a, 'tcx> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx> {
    fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
        if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
            let mut borrowed_locals = self.borrowed_locals.borrow_mut();
            borrowed_locals.seek_before(loc);
            if !borrowed_locals.contains(*local) {
                self.sets.kill(*local);
            }
        }
    }
}
<commit_msg>Use match ergonomics to simplify match<commit_after>pub use super::*;

use crate::dataflow::generic::{Results, ResultsRefCursor};
use crate::dataflow::BitDenotation;
use crate::dataflow::MaybeBorrowedLocals;
use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use std::cell::RefCell;

#[derive(Copy, Clone)]
pub struct MaybeStorageLive<'a, 'tcx> {
    body: &'a Body<'tcx>,
}

impl<'a, 'tcx> MaybeStorageLive<'a, 'tcx> {
    pub fn new(body: &'a Body<'tcx>) -> Self {
        MaybeStorageLive { body }
    }

    pub fn body(&self) -> &Body<'tcx> {
        self.body
    }
}

impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str {
        "maybe_storage_live"
    }
    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
        // The resume argument is live on function entry (we don't care about
        // the `self` argument)
        for arg in self.body.args_iter().skip(1) {
            on_entry.insert(arg);
        }
    }

    fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];

        match stmt.kind {
            StatementKind::StorageLive(l) => trans.gen(l),
            StatementKind::StorageDead(l) => trans.kill(l),
            _ => (),
        }
    }

    fn terminator_effect(&self, _trans: &mut GenKillSet<Local>, _loc: Location) {
        // Terminators have no effect
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
    /// bottom = dead
    const BOTTOM_VALUE: bool = false;
}

type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;

/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct RequiresStorage<'mir, 'tcx> {
    body: ReadOnlyBodyAndCache<'mir, 'tcx>,
    borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
}

impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
    pub fn new(
        body: ReadOnlyBodyAndCache<'mir, 'tcx>,
        borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
    ) -> Self {
        RequiresStorage {
            body,
            borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
        }
    }

    pub fn body(&self) -> &Body<'tcx> {
        &self.body
    }
}

impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str {
        "requires_storage"
    }
    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
        // The resume argument is live on function entry (we don't care about
        // the `self` argument)
        for arg in self.body.args_iter().skip(1) {
            on_entry.insert(arg);
        }
    }

    fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];

        // If a place is borrowed in a statement, it needs storage for that statement.
        self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);

        // If a place is assigned to in a statement, it needs storage for that statement.
        match &stmt.kind {
            StatementKind::StorageDead(l) => sets.kill(*l),
            StatementKind::Assign(box (place, _))
            | StatementKind::SetDiscriminant { box place, .. } => {
                sets.gen(place.local);
            }
            StatementKind::InlineAsm(box InlineAsm { outputs, .. }) => {
                for place in &**outputs {
                    sets.gen(place.local);
                }
            }

            // Nothing to do for these. Match exhaustively so this fails to compile when new
            // variants are added.
            StatementKind::AscribeUserType(..)
            | StatementKind::FakeRead(..)
            | StatementKind::Nop
            | StatementKind::Retag(..)
            | StatementKind::StorageLive(..) => {}
        }
    }

    fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        // If we move from a place then only stops needing storage *after*
        // that statement.
        self.check_for_move(sets, loc);
    }

    fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        let terminator = self.body[loc.block].terminator();

        // If a place is borrowed in a terminator, it needs storage for that terminator.
        self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);

        if let TerminatorKind::Call { destination: Some((place, _)), .. } = terminator.kind {
            sets.gen(place.local);
        }
    }

    fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        // For call terminators the destination requires storage for the call
        // and after the call returns successfully, but not after a panic.
        // Since `propagate_call_unwind` doesn't exist, we have to kill the
        // destination here, and then gen it again in `propagate_call_return`.
        if let TerminatorKind::Call { destination: Some((ref place, _)), .. } =
            self.body[loc.block].terminator().kind
        {
            if let Some(local) = place.as_local() {
                sets.kill(local);
            }
        }
        self.check_for_move(sets, loc);
    }

    fn propagate_call_return(
        &self,
        in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        dest_place: &mir::Place<'tcx>,
    ) {
        in_out.insert(dest_place.local);
    }
}

impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
    /// Kill locals that are fully moved and have not been borrowed.
    fn check_for_move(&self, sets: &mut GenKillSet<Local>, loc: Location) {
        let mut visitor = MoveVisitor { sets, borrowed_locals: &self.borrowed_locals };
        visitor.visit_location(self.body, loc);
    }
}

impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
    /// bottom = dead
    const BOTTOM_VALUE: bool = false;
}

struct MoveVisitor<'a, 'mir, 'tcx> {
    borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
    sets: &'a mut GenKillSet<Local>,
}

impl<'a, 'mir: 'a, 'tcx> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx> {
    fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
        if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
            let mut borrowed_locals = self.borrowed_locals.borrow_mut();
            borrowed_locals.seek_before(loc);
            if !borrowed_locals.contains(*local) {
                self.sets.kill(*local);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Propagate `Qualif`s between locals and query the results.
//!
//! This also contains the dataflow analysis used to track `Qualif`s on complex control-flow
//! graphs.

use rustc::mir::visit::Visitor;
use rustc::mir::{self, BasicBlock, Local, Location};
use rustc_index::bit_set::BitSet;

use std::cell::RefCell;
use std::marker::PhantomData;

use crate::dataflow::{self as old_dataflow, generic as dataflow};
use super::{Item, Qualif};
use self::old_dataflow::IndirectlyMutableLocals;

/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
/// `FlowSensitiveAnalysis` as well as the logic underlying `TempPromotionResolver`.
///
/// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
/// the `IndirectlyMutableLocals` dataflow pass to see if a `Local` may have become qualified via
/// an indirect assignment or function call.
struct TransferFunction<'a, 'mir, 'tcx, Q> {
    item: &'a Item<'mir, 'tcx>,
    qualifs_per_local: &'a mut BitSet<Local>,

    _qualif: PhantomData<Q>,
}

impl<Q> TransferFunction<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    fn new(
        item: &'a Item<'mir, 'tcx>,
        qualifs_per_local: &'a mut BitSet<Local>,
    ) -> Self {
        TransferFunction {
            item,
            qualifs_per_local,
            _qualif: PhantomData,
        }
    }

    fn initialize_state(&mut self) {
        self.qualifs_per_local.clear();

        for arg in self.item.body.args_iter() {
            let arg_ty = self.item.body.local_decls[arg].ty;
            if Q::in_any_value_of_ty(self.item, arg_ty) {
                self.qualifs_per_local.insert(arg);
            }
        }
    }

    fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) {
        debug_assert!(!place.is_indirect());

        match (value, place) {
            (true, mir::Place { base: mir::PlaceBase::Local(local), .. }) => {
                self.qualifs_per_local.insert(*local);
            }

            // For now, we do not clear the qualif if a local is overwritten in full by
            // an unqualified rvalue (e.g. `y = 5`). This is to be consistent
            // with aggregates where we overwrite all fields with assignments, which would not
            // get this feature.
            (false, mir::Place { base: mir::PlaceBase::Local(_local), projection: box [] }) => {
                // self.qualifs_per_local.remove(*local);
            }

            _ => {}
        }
    }

    fn apply_call_return_effect(
        &mut self,
        _block: BasicBlock,
        func: &mir::Operand<'tcx>,
        args: &[mir::Operand<'tcx>],
        return_place: &mir::Place<'tcx>,
    ) {
        let return_ty = return_place.ty(self.item.body, self.item.tcx).ty;
        let qualif = Q::in_call(self.item, &mut self.qualifs_per_local, func, args, return_ty);
        if !return_place.is_indirect() {
            self.assign_qualif_direct(return_place, qualif);
        }
    }
}

impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
where
    Q: Qualif,
{
    fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
        self.super_operand(operand, location);

        if !Q::IS_CLEARED_ON_MOVE {
            return;
        }

        // If a local with no projections is moved from (e.g. `x` in `y = x`), record that
        // it no longer needs to be dropped.
        if let mir::Operand::Move(mir::Place {
            base: mir::PlaceBase::Local(local),
            projection: box [],
        }) = *operand {
            self.qualifs_per_local.remove(local);
        }
    }

    fn visit_assign(
        &mut self,
        place: &mir::Place<'tcx>,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        let qualif = Q::in_rvalue(self.item, self.qualifs_per_local, rvalue);
        if !place.is_indirect() {
            self.assign_qualif_direct(place, qualif);
        }

        // We need to assign qualifs to the left-hand side before visiting `rvalue` since
        // qualifs can be cleared on move.
        self.super_assign(place, rvalue, location);
    }

    fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) {
        // The effect of assignment to the return place in `TerminatorKind::Call` is not applied
        // here; that occurs in `apply_call_return_effect`.

        if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind {
            let qualif = Q::in_operand(self.item, self.qualifs_per_local, value);
            if !dest.is_indirect() {
                self.assign_qualif_direct(dest, qualif);
            }
        }

        // We need to assign qualifs to the dropped location before visiting the operand that
        // replaces it since qualifs can be cleared on move.
        self.super_terminator_kind(kind, location);
    }
}

/// Types that can compute the qualifs of each local at each location in a `mir::Body`.
///
/// Code that wishes to use a `QualifResolver` must call `visit_{statement,terminator}` for each
/// statement or terminator, processing blocks in reverse post-order beginning from the
/// `START_BLOCK`. Calling code may optionally call `get` after visiting each statement or
/// terminator to query the qualification state immediately before that statement or terminator.
///
/// These conditions are much more restrictive than woud be required by `FlowSensitiveResolver`
/// alone. This is to allow a linear, on-demand `TempPromotionResolver` that can operate
/// efficiently on simple CFGs.
pub trait QualifResolver<Q> {
    /// Get the qualifs of each local at the last location visited.
    ///
    /// This takes `&mut self` so qualifs can be computed lazily.
    fn get(&mut self) -> &BitSet<Local>;

    /// A convenience method for `self.get().contains(local)`.
    fn contains(&mut self, local: Local) -> bool {
        self.get().contains(local)
    }

    /// Resets the resolver to the `START_BLOCK`. This allows a resolver to be reused
    /// for multiple passes over a `mir::Body`.
    fn reset(&mut self);
}

pub type IndirectlyMutableResults<'mir, 'tcx> =
    old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;

/// A resolver for qualifs that works on arbitrarily complex CFGs.
///
/// As soon as a `Local` becomes writable through a reference (as determined by the
/// `IndirectlyMutableLocals` dataflow pass), we must assume that it takes on all other qualifs
/// possible for its type. This is because no effort is made to track qualifs across indirect
/// assignments (e.g. `*p = x` or calls to opaque functions).
///
/// It is possible to be more precise here by waiting until an indirect assignment actually occurs
/// before marking a borrowed `Local` as qualified.
pub struct FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    location: Location,
    indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
    cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
    qualifs_per_local: BitSet<Local>,

    /// The value of `Q::in_any_value_of_ty` for each local.
    qualifs_in_any_value_of_ty: BitSet<Local>,
}

impl<Q> FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    pub fn new(
        _: Q,
        item: &'a Item<'mir, 'tcx>,
        indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
        dead_unwinds: &BitSet<BasicBlock>,
    ) -> Self {
        let analysis = FlowSensitiveAnalysis {
            item,
            _qualif: PhantomData,
        };
        let results =
            dataflow::Engine::new(item.body, dead_unwinds, analysis).iterate_to_fixpoint();
        let cursor = dataflow::ResultsCursor::new(item.body, results);

        let mut qualifs_in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
        for (local, decl) in item.body.local_decls.iter_enumerated() {
            if Q::in_any_value_of_ty(item, decl.ty) {
                qualifs_in_any_value_of_ty.insert(local);
            }
        }

        FlowSensitiveResolver {
            cursor,
            indirectly_mutable_locals,
            qualifs_per_local: BitSet::new_empty(item.body.local_decls.len()),
            qualifs_in_any_value_of_ty,
            location: Location { block: mir::START_BLOCK, statement_index: 0 },
        }
    }
}

impl<Q> Visitor<'tcx> for FlowSensitiveResolver<'_, '_, 'tcx, Q>
where
    Q: Qualif
{
    fn visit_statement(&mut self, _: &mir::Statement<'tcx>, location: Location) {
        self.location = location;
    }

    fn visit_terminator(&mut self, _: &mir::Terminator<'tcx>, location: Location) {
        self.location = location;
    }
}

impl<Q> QualifResolver<Q> for FlowSensitiveResolver<'_, '_, '_, Q>
where
    Q: Qualif
{
    fn get(&mut self) -> &BitSet<Local> {
        let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();

        indirectly_mutable_locals.seek(self.location);
        self.cursor.seek_before(self.location);

        self.qualifs_per_local.overwrite(indirectly_mutable_locals.get());
        self.qualifs_per_local.union(self.cursor.get());
        self.qualifs_per_local.intersect(&self.qualifs_in_any_value_of_ty);
        &self.qualifs_per_local
    }

    fn contains(&mut self, local: Local) -> bool {
        // No need to update the cursor if we know that `Local` cannot possibly be qualified.
        if !self.qualifs_in_any_value_of_ty.contains(local) {
            return false;
        }

        // Otherwise, return `true` if this local is qualified or was indirectly mutable at any
        // point before this statement.
        self.cursor.seek_before(self.location);
        if self.cursor.get().contains(local) {
            return true;
        }

        let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();
        indirectly_mutable_locals.seek(self.location);
        indirectly_mutable_locals.get().contains(local)
    }

    fn reset(&mut self)  {
        self.location = Location { block: mir::START_BLOCK, statement_index: 0 };
    }
}

/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
    item: &'a Item<'mir, 'tcx>,
    _qualif: PhantomData<Q>,
}

impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    fn transfer_function(
        &self,
        state: &'a mut BitSet<Local>,
    ) -> TransferFunction<'a, 'mir, 'tcx, Q> {
        TransferFunction::<Q>::new(self.item, state)
    }
}

impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
    const BOTTOM_VALUE: bool = false;
}

impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
where
    Q: Qualif,
{
    type Idx = Local;

    const NAME: &'static str = "flow_sensitive_qualif";

    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
        body.local_decls.len()
    }

    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
        self.transfer_function(state).initialize_state();
    }

    fn apply_statement_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(state).visit_statement(statement, location);
    }

    fn apply_terminator_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(state).visit_terminator(terminator, location);
    }

    fn apply_call_return_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        block: BasicBlock,
        func: &mir::Operand<'tcx>,
        args: &[mir::Operand<'tcx>],
        return_place: &mir::Place<'tcx>,
    ) {
        self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
    }
}
<commit_msg>Update consumers of `generic::Engine` API<commit_after>//! Propagate `Qualif`s between locals and query the results.
//!
//! This also contains the dataflow analysis used to track `Qualif`s on complex control-flow
//! graphs.

use rustc::mir::visit::Visitor;
use rustc::mir::{self, BasicBlock, Local, Location};
use rustc_index::bit_set::BitSet;

use std::cell::RefCell;
use std::marker::PhantomData;

use crate::dataflow::{self as old_dataflow, generic as dataflow};
use super::{Item, Qualif};
use self::old_dataflow::IndirectlyMutableLocals;

/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
/// `FlowSensitiveAnalysis` as well as the logic underlying `TempPromotionResolver`.
///
/// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
/// the `IndirectlyMutableLocals` dataflow pass to see if a `Local` may have become qualified via
/// an indirect assignment or function call.
struct TransferFunction<'a, 'mir, 'tcx, Q> {
    item: &'a Item<'mir, 'tcx>,
    qualifs_per_local: &'a mut BitSet<Local>,

    _qualif: PhantomData<Q>,
}

impl<Q> TransferFunction<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    fn new(
        item: &'a Item<'mir, 'tcx>,
        qualifs_per_local: &'a mut BitSet<Local>,
    ) -> Self {
        TransferFunction {
            item,
            qualifs_per_local,
            _qualif: PhantomData,
        }
    }

    fn initialize_state(&mut self) {
        self.qualifs_per_local.clear();

        for arg in self.item.body.args_iter() {
            let arg_ty = self.item.body.local_decls[arg].ty;
            if Q::in_any_value_of_ty(self.item, arg_ty) {
                self.qualifs_per_local.insert(arg);
            }
        }
    }

    fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) {
        debug_assert!(!place.is_indirect());

        match (value, place) {
            (true, mir::Place { base: mir::PlaceBase::Local(local), .. }) => {
                self.qualifs_per_local.insert(*local);
            }

            // For now, we do not clear the qualif if a local is overwritten in full by
            // an unqualified rvalue (e.g. `y = 5`). This is to be consistent
            // with aggregates where we overwrite all fields with assignments, which would not
            // get this feature.
            (false, mir::Place { base: mir::PlaceBase::Local(_local), projection: box [] }) => {
                // self.qualifs_per_local.remove(*local);
            }

            _ => {}
        }
    }

    fn apply_call_return_effect(
        &mut self,
        _block: BasicBlock,
        func: &mir::Operand<'tcx>,
        args: &[mir::Operand<'tcx>],
        return_place: &mir::Place<'tcx>,
    ) {
        let return_ty = return_place.ty(self.item.body, self.item.tcx).ty;
        let qualif = Q::in_call(self.item, &mut self.qualifs_per_local, func, args, return_ty);
        if !return_place.is_indirect() {
            self.assign_qualif_direct(return_place, qualif);
        }
    }
}

impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
where
    Q: Qualif,
{
    fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
        self.super_operand(operand, location);

        if !Q::IS_CLEARED_ON_MOVE {
            return;
        }

        // If a local with no projections is moved from (e.g. `x` in `y = x`), record that
        // it no longer needs to be dropped.
        if let mir::Operand::Move(mir::Place {
            base: mir::PlaceBase::Local(local),
            projection: box [],
        }) = *operand {
            self.qualifs_per_local.remove(local);
        }
    }

    fn visit_assign(
        &mut self,
        place: &mir::Place<'tcx>,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        let qualif = Q::in_rvalue(self.item, self.qualifs_per_local, rvalue);
        if !place.is_indirect() {
            self.assign_qualif_direct(place, qualif);
        }

        // We need to assign qualifs to the left-hand side before visiting `rvalue` since
        // qualifs can be cleared on move.
        self.super_assign(place, rvalue, location);
    }

    fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) {
        // The effect of assignment to the return place in `TerminatorKind::Call` is not applied
        // here; that occurs in `apply_call_return_effect`.

        if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind {
            let qualif = Q::in_operand(self.item, self.qualifs_per_local, value);
            if !dest.is_indirect() {
                self.assign_qualif_direct(dest, qualif);
            }
        }

        // We need to assign qualifs to the dropped location before visiting the operand that
        // replaces it since qualifs can be cleared on move.
        self.super_terminator_kind(kind, location);
    }
}

/// Types that can compute the qualifs of each local at each location in a `mir::Body`.
///
/// Code that wishes to use a `QualifResolver` must call `visit_{statement,terminator}` for each
/// statement or terminator, processing blocks in reverse post-order beginning from the
/// `START_BLOCK`. Calling code may optionally call `get` after visiting each statement or
/// terminator to query the qualification state immediately before that statement or terminator.
///
/// These conditions are much more restrictive than woud be required by `FlowSensitiveResolver`
/// alone. This is to allow a linear, on-demand `TempPromotionResolver` that can operate
/// efficiently on simple CFGs.
pub trait QualifResolver<Q> {
    /// Get the qualifs of each local at the last location visited.
    ///
    /// This takes `&mut self` so qualifs can be computed lazily.
    fn get(&mut self) -> &BitSet<Local>;

    /// A convenience method for `self.get().contains(local)`.
    fn contains(&mut self, local: Local) -> bool {
        self.get().contains(local)
    }

    /// Resets the resolver to the `START_BLOCK`. This allows a resolver to be reused
    /// for multiple passes over a `mir::Body`.
    fn reset(&mut self);
}

pub type IndirectlyMutableResults<'mir, 'tcx> =
    old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;

/// A resolver for qualifs that works on arbitrarily complex CFGs.
///
/// As soon as a `Local` becomes writable through a reference (as determined by the
/// `IndirectlyMutableLocals` dataflow pass), we must assume that it takes on all other qualifs
/// possible for its type. This is because no effort is made to track qualifs across indirect
/// assignments (e.g. `*p = x` or calls to opaque functions).
///
/// It is possible to be more precise here by waiting until an indirect assignment actually occurs
/// before marking a borrowed `Local` as qualified.
pub struct FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    location: Location,
    indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
    cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
    qualifs_per_local: BitSet<Local>,

    /// The value of `Q::in_any_value_of_ty` for each local.
    qualifs_in_any_value_of_ty: BitSet<Local>,
}

impl<Q> FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    pub fn new(
        _: Q,
        item: &'a Item<'mir, 'tcx>,
        indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
        dead_unwinds: &BitSet<BasicBlock>,
    ) -> Self {
        let analysis = FlowSensitiveAnalysis {
            item,
            _qualif: PhantomData,
        };
        let results =
            dataflow::Engine::new(item.tcx, item.body, item.def_id, dead_unwinds, analysis)
                .iterate_to_fixpoint();
        let cursor = dataflow::ResultsCursor::new(item.body, results);

        let mut qualifs_in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
        for (local, decl) in item.body.local_decls.iter_enumerated() {
            if Q::in_any_value_of_ty(item, decl.ty) {
                qualifs_in_any_value_of_ty.insert(local);
            }
        }

        FlowSensitiveResolver {
            cursor,
            indirectly_mutable_locals,
            qualifs_per_local: BitSet::new_empty(item.body.local_decls.len()),
            qualifs_in_any_value_of_ty,
            location: Location { block: mir::START_BLOCK, statement_index: 0 },
        }
    }
}

impl<Q> Visitor<'tcx> for FlowSensitiveResolver<'_, '_, 'tcx, Q>
where
    Q: Qualif
{
    fn visit_statement(&mut self, _: &mir::Statement<'tcx>, location: Location) {
        self.location = location;
    }

    fn visit_terminator(&mut self, _: &mir::Terminator<'tcx>, location: Location) {
        self.location = location;
    }
}

impl<Q> QualifResolver<Q> for FlowSensitiveResolver<'_, '_, '_, Q>
where
    Q: Qualif
{
    fn get(&mut self) -> &BitSet<Local> {
        let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();

        indirectly_mutable_locals.seek(self.location);
        self.cursor.seek_before(self.location);

        self.qualifs_per_local.overwrite(indirectly_mutable_locals.get());
        self.qualifs_per_local.union(self.cursor.get());
        self.qualifs_per_local.intersect(&self.qualifs_in_any_value_of_ty);
        &self.qualifs_per_local
    }

    fn contains(&mut self, local: Local) -> bool {
        // No need to update the cursor if we know that `Local` cannot possibly be qualified.
        if !self.qualifs_in_any_value_of_ty.contains(local) {
            return false;
        }

        // Otherwise, return `true` if this local is qualified or was indirectly mutable at any
        // point before this statement.
        self.cursor.seek_before(self.location);
        if self.cursor.get().contains(local) {
            return true;
        }

        let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();
        indirectly_mutable_locals.seek(self.location);
        indirectly_mutable_locals.get().contains(local)
    }

    fn reset(&mut self)  {
        self.location = Location { block: mir::START_BLOCK, statement_index: 0 };
    }
}

/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
    item: &'a Item<'mir, 'tcx>,
    _qualif: PhantomData<Q>,
}

impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
where
    Q: Qualif,
{
    fn transfer_function(
        &self,
        state: &'a mut BitSet<Local>,
    ) -> TransferFunction<'a, 'mir, 'tcx, Q> {
        TransferFunction::<Q>::new(self.item, state)
    }
}

impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
    const BOTTOM_VALUE: bool = false;
}

impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
where
    Q: Qualif,
{
    type Idx = Local;

    const NAME: &'static str = "flow_sensitive_qualif";

    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
        body.local_decls.len()
    }

    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
        self.transfer_function(state).initialize_state();
    }

    fn apply_statement_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(state).visit_statement(statement, location);
    }

    fn apply_terminator_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(state).visit_terminator(terminator, location);
    }

    fn apply_call_return_effect(
        &self,
        state: &mut BitSet<Self::Idx>,
        block: BasicBlock,
        func: &mir::Operand<'tcx>,
        args: &[mir::Operand<'tcx>],
        return_place: &mir::Place<'tcx>,
    ) {
        self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
    }
}
<|endoftext|>


<commit_before>/// `ExpectedVersion` represents the different modes of optimistic locking when writing to a stream
/// using `WriteEventsBuilder`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ExpectedVersion {
    /// No optimistic locking
    Any,
    /// Expect a stream not to exist
    NoStream,
    /// Expect exact number of events in the stream
    Exact(u32)
}

impl Into<i32> for ExpectedVersion {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        use self::ExpectedVersion::*;
        match self {
            Any => -2,
            NoStream => -1,
            Exact(ver) => ver as i32
        }
    }
}
<commit_msg>expected-version: Replace the Into impl by a From impl<commit_after>/// `ExpectedVersion` represents the different modes of optimistic locking when writing to a stream
/// using `WriteEventsBuilder`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ExpectedVersion {
    /// No optimistic locking
    Any,
    /// Expect a stream not to exist
    NoStream,
    /// Expect exact number of events in the stream
    Exact(u32)
}

impl From<ExpectedVersion> for i32 {
    /// Returns the wire representation.
    fn from(version: ExpectedVersion) -> Self {
        use self::ExpectedVersion::*;
        match version {
            Any => -2,
            NoStream => -1,
            Exact(ver) => ver as i32
        }
    }
}
<|endoftext|>