<commit_before>//! Render combinatory blocks.
//!
//! Render blocks are logical rendering units that have inputs and outputs. Inputs can represent
//! vertex attributes, user-specified values, built-ins or previous blocks’ outputs.

use glsl::syntax::ExternalDeclaration;
use std::iter::once;

use crate::render::input::{Input, inputs_to_struct_decl};
use crate::render::output::{Output, outputs_to_struct_decl};

/// A render block, allowing for combining blocks in order to create more complex rendering
/// computations.
#[derive(Clone, Debug)]
struct Block {
  /// Unique identifier of the block. Used to get mangled names for structures and functions.
  id: String,
  inputs: Vec<Input>,
  outputs: Vec<Output>,
  code: Vec<ExternalDeclaration>
}

impl Block {
  /// Create a new block out of inputs, outputs and GLSL code.
  pub fn new<S, I, O, C>(id: S, inputs: I, outputs: O, code: C) -> Self
  where S: Into<String>,
        I: IntoIterator<Item = Input>,
        O: IntoIterator<Item = Output>,
        C: IntoIterator<Item = ExternalDeclaration> {
    Block {
      id: id.into(),
      inputs: inputs.into_iter().collect(),
      outputs: outputs.into_iter().collect(),
      code: code.into_iter().collect()
    }
  }

  /// Turn a block into its GLSL header representation.
  ///
  /// The GLSL header contains the inputs and the outputs struct definitions.
  pub fn to_glsl_header(&self) -> Vec<ExternalDeclaration> {
    // get the input and output GLSL representations
    let input_struct = inputs_to_struct_decl("In", &self.inputs);
    let output_struct = outputs_to_struct_decl("Out", &self.outputs);

    once(input_struct).chain(once(output_struct)).flatten().collect()
  }
}

#[cfg(test)]
mod tests {
  use glsl_quasiquote::glsl;

  use crate::render::types::*;
  use super::*;

  #[test]
  fn block_to_glsl_header() {
    use crate::render::input::Input;
    use crate::render::output::Output;

    let inputs = vec![Input::new::<Float, _>("time"), Input::new::<RGBAU, _>("bias")];
    let outputs = vec![Output::new::<RGBF, _>("color")];
    let code = vec![];
    let block = Block::new("simple", inputs, outputs, code);
    let expected = glsl!{
      struct In {
        float time;
        uvec4 bias;
      };

      struct Out {
        vec3 color;
      };
    };

    assert_eq!(block.to_glsl_header(), expected);
  }
}
<commit_msg>Premise of Block::to_glsl. #53<commit_after>//! Render combinatory blocks.
//!
//! Render blocks are logical rendering units that have inputs and outputs. Inputs can represent
//! vertex attributes, user-specified values, built-ins or previous blocks’ outputs.

use glsl::syntax::ExternalDeclaration;
use std::iter::once;

use crate::render::input::{Input, inputs_to_struct_decl};
use crate::render::output::{Output, outputs_to_struct_decl};

/// A render block, allowing for combining blocks in order to create more complex rendering
/// computations.
#[derive(Clone, Debug)]
struct Block {
  /// Unique identifier of the block. Used to get mangled names for structures and functions.
  id: String,
  inputs: Vec<Input>,
  outputs: Vec<Output>,
  code: Vec<ExternalDeclaration>
}

impl Block {
  /// Create a new block out of inputs, outputs and GLSL code.
  pub fn new<S, I, O, C>(id: S, inputs: I, outputs: O, code: C) -> Self
  where S: Into<String>,
        I: IntoIterator<Item = Input>,
        O: IntoIterator<Item = Output>,
        C: IntoIterator<Item = ExternalDeclaration> {
    Block {
      id: id.into(),
      inputs: inputs.into_iter().collect(),
      outputs: outputs.into_iter().collect(),
      code: code.into_iter().collect()
    }
  }

  /// Turn a block into its GLSL header representation.
  ///
  /// The GLSL header contains the inputs and the outputs struct definitions.
  fn to_glsl_header(&self) -> Vec<ExternalDeclaration> {
    // get the input and output GLSL representations
    let input_struct = inputs_to_struct_decl("In", &self.inputs);
    let output_struct = outputs_to_struct_decl("Out", &self.outputs);

    once(input_struct).chain(once(output_struct)).flatten().collect()
  }

  /// Turn a block into its GLSL full representation.
  ///
  /// The full GLSL representation includes:
  ///
  ///   - All intermediates structures, constants and functions.
  ///   - The *input* and *output* structures, renamed to:
  ///     - `In` becomes `In_<blockid>`. For instance, the block with ID `"Blur"` has its input type
  ///        going from `In` to `In_Blur`.
  ///     - `Out` becomes `Out_<blockid>`. For instance, the block with ID `"Blur"` has its output
  ///        type going from `Out` to `Out_Blur`.
  ///     - The function named `call` is searched for in the *code* part of the block and renamed
  ///       `call_<blockid>`. For instance, the block with ID `"Blur"` has its `call` function
  ///       renamed `call_Blur`.
  ///   - All references to `In`, `Out` and `call` are replaced with the appropriate new name.
  fn to_glsl(&self) -> Vec<ExternalDeclaration> {
    unimplemented!()
  }
}

#[cfg(test)]
mod tests {
  use glsl_quasiquote::glsl;

  use crate::render::types::*;
  use super::*;

  #[test]
  fn block_to_glsl_header() {
    use crate::render::input::Input;
    use crate::render::output::Output;

    let inputs = vec![Input::new::<Float, _>("time"), Input::new::<RGBAU, _>("bias")];
    let outputs = vec![Output::new::<RGBF, _>("color")];
    let code = vec![];
    let block = Block::new("simple", inputs, outputs, code);
    let expected = glsl!{
      struct In {
        float time;
        uvec4 bias;
      };

      struct Out {
        vec3 color;
      };
    };

    assert_eq!(block.to_glsl_header(), expected);
  }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::error::Error;

use pg::Pg;
use types::{self, FromSql, IsNull, ToSql, ToSqlOutput};

impl FromSql<types::Bool, Pg> for bool {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error + Send + Sync>> {
        match bytes {
            Some(bytes) => Ok(bytes[0] != 0),
            None => Ok(false),
        }
    }
}

impl ToSql<types::Bool, Pg> for bool {
    fn to_sql<W: Write>(
        &self,
        out: &mut ToSqlOutput<W, Pg>,
    ) -> Result<IsNull, Box<Error + Send + Sync>> {
        let write_result = if *self {
            out.write_all(&[1])
        } else {
            out.write_all(&[0])
        };
        write_result
            .map(|_| IsNull::No)
            .map_err(|e| Box::new(e) as Box<Error + Send + Sync>)
    }
}

#[test]
fn bool_to_sql() {
    let mut bytes = ToSqlOutput::test();
    ToSql::<types::Bool, Pg>::to_sql(&true, &mut bytes).unwrap();
    ToSql::<types::Bool, Pg>::to_sql(&false, &mut bytes).unwrap();
    assert_eq!(bytes, vec![1u8, 0u8]);
}

#[test]
fn bool_from_sql_treats_null_as_false() {
    let result = <bool as FromSql<types::Bool, Pg>>::from_sql(None).unwrap();
    assert!(!result);
}
<commit_msg>Refactor the `ToSql` implementation of `bool` on PG<commit_after>use std::io::prelude::*;
use std::error::Error;

use pg::Pg;
use types::{self, FromSql, IsNull, ToSql, ToSqlOutput};

impl FromSql<types::Bool, Pg> for bool {
    fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error + Send + Sync>> {
        match bytes {
            Some(bytes) => Ok(bytes[0] != 0),
            None => Ok(false),
        }
    }
}

impl ToSql<types::Bool, Pg> for bool {
    fn to_sql<W: Write>(
        &self,
        out: &mut ToSqlOutput<W, Pg>,
    ) -> Result<IsNull, Box<Error + Send + Sync>> {
        out.write_all(&[*self as u8])
            .map(|_| IsNull::No)
            .map_err(|e| Box::new(e) as Box<Error + Send + Sync>)
    }
}

#[test]
fn bool_to_sql() {
    let mut bytes = ToSqlOutput::test();
    ToSql::<types::Bool, Pg>::to_sql(&true, &mut bytes).unwrap();
    ToSql::<types::Bool, Pg>::to_sql(&false, &mut bytes).unwrap();
    assert_eq!(bytes, vec![1u8, 0u8]);
}

#[test]
fn bool_from_sql_treats_null_as_false() {
    let result = <bool as FromSql<types::Bool, Pg>>::from_sql(None).unwrap();
    assert!(!result);
}
<|endoftext|>


<commit_before>use std::fmt;

use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::util::lev_distance::lev_distance;
use crate::util::{Config, VersionExt};
use anyhow::Error;

use super::context::Context;
use super::types::{ConflictMap, ConflictReason};

/// Error during resolution providing a path of `PackageId`s.
pub struct ResolveError {
    cause: Error,
    package_path: Vec<PackageId>,
}

impl ResolveError {
    pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
        Self {
            cause: cause.into(),
            package_path,
        }
    }

    /// Returns a path of packages from the package whose requirements could not be resolved up to
    /// the root.
    pub fn package_path(&self) -> &[PackageId] {
        &self.package_path
    }
}

impl std::error::Error for ResolveError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

pub type ActivateResult<T> = Result<T, ActivateError>;

#[derive(Debug)]
pub enum ActivateError {
    Fatal(anyhow::Error),
    Conflict(PackageId, ConflictReason),
}

impl From<::anyhow::Error> for ActivateError {
    fn from(t: ::anyhow::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

pub(super) fn activation_error(
    cx: &Context,
    registry: &mut dyn Registry,
    parent: &Summary,
    dep: &Dependency,
    conflicting_activations: &ConflictMap,
    candidates: &[Summary],
    config: Option<&Config>,
) -> ResolveError {
    let to_resolve_err = |err| {
        ResolveError::new(
            err,
            cx.parents
                .path_to_bottom(&parent.package_id())
                .into_iter()
                .map(|(node, _)| node)
                .cloned()
                .collect(),
        )
    };

    if !candidates.is_empty() {
        let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
        msg.push_str("\n    ... required by ");
        msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

        msg.push_str("\nversions that meet the requirements `");
        msg.push_str(&dep.version_req().to_string());
        msg.push_str("` are: ");
        msg.push_str(
            &candidates
                .iter()
                .map(|v| v.version())
                .map(|v| v.to_string())
                .collect::<Vec<_>>()
                .join(", "),
        );

        let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
        conflicting_activations.sort_unstable();
        // This is reversed to show the newest versions first. I don't know if there is
        // a strong reason to do this, but that is how the code previously worked
        // (see https://github.com/rust-lang/cargo/pull/5037) and I don't feel like changing it.
        conflicting_activations.reverse();
        // Flag used for grouping all semver errors together.
        let mut has_semver = false;

        for (p, r) in &conflicting_activations {
            match r {
                ConflictReason::Semver => {
                    has_semver = true;
                }
                ConflictReason::Links(link) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` links to the native library `");
                    msg.push_str(link);
                    msg.push_str("`, but it conflicts with a previous package which links to `");
                    msg.push_str(link);
                    msg.push_str("` as well:\n");
                    msg.push_str(&describe_path_in_context(cx, p));
                    msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
                    msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
                }
                ConflictReason::MissingFeatures(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    // p == parent so the full path is redundant.
                }
                ConflictReason::RequiredDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has a required dependency with that name, \
                         but only optional dependencies can be used as features.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::NonImplicitDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has an optional dependency with that name, \
                         but but that dependency uses the \"dep:\" \
                         syntax in the features table, so it does not have an \
                         implicit feature with that name.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::PublicDependency(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub dep {:?}", pkg_id);
                }
                ConflictReason::PubliclyExports(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub exp {:?}", pkg_id);
                }
            }
        }

        if has_semver {
            // Group these errors together.
            msg.push_str("\n\nall possible versions conflict with previously selected packages.");
            for (p, r) in &conflicting_activations {
                if let ConflictReason::Semver = r {
                    msg.push_str("\n\n  previously selected ");
                    msg.push_str(&describe_path_in_context(cx, p));
                }
            }
        }

        msg.push_str("\n\nfailed to select a version for `");
        msg.push_str(&*dep.package_name());
        msg.push_str("` which could resolve this conflict");

        return to_resolve_err(anyhow::format_err!("{}", msg));
    }

    // We didn't actually find any candidates, so we need to
    // give an error message that nothing was found.
    //
    // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
    // was meant. So we re-query the registry with `deb="*"` so we can
    // list a few versions that were actually found.
    let all_req = semver::VersionReq::parse("*").unwrap();
    let mut new_dep = dep.clone();
    new_dep.set_version_req(all_req);
    let mut candidates = match registry.query_vec(&new_dep, false) {
        Ok(candidates) => candidates,
        Err(e) => return to_resolve_err(e),
    };
    candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));

    let mut msg =
        if !candidates.is_empty() {
            let versions = {
                let mut versions = candidates
                    .iter()
                    .take(3)
                    .map(|cand| cand.version().to_string())
                    .collect::<Vec<_>>();

                if candidates.len() > 3 {
                    versions.push("...".into());
                }

                versions.join(", ")
            };

            let mut msg = format!(
                "failed to select a version for the requirement `{} = \"{}\"`\n\
                 candidate versions found which didn't match: {}\n\
                 location searched: {}\n",
                dep.package_name(),
                dep.version_req(),
                versions,
                registry.describe_source(dep.source_id()),
            );
            msg.push_str("required by ");
            msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

            // If we have a path dependency with a locked version, then this may
            // indicate that we updated a sub-package and forgot to run `cargo
            // update`. In this case try to print a helpful error!
            if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') {
                msg.push_str(
                    "\nconsider running `cargo update` to update \
                     a path dependency's locked version",
                );
            }

            if registry.is_replaced(dep.source_id()) {
                msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
            }

            msg
        } else {
            // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
            // was meant. So we try asking the registry for a `fuzzy` search for suggestions.
            let mut candidates = Vec::new();
            if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s), true) {
                return to_resolve_err(e);
            };
            candidates.sort_unstable_by_key(|a| a.name());
            candidates.dedup_by(|a, b| a.name() == b.name());
            let mut candidates: Vec<_> = candidates
                .iter()
                .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n))
                .filter(|&(d, _)| d < 4)
                .collect();
            candidates.sort_by_key(|o| o.0);
            let mut msg: String;
            if candidates.is_empty() {
                msg = format!("no matching package named `{}` found\n", dep.package_name());
            } else {
                msg = format!(
                    "no matching package found\nsearched package name: `{}`\n",
                    dep.package_name()
                );

                // If dependency package name is equal to the name of the candidate here
                // it may be a prerelease package which hasn't been specified correctly
                if dep.package_name() == candidates[0].1.name()
                    && candidates[0].1.package_id().version().is_prerelease()
                {
                    msg.push_str("prerelease package needs to be specified explicitly\n");
                    msg.push_str(&format!(
                        "{name} = {{ version = \"{version}\" }}",
                        name = candidates[0].1.name(),
                        version = candidates[0].1.package_id().version()
                    ));
                } else {
                    let mut names = candidates
                        .iter()
                        .take(3)
                        .map(|c| c.1.name().as_str())
                        .collect::<Vec<_>>();

                    if candidates.len() > 3 {
                        names.push("...");
                    }
                    // Vertically align first suggestion with missing crate name
                    // so a typo jumps out at you.
                    msg.push_str("perhaps you meant:      ");
                    msg.push_str(&names.iter().enumerate().fold(
                        String::default(),
                        |acc, (i, el)| match i {
                            0 => acc + el,
                            i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
                            _ => acc + ", " + el,
                        },
                    ));
                }
                msg.push('\n');
            }
            msg.push_str(&format!("location searched: {}\n", dep.source_id()));
            msg.push_str("required by ");
            msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

            msg
        };

    if let Some(config) = config {
        if config.offline() {
            msg.push_str(
                "\nAs a reminder, you're using offline mode (--offline) \
                 which can sometimes cause surprising resolution failures, \
                 if this error is too confusing you may wish to retry \
                 without the offline flag.",
            );
        }
    }

    to_resolve_err(anyhow::format_err!("{}", msg))
}

/// Returns String representation of dependency chain for a particular `pkgid`
/// within given context.
pub(super) fn describe_path_in_context(cx: &Context, id: &PackageId) -> String {
    let iter = cx
        .parents
        .path_to_bottom(id)
        .into_iter()
        .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
    describe_path(iter)
}

/// Returns String representation of dependency chain for a particular `pkgid`.
///
/// Note that all elements of `path` iterator should have `Some` dependency
/// except the first one. It would look like:
///
/// (pkg0, None)
/// -> (pkg1, dep from pkg1 satisfied by pkg0)
/// -> (pkg2, dep from pkg2 satisfied by pkg1)
/// -> ...
pub(crate) fn describe_path<'a>(
    mut path: impl Iterator<Item = (&'a PackageId, Option<&'a Dependency>)>,
) -> String {
    use std::fmt::Write;

    if let Some(p) = path.next() {
        let mut dep_path_desc = format!("package `{}`", p.0);
        for (pkg, dep) in path {
            let dep = dep.unwrap();
            let source_kind = if dep.source_id().is_path() {
                "path "
            } else if dep.source_id().is_git() {
                "git "
            } else {
                ""
            };
            let requirement = if source_kind.is_empty() {
                format!("{} = \"{}\"", dep.name_in_toml(), dep.version_req())
            } else {
                dep.name_in_toml().to_string()
            };
            let locked_version = dep
                .version_req()
                .locked_version()
                .map(|v| format!("(locked to {}) ", v))
                .unwrap_or_default();

            write!(
                dep_path_desc,
                "\n    ... which satisfies {}dependency `{}` {}of package `{}`",
                source_kind, requirement, locked_version, pkg
            )
            .unwrap();
        }

        return dep_path_desc;
    }

    String::new()
}
<commit_msg>Use locked_version more<commit_after>use std::fmt;

use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::util::lev_distance::lev_distance;
use crate::util::{Config, VersionExt};
use anyhow::Error;

use super::context::Context;
use super::types::{ConflictMap, ConflictReason};

/// Error during resolution providing a path of `PackageId`s.
pub struct ResolveError {
    cause: Error,
    package_path: Vec<PackageId>,
}

impl ResolveError {
    pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
        Self {
            cause: cause.into(),
            package_path,
        }
    }

    /// Returns a path of packages from the package whose requirements could not be resolved up to
    /// the root.
    pub fn package_path(&self) -> &[PackageId] {
        &self.package_path
    }
}

impl std::error::Error for ResolveError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ResolveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

pub type ActivateResult<T> = Result<T, ActivateError>;

#[derive(Debug)]
pub enum ActivateError {
    Fatal(anyhow::Error),
    Conflict(PackageId, ConflictReason),
}

impl From<::anyhow::Error> for ActivateError {
    fn from(t: ::anyhow::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

pub(super) fn activation_error(
    cx: &Context,
    registry: &mut dyn Registry,
    parent: &Summary,
    dep: &Dependency,
    conflicting_activations: &ConflictMap,
    candidates: &[Summary],
    config: Option<&Config>,
) -> ResolveError {
    let to_resolve_err = |err| {
        ResolveError::new(
            err,
            cx.parents
                .path_to_bottom(&parent.package_id())
                .into_iter()
                .map(|(node, _)| node)
                .cloned()
                .collect(),
        )
    };

    if !candidates.is_empty() {
        let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
        msg.push_str("\n    ... required by ");
        msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

        msg.push_str("\nversions that meet the requirements `");
        msg.push_str(&dep.version_req().to_string());
        msg.push_str("` ");

        if let Some(v) = dep.version_req().locked_version() {
            msg.push_str("(locked to ");
            msg.push_str(&v.to_string());
            msg.push_str(") ");
        }

        msg.push_str("are: ");
        msg.push_str(
            &candidates
                .iter()
                .map(|v| v.version())
                .map(|v| v.to_string())
                .collect::<Vec<_>>()
                .join(", "),
        );

        let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
        conflicting_activations.sort_unstable();
        // This is reversed to show the newest versions first. I don't know if there is
        // a strong reason to do this, but that is how the code previously worked
        // (see https://github.com/rust-lang/cargo/pull/5037) and I don't feel like changing it.
        conflicting_activations.reverse();
        // Flag used for grouping all semver errors together.
        let mut has_semver = false;

        for (p, r) in &conflicting_activations {
            match r {
                ConflictReason::Semver => {
                    has_semver = true;
                }
                ConflictReason::Links(link) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` links to the native library `");
                    msg.push_str(link);
                    msg.push_str("`, but it conflicts with a previous package which links to `");
                    msg.push_str(link);
                    msg.push_str("` as well:\n");
                    msg.push_str(&describe_path_in_context(cx, p));
                    msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
                    msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
                }
                ConflictReason::MissingFeatures(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    // p == parent so the full path is redundant.
                }
                ConflictReason::RequiredDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has a required dependency with that name, \
                         but only optional dependencies can be used as features.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::NonImplicitDependencyAsFeature(features) => {
                    msg.push_str("\n\nthe package `");
                    msg.push_str(&*p.name());
                    msg.push_str("` depends on `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("`, with features: `");
                    msg.push_str(features);
                    msg.push_str("` but `");
                    msg.push_str(&*dep.package_name());
                    msg.push_str("` does not have these features.\n");
                    msg.push_str(
                        " It has an optional dependency with that name, \
                         but but that dependency uses the \"dep:\" \
                         syntax in the features table, so it does not have an \
                         implicit feature with that name.\n",
                    );
                    // p == parent so the full path is redundant.
                }
                ConflictReason::PublicDependency(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub dep {:?}", pkg_id);
                }
                ConflictReason::PubliclyExports(pkg_id) => {
                    // TODO: This needs to be implemented.
                    unimplemented!("pub exp {:?}", pkg_id);
                }
            }
        }

        if has_semver {
            // Group these errors together.
            msg.push_str("\n\nall possible versions conflict with previously selected packages.");
            for (p, r) in &conflicting_activations {
                if let ConflictReason::Semver = r {
                    msg.push_str("\n\n  previously selected ");
                    msg.push_str(&describe_path_in_context(cx, p));
                }
            }
        }

        msg.push_str("\n\nfailed to select a version for `");
        msg.push_str(&*dep.package_name());
        msg.push_str("` which could resolve this conflict");

        return to_resolve_err(anyhow::format_err!("{}", msg));
    }

    // We didn't actually find any candidates, so we need to
    // give an error message that nothing was found.
    //
    // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
    // was meant. So we re-query the registry with `deb="*"` so we can
    // list a few versions that were actually found.
    let all_req = semver::VersionReq::parse("*").unwrap();
    let mut new_dep = dep.clone();
    new_dep.set_version_req(all_req);
    let mut candidates = match registry.query_vec(&new_dep, false) {
        Ok(candidates) => candidates,
        Err(e) => return to_resolve_err(e),
    };
    candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));

    let mut msg =
        if !candidates.is_empty() {
            let versions = {
                let mut versions = candidates
                    .iter()
                    .take(3)
                    .map(|cand| cand.version().to_string())
                    .collect::<Vec<_>>();

                if candidates.len() > 3 {
                    versions.push("...".into());
                }

                versions.join(", ")
            };

            let locked_version = dep
                .version_req()
                .locked_version()
                .map(|v| format!(" (locked to {})", v))
                .unwrap_or_default();

            let mut msg = format!(
                "failed to select a version for the requirement `{} = \"{}\"`{}\n\
                 candidate versions found which didn't match: {}\n\
                 location searched: {}\n",
                dep.package_name(),
                dep.version_req(),
                locked_version,
                versions,
                registry.describe_source(dep.source_id()),
            );
            msg.push_str("required by ");
            msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

            // If we have a path dependency with a locked version, then this may
            // indicate that we updated a sub-package and forgot to run `cargo
            // update`. In this case try to print a helpful error!
            if dep.source_id().is_path() && dep.version_req().is_locked() {
                msg.push_str(
                    "\nconsider running `cargo update` to update \
                     a path dependency's locked version",
                );
            }

            if registry.is_replaced(dep.source_id()) {
                msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
            }

            msg
        } else {
            // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
            // was meant. So we try asking the registry for a `fuzzy` search for suggestions.
            let mut candidates = Vec::new();
            if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s), true) {
                return to_resolve_err(e);
            };
            candidates.sort_unstable_by_key(|a| a.name());
            candidates.dedup_by(|a, b| a.name() == b.name());
            let mut candidates: Vec<_> = candidates
                .iter()
                .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n))
                .filter(|&(d, _)| d < 4)
                .collect();
            candidates.sort_by_key(|o| o.0);
            let mut msg: String;
            if candidates.is_empty() {
                msg = format!("no matching package named `{}` found\n", dep.package_name());
            } else {
                msg = format!(
                    "no matching package found\nsearched package name: `{}`\n",
                    dep.package_name()
                );

                // If dependency package name is equal to the name of the candidate here
                // it may be a prerelease package which hasn't been specified correctly
                if dep.package_name() == candidates[0].1.name()
                    && candidates[0].1.package_id().version().is_prerelease()
                {
                    msg.push_str("prerelease package needs to be specified explicitly\n");
                    msg.push_str(&format!(
                        "{name} = {{ version = \"{version}\" }}",
                        name = candidates[0].1.name(),
                        version = candidates[0].1.package_id().version()
                    ));
                } else {
                    let mut names = candidates
                        .iter()
                        .take(3)
                        .map(|c| c.1.name().as_str())
                        .collect::<Vec<_>>();

                    if candidates.len() > 3 {
                        names.push("...");
                    }
                    // Vertically align first suggestion with missing crate name
                    // so a typo jumps out at you.
                    msg.push_str("perhaps you meant:      ");
                    msg.push_str(&names.iter().enumerate().fold(
                        String::default(),
                        |acc, (i, el)| match i {
                            0 => acc + el,
                            i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
                            _ => acc + ", " + el,
                        },
                    ));
                }
                msg.push('\n');
            }
            msg.push_str(&format!("location searched: {}\n", dep.source_id()));
            msg.push_str("required by ");
            msg.push_str(&describe_path_in_context(cx, &parent.package_id()));

            msg
        };

    if let Some(config) = config {
        if config.offline() {
            msg.push_str(
                "\nAs a reminder, you're using offline mode (--offline) \
                 which can sometimes cause surprising resolution failures, \
                 if this error is too confusing you may wish to retry \
                 without the offline flag.",
            );
        }
    }

    to_resolve_err(anyhow::format_err!("{}", msg))
}

/// Returns String representation of dependency chain for a particular `pkgid`
/// within given context.
pub(super) fn describe_path_in_context(cx: &Context, id: &PackageId) -> String {
    let iter = cx
        .parents
        .path_to_bottom(id)
        .into_iter()
        .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
    describe_path(iter)
}

/// Returns String representation of dependency chain for a particular `pkgid`.
///
/// Note that all elements of `path` iterator should have `Some` dependency
/// except the first one. It would look like:
///
/// (pkg0, None)
/// -> (pkg1, dep from pkg1 satisfied by pkg0)
/// -> (pkg2, dep from pkg2 satisfied by pkg1)
/// -> ...
pub(crate) fn describe_path<'a>(
    mut path: impl Iterator<Item = (&'a PackageId, Option<&'a Dependency>)>,
) -> String {
    use std::fmt::Write;

    if let Some(p) = path.next() {
        let mut dep_path_desc = format!("package `{}`", p.0);
        for (pkg, dep) in path {
            let dep = dep.unwrap();
            let source_kind = if dep.source_id().is_path() {
                "path "
            } else if dep.source_id().is_git() {
                "git "
            } else {
                ""
            };
            let requirement = if source_kind.is_empty() {
                format!("{} = \"{}\"", dep.name_in_toml(), dep.version_req())
            } else {
                dep.name_in_toml().to_string()
            };
            let locked_version = dep
                .version_req()
                .locked_version()
                .map(|v| format!("(locked to {}) ", v))
                .unwrap_or_default();

            write!(
                dep_path_desc,
                "\n    ... which satisfies {}dependency `{}` {}of package `{}`",
                source_kind, requirement, locked_version, pkg
            )
            .unwrap();
        }

        return dep_path_desc;
    }

    String::new()
}
<|endoftext|>


<commit_before>use lsp_types::{
    self, CreateFile, Documentation, DocumentChangeOperation, DocumentChanges, Location, LocationLink,
    MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
    TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
    WorkspaceEdit,
};
use ra_ide_api::{
    CompletionItem, CompletionItemKind, FileId, FilePosition, FileRange, FileSystemEdit,
    NavigationTarget, SourceChange, SourceFileEdit, RangeInfo,
    LineCol, LineIndex, translate_offset_with_edit, InsertTextFormat
};
use ra_syntax::{SyntaxKind, TextRange, TextUnit};
use ra_text_edit::{AtomTextEdit, TextEdit};

use crate::{req, server_world::ServerWorld, Result};

pub trait Conv {
    type Output;
    fn conv(self) -> Self::Output;
}

pub trait ConvWith {
    type Ctx;
    type Output;
    fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
}

pub trait TryConvWith {
    type Ctx;
    type Output;
    fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>;
}

impl Conv for SyntaxKind {
    type Output = SymbolKind;

    fn conv(self) -> <Self as Conv>::Output {
        match self {
            SyntaxKind::FN_DEF => SymbolKind::Function,
            SyntaxKind::STRUCT_DEF => SymbolKind::Struct,
            SyntaxKind::ENUM_DEF => SymbolKind::Enum,
            SyntaxKind::TRAIT_DEF => SymbolKind::Interface,
            SyntaxKind::MODULE => SymbolKind::Module,
            SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter,
            SyntaxKind::STATIC_DEF => SymbolKind::Constant,
            SyntaxKind::CONST_DEF => SymbolKind::Constant,
            SyntaxKind::IMPL_BLOCK => SymbolKind::Object,
            _ => SymbolKind::Variable,
        }
    }
}

impl Conv for CompletionItemKind {
    type Output = ::lsp_types::CompletionItemKind;

    fn conv(self) -> <Self as Conv>::Output {
        use lsp_types::CompletionItemKind::*;
        match self {
            CompletionItemKind::Keyword => Keyword,
            CompletionItemKind::Snippet => Snippet,
            CompletionItemKind::Module => Module,
            CompletionItemKind::Function => Function,
            CompletionItemKind::Struct => Struct,
            CompletionItemKind::Enum => Enum,
            CompletionItemKind::EnumVariant => EnumMember,
            CompletionItemKind::Binding => Variable,
            CompletionItemKind::Field => Field,
            CompletionItemKind::Trait => Interface,
            CompletionItemKind::TypeAlias => Struct,
            CompletionItemKind::Const => Constant,
            CompletionItemKind::Static => Value,
            CompletionItemKind::Method => Method,
        }
    }
}

impl ConvWith for CompletionItem {
    type Ctx = LineIndex;
    type Output = ::lsp_types::CompletionItem;

    fn conv_with(mut self, ctx: &LineIndex) -> ::lsp_types::CompletionItem {
        let atom_text_edit = AtomTextEdit::replace(self.source_range(), self.insert_text());
        let text_edit = (&atom_text_edit).conv_with(ctx);
        let additional_text_edits = if let Some(edit) = self.take_text_edit() {
            Some(edit.conv_with(ctx))
        } else {
            None
        };

        let documentation = self.documentation().map(|value| {
            Documentation::MarkupContent(MarkupContent {
                kind: MarkupKind::Markdown,
                value: value.to_string(),
            })
        });

        let mut res = lsp_types::CompletionItem {
            label: self.label().to_string(),
            detail: self.detail().map(|it| it.to_string()),
            filter_text: Some(self.lookup().to_string()),
            kind: self.kind().map(|it| it.conv()),
            text_edit: Some(text_edit),
            additional_text_edits,
            documentation: documentation,
            ..Default::default()
        };
        res.insert_text_format = Some(match self.insert_text_format() {
            InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet,
            InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText,
        });

        res
    }
}

impl ConvWith for Position {
    type Ctx = LineIndex;
    type Output = TextUnit;

    fn conv_with(self, line_index: &LineIndex) -> TextUnit {
        let line_col = LineCol {
            line: self.line as u32,
            col_utf16: self.character as u32,
        };
        line_index.offset(line_col)
    }
}

impl ConvWith for TextUnit {
    type Ctx = LineIndex;
    type Output = Position;

    fn conv_with(self, line_index: &LineIndex) -> Position {
        let line_col = line_index.line_col(self);
        Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16))
    }
}

impl ConvWith for TextRange {
    type Ctx = LineIndex;
    type Output = Range;

    fn conv_with(self, line_index: &LineIndex) -> Range {
        Range::new(
            self.start().conv_with(line_index),
            self.end().conv_with(line_index),
        )
    }
}

impl ConvWith for Range {
    type Ctx = LineIndex;
    type Output = TextRange;

    fn conv_with(self, line_index: &LineIndex) -> TextRange {
        TextRange::from_to(
            self.start.conv_with(line_index),
            self.end.conv_with(line_index),
        )
    }
}

impl ConvWith for TextEdit {
    type Ctx = LineIndex;
    type Output = Vec<lsp_types::TextEdit>;

    fn conv_with(self, line_index: &LineIndex) -> Vec<lsp_types::TextEdit> {
        self.as_atoms()
            .into_iter()
            .map_conv_with(line_index)
            .collect()
    }
}

impl<'a> ConvWith for &'a AtomTextEdit {
    type Ctx = LineIndex;
    type Output = lsp_types::TextEdit;

    fn conv_with(self, line_index: &LineIndex) -> lsp_types::TextEdit {
        lsp_types::TextEdit {
            range: self.delete.conv_with(line_index),
            new_text: self.insert.clone(),
        }
    }
}

impl<T: ConvWith> ConvWith for Option<T> {
    type Ctx = <T as ConvWith>::Ctx;
    type Output = Option<<T as ConvWith>::Output>;
    fn conv_with(self, ctx: &Self::Ctx) -> Self::Output {
        self.map(|x| ConvWith::conv_with(x, ctx))
    }
}

impl<'a> TryConvWith for &'a Url {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        world.uri_to_file_id(self)
    }
}

impl TryConvWith for FileId {
    type Ctx = ServerWorld;
    type Output = Url;
    fn try_conv_with(self, world: &ServerWorld) -> Result<Url> {
        world.file_id_to_uri(self)
    }
}

impl<'a> TryConvWith for &'a TextDocumentItem {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        self.uri.try_conv_with(world)
    }
}

impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        self.uri.try_conv_with(world)
    }
}

impl<'a> TryConvWith for &'a TextDocumentIdentifier {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        world.uri_to_file_id(&self.uri)
    }
}

impl<'a> TryConvWith for &'a TextDocumentPositionParams {
    type Ctx = ServerWorld;
    type Output = FilePosition;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> {
        let file_id = self.text_document.try_conv_with(world)?;
        let line_index = world.analysis().file_line_index(file_id);
        let offset = self.position.conv_with(&line_index);
        Ok(FilePosition { file_id, offset })
    }
}

impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) {
    type Ctx = ServerWorld;
    type Output = FileRange;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileRange> {
        let file_id = self.0.try_conv_with(world)?;
        let line_index = world.analysis().file_line_index(file_id);
        let range = self.1.conv_with(&line_index);
        Ok(FileRange { file_id, range })
    }
}

impl<T: TryConvWith> TryConvWith for Vec<T> {
    type Ctx = <T as TryConvWith>::Ctx;
    type Output = Vec<<T as TryConvWith>::Output>;
    fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> {
        let mut res = Vec::with_capacity(self.len());
        for item in self {
            res.push(item.try_conv_with(ctx)?);
        }
        Ok(res)
    }
}

impl TryConvWith for SourceChange {
    type Ctx = ServerWorld;
    type Output = req::SourceChange;
    fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> {
        let cursor_position = match self.cursor_position {
            None => None,
            Some(pos) => {
                let line_index = world.analysis().file_line_index(pos.file_id);
                let edit = self
                    .source_file_edits
                    .iter()
                    .find(|it| it.file_id == pos.file_id)
                    .map(|it| &it.edit);
                let line_col = match edit {
                    Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
                    None => line_index.line_col(pos.offset),
                };
                let position =
                    Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
                Some(TextDocumentPositionParams {
                    text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
                    position,
                })
            }
        };
        let mut document_changes: Vec<DocumentChangeOperation> = Vec::new();
        for resource_op in self.file_system_edits.try_conv_with(world)? {
            document_changes.push(DocumentChangeOperation::Op(resource_op));
        }
        for text_document_edit in self.source_file_edits.try_conv_with(world)? {
            document_changes.push(DocumentChangeOperation::Edit(text_document_edit));
        }
        let workspace_edit = WorkspaceEdit {
            changes: None,
            document_changes: Some(DocumentChanges::Operations(document_changes)),
        };
        Ok(req::SourceChange {
            label: self.label,
            workspace_edit,
            cursor_position,
        })
    }
}

impl TryConvWith for SourceFileEdit {
    type Ctx = ServerWorld;
    type Output = TextDocumentEdit;
    fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> {
        let text_document = VersionedTextDocumentIdentifier {
            uri: self.file_id.try_conv_with(world)?,
            version: None,
        };
        let line_index = world.analysis().file_line_index(self.file_id);
        let edits = self
            .edit
            .as_atoms()
            .iter()
            .map_conv_with(&line_index)
            .collect();
        Ok(TextDocumentEdit {
            text_document,
            edits,
        })
    }
}

impl TryConvWith for FileSystemEdit {
    type Ctx = ServerWorld;
    type Output = ResourceOp;
    fn try_conv_with(self, world: &ServerWorld) -> Result<ResourceOp> {
        let res = match self {
            FileSystemEdit::CreateFile { source_root, path } => {
                let uri = world.path_to_uri(source_root, &path)?;
                ResourceOp::Create(CreateFile { uri, options: None })
            }
            FileSystemEdit::MoveFile {
                src,
                dst_source_root,
                dst_path,
            } => {
                let old_uri = world.file_id_to_uri(src)?;
                let new_uri = world.path_to_uri(dst_source_root, &dst_path)?;
                ResourceOp::Rename(RenameFile {
                    old_uri,
                    new_uri,
                    options: None,
                })
            }
        };
        Ok(res)
    }
}

impl TryConvWith for &NavigationTarget {
    type Ctx = ServerWorld;
    type Output = Location;
    fn try_conv_with(self, world: &ServerWorld) -> Result<Location> {
        let line_index = world.analysis().file_line_index(self.file_id());
        let range = self.focus_range().unwrap_or(self.full_range());
        to_location(self.file_id(), range, &world, &line_index)
    }
}

pub fn to_location_link(
    target: &RangeInfo<NavigationTarget>,
    world: &ServerWorld,
    // line index for original range file
    line_index: &LineIndex,
) -> Result<LocationLink> {
    let target_uri = target.info.file_id().try_conv_with(world)?;
    let tgt_line_index = world.analysis().file_line_index(target.info.file_id());

    let target_range = target.info.full_range().conv_with(&tgt_line_index);

    let target_selection_range = target
        .info
        .focus_range()
        .map(|it| it.conv_with(&tgt_line_index))
        .unwrap_or(target_range);

    let res = LocationLink {
        origin_selection_range: Some(target.range.conv_with(line_index)),
        target_uri,
        target_range,
        target_selection_range: Some(target_selection_range),
    };
    Ok(res)
}

pub fn to_location(
    file_id: FileId,
    range: TextRange,
    world: &ServerWorld,
    line_index: &LineIndex,
) -> Result<Location> {
    let url = file_id.try_conv_with(world)?;
    let loc = Location::new(url, range.conv_with(line_index));
    Ok(loc)
}

pub trait MapConvWith<'a>: Sized + 'a {
    type Ctx;
    type Output;

    fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> {
        ConvWithIter { iter: self, ctx }
    }
}

impl<'a, I> MapConvWith<'a> for I
where
    I: Iterator + 'a,
    I::Item: ConvWith,
{
    type Ctx = <I::Item as ConvWith>::Ctx;
    type Output = <I::Item as ConvWith>::Output;
}

pub struct ConvWithIter<'a, I, Ctx: 'a> {
    iter: I,
    ctx: &'a Ctx,
}

impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
where
    I: Iterator,
    I::Item: ConvWith<Ctx = Ctx>,
{
    type Item = <I::Item as ConvWith>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|item| item.conv_with(self.ctx))
    }
}
<commit_msg>Fix target_selection_range type<commit_after>use lsp_types::{
    self, CreateFile, Documentation, DocumentChangeOperation, DocumentChanges, Location, LocationLink,
    MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
    TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
    WorkspaceEdit,
};
use ra_ide_api::{
    CompletionItem, CompletionItemKind, FileId, FilePosition, FileRange, FileSystemEdit,
    NavigationTarget, SourceChange, SourceFileEdit, RangeInfo,
    LineCol, LineIndex, translate_offset_with_edit, InsertTextFormat
};
use ra_syntax::{SyntaxKind, TextRange, TextUnit};
use ra_text_edit::{AtomTextEdit, TextEdit};

use crate::{req, server_world::ServerWorld, Result};

pub trait Conv {
    type Output;
    fn conv(self) -> Self::Output;
}

pub trait ConvWith {
    type Ctx;
    type Output;
    fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
}

pub trait TryConvWith {
    type Ctx;
    type Output;
    fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>;
}

impl Conv for SyntaxKind {
    type Output = SymbolKind;

    fn conv(self) -> <Self as Conv>::Output {
        match self {
            SyntaxKind::FN_DEF => SymbolKind::Function,
            SyntaxKind::STRUCT_DEF => SymbolKind::Struct,
            SyntaxKind::ENUM_DEF => SymbolKind::Enum,
            SyntaxKind::TRAIT_DEF => SymbolKind::Interface,
            SyntaxKind::MODULE => SymbolKind::Module,
            SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter,
            SyntaxKind::STATIC_DEF => SymbolKind::Constant,
            SyntaxKind::CONST_DEF => SymbolKind::Constant,
            SyntaxKind::IMPL_BLOCK => SymbolKind::Object,
            _ => SymbolKind::Variable,
        }
    }
}

impl Conv for CompletionItemKind {
    type Output = ::lsp_types::CompletionItemKind;

    fn conv(self) -> <Self as Conv>::Output {
        use lsp_types::CompletionItemKind::*;
        match self {
            CompletionItemKind::Keyword => Keyword,
            CompletionItemKind::Snippet => Snippet,
            CompletionItemKind::Module => Module,
            CompletionItemKind::Function => Function,
            CompletionItemKind::Struct => Struct,
            CompletionItemKind::Enum => Enum,
            CompletionItemKind::EnumVariant => EnumMember,
            CompletionItemKind::Binding => Variable,
            CompletionItemKind::Field => Field,
            CompletionItemKind::Trait => Interface,
            CompletionItemKind::TypeAlias => Struct,
            CompletionItemKind::Const => Constant,
            CompletionItemKind::Static => Value,
            CompletionItemKind::Method => Method,
        }
    }
}

impl ConvWith for CompletionItem {
    type Ctx = LineIndex;
    type Output = ::lsp_types::CompletionItem;

    fn conv_with(mut self, ctx: &LineIndex) -> ::lsp_types::CompletionItem {
        let atom_text_edit = AtomTextEdit::replace(self.source_range(), self.insert_text());
        let text_edit = (&atom_text_edit).conv_with(ctx);
        let additional_text_edits = if let Some(edit) = self.take_text_edit() {
            Some(edit.conv_with(ctx))
        } else {
            None
        };

        let documentation = self.documentation().map(|value| {
            Documentation::MarkupContent(MarkupContent {
                kind: MarkupKind::Markdown,
                value: value.to_string(),
            })
        });

        let mut res = lsp_types::CompletionItem {
            label: self.label().to_string(),
            detail: self.detail().map(|it| it.to_string()),
            filter_text: Some(self.lookup().to_string()),
            kind: self.kind().map(|it| it.conv()),
            text_edit: Some(text_edit),
            additional_text_edits,
            documentation: documentation,
            ..Default::default()
        };
        res.insert_text_format = Some(match self.insert_text_format() {
            InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet,
            InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText,
        });

        res
    }
}

impl ConvWith for Position {
    type Ctx = LineIndex;
    type Output = TextUnit;

    fn conv_with(self, line_index: &LineIndex) -> TextUnit {
        let line_col = LineCol {
            line: self.line as u32,
            col_utf16: self.character as u32,
        };
        line_index.offset(line_col)
    }
}

impl ConvWith for TextUnit {
    type Ctx = LineIndex;
    type Output = Position;

    fn conv_with(self, line_index: &LineIndex) -> Position {
        let line_col = line_index.line_col(self);
        Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16))
    }
}

impl ConvWith for TextRange {
    type Ctx = LineIndex;
    type Output = Range;

    fn conv_with(self, line_index: &LineIndex) -> Range {
        Range::new(
            self.start().conv_with(line_index),
            self.end().conv_with(line_index),
        )
    }
}

impl ConvWith for Range {
    type Ctx = LineIndex;
    type Output = TextRange;

    fn conv_with(self, line_index: &LineIndex) -> TextRange {
        TextRange::from_to(
            self.start.conv_with(line_index),
            self.end.conv_with(line_index),
        )
    }
}

impl ConvWith for TextEdit {
    type Ctx = LineIndex;
    type Output = Vec<lsp_types::TextEdit>;

    fn conv_with(self, line_index: &LineIndex) -> Vec<lsp_types::TextEdit> {
        self.as_atoms()
            .into_iter()
            .map_conv_with(line_index)
            .collect()
    }
}

impl<'a> ConvWith for &'a AtomTextEdit {
    type Ctx = LineIndex;
    type Output = lsp_types::TextEdit;

    fn conv_with(self, line_index: &LineIndex) -> lsp_types::TextEdit {
        lsp_types::TextEdit {
            range: self.delete.conv_with(line_index),
            new_text: self.insert.clone(),
        }
    }
}

impl<T: ConvWith> ConvWith for Option<T> {
    type Ctx = <T as ConvWith>::Ctx;
    type Output = Option<<T as ConvWith>::Output>;
    fn conv_with(self, ctx: &Self::Ctx) -> Self::Output {
        self.map(|x| ConvWith::conv_with(x, ctx))
    }
}

impl<'a> TryConvWith for &'a Url {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        world.uri_to_file_id(self)
    }
}

impl TryConvWith for FileId {
    type Ctx = ServerWorld;
    type Output = Url;
    fn try_conv_with(self, world: &ServerWorld) -> Result<Url> {
        world.file_id_to_uri(self)
    }
}

impl<'a> TryConvWith for &'a TextDocumentItem {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        self.uri.try_conv_with(world)
    }
}

impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        self.uri.try_conv_with(world)
    }
}

impl<'a> TryConvWith for &'a TextDocumentIdentifier {
    type Ctx = ServerWorld;
    type Output = FileId;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
        world.uri_to_file_id(&self.uri)
    }
}

impl<'a> TryConvWith for &'a TextDocumentPositionParams {
    type Ctx = ServerWorld;
    type Output = FilePosition;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> {
        let file_id = self.text_document.try_conv_with(world)?;
        let line_index = world.analysis().file_line_index(file_id);
        let offset = self.position.conv_with(&line_index);
        Ok(FilePosition { file_id, offset })
    }
}

impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) {
    type Ctx = ServerWorld;
    type Output = FileRange;
    fn try_conv_with(self, world: &ServerWorld) -> Result<FileRange> {
        let file_id = self.0.try_conv_with(world)?;
        let line_index = world.analysis().file_line_index(file_id);
        let range = self.1.conv_with(&line_index);
        Ok(FileRange { file_id, range })
    }
}

impl<T: TryConvWith> TryConvWith for Vec<T> {
    type Ctx = <T as TryConvWith>::Ctx;
    type Output = Vec<<T as TryConvWith>::Output>;
    fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> {
        let mut res = Vec::with_capacity(self.len());
        for item in self {
            res.push(item.try_conv_with(ctx)?);
        }
        Ok(res)
    }
}

impl TryConvWith for SourceChange {
    type Ctx = ServerWorld;
    type Output = req::SourceChange;
    fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> {
        let cursor_position = match self.cursor_position {
            None => None,
            Some(pos) => {
                let line_index = world.analysis().file_line_index(pos.file_id);
                let edit = self
                    .source_file_edits
                    .iter()
                    .find(|it| it.file_id == pos.file_id)
                    .map(|it| &it.edit);
                let line_col = match edit {
                    Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
                    None => line_index.line_col(pos.offset),
                };
                let position =
                    Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
                Some(TextDocumentPositionParams {
                    text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
                    position,
                })
            }
        };
        let mut document_changes: Vec<DocumentChangeOperation> = Vec::new();
        for resource_op in self.file_system_edits.try_conv_with(world)? {
            document_changes.push(DocumentChangeOperation::Op(resource_op));
        }
        for text_document_edit in self.source_file_edits.try_conv_with(world)? {
            document_changes.push(DocumentChangeOperation::Edit(text_document_edit));
        }
        let workspace_edit = WorkspaceEdit {
            changes: None,
            document_changes: Some(DocumentChanges::Operations(document_changes)),
        };
        Ok(req::SourceChange {
            label: self.label,
            workspace_edit,
            cursor_position,
        })
    }
}

impl TryConvWith for SourceFileEdit {
    type Ctx = ServerWorld;
    type Output = TextDocumentEdit;
    fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> {
        let text_document = VersionedTextDocumentIdentifier {
            uri: self.file_id.try_conv_with(world)?,
            version: None,
        };
        let line_index = world.analysis().file_line_index(self.file_id);
        let edits = self
            .edit
            .as_atoms()
            .iter()
            .map_conv_with(&line_index)
            .collect();
        Ok(TextDocumentEdit {
            text_document,
            edits,
        })
    }
}

impl TryConvWith for FileSystemEdit {
    type Ctx = ServerWorld;
    type Output = ResourceOp;
    fn try_conv_with(self, world: &ServerWorld) -> Result<ResourceOp> {
        let res = match self {
            FileSystemEdit::CreateFile { source_root, path } => {
                let uri = world.path_to_uri(source_root, &path)?;
                ResourceOp::Create(CreateFile { uri, options: None })
            }
            FileSystemEdit::MoveFile {
                src,
                dst_source_root,
                dst_path,
            } => {
                let old_uri = world.file_id_to_uri(src)?;
                let new_uri = world.path_to_uri(dst_source_root, &dst_path)?;
                ResourceOp::Rename(RenameFile {
                    old_uri,
                    new_uri,
                    options: None,
                })
            }
        };
        Ok(res)
    }
}

impl TryConvWith for &NavigationTarget {
    type Ctx = ServerWorld;
    type Output = Location;
    fn try_conv_with(self, world: &ServerWorld) -> Result<Location> {
        let line_index = world.analysis().file_line_index(self.file_id());
        let range = self.focus_range().unwrap_or(self.full_range());
        to_location(self.file_id(), range, &world, &line_index)
    }
}

pub fn to_location_link(
    target: &RangeInfo<NavigationTarget>,
    world: &ServerWorld,
    // line index for original range file
    line_index: &LineIndex,
) -> Result<LocationLink> {
    let target_uri = target.info.file_id().try_conv_with(world)?;
    let tgt_line_index = world.analysis().file_line_index(target.info.file_id());

    let target_range = target.info.full_range().conv_with(&tgt_line_index);

    let target_selection_range = target
        .info
        .focus_range()
        .map(|it| it.conv_with(&tgt_line_index))
        .unwrap_or(target_range);

    let res = LocationLink {
        origin_selection_range: Some(target.range.conv_with(line_index)),
        target_uri,
        target_range,
        target_selection_range: target_selection_range,
    };
    Ok(res)
}

pub fn to_location(
    file_id: FileId,
    range: TextRange,
    world: &ServerWorld,
    line_index: &LineIndex,
) -> Result<Location> {
    let url = file_id.try_conv_with(world)?;
    let loc = Location::new(url, range.conv_with(line_index));
    Ok(loc)
}

pub trait MapConvWith<'a>: Sized + 'a {
    type Ctx;
    type Output;

    fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> {
        ConvWithIter { iter: self, ctx }
    }
}

impl<'a, I> MapConvWith<'a> for I
where
    I: Iterator + 'a,
    I::Item: ConvWith,
{
    type Ctx = <I::Item as ConvWith>::Ctx;
    type Output = <I::Item as ConvWith>::Output;
}

pub struct ConvWithIter<'a, I, Ctx: 'a> {
    iter: I,
    ctx: &'a Ctx,
}

impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
where
    I: Iterator,
    I::Item: ConvWith<Ctx = Ctx>,
{
    type Item = <I::Item as ConvWith>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|item| item.conv_with(self.ctx))
    }
}
<|endoftext|>


<commit_before>//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
//! for LSIF and LSP.

use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
use ide_db::{
    base_db::{CrateOrigin, FileId, FileLoader, FilePosition},
    defs::Definition,
    helpers::pick_best_token,
    RootDatabase,
};
use itertools::Itertools;
use syntax::{AstNode, SyntaxKind::*, T};

use crate::{doc_links::token_as_doc_comment, RangeInfo};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerIdentifier {
    crate_name: String,
    path: Vec<Name>,
}

impl ToString for MonikerIdentifier {
    fn to_string(&self) -> String {
        match self {
            MonikerIdentifier { path, crate_name } => {
                format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MonikerKind {
    Import,
    Export,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MonikerResult {
    pub identifier: MonikerIdentifier,
    pub kind: MonikerKind,
    pub package_information: PackageInformation,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageInformation {
    pub name: String,
    pub repo: String,
    pub version: String,
}

pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option<Crate> {
    for &krate in db.relevant_crates(file_id).iter() {
        let crate_def_map = db.crate_def_map(krate);
        for (_, data) in crate_def_map.modules() {
            if data.origin.file_id() == Some(file_id) {
                return Some(krate.into());
            }
        }
    }
    None
}

pub(crate) fn moniker(
    db: &RootDatabase,
    FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<MonikerResult>>> {
    let sema = &Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();
    let current_crate = crate_for_file(db, file_id)?;
    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;
    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
            let m = def_to_moniker(db, def, current_crate)?;
            Some(RangeInfo::new(original_token.text_range(), vec![m]))
        });
    }
    let navs = sema
        .descend_into_macros(original_token.clone())
        .into_iter()
        .map(|token| {
            Definition::from_token(sema, &token)
                .into_iter()
                .flat_map(|def| def_to_moniker(sema.db, def, current_crate))
                .collect::<Vec<_>>()
        })
        .flatten()
        .unique()
        .collect::<Vec<_>>();
    Some(RangeInfo::new(original_token.text_range(), navs))
}

pub(crate) fn def_to_moniker(
    db: &RootDatabase,
    def: Definition,
    from_crate: Crate,
) -> Option<MonikerResult> {
    if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
        return None;
    }
    let module = def.module(db)?;
    let krate = module.krate();
    let mut path = vec![];
    path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));

    // Handle associated items within a trait
    if let Some(assoc) = def.as_assoc_item(db) {
        let container = assoc.container(db);
        if let AssocItemContainer::Trait(parent_trait) = container {
            path.push(parent_trait.name(db));
        }
    }

    if let Definition::Field(it) = def {
        path.push(it.parent_def(db).name(db));
    }

    path.push(def.name(db)?);
    Some(MonikerResult {
        identifier: MonikerIdentifier {
            crate_name: krate.display_name(db)?.crate_name().to_string(),
            path,
        },
        kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
        package_information: {
            let name = krate.display_name(db)?.to_string();
            let (repo, version) = match krate.origin(db) {
                CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?),
                CrateOrigin::Lang => (
                    "https://github.com/rust-lang/rust/".to_string(),
                    "compiler_version".to_string(),
                ),
                CrateOrigin::Unknown => return None,
            };
            PackageInformation { name, repo, version }
        },
    })
}

#[cfg(test)]
mod tests {
    use crate::fixture;

    use super::MonikerKind;

    #[track_caller]
    fn no_moniker(ra_fixture: &str) {
        let (analysis, position) = fixture::position(ra_fixture);
        if let Some(x) = analysis.moniker(position).unwrap() {
            assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {:?}", x);
        }
    }

    #[track_caller]
    fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
        let (analysis, position) = fixture::position(ra_fixture);
        let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
        assert_eq!(x.len(), 1);
        let x = x.into_iter().next().unwrap();
        assert_eq!(identifier, x.identifier.to_string());
        assert_eq!(package, format!("{:?}", x.package_information));
        assert_eq!(kind, x.kind);
    }

    #[test]
    fn basic() {
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func$0();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func() {}
}
"#,
            "foo::module::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Import,
        );
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func$0() {}
}
"#,
            "foo::module::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        pub fn func$0() {}
    }
}
"#,
            "foo::module::MyTrait::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait_constant() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        const MY_CONST$0: u8;
    }
}
"#,
            "foo::module::MyTrait::MY_CONST",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait_type() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        type MyType$0;
    }
}
"#,
            "foo::module::MyTrait::MyType",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_field() {
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::St;
fn main() {
    let x = St { a$0: 2 };
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub struct St {
    pub a: i32,
}
"#,
            "foo::St::a",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Import,
        );
    }

    #[test]
    fn no_moniker_for_local() {
        no_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func() {
        let x$0 = 2;
    }
}
"#,
        );
    }
}
<commit_msg>fixup: include more information for impls<commit_after>//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
//! for LSIF and LSP.

use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
use ide_db::{
    base_db::{CrateOrigin, FileId, FileLoader, FilePosition},
    defs::Definition,
    helpers::pick_best_token,
    RootDatabase,
};
use itertools::Itertools;
use syntax::{AstNode, SyntaxKind::*, T};

use crate::{doc_links::token_as_doc_comment, RangeInfo};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerIdentifier {
    crate_name: String,
    path: Vec<Name>,
}

impl ToString for MonikerIdentifier {
    fn to_string(&self) -> String {
        match self {
            MonikerIdentifier { path, crate_name } => {
                format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MonikerKind {
    Import,
    Export,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MonikerResult {
    pub identifier: MonikerIdentifier,
    pub kind: MonikerKind,
    pub package_information: PackageInformation,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageInformation {
    pub name: String,
    pub repo: String,
    pub version: String,
}

pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option<Crate> {
    for &krate in db.relevant_crates(file_id).iter() {
        let crate_def_map = db.crate_def_map(krate);
        for (_, data) in crate_def_map.modules() {
            if data.origin.file_id() == Some(file_id) {
                return Some(krate.into());
            }
        }
    }
    None
}

pub(crate) fn moniker(
    db: &RootDatabase,
    FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<MonikerResult>>> {
    let sema = &Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();
    let current_crate = crate_for_file(db, file_id)?;
    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;
    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
            let m = def_to_moniker(db, def, current_crate)?;
            Some(RangeInfo::new(original_token.text_range(), vec![m]))
        });
    }
    let navs = sema
        .descend_into_macros(original_token.clone())
        .into_iter()
        .map(|token| {
            Definition::from_token(sema, &token)
                .into_iter()
                .flat_map(|def| def_to_moniker(sema.db, def, current_crate))
                .collect::<Vec<_>>()
        })
        .flatten()
        .unique()
        .collect::<Vec<_>>();
    Some(RangeInfo::new(original_token.text_range(), navs))
}

pub(crate) fn def_to_moniker(
    db: &RootDatabase,
    def: Definition,
    from_crate: Crate,
) -> Option<MonikerResult> {
    if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
        return None;
    }
    let module = def.module(db)?;
    let krate = module.krate();
    let mut path = vec![];
    path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));

    // Handle associated items within a trait
    if let Some(assoc) = def.as_assoc_item(db) {
        let container = assoc.container(db);
        match container {
            AssocItemContainer::Trait(trait_) => {
                // Because different traits can have functions with the same name,
                // we have to include the trait name as part of the moniker for uniqueness.
                path.push(trait_.name(db));
            }
            AssocItemContainer::Impl(impl_) => {
                // Because a struct can implement multiple traits, for implementations
                // we add both the struct name and the trait name to the path
                if let Some(adt) = impl_.self_ty(db).as_adt() {
                    path.push(adt.name(db));
                }

                if let Some(trait_) = impl_.trait_(db) {
                    path.push(trait_.name(db));
                }
            }
        }
    }

    if let Definition::Field(it) = def {
        path.push(it.parent_def(db).name(db));
    }

    path.push(def.name(db)?);
    Some(MonikerResult {
        identifier: MonikerIdentifier {
            crate_name: krate.display_name(db)?.crate_name().to_string(),
            path,
        },
        kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
        package_information: {
            let name = krate.display_name(db)?.to_string();
            let (repo, version) = match krate.origin(db) {
                CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?),
                CrateOrigin::Lang => (
                    "https://github.com/rust-lang/rust/".to_string(),
                    "compiler_version".to_string(),
                ),
                CrateOrigin::Unknown => return None,
            };
            PackageInformation { name, repo, version }
        },
    })
}

#[cfg(test)]
mod tests {
    use crate::fixture;

    use super::MonikerKind;

    #[track_caller]
    fn no_moniker(ra_fixture: &str) {
        let (analysis, position) = fixture::position(ra_fixture);
        if let Some(x) = analysis.moniker(position).unwrap() {
            assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {:?}", x);
        }
    }

    #[track_caller]
    fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
        let (analysis, position) = fixture::position(ra_fixture);
        let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
        assert_eq!(x.len(), 1);
        let x = x.into_iter().next().unwrap();
        assert_eq!(identifier, x.identifier.to_string());
        assert_eq!(package, format!("{:?}", x.package_information));
        assert_eq!(kind, x.kind);
    }

    #[test]
    fn basic() {
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func$0();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func() {}
}
"#,
            "foo::module::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Import,
        );
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func$0() {}
}
"#,
            "foo::module::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        pub fn func$0() {}
    }
}
"#,
            "foo::module::MyTrait::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait_constant() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        const MY_CONST$0: u8;
    }
}
"#,
            "foo::module::MyTrait::MY_CONST",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait_type() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        type MyType$0;
    }
}
"#,
            "foo::module::MyTrait::MyType",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_trait_impl_function() {
        check_moniker(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        pub fn func() {}
    }

    struct MyStruct {}

    impl MyTrait for MyStruct {
        pub fn func$0() {}
    }
}
"#,
            "foo::module::MyStruct::MyTrait::func",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Export,
        );
    }

    #[test]
    fn moniker_for_field() {
        check_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::St;
fn main() {
    let x = St { a$0: 2 };
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub struct St {
    pub a: i32,
}
"#,
            "foo::St::a",
            r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
            MonikerKind::Import,
        );
    }

    #[test]
    fn no_moniker_for_local() {
        no_moniker(
            r#"
//- /lib.rs crate:main deps:foo
use foo::module::func;
fn main() {
    func();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub fn func() {
        let x$0 = 2;
    }
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>/// This modules specifies the input to rust-analyzer. In some sense, this is
/// **the** most important module, because all other fancy stuff is strictly
/// derived from this input.
///
/// Note that neither this module, nor any other part of the analyzer's core do
/// actual IO. See `vfs` and `project_model` in `ra_lsp_server` crate for how
/// actual IO is done and lowered to input.
use std::sync::Arc;

use rustc_hash::{FxHashMap};
use relative_path::RelativePathBuf;
use ra_syntax::SmolStr;
use salsa;

/// `FileId` is an integer which uniquely identifies a file. File paths are
/// messy and system-dependent, so most of the code should work directly with
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
/// and `SourceRoot` is constant. File rename is represented as a pair of
/// deletion/creation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Cargo package. Source roots *might* be nested: in this case, file belongs to
/// the nearest enclosing source root. Path to files are always relative to a
/// source root, and analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refere a file in another source
/// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot {
    pub files: FxHashMap<RelativePathBuf, FileId>,
}

/// `CrateGraph` is a bit of information which turns a set of text files into a
/// number of Rust crates. Each Crate is the `FileId` of it's root module, the
/// set of cfg flags (not yet implemented) and the set of dependencies. Note
/// that, due to cfg's, there might be several crates for a single `FileId`! As
/// in the rust-lang proper, a crate does not have a name. Instead, names are
/// specified on dependency edges. That is, a crate might be known under
/// different names in different dependant crates.
///
/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
/// langauge proper, not a concept of the build system. In practice, we get
/// `CrateGraph` by lowering `cargo metadata` output.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CrateGraph {
    arena: FxHashMap<CrateId, CrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32);

#[derive(Debug, Clone, PartialEq, Eq)]
struct CrateData {
    file_id: FileId,
    dependencies: Vec<Dependency>,
}

impl CrateData {
    fn new(file_id: FileId) -> CrateData {
        CrateData {
            file_id,
            dependencies: Vec::new(),
        }
    }

    fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
        self.dependencies.push(Dependency { name, crate_id })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dependency {
    pub crate_id: CrateId,
    pub name: SmolStr,
}

impl Dependency {
    pub fn crate_id(&self) -> CrateId {
        self.crate_id
    }
}

impl CrateGraph {
    pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
        let crate_id = CrateId(self.arena.len() as u32);
        let prev = self.arena.insert(crate_id, CrateData::new(file_id));
        assert!(prev.is_none());
        crate_id
    }
    // FIXME: check that we don't have cycles here.
    // Just a simple depth first search from `to` should work,
    // the graph is small.
    pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) {
        self.arena.get_mut(&from).unwrap().add_dep(name, to)
    }
    pub fn crate_root(&self, crate_id: CrateId) -> FileId {
        self.arena[&crate_id].file_id
    }
    pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
        let (&crate_id, _) = self
            .arena
            .iter()
            .find(|(_crate_id, data)| data.file_id == file_id)?;
        Some(crate_id)
    }
    pub fn dependencies<'a>(
        &'a self,
        crate_id: CrateId,
    ) -> impl Iterator<Item = &'a Dependency> + 'a {
        self.arena[&crate_id].dependencies.iter()
    }
}

salsa::query_group! {
    pub trait FilesDatabase: salsa::Database {
        /// Text of the file.
        fn file_text(file_id: FileId) -> Arc<String> {
            type FileTextQuery;
            storage input;
        }
        /// Path to a file, relative to the root of its source root.
        fn file_relative_path(file_id: FileId) -> RelativePathBuf {
            type FileRelativePathQuery;
            storage input;
        }
        /// Source root of the file.
        fn file_source_root(file_id: FileId) -> SourceRootId {
            type FileSourceRootQuery;
            storage input;
        }
        /// Contents of the source root.
        fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
            type SourceRootQuery;
            storage input;
        }
        /// The set of "local" (that is, from the current workspace) roots.
        /// Files in local roots are assumed to change frequently.
        fn local_roots() -> Arc<Vec<SourceRootId>> {
            type LocalRootsQuery;
            storage input;
        }
        /// The set of roots for crates.io libraries.
        /// Files in libraries are assumed to never change.
        fn library_roots() -> Arc<Vec<SourceRootId>> {
            type LibraryRootsQuery;
            storage input;
        }
        /// The crate graph.
        fn crate_graph() -> Arc<CrateGraph> {
            type CrateGraphQuery;
            storage input;
        }
    }
}
<commit_msg>When constructing a crate graph, detect and forbid cycles.<commit_after>/// This modules specifies the input to rust-analyzer. In some sense, this is
/// **the** most important module, because all other fancy stuff is strictly
/// derived from this input.
///
/// Note that neither this module, nor any other part of the analyzer's core do
/// actual IO. See `vfs` and `project_model` in `ra_lsp_server` crate for how
/// actual IO is done and lowered to input.
use std::sync::Arc;

use relative_path::RelativePathBuf;
use rustc_hash::FxHashMap;
use salsa;

use ra_syntax::SmolStr;

/// `FileId` is an integer which uniquely identifies a file. File paths are
/// messy and system-dependent, so most of the code should work directly with
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
/// and `SourceRoot` is constant. File rename is represented as a pair of
/// deletion/creation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Cargo package. Source roots *might* be nested: in this case, file belongs to
/// the nearest enclosing source root. Path to files are always relative to a
/// source root, and analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refere a file in another source
/// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot {
    pub files: FxHashMap<RelativePathBuf, FileId>,
}

/// `CrateGraph` is a bit of information which turns a set of text files into a
/// number of Rust crates. Each Crate is the `FileId` of it's root module, the
/// set of cfg flags (not yet implemented) and the set of dependencies. Note
/// that, due to cfg's, there might be several crates for a single `FileId`! As
/// in the rust-lang proper, a crate does not have a name. Instead, names are
/// specified on dependency edges. That is, a crate might be known under
/// different names in different dependant crates.
///
/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
/// langauge proper, not a concept of the build system. In practice, we get
/// `CrateGraph` by lowering `cargo metadata` output.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CrateGraph {
    arena: FxHashMap<CrateId, CrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32);

#[derive(Debug, Clone, PartialEq, Eq)]
struct CrateData {
    file_id: FileId,
    dependencies: Vec<Dependency>,
}

impl CrateData {
    fn new(file_id: FileId) -> CrateData {
        CrateData {
            file_id,
            dependencies: Vec::new(),
        }
    }

    fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
        self.dependencies.push(Dependency { name, crate_id })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dependency {
    pub crate_id: CrateId,
    pub name: SmolStr,
}

impl Dependency {
    pub fn crate_id(&self) -> CrateId {
        self.crate_id
    }
}

impl CrateGraph {
    pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
        let crate_id = CrateId(self.arena.len() as u32);
        let prev = self.arena.insert(crate_id, CrateData::new(file_id));
        assert!(prev.is_none());
        crate_id
    }
    pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) {
        if self.dfs(from, to) {
           panic!("Cycle dependencies found.")
        }
        self.arena.get_mut(&from).unwrap().add_dep(name, to)
    }
    pub fn crate_root(&self, crate_id: CrateId) -> FileId {
        self.arena[&crate_id].file_id
    }
    pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
        let (&crate_id, _) = self
            .arena
            .iter()
            .find(|(_crate_id, data)| data.file_id == file_id)?;
        Some(crate_id)
    }
    pub fn dependencies<'a>(
        &'a self,
        crate_id: CrateId,
    ) -> impl Iterator<Item=&'a Dependency> + 'a {
        self.arena[&crate_id].dependencies.iter()
    }
    fn dfs(&self, target: CrateId, from: CrateId) -> bool {
        for dep in self.dependencies(from) {
            let crate_id = dep.crate_id();
            if crate_id == target {
                return true;
            }
            if self.arena.contains_key(&crate_id) {
                if self.dfs(target, crate_id) {
                    return true;
                }
            }
        }
        return false;
    }
}

mod test {
    use super::{CrateGraph, FxHashMap, FileId, SmolStr};
    #[test]
    #[should_panic]
    fn it_should_painc_because_of_cycle_dependencies() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
        graph.add_dep(crate3, SmolStr::new("crate1"), crate1);
    }

    #[test]
    fn it_works() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
    }
}

salsa::query_group! {
    pub trait FilesDatabase: salsa::Database {
        /// Text of the file.
        fn file_text(file_id: FileId) -> Arc<String> {
            type FileTextQuery;
            storage input;
        }
        /// Path to a file, relative to the root of its source root.
        fn file_relative_path(file_id: FileId) -> RelativePathBuf {
            type FileRelativePathQuery;
            storage input;
        }
        /// Source root of the file.
        fn file_source_root(file_id: FileId) -> SourceRootId {
            type FileSourceRootQuery;
            storage input;
        }
        /// Contents of the source root.
        fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
            type SourceRootQuery;
            storage input;
        }
        /// The set of "local" (that is, from the current workspace) roots.
        /// Files in local roots are assumed to change frequently.
        fn local_roots() -> Arc<Vec<SourceRootId>> {
            type LocalRootsQuery;
            storage input;
        }
        /// The set of roots for crates.io libraries.
        /// Files in libraries are assumed to never change.
        fn library_roots() -> Arc<Vec<SourceRootId>> {
            type LibraryRootsQuery;
            storage input;
        }
        /// The crate graph.
        fn crate_graph() -> Arc<CrateGraph> {
            type CrateGraphQuery;
            storage input;
        }
    }
}
<|endoftext|>