<commit_before>//! the rustc crate store interface. This also includes types that
//! are *mostly* used as a part of that interface, but these should
//! probably get a better home if someone can find one.

use crate::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use crate::hir::map as hir_map;
use crate::hir::map::definitions::{DefKey, DefPathTable};
use rustc_data_structures::svh::Svh;
use crate::ty::{self, TyCtxt};
use crate::session::{Session, CrateDisambiguator};
use crate::session::search_paths::PathKind;

use std::any::Any;
use std::path::{Path, PathBuf};
use syntax::ast;
use syntax::symbol::Symbol;
use syntax_pos::Span;
use rustc_target::spec::Target;
use rustc_data_structures::sync::{self, MetadataRef, Lrc};
use rustc_macros::HashStable;

pub use self::NativeLibraryKind::*;

// lonely orphan structs and enums looking for a better home

/// Where a crate came from on the local filesystem. One of these three options
/// must be non-None.
#[derive(PartialEq, Clone, Debug, HashStable)]
pub struct CrateSource {
    pub dylib: Option<(PathBuf, PathKind)>,
    pub rlib: Option<(PathBuf, PathKind)>,
    pub rmeta: Option<(PathBuf, PathKind)>,
}

#[derive(RustcEncodable, RustcDecodable, Copy, Clone,
         Ord, PartialOrd, Eq, PartialEq, Debug, HashStable)]
pub enum DepKind {
    /// A dependency that is only used for its macros, none of which are visible from other crates.
    /// These are included in the metadata only as placeholders and are ignored when decoding.
    UnexportedMacrosOnly,
    /// A dependency that is only used for its macros.
    MacrosOnly,
    /// A dependency that is always injected into the dependency list and so
    /// doesn't need to be linked to an rlib, e.g., the injected allocator.
    Implicit,
    /// A dependency that is required by an rlib version of this crate.
    /// Ordinary `extern crate`s result in `Explicit` dependencies.
    Explicit,
}

impl DepKind {
    pub fn macros_only(self) -> bool {
        match self {
            DepKind::UnexportedMacrosOnly | DepKind::MacrosOnly => true,
            DepKind::Implicit | DepKind::Explicit => false,
        }
    }
}

#[derive(PartialEq, Clone, Debug)]
pub enum LibSource {
    Some(PathBuf),
    MetadataOnly,
    None,
}

impl LibSource {
    pub fn is_some(&self) -> bool {
        if let LibSource::Some(_) = *self {
            true
        } else {
            false
        }
    }

    pub fn option(&self) -> Option<PathBuf> {
        match *self {
            LibSource::Some(ref p) => Some(p.clone()),
            LibSource::MetadataOnly | LibSource::None => None,
        }
    }
}

#[derive(Copy, Debug, PartialEq, Clone, RustcEncodable, RustcDecodable, HashStable)]
pub enum LinkagePreference {
    RequireDynamic,
    RequireStatic,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
         RustcEncodable, RustcDecodable, HashStable)]
pub enum NativeLibraryKind {
    /// native static library (.a archive)
    NativeStatic,
    /// native static library, which doesn't get bundled into .rlibs
    NativeStaticNobundle,
    /// macOS-specific
    NativeFramework,
    /// default way to specify a dynamic library
    NativeUnknown,
}

#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
pub struct NativeLibrary {
    pub kind: NativeLibraryKind,
    pub name: Option<Symbol>,
    pub cfg: Option<ast::MetaItem>,
    pub foreign_module: Option<DefId>,
    pub wasm_import_module: Option<Symbol>,
}

#[derive(Clone, Hash, RustcEncodable, RustcDecodable, HashStable)]
pub struct ForeignModule {
    pub foreign_items: Vec<DefId>,
    pub def_id: DefId,
}

#[derive(Copy, Clone, Debug, HashStable)]
pub struct ExternCrate {
    pub src: ExternCrateSource,

    /// span of the extern crate that caused this to be loaded
    pub span: Span,

    /// Number of links to reach the extern;
    /// used to select the extern with the shortest path
    pub path_len: usize,

    /// If true, then this crate is the crate named by the extern
    /// crate referenced above. If false, then this crate is a dep
    /// of the crate.
    pub direct: bool,
}

#[derive(Copy, Clone, Debug, HashStable)]
pub enum ExternCrateSource {
    /// Crate is loaded by `extern crate`.
    Extern(
        /// def_id of the item in the current crate that caused
        /// this crate to be loaded; note that there could be multiple
        /// such ids
        DefId,
    ),
    // Crate is loaded by `use`.
    Use,
    /// Crate is implicitly loaded by an absolute path.
    Path,
}

pub struct EncodedMetadata {
    pub raw_data: Vec<u8>
}

impl EncodedMetadata {
    pub fn new() -> EncodedMetadata {
        EncodedMetadata {
            raw_data: Vec::new(),
        }
    }
}

/// The backend's way to give the crate store access to the metadata in a library.
/// Note that it returns the raw metadata bytes stored in the library file, whether
/// it is compressed, uncompressed, some weird mix, etc.
/// rmeta files are backend independent and not handled here.
///
/// At the time of this writing, there is only one backend and one way to store
/// metadata in library -- this trait just serves to decouple rustc_metadata from
/// the archive reader, which depends on LLVM.
pub trait MetadataLoader {
    fn get_rlib_metadata(&self,
                         target: &Target,
                         filename: &Path)
                         -> Result<MetadataRef, String>;
    fn get_dylib_metadata(&self,
                          target: &Target,
                          filename: &Path)
                          -> Result<MetadataRef, String>;
}

/// A store of Rust crates, through with their metadata
/// can be accessed.
///
/// Note that this trait should probably not be expanding today. All new
/// functionality should be driven through queries instead!
///
/// If you find a method on this trait named `{name}_untracked` it signifies
/// that it's *not* tracked for dependency information throughout compilation
/// (it'd break incremental compilation) and should only be called pre-HIR (e.g.
/// during resolve)
pub trait CrateStore {
    fn crate_data_as_rc_any(&self, krate: CrateNum) -> Lrc<dyn Any>;

    // resolve
    fn def_key(&self, def: DefId) -> DefKey;
    fn def_path(&self, def: DefId) -> hir_map::DefPath;
    fn def_path_hash(&self, def: DefId) -> hir_map::DefPathHash;
    fn def_path_table(&self, cnum: CrateNum) -> Lrc<DefPathTable>;

    // "queries" used in resolve that aren't tracked for incremental compilation
    fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol;
    fn crate_is_private_dep_untracked(&self, cnum: CrateNum) -> bool;
    fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator;
    fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
    fn extern_mod_stmt_cnum_untracked(&self, emod_id: ast::NodeId) -> Option<CrateNum>;
    fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics;
    fn postorder_cnums_untracked(&self) -> Vec<CrateNum>;

    // This is basically a 1-based range of ints, which is a little
    // silly - I may fix that.
    fn crates_untracked(&self) -> Vec<CrateNum>;

    // utility functions
    fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata;
    fn metadata_encoding_version(&self) -> &[u8];
}

pub type CrateStoreDyn = dyn CrateStore + sync::Sync;

// This method is used when generating the command line to pass through to
// system linker. The linker expects undefined symbols on the left of the
// command line to be defined in libraries on the right, not the other way
// around. For more info, see some comments in the add_used_library function
// below.
//
// In order to get this left-to-right dependency ordering, we perform a
// topological sort of all crates putting the leaves at the right-most
// positions.
pub fn used_crates(tcx: TyCtxt<'_>, prefer: LinkagePreference) -> Vec<(CrateNum, LibSource)> {
    let mut libs = tcx.crates()
        .iter()
        .cloned()
        .filter_map(|cnum| {
            if tcx.dep_kind(cnum).macros_only() {
                return None
            }
            let source = tcx.used_crate_source(cnum);
            let path = match prefer {
                LinkagePreference::RequireDynamic => source.dylib.clone().map(|p| p.0),
                LinkagePreference::RequireStatic => source.rlib.clone().map(|p| p.0),
            };
            let path = match path {
                Some(p) => LibSource::Some(p),
                None => {
                    if source.rmeta.is_some() {
                        LibSource::MetadataOnly
                    } else {
                        LibSource::None
                    }
                }
            };
            Some((cnum, path))
        })
        .collect::<Vec<_>>();
    let mut ordering = tcx.postorder_cnums(LOCAL_CRATE).to_owned();
    ordering.reverse();
    libs.sort_by_cached_key(|&(a, _)| {
        ordering.iter().position(|x| *x == a)
    });
    libs
}
<commit_msg>Derive Debug for NativeLibrary and NativeLibraryKind<commit_after>//! the rustc crate store interface. This also includes types that
//! are *mostly* used as a part of that interface, but these should
//! probably get a better home if someone can find one.

use crate::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use crate::hir::map as hir_map;
use crate::hir::map::definitions::{DefKey, DefPathTable};
use rustc_data_structures::svh::Svh;
use crate::ty::{self, TyCtxt};
use crate::session::{Session, CrateDisambiguator};
use crate::session::search_paths::PathKind;

use std::any::Any;
use std::path::{Path, PathBuf};
use syntax::ast;
use syntax::symbol::Symbol;
use syntax_pos::Span;
use rustc_target::spec::Target;
use rustc_data_structures::sync::{self, MetadataRef, Lrc};
use rustc_macros::HashStable;

pub use self::NativeLibraryKind::*;

// lonely orphan structs and enums looking for a better home

/// Where a crate came from on the local filesystem. One of these three options
/// must be non-None.
#[derive(PartialEq, Clone, Debug, HashStable)]
pub struct CrateSource {
    pub dylib: Option<(PathBuf, PathKind)>,
    pub rlib: Option<(PathBuf, PathKind)>,
    pub rmeta: Option<(PathBuf, PathKind)>,
}

#[derive(RustcEncodable, RustcDecodable, Copy, Clone,
         Ord, PartialOrd, Eq, PartialEq, Debug, HashStable)]
pub enum DepKind {
    /// A dependency that is only used for its macros, none of which are visible from other crates.
    /// These are included in the metadata only as placeholders and are ignored when decoding.
    UnexportedMacrosOnly,
    /// A dependency that is only used for its macros.
    MacrosOnly,
    /// A dependency that is always injected into the dependency list and so
    /// doesn't need to be linked to an rlib, e.g., the injected allocator.
    Implicit,
    /// A dependency that is required by an rlib version of this crate.
    /// Ordinary `extern crate`s result in `Explicit` dependencies.
    Explicit,
}

impl DepKind {
    pub fn macros_only(self) -> bool {
        match self {
            DepKind::UnexportedMacrosOnly | DepKind::MacrosOnly => true,
            DepKind::Implicit | DepKind::Explicit => false,
        }
    }
}

#[derive(PartialEq, Clone, Debug)]
pub enum LibSource {
    Some(PathBuf),
    MetadataOnly,
    None,
}

impl LibSource {
    pub fn is_some(&self) -> bool {
        if let LibSource::Some(_) = *self {
            true
        } else {
            false
        }
    }

    pub fn option(&self) -> Option<PathBuf> {
        match *self {
            LibSource::Some(ref p) => Some(p.clone()),
            LibSource::MetadataOnly | LibSource::None => None,
        }
    }
}

#[derive(Copy, Debug, PartialEq, Clone, RustcEncodable, RustcDecodable, HashStable)]
pub enum LinkagePreference {
    RequireDynamic,
    RequireStatic,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
         RustcEncodable, RustcDecodable, HashStable)]
pub enum NativeLibraryKind {
    /// native static library (.a archive)
    NativeStatic,
    /// native static library, which doesn't get bundled into .rlibs
    NativeStaticNobundle,
    /// macOS-specific
    NativeFramework,
    /// default way to specify a dynamic library
    NativeUnknown,
}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
pub struct NativeLibrary {
    pub kind: NativeLibraryKind,
    pub name: Option<Symbol>,
    pub cfg: Option<ast::MetaItem>,
    pub foreign_module: Option<DefId>,
    pub wasm_import_module: Option<Symbol>,
}

#[derive(Clone, Hash, RustcEncodable, RustcDecodable, HashStable)]
pub struct ForeignModule {
    pub foreign_items: Vec<DefId>,
    pub def_id: DefId,
}

#[derive(Copy, Clone, Debug, HashStable)]
pub struct ExternCrate {
    pub src: ExternCrateSource,

    /// span of the extern crate that caused this to be loaded
    pub span: Span,

    /// Number of links to reach the extern;
    /// used to select the extern with the shortest path
    pub path_len: usize,

    /// If true, then this crate is the crate named by the extern
    /// crate referenced above. If false, then this crate is a dep
    /// of the crate.
    pub direct: bool,
}

#[derive(Copy, Clone, Debug, HashStable)]
pub enum ExternCrateSource {
    /// Crate is loaded by `extern crate`.
    Extern(
        /// def_id of the item in the current crate that caused
        /// this crate to be loaded; note that there could be multiple
        /// such ids
        DefId,
    ),
    // Crate is loaded by `use`.
    Use,
    /// Crate is implicitly loaded by an absolute path.
    Path,
}

pub struct EncodedMetadata {
    pub raw_data: Vec<u8>
}

impl EncodedMetadata {
    pub fn new() -> EncodedMetadata {
        EncodedMetadata {
            raw_data: Vec::new(),
        }
    }
}

/// The backend's way to give the crate store access to the metadata in a library.
/// Note that it returns the raw metadata bytes stored in the library file, whether
/// it is compressed, uncompressed, some weird mix, etc.
/// rmeta files are backend independent and not handled here.
///
/// At the time of this writing, there is only one backend and one way to store
/// metadata in library -- this trait just serves to decouple rustc_metadata from
/// the archive reader, which depends on LLVM.
pub trait MetadataLoader {
    fn get_rlib_metadata(&self,
                         target: &Target,
                         filename: &Path)
                         -> Result<MetadataRef, String>;
    fn get_dylib_metadata(&self,
                          target: &Target,
                          filename: &Path)
                          -> Result<MetadataRef, String>;
}

/// A store of Rust crates, through with their metadata
/// can be accessed.
///
/// Note that this trait should probably not be expanding today. All new
/// functionality should be driven through queries instead!
///
/// If you find a method on this trait named `{name}_untracked` it signifies
/// that it's *not* tracked for dependency information throughout compilation
/// (it'd break incremental compilation) and should only be called pre-HIR (e.g.
/// during resolve)
pub trait CrateStore {
    fn crate_data_as_rc_any(&self, krate: CrateNum) -> Lrc<dyn Any>;

    // resolve
    fn def_key(&self, def: DefId) -> DefKey;
    fn def_path(&self, def: DefId) -> hir_map::DefPath;
    fn def_path_hash(&self, def: DefId) -> hir_map::DefPathHash;
    fn def_path_table(&self, cnum: CrateNum) -> Lrc<DefPathTable>;

    // "queries" used in resolve that aren't tracked for incremental compilation
    fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol;
    fn crate_is_private_dep_untracked(&self, cnum: CrateNum) -> bool;
    fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator;
    fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
    fn extern_mod_stmt_cnum_untracked(&self, emod_id: ast::NodeId) -> Option<CrateNum>;
    fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics;
    fn postorder_cnums_untracked(&self) -> Vec<CrateNum>;

    // This is basically a 1-based range of ints, which is a little
    // silly - I may fix that.
    fn crates_untracked(&self) -> Vec<CrateNum>;

    // utility functions
    fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata;
    fn metadata_encoding_version(&self) -> &[u8];
}

pub type CrateStoreDyn = dyn CrateStore + sync::Sync;

// This method is used when generating the command line to pass through to
// system linker. The linker expects undefined symbols on the left of the
// command line to be defined in libraries on the right, not the other way
// around. For more info, see some comments in the add_used_library function
// below.
//
// In order to get this left-to-right dependency ordering, we perform a
// topological sort of all crates putting the leaves at the right-most
// positions.
pub fn used_crates(tcx: TyCtxt<'_>, prefer: LinkagePreference) -> Vec<(CrateNum, LibSource)> {
    let mut libs = tcx.crates()
        .iter()
        .cloned()
        .filter_map(|cnum| {
            if tcx.dep_kind(cnum).macros_only() {
                return None
            }
            let source = tcx.used_crate_source(cnum);
            let path = match prefer {
                LinkagePreference::RequireDynamic => source.dylib.clone().map(|p| p.0),
                LinkagePreference::RequireStatic => source.rlib.clone().map(|p| p.0),
            };
            let path = match path {
                Some(p) => LibSource::Some(p),
                None => {
                    if source.rmeta.is_some() {
                        LibSource::MetadataOnly
                    } else {
                        LibSource::None
                    }
                }
            };
            Some((cnum, path))
        })
        .collect::<Vec<_>>();
    let mut ordering = tcx.postorder_cnums(LOCAL_CRATE).to_owned();
    ordering.reverse();
    libs.sort_by_cached_key(|&(a, _)| {
        ordering.iter().position(|x| *x == a)
    });
    libs
}
<|endoftext|>


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

/*!
Attribute parsing

The attribute parser provides methods for pulling documentation out of
an AST's attributes.
*/

use syntax::ast;
use syntax::attr;
use core::tuple;

pub type CrateAttrs = {
    name: Option<~str>
};

#[cfg(test)]
mod test {
    #[legacy_exports];

    fn parse_attributes(+source: ~str) -> ~[ast::attribute] {
        use syntax::parse;
        use parse::parser;
        use parse::attr::parser_attr;
        use syntax::codemap;
        use syntax::diagnostic;

        let parse_sess = syntax::parse::new_parse_sess(None);
        let parser = parse::new_parser_from_source_str(
            parse_sess, ~[], ~"-", codemap::FssNone, @source);

        parser.parse_outer_attributes()
    }
}

fn doc_metas(
    +attrs: ~[ast::attribute]
) -> ~[@ast::meta_item] {

    let doc_attrs = attr::find_attrs_by_name(attrs, ~"doc");
    let doc_metas = do doc_attrs.map |attr| {
        attr::attr_meta(attr::desugar_doc_attr(attr))
    };

    return doc_metas;
}

pub fn parse_crate(+attrs: ~[ast::attribute]) -> CrateAttrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::last_meta_item_value_str_by_name(link_metas, ~"name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = ~"#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == Some(~"snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = ~"";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == None;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = ~"#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == None;
}

pub fn parse_desc(+attrs: ~[ast::attribute]) -> Option<~str> {
    let doc_strs = do doc_metas(attrs).filter_map |meta| {
        attr::get_meta_item_value_str(*meta)
    };
    if doc_strs.is_empty() {
        None
    } else {
        Some(str::connect(doc_strs, "\n"))
    }
}

#[test]
fn parse_desc_should_handle_undocumented_mods() {
    let source = ~"";
    let attrs = test::parse_attributes(source);
    let attrs = parse_desc(attrs);
    assert attrs == None;
}

#[test]
fn parse_desc_should_parse_simple_doc_attributes() {
    let source = ~"#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_desc(attrs);
    assert attrs == Some(~"basic");
}

pub fn parse_hidden(+attrs: ~[ast::attribute]) -> bool {
    do doc_metas(attrs).find |meta| {
        match attr::get_meta_item_list(meta) {
          Some(metas) => {
            let hiddens = attr::find_meta_items_by_name(metas, ~"hidden");
            vec::is_not_empty(hiddens)
          }
          None => false
        }
    }.is_some()
}

#[test]
fn should_parse_hidden_attribute() {
    let source = ~"#[doc(hidden)]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == true;
}

#[test]
fn should_parse_hidden_attribute_with_other_docs() {
    let source = ~"#[doc = \"foo\"] #[doc(hidden)] #[doc = \"foo\"]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == true;
}

#[test]
fn should_not_parse_non_hidden_attribute() {
    let source = ~"#[doc = \"\"]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == false;
}

#[test]
fn should_concatenate_multiple_doc_comments() {
    let source = ~"/// foo\n/// bar";
    let desc = parse_desc(test::parse_attributes(source));
    assert desc == Some(~"foo\nbar");
}

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

/*!
Attribute parsing

The attribute parser provides methods for pulling documentation out of
an AST's attributes.
*/

use syntax::ast;
use syntax::attr;
use core::tuple;

pub type CrateAttrs = {
    name: Option<~str>
};

#[cfg(test)]
mod test {
    #[legacy_exports];

    fn parse_attributes(+source: ~str) -> ~[ast::attribute] {
        use syntax::parse;
        use syntax::parse::parser;
        use syntax::parse::attr::parser_attr;
        use syntax::codemap;
        use syntax::diagnostic;

        let parse_sess = syntax::parse::new_parse_sess(None);
        let parser = parse::new_parser_from_source_str(
            parse_sess, ~[], ~"-", codemap::FssNone, @source);

        parser.parse_outer_attributes()
    }
}

fn doc_metas(
    +attrs: ~[ast::attribute]
) -> ~[@ast::meta_item] {

    let doc_attrs = attr::find_attrs_by_name(attrs, ~"doc");
    let doc_metas = do doc_attrs.map |attr| {
        attr::attr_meta(attr::desugar_doc_attr(attr))
    };

    return doc_metas;
}

pub fn parse_crate(+attrs: ~[ast::attribute]) -> CrateAttrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::last_meta_item_value_str_by_name(link_metas, ~"name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = ~"#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == Some(~"snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = ~"";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == None;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = ~"#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == None;
}

pub fn parse_desc(+attrs: ~[ast::attribute]) -> Option<~str> {
    let doc_strs = do doc_metas(attrs).filter_map |meta| {
        attr::get_meta_item_value_str(*meta)
    };
    if doc_strs.is_empty() {
        None
    } else {
        Some(str::connect(doc_strs, "\n"))
    }
}

#[test]
fn parse_desc_should_handle_undocumented_mods() {
    let source = ~"";
    let attrs = test::parse_attributes(source);
    let attrs = parse_desc(attrs);
    assert attrs == None;
}

#[test]
fn parse_desc_should_parse_simple_doc_attributes() {
    let source = ~"#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_desc(attrs);
    assert attrs == Some(~"basic");
}

pub fn parse_hidden(+attrs: ~[ast::attribute]) -> bool {
    do doc_metas(attrs).find |meta| {
        match attr::get_meta_item_list(meta) {
          Some(metas) => {
            let hiddens = attr::find_meta_items_by_name(metas, ~"hidden");
            vec::is_not_empty(hiddens)
          }
          None => false
        }
    }.is_some()
}

#[test]
fn should_parse_hidden_attribute() {
    let source = ~"#[doc(hidden)]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == true;
}

#[test]
fn should_parse_hidden_attribute_with_other_docs() {
    let source = ~"#[doc = \"foo\"] #[doc(hidden)] #[doc = \"foo\"]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == true;
}

#[test]
fn should_not_parse_non_hidden_attribute() {
    let source = ~"#[doc = \"\"]";
    let attrs = test::parse_attributes(source);
    assert parse_hidden(attrs) == false;
}

#[test]
fn should_concatenate_multiple_doc_comments() {
    let source = ~"/// foo\n/// bar";
    let desc = parse_desc(test::parse_attributes(source));
    assert desc == Some(~"foo\nbar");
}

<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    local_client::LocalClient,
    persistent_safety_storage::PersistentSafetyStorage,
    process::ProcessService,
    remote_service::RemoteService,
    serializer::{SerializerClient, SerializerService},
    spawned_process::SpawnedProcess,
    thread::ThreadService,
    SafetyRules, TSafetyRules,
};
use consensus_types::common::Author;
use libra_config::config::{NodeConfig, SafetyRulesService};
use libra_secure_storage::Storage;
use std::{
    convert::TryInto,
    net::SocketAddr,
    sync::{Arc, RwLock},
};

pub fn extract_service_inputs(config: &mut NodeConfig) -> (Author, PersistentSafetyStorage) {
    let author = config
        .validator_network
        .as_ref()
        .expect("Missing validator network")
        .peer_id();

    let backend = &config.consensus.safety_rules.backend;
    let internal_storage: Storage = backend.try_into().expect("Unable to initialize storage");

    let storage = if let Some(test_config) = config.test.as_mut() {
        let private_key = test_config
            .consensus_keypair
            .as_mut()
            .expect("Missing consensus keypair in test config")
            .take_private()
            .expect("Failed to take Consensus private key, key absent or already read");
        let waypoint = config.base.waypoint.waypoint();

        PersistentSafetyStorage::initialize(internal_storage, private_key, waypoint)
    } else {
        PersistentSafetyStorage::new(internal_storage)
    };

    (author, storage)
}

enum SafetyRulesWrapper {
    Local(Arc<RwLock<SafetyRules>>),
    Process(ProcessService),
    Serializer(Arc<RwLock<SerializerService>>),
    SpawnedProcess(SpawnedProcess),
    Thread(ThreadService),
}

pub struct SafetyRulesManager {
    internal_safety_rules: SafetyRulesWrapper,
}

impl SafetyRulesManager {
    pub fn new(config: &mut NodeConfig) -> Self {
        match &config.consensus.safety_rules.service {
            SafetyRulesService::Process(conf) => return Self::new_process(conf.server_address),
            SafetyRulesService::SpawnedProcess(_) => return Self::new_spawned_process(config),
            _ => (),
        };

        let (author, storage) = extract_service_inputs(config);
        let sr_config = &config.consensus.safety_rules;
        match sr_config.service {
            SafetyRulesService::Local => Self::new_local(author, storage),
            SafetyRulesService::Serializer => Self::new_serializer(author, storage),
            SafetyRulesService::Thread => Self::new_thread(author, storage),
            _ => panic!("Unimplemented SafetyRulesService: {:?}", sr_config.service),
        }
    }

    pub fn new_local(author: Author, storage: PersistentSafetyStorage) -> Self {
        let safety_rules = SafetyRules::new(author, storage);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Local(Arc::new(RwLock::new(safety_rules))),
        }
    }

    pub fn new_process(server_addr: SocketAddr) -> Self {
        let process_service = ProcessService::new(server_addr);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Process(process_service),
        }
    }

    pub fn new_serializer(author: Author, storage: PersistentSafetyStorage) -> Self {
        let safety_rules = SafetyRules::new(author, storage);
        let serializer_service = SerializerService::new(safety_rules);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Serializer(Arc::new(RwLock::new(
                serializer_service,
            ))),
        }
    }

    pub fn new_spawned_process(config: &NodeConfig) -> Self {
        let process = SpawnedProcess::new(config);
        Self {
            internal_safety_rules: SafetyRulesWrapper::SpawnedProcess(process),
        }
    }

    pub fn new_thread(author: Author, storage: PersistentSafetyStorage) -> Self {
        let thread = ThreadService::new(author, storage);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Thread(thread),
        }
    }

    pub fn client(&self) -> Box<dyn TSafetyRules + Send + Sync> {
        match &self.internal_safety_rules {
            SafetyRulesWrapper::Local(safety_rules) => {
                Box::new(LocalClient::new(safety_rules.clone()))
            }
            SafetyRulesWrapper::Process(process) => Box::new(process.client()),
            SafetyRulesWrapper::Serializer(serializer_service) => {
                Box::new(SerializerClient::new(serializer_service.clone()))
            }
            SafetyRulesWrapper::SpawnedProcess(process) => Box::new(process.client()),
            SafetyRulesWrapper::Thread(thread) => Box::new(thread.client()),
        }
    }
}
<commit_msg>[safety-rules] ensure storage is available at startup or fail<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    local_client::LocalClient,
    persistent_safety_storage::PersistentSafetyStorage,
    process::ProcessService,
    remote_service::RemoteService,
    serializer::{SerializerClient, SerializerService},
    spawned_process::SpawnedProcess,
    thread::ThreadService,
    SafetyRules, TSafetyRules,
};
use consensus_types::common::Author;
use libra_config::config::{NodeConfig, SafetyRulesService};
use libra_secure_storage::{KVStorage, Storage};
use std::{
    convert::TryInto,
    net::SocketAddr,
    sync::{Arc, RwLock},
};

pub fn extract_service_inputs(config: &mut NodeConfig) -> (Author, PersistentSafetyStorage) {
    let author = config
        .validator_network
        .as_ref()
        .expect("Missing validator network")
        .peer_id();

    let backend = &config.consensus.safety_rules.backend;
    let internal_storage: Storage = backend.try_into().expect("Unable to initialize storage");
    internal_storage
        .available()
        .expect("Storage is not available");

    let storage = if let Some(test_config) = config.test.as_mut() {
        let private_key = test_config
            .consensus_keypair
            .as_mut()
            .expect("Missing consensus keypair in test config")
            .take_private()
            .expect("Failed to take Consensus private key, key absent or already read");
        let waypoint = config.base.waypoint.waypoint();

        PersistentSafetyStorage::initialize(internal_storage, private_key, waypoint)
    } else {
        PersistentSafetyStorage::new(internal_storage)
    };

    (author, storage)
}

enum SafetyRulesWrapper {
    Local(Arc<RwLock<SafetyRules>>),
    Process(ProcessService),
    Serializer(Arc<RwLock<SerializerService>>),
    SpawnedProcess(SpawnedProcess),
    Thread(ThreadService),
}

pub struct SafetyRulesManager {
    internal_safety_rules: SafetyRulesWrapper,
}

impl SafetyRulesManager {
    pub fn new(config: &mut NodeConfig) -> Self {
        match &config.consensus.safety_rules.service {
            SafetyRulesService::Process(conf) => return Self::new_process(conf.server_address),
            SafetyRulesService::SpawnedProcess(_) => return Self::new_spawned_process(config),
            _ => (),
        };

        let (author, storage) = extract_service_inputs(config);
        let sr_config = &config.consensus.safety_rules;
        match sr_config.service {
            SafetyRulesService::Local => Self::new_local(author, storage),
            SafetyRulesService::Serializer => Self::new_serializer(author, storage),
            SafetyRulesService::Thread => Self::new_thread(author, storage),
            _ => panic!("Unimplemented SafetyRulesService: {:?}", sr_config.service),
        }
    }

    pub fn new_local(author: Author, storage: PersistentSafetyStorage) -> Self {
        let safety_rules = SafetyRules::new(author, storage);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Local(Arc::new(RwLock::new(safety_rules))),
        }
    }

    pub fn new_process(server_addr: SocketAddr) -> Self {
        let process_service = ProcessService::new(server_addr);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Process(process_service),
        }
    }

    pub fn new_serializer(author: Author, storage: PersistentSafetyStorage) -> Self {
        let safety_rules = SafetyRules::new(author, storage);
        let serializer_service = SerializerService::new(safety_rules);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Serializer(Arc::new(RwLock::new(
                serializer_service,
            ))),
        }
    }

    pub fn new_spawned_process(config: &NodeConfig) -> Self {
        let process = SpawnedProcess::new(config);
        Self {
            internal_safety_rules: SafetyRulesWrapper::SpawnedProcess(process),
        }
    }

    pub fn new_thread(author: Author, storage: PersistentSafetyStorage) -> Self {
        let thread = ThreadService::new(author, storage);
        Self {
            internal_safety_rules: SafetyRulesWrapper::Thread(thread),
        }
    }

    pub fn client(&self) -> Box<dyn TSafetyRules + Send + Sync> {
        match &self.internal_safety_rules {
            SafetyRulesWrapper::Local(safety_rules) => {
                Box::new(LocalClient::new(safety_rules.clone()))
            }
            SafetyRulesWrapper::Process(process) => Box::new(process.client()),
            SafetyRulesWrapper::Serializer(serializer_service) => {
                Box::new(SerializerClient::new(serializer_service.clone()))
            }
            SafetyRulesWrapper::SpawnedProcess(process) => Box::new(process.client()),
            SafetyRulesWrapper::Thread(thread) => Box::new(thread.client()),
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use parse::lexer::*;
use parse::tokens::*;
use ast::{Stmt, Expr, Block, TType, Local, Decl};
use ast::Stmt::*;
use ast::Expr::*;
use ast::TType::*;
use ast::Decl::*;
//use ast::*;
use ptr::{B};
//use ast::{Expr, Stmt};

type BlockStack = Vec<Block>;

pub struct Parser{
    lexer : Lexer,
    block_stack : BlockStack,
    paren_stack : Vec<char>,
    seq_expr_list : Vec<B<Expr>>
}

impl Parser{
    pub fn new(src : String)->Self{
        Parser {lexer : Lexer::new(src),
                block_stack : BlockStack::new(),
                paren_stack : Vec::new(),
                seq_expr_list : Vec::new() }
    }

    pub fn run(& mut self)->Option<Block>{
        self.parse_block()
        //self.block.generate();
    }

    fn parse_block(& mut self)->Option<Block>{
        //let mut b = Block::new();
        //self.block_stack.push(b);
        self.lexer.get_char();
        self.program(); //begin parsing
        debug_assert!(self.block_stack.len() == 1, "Only parent block should be on
                                                    the stack when the parsing is finished");
        let mut main_block = self.block_stack.pop().unwrap();
        //main_block.generate();
        //if main_block.statements.len() == 0{
        if !main_block.expr.is_some(){
            None
        }
        else{
            Some(main_block)
        }
    }

    fn program(&mut self){
      loop{
        match self.lexer.get_token(){
            //FIXME semicolon handling should change:
            Token::SemiColon => continue,
            Token::Nil |
            Token::Number |
            Token::LeftParen |
            Token::Minus |
            Token::If |
            Token::While |
            Token::For |
            Token::Break |
            Token::Let |
            Token::Ident => {
                let expr = self.expr();
                self.block_stack.last_mut().unwrap().expr = expr;
                //FIXME should we break?
                break;
            },
            /*Token::Break => {
                let mut curr_block = self.block_stack.last_mut().unwrap();
                curr_block.statements.push(Self::mk_break_stmt());
            },*/
            /*Token::Do => {
                debug_assert!(self.block_stack.len() > 0, "No parent block on the stack");
                self.block_stack.push(Block::new());
                self.expr();
                if self.lexer.curr_token == Token::End{
                    //TODO make sure we track all block openings
                    let block = self.block_stack.pop().unwrap();
                    let mut curr_block = self.block_stack.last_mut().unwrap();
                    //curr_block.statements.push(Self::mk_block_stmt(block));
                }
            },*/
            Token::Eof => {return},
            Token::End => {
                //TODO block stack pop
                return
                //continue;
            },
            _ => {panic!("Invalid token");}
        }
      }
    }

    // fn mk_var_decl(local : Local)->B<Stmt>{
    //     B(VarDeclStmt(local))
    // }
    //
    // fn mk_label_stmt(label : String)->B<Stmt>{
    //     B(ExprStmt(Self::mk_label_expr(label)))
    // }
    //
    // fn mk_label_expr(label: String)->B<Expr>{
    //     B(LabelExpr(label))
    // }
    //
    // fn mk_goto_stmt(label : String) -> B<Stmt>{
    //     B(ExprStmt(Self::mk_goto_expr(label)))
    // }
    //
    // fn mk_goto_expr(label : String) -> B<Expr>{
    //     B(GotoExpr(label))
    // }
    //
    // fn mk_break_stmt() -> B<Stmt>{
    //     B(ExprStmt(B(BreakExpr)))
    // }
    //
    // fn mk_block_stmt(block : Block) -> B<Stmt>{
    //     B(Stmt::ExprStmt(Self::mk_block_expr(block)))
    // }
    //
    // fn mk_block_expr(block : Block) -> B<Expr>{
    //     B(Expr::BlockExpr(B(block)))
    // }
    //
    // fn exprlist(&mut self){
    //      self.expr();
    //      match self.lexer.get_token(){
    //         Token::Ident => {},
    //         _ => {}
    //      }
    // }
    //
    // fn varlist(&mut self){
    //
    // }

    fn expr(&mut self) -> Option<B<Expr>> {
        match self.lexer.curr_token{
            Token::Nil => {Some(B(NilExpr))},
            Token::Number => {
                Some(B(NumExpr(self.lexer.curr_string.clone().parse::<i32>().unwrap())))
            },
            Token::Ident => {
                //check if symbol defined in the sym tab
                //if self.block_stack.last().unwrap().contains(self.lexer.curr_string)
                match self.lexer.get_token(){
                    Token::LeftSquare => {}, //subscript
                    Token::Dot => {}, //fieldexp
                    Token::LeftParen => {}, //callexpr

                    _ => {
                        return Some(B(IdExpr(self.lexer.curr_string.clone())))
                    }
                }
                Some(B(IdentExpr(self.lexer.curr_string.clone())))
            },
            Token::Let => {
                let mut b = Block::new();
                //set parent-child relationship
                self.block_stack.push(b);
                let mut decls = Vec::new();
                loop{
                    match self.lexer.get_token() {
                        Token::Type => { //typedec
                            match self.lexer.get_token() {
                                Token::Ident => {
                                    let id = self.lexer.curr_string.clone();
                                    match self.lexer.get_token(){
                                        Token::Equals => {
                                            match self.lexer.get_token(){
                                                Token::Int => decls.push(TyDec(id, TInt32)),
                                                Token::TokString => decls.push(TyDec(id, TString)),
                                                Token::Ident => decls.push(TyDec(id, TCustom(self.lexer.curr_string.clone()))),
                                                Token::Array => {
                                                    match self.lexer.get_token() {
                                                        Token::Of => {
                                                            match self.lexer.get_token() {
                                                                Token::Int => {},
                                                                Token::TokString => {},
                                                                Token::Ident => {},
                                                                _ => panic!("Expected either int, string or type-id")
                                                            }
                                                        },
                                                        _ => panic!("Expected 'of' after 'array'")
                                                    }
                                                },
                                                Token::LeftCurly => { //rectype

                                                },
                                                _ => panic!("Expected either int, string, type-id, array of, '{' after '='")
                                            }
                                        },
                                        _ => panic!("Expected '=' after type-id")
                                    }
                                },
                                _ => panic!("Expected identifier after 'type'")
                            }
                        },
                        Token::Var => { //Vardec
                            match self.lexer.get_token() {
                                Token::Ident => {
                                    let id = self.lexer.curr_string.clone();
                                    match self.lexer.get_token() {
                                        Token::Colon => {
                                            match self.lexer.get_token() {
                                                Token::Int => {
                                                    match self.lexer.get_token(){
                                                        Token::ColonEquals => {
                                                            //get rhs expr and its type
                                                            let (ty, expr) = self.evaluable_expr();
                                                            self.block_stack.last_mut().unwrap().sym_tab.borrow_mut().insert(id.clone(), ty);
                                                            decls.push(VarDec(id.clone(), TInt32, expr));
                                                        },
                                                        _ => panic!("Expected ':='")
                                                    }
                                                },
                                                Token::TokString => {
                                                    match self.lexer.get_token(){
                                                        Token::ColonEquals => {
                                                            self.expr();
                                                        },
                                                        _ => panic!("Expected ':='")
                                                    }
                                                },
                                                _ => panic!("expr : pattern not covered")
                                            }
                                        },
                                        _ => panic!("Expected ':' after identifier")
                                    }
                                },
                                _ => panic!("Expected an identifier")
                            }
                        },
                        Token::Function => { //functiondec

                        },
                        Token::In => break,
                        //FIXME Eof occurrence is an error
                        Token::Eof => break,
                        //FIXME End occurrence is an error
                        Token::End => break,
                        _ => panic!("Unexpected token. Expected a declaration or 'in'")
                    }
                }//let loop ends
                //FIXME start scanning expressions after 'in'
                return Some(B(LetExpr(decls, None)))
            },
            Token::LeftParen => { //seqexpr
                self.paren_stack.push('(');
                let e = self.expr();
                if e.is_some() {
                    self.seq_expr_list.push(e.unwrap());
                }
                //FIXME remove this:
                Some(B(SeqExpr(None)))
            },
            Token::RightParen => {
                if self.paren_stack.is_empty(){
                    panic!("Mismatched parenthesis");
                }
                self.paren_stack.pop();
                //TODO mem::replace self.seq_expr_list with Vec::new and assign it to SeqExpr
                Some(B(SeqExpr(None)))
            },
            _ => Some(B(IdentExpr("fsf".to_string())))
        }
    }

    fn evaluable_expr(&mut self)->(TType, B<Expr>){
        match self.lexer.get_token() {
            Token::Ident => {
                let id = self.lexer.curr_string.clone();
                match self.lexer.get_token() {
                    Token::Dot => {
                        match self.lexer.get_token() {
                            Token::Ident => {

                            },
                            _ => panic!("evaluable_expr : pattern not covered")
                        }
                    },
                    Token::LeftSquare => {

                    },
                    //FIXME can new line be replaced with a semicolon as a decl terminator instead?
                    Token::NewLine => {
                        //search the ident in the current symbol table
                        let mut b = self.block_stack.last_mut().unwrap();
                        if b.contains(&self.lexer.curr_string){
                            let m = b.sym_tab.borrow();
                            let ty = m.get(&self.lexer.curr_string).unwrap().clone();
                            return (ty.clone(), B(IdExpr(self.lexer.curr_string.clone())))
                        }
                        else{
                            panic!("Undefined symbol - {}", self.lexer.curr_string);
                        }
                    }
                    _ => panic!("evaluable_expr : pattern not covered")
                }
            },
            Token::Number => {
                //FIXME parse the whole numeric expression
                let num = self.lexer.curr_string.parse::<i32>().unwrap();
                let op1 = B(NumExpr(num));
                match self.lexer.get_token(){
                    Token::Plus => {
                        let (t, op2) = self.evaluable_expr();
                        //FIXME it's better to use a type-checker
                        if t == TInt32{
                            return (TInt32, B(AddExpr(op1, op2)))
                        }
                        else{
                            panic!("Expected i32 as the type of rhs expression");
                        }
                    },
                    _ => return (TInt32, op1)
                }
                //return (TInt32, B(NumExpr(num)))
            },
            _ => panic!("evaluable_expr : pattern not covered")
        }
        //FIXME remove this:
        return (TInt32, B(NumExpr(1)))
    }
}

#[test]
fn test_let_var_decl_returns_block() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    assert_eq!(p.run().is_some(), true);
}

#[test]
fn test_let_var_decl_returns_let_expr() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 1);
            assert_eq!(o.is_some(), false);
            match v[0]{
                VarDec(ref id, ref ty, ref e) => {
                    assert_eq!(*id, "a".to_string());
                    match **e{ //**e means deref deref B<T> which results in T
                        NumExpr(ref n) => assert_eq!(1, *n),
                        _ => {}
                    }
                },
                _ => {}
            }
        },
        _ => {}
    }
}

#[test]
fn test_let_var_decl_sym_tab_count() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    let b = p.run().unwrap();
    assert_eq!(b.sym_tab.borrow().len(), 1);
    assert_eq!(b.sym_tab.borrow().get(&"a".to_string()), Some(&TInt32));
}

#[test]
fn test_let_add_expr() {
    let mut p = Parser::new("let var a : int := 1 + 3 + 1 in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 1);
            assert_eq!(o.is_some(), false);
            match v[0]{
                VarDec(ref id, ref ty, ref e) => {
                    assert_eq!(*id, "a".to_string());
                    match **e{ //**e means deref deref B<T> which results in T
                        AddExpr(ref e1, ref e2) => {
                            match **e1{
                                NumExpr(ref n) => assert_eq!(*n, 1),
                                _ => panic!("num expr expected")
                            }

                            match **e2{
                                AddExpr(ref e1, ref e2) => {
                                    match **e1{
                                        NumExpr(ref n) => assert_eq!(*n, 3),
                                        _ => panic!("num expr expected")
                                    }

                                    match **e2{
                                        NumExpr(ref n) => assert_eq!(*n, 1),
                                        _ => panic!("num expr expected")
                                    }
                                },
                                _ => panic!("add expr expected")
                            }
                        },
                        _ => panic!("add expr expected")
                    }
                },
                _ => panic!("ver decl expected")
            }
        },
        _ => panic!("let expr expected")
    }
}

#[test]
fn test_parse_2_vars_in_let() {
    let mut p = Parser::new("let var a : int := 1\nvar b : int:=2\n in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 2);
        },
        _ => {}
    }
}
<commit_msg>adding operator handling in evaluable_expr() for idents<commit_after>#![allow(dead_code)]

use parse::lexer::*;
use parse::tokens::*;
use ast::{Stmt, Expr, Block, TType, Local, Decl};
use ast::Stmt::*;
use ast::Expr::*;
use ast::TType::*;
use ast::Decl::*;
//use ast::*;
use ptr::{B};
//use ast::{Expr, Stmt};

type BlockStack = Vec<Block>;

pub struct Parser{
    lexer : Lexer,
    block_stack : BlockStack,
    paren_stack : Vec<char>,
    seq_expr_list : Vec<B<Expr>>
}

impl Parser{
    pub fn new(src : String)->Self{
        Parser {lexer : Lexer::new(src),
                block_stack : BlockStack::new(),
                paren_stack : Vec::new(),
                seq_expr_list : Vec::new() }
    }

    pub fn run(& mut self)->Option<Block>{
        self.parse_block()
        //self.block.generate();
    }

    fn parse_block(& mut self)->Option<Block>{
        //let mut b = Block::new();
        //self.block_stack.push(b);
        self.lexer.get_char();
        self.program(); //begin parsing
        debug_assert!(self.block_stack.len() == 1, "Only parent block should be on
                                                    the stack when the parsing is finished");
        let mut main_block = self.block_stack.pop().unwrap();
        //main_block.generate();
        //if main_block.statements.len() == 0{
        if !main_block.expr.is_some(){
            None
        }
        else{
            Some(main_block)
        }
    }

    fn program(&mut self){
      loop{
        match self.lexer.get_token(){
            //FIXME semicolon handling should change:
            Token::SemiColon => continue,
            Token::Nil |
            Token::Number |
            Token::LeftParen |
            Token::Minus |
            Token::If |
            Token::While |
            Token::For |
            Token::Break |
            Token::Let |
            Token::Ident => {
                let expr = self.expr();
                self.block_stack.last_mut().unwrap().expr = expr;
                //FIXME should we break?
                break;
            },
            /*Token::Break => {
                let mut curr_block = self.block_stack.last_mut().unwrap();
                curr_block.statements.push(Self::mk_break_stmt());
            },*/
            /*Token::Do => {
                debug_assert!(self.block_stack.len() > 0, "No parent block on the stack");
                self.block_stack.push(Block::new());
                self.expr();
                if self.lexer.curr_token == Token::End{
                    //TODO make sure we track all block openings
                    let block = self.block_stack.pop().unwrap();
                    let mut curr_block = self.block_stack.last_mut().unwrap();
                    //curr_block.statements.push(Self::mk_block_stmt(block));
                }
            },*/
            Token::Eof => {return},
            Token::End => {
                //TODO block stack pop
                return
                //continue;
            },
            _ => {panic!("Invalid token");}
        }
      }
    }

    // fn mk_var_decl(local : Local)->B<Stmt>{
    //     B(VarDeclStmt(local))
    // }
    //
    // fn mk_label_stmt(label : String)->B<Stmt>{
    //     B(ExprStmt(Self::mk_label_expr(label)))
    // }
    //
    // fn mk_label_expr(label: String)->B<Expr>{
    //     B(LabelExpr(label))
    // }
    //
    // fn mk_goto_stmt(label : String) -> B<Stmt>{
    //     B(ExprStmt(Self::mk_goto_expr(label)))
    // }
    //
    // fn mk_goto_expr(label : String) -> B<Expr>{
    //     B(GotoExpr(label))
    // }
    //
    // fn mk_break_stmt() -> B<Stmt>{
    //     B(ExprStmt(B(BreakExpr)))
    // }
    //
    // fn mk_block_stmt(block : Block) -> B<Stmt>{
    //     B(Stmt::ExprStmt(Self::mk_block_expr(block)))
    // }
    //
    // fn mk_block_expr(block : Block) -> B<Expr>{
    //     B(Expr::BlockExpr(B(block)))
    // }
    //
    // fn exprlist(&mut self){
    //      self.expr();
    //      match self.lexer.get_token(){
    //         Token::Ident => {},
    //         _ => {}
    //      }
    // }
    //
    // fn varlist(&mut self){
    //
    // }

    fn expr(&mut self) -> Option<B<Expr>> {
        match self.lexer.curr_token{
            Token::Nil => {Some(B(NilExpr))},
            Token::Number => {
                Some(B(NumExpr(self.lexer.curr_string.clone().parse::<i32>().unwrap())))
            },
            Token::Ident => {
                //check if symbol defined in the sym tab
                //if self.block_stack.last().unwrap().contains(self.lexer.curr_string)
                match self.lexer.get_token(){
                    Token::LeftSquare => {}, //subscript
                    Token::Dot => {}, //fieldexp
                    Token::LeftParen => {}, //callexpr

                    _ => {
                        return Some(B(IdExpr(self.lexer.curr_string.clone())))
                    }
                }
                Some(B(IdentExpr(self.lexer.curr_string.clone())))
            },
            Token::Let => {
                let mut b = Block::new();
                //set parent-child relationship
                self.block_stack.push(b);
                let mut decls = Vec::new();
                loop{
                    match self.lexer.get_token() {
                        Token::Type => { //typedec
                            match self.lexer.get_token() {
                                Token::Ident => {
                                    let id = self.lexer.curr_string.clone();
                                    match self.lexer.get_token(){
                                        Token::Equals => {
                                            match self.lexer.get_token(){
                                                Token::Int => decls.push(TyDec(id, TInt32)),
                                                Token::TokString => decls.push(TyDec(id, TString)),
                                                Token::Ident => decls.push(TyDec(id, TCustom(self.lexer.curr_string.clone()))),
                                                Token::Array => {
                                                    match self.lexer.get_token() {
                                                        Token::Of => {
                                                            match self.lexer.get_token() {
                                                                Token::Int => {},
                                                                Token::TokString => {},
                                                                Token::Ident => {},
                                                                _ => panic!("Expected either int, string or type-id")
                                                            }
                                                        },
                                                        _ => panic!("Expected 'of' after 'array'")
                                                    }
                                                },
                                                Token::LeftCurly => { //rectype

                                                },
                                                _ => panic!("Expected either int, string, type-id, array of, '{' after '='")
                                            }
                                        },
                                        _ => panic!("Expected '=' after type-id")
                                    }
                                },
                                _ => panic!("Expected identifier after 'type'")
                            }
                        },
                        Token::Var => { //Vardec
                            match self.lexer.get_token() {
                                Token::Ident => {
                                    let id = self.lexer.curr_string.clone();
                                    match self.lexer.get_token() {
                                        Token::Colon => {
                                            match self.lexer.get_token() {
                                                Token::Int => {
                                                    match self.lexer.get_token(){
                                                        Token::ColonEquals => {
                                                            //get rhs expr and its type
                                                            let (ty, expr) = self.evaluable_expr();
                                                            self.block_stack.last_mut().unwrap().sym_tab.borrow_mut().insert(id.clone(), ty);
                                                            decls.push(VarDec(id.clone(), TInt32, expr));
                                                        },
                                                        _ => panic!("Expected ':='")
                                                    }
                                                },
                                                Token::TokString => {
                                                    match self.lexer.get_token(){
                                                        Token::ColonEquals => {
                                                            self.expr();
                                                        },
                                                        _ => panic!("Expected ':='")
                                                    }
                                                },
                                                _ => panic!("expr : pattern not covered")
                                            }
                                        },
                                        _ => panic!("Expected ':' after identifier")
                                    }
                                },
                                _ => panic!("Expected an identifier")
                            }
                        },
                        Token::Function => { //functiondec

                        },
                        Token::In => break,
                        //FIXME Eof occurrence is an error
                        Token::Eof => break,
                        //FIXME End occurrence is an error
                        Token::End => break,
                        _ => panic!("Unexpected token. Expected a declaration or 'in'")
                    }
                }//let loop ends
                //FIXME start scanning expressions after 'in'
                return Some(B(LetExpr(decls, None)))
            },
            Token::LeftParen => { //seqexpr
                self.paren_stack.push('(');
                let e = self.expr();
                if e.is_some() {
                    self.seq_expr_list.push(e.unwrap());
                }
                //FIXME remove this:
                Some(B(SeqExpr(None)))
            },
            Token::RightParen => {
                if self.paren_stack.is_empty(){
                    panic!("Mismatched parenthesis");
                }
                self.paren_stack.pop();
                //TODO mem::replace self.seq_expr_list with Vec::new and assign it to SeqExpr
                Some(B(SeqExpr(None)))
            },
            _ => Some(B(IdentExpr("fsf".to_string())))
        }
    }

    fn evaluable_expr(&mut self)->(TType, B<Expr>){
        match self.lexer.get_token() {
            Token::Ident => {
                let id = self.lexer.curr_string.clone();
                match self.lexer.get_token() {
                    Token::Dot => {
                        match self.lexer.get_token() {
                            Token::Ident => {

                            },
                            _ => panic!("evaluable_expr : pattern not covered")
                        }
                    },
                    Token::LeftSquare => {

                    },
                    //FIXME can new line be replaced with a semicolon as a decl terminator instead?
                    Token::NewLine => {
                        //search the ident in the current symbol table
                        let mut b = self.block_stack.last_mut().unwrap();
                        if b.contains(&self.lexer.curr_string){
                            let m = b.sym_tab.borrow();
                            let ty = m.get(&self.lexer.curr_string).unwrap().clone();
                            return (ty.clone(), B(IdExpr(self.lexer.curr_string.clone())))
                        }
                        else{
                            panic!("Undefined symbol - {}", self.lexer.curr_string);
                        }
                    },
                    Token::Plus => {
                        return (TInt32, self.evaluable_expr().1)
                    },
                    //TODO add more binary operators
                    _ => panic!("evaluable_expr : pattern not covered")
                }
            },
            Token::Number => {
                //FIXME parse the whole numeric expression
                let num = self.lexer.curr_string.parse::<i32>().unwrap();
                let op1 = B(NumExpr(num));
                match self.lexer.get_token(){
                    Token::Plus => {
                        let (t, op2) = self.evaluable_expr();
                        //FIXME it's better to use a type-checker
                        if t == TInt32{
                            return (TInt32, B(AddExpr(op1, op2)))
                        }
                        else{
                            panic!("Expected i32 as the type of rhs expression");
                        }
                    },
                    _ => return (TInt32, op1)
                }
                //return (TInt32, B(NumExpr(num)))
            },
            _ => panic!("evaluable_expr : pattern not covered")
        }
        //FIXME remove this:
        return (TInt32, B(NumExpr(1)))
    }
}

#[test]
fn test_let_var_decl_returns_block() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    assert_eq!(p.run().is_some(), true);
}

#[test]
fn test_let_var_decl_returns_let_expr() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 1);
            assert_eq!(o.is_some(), false);
            match v[0]{
                VarDec(ref id, ref ty, ref e) => {
                    assert_eq!(*id, "a".to_string());
                    match **e{ //**e means deref deref B<T> which results in T
                        NumExpr(ref n) => assert_eq!(1, *n),
                        _ => {}
                    }
                },
                _ => {}
            }
        },
        _ => {}
    }
}

#[test]
fn test_let_var_decl_sym_tab_count() {
    let mut p = Parser::new("let var a : int := 1 in end".to_string());
    let b = p.run().unwrap();
    assert_eq!(b.sym_tab.borrow().len(), 1);
    assert_eq!(b.sym_tab.borrow().get(&"a".to_string()), Some(&TInt32));
}

#[test]
fn test_let_add_expr() {
    let mut p = Parser::new("let var a : int := 1 + 3 + 1 in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 1);
            assert_eq!(o.is_some(), false);
            match v[0]{
                VarDec(ref id, ref ty, ref e) => {
                    assert_eq!(*id, "a".to_string());
                    match **e{ //**e means deref deref B<T> which results in T
                        AddExpr(ref e1, ref e2) => {
                            match **e1{
                                NumExpr(ref n) => assert_eq!(*n, 1),
                                _ => panic!("num expr expected")
                            }

                            match **e2{
                                AddExpr(ref e1, ref e2) => {
                                    match **e1{
                                        NumExpr(ref n) => assert_eq!(*n, 3),
                                        _ => panic!("num expr expected")
                                    }

                                    match **e2{
                                        NumExpr(ref n) => assert_eq!(*n, 1),
                                        _ => panic!("num expr expected")
                                    }
                                },
                                _ => panic!("add expr expected")
                            }
                        },
                        _ => panic!("add expr expected")
                    }
                },
                _ => panic!("ver decl expected")
            }
        },
        _ => panic!("let expr expected")
    }
}

#[test]
fn test_parse_2_vars_in_let() {
    let mut p = Parser::new("let var a : int := 1\nvar b : int:=2\n in end".to_string());
    let b = p.run().unwrap();
    match *b.expr.unwrap(){
        LetExpr(ref v, ref o) => {
            assert_eq!(v.len(), 2);
        },
        _ => {}
    }
}
<|endoftext|>


<commit_before>//! Completion for attributes
//!
//! This module uses a bit of static metadata to provide completions
//! for built-in attributes.

use ra_syntax::{ast, AstNode, SyntaxKind};
use rustc_hash::FxHashSet;

use crate::completion::{
    completion_context::CompletionContext,
    completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
};

pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let attribute = ctx.attribute_under_caret.as_ref()?;

    match (attribute.path(), attribute.input()) {
        (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
            if path.to_string() == "derive" =>
        {
            complete_derive(acc, ctx, token_tree)
        }
        (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {}
        _ => complete_attribute_start(acc, ctx, attribute),
    }
    Some(())
}

fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
    for attr_completion in ATTRIBUTES {
        let mut item = CompletionItem::new(
            CompletionKind::Attribute,
            ctx.source_range(),
            attr_completion.label,
        )
        .kind(CompletionItemKind::Attribute);

        if let Some(lookup) = attr_completion.lookup {
            item = item.lookup_by(lookup);
        }

        match (attr_completion.snippet, ctx.config.snippet_cap) {
            (Some(snippet), Some(cap)) => {
                item = item.insert_snippet(cap, snippet);
            }
            _ => {}
        }

        if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
            acc.add(item);
        }
    }
}

struct AttrCompletion {
    label: &'static str,
    lookup: Option<&'static str>,
    snippet: Option<&'static str>,
    prefer_inner: bool,
}

impl AttrCompletion {
    const fn prefer_inner(self) -> AttrCompletion {
        AttrCompletion { prefer_inner: true, ..self }
    }
}

const fn attr(
    label: &'static str,
    lookup: Option<&'static str>,
    snippet: Option<&'static str>,
) -> AttrCompletion {
    AttrCompletion { label, lookup, snippet, prefer_inner: false }
}

const ATTRIBUTES: &[AttrCompletion] = &[
    attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
    attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
    attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
    attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
    attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
    attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
    attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
    attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
    attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
    // FIXME: resolve through macro resolution?
    attr("global_allocator", None, None).prefer_inner(),
    attr("ignore(…)", Some("ignore"), Some("ignore(${0:lint})")),
    attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
    attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
    attr("link", None, None),
    attr("macro_export", None, None),
    attr("macro_use", None, None),
    attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
    attr("no_mangle", None, None),
    attr("no_std", None, None).prefer_inner(),
    attr("non_exhaustive", None, None),
    attr("panic_handler", None, None).prefer_inner(),
    attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
    attr("proc_macro", None, None),
    attr("proc_macro_attribute", None, None),
    attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
    attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
        .prefer_inner(),
    attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
    attr(
        "should_panic(…)",
        Some("should_panic"),
        Some(r#"should_panic(expected = "${0:reason}")"#),
    ),
    attr(
        r#"target_feature = "…""#,
        Some("target_feature"),
        Some("target_feature = \"${0:feature}\""),
    ),
    attr("test", None, None),
    attr("used", None, None),
    attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
    attr(
        r#"windows_subsystem = "…""#,
        Some("windows_subsystem"),
        Some(r#"windows_subsystem = "${0:subsystem}""#),
    )
    .prefer_inner(),
];

fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
    if let Ok(existing_derives) = parse_derive_input(derive_input) {
        for derive_completion in DEFAULT_DERIVE_COMPLETIONS
            .into_iter()
            .filter(|completion| !existing_derives.contains(completion.label))
        {
            let mut label = derive_completion.label.to_owned();
            for dependency in derive_completion
                .dependencies
                .into_iter()
                .filter(|&&dependency| !existing_derives.contains(dependency))
            {
                label.push_str(", ");
                label.push_str(dependency);
            }
            acc.add(
                CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
                    .kind(CompletionItemKind::Attribute),
            );
        }

        for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
            acc.add(
                CompletionItem::new(
                    CompletionKind::Attribute,
                    ctx.source_range(),
                    custom_derive_name,
                )
                .kind(CompletionItemKind::Attribute),
            );
        }
    }
}

fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
    match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
        (Some(left_paren), Some(right_paren))
            if left_paren.kind() == SyntaxKind::L_PAREN
                && right_paren.kind() == SyntaxKind::R_PAREN =>
        {
            let mut input_derives = FxHashSet::default();
            let mut current_derive = String::new();
            for token in derive_input
                .syntax()
                .children_with_tokens()
                .filter_map(|token| token.into_token())
                .skip_while(|token| token != &left_paren)
                .skip(1)
                .take_while(|token| token != &right_paren)
            {
                if SyntaxKind::COMMA == token.kind() {
                    if !current_derive.is_empty() {
                        input_derives.insert(current_derive);
                        current_derive = String::new();
                    }
                } else {
                    current_derive.push_str(token.to_string().trim());
                }
            }

            if !current_derive.is_empty() {
                input_derives.insert(current_derive);
            }
            Ok(input_derives)
        }
        _ => Err(()),
    }
}

fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
    let mut result = FxHashSet::default();
    ctx.scope.process_all_names(&mut |name, scope_def| {
        if let hir::ScopeDef::MacroDef(mac) = scope_def {
            if mac.is_derive_macro() {
                result.insert(name.to_string());
            }
        }
    });
    result
}

struct DeriveCompletion {
    label: &'static str,
    dependencies: &'static [&'static str],
}

/// Standard Rust derives and the information about their dependencies
/// (the dependencies are needed so that the main derive don't break the compilation when added)
const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
    DeriveCompletion { label: "Clone", dependencies: &[] },
    DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
    DeriveCompletion { label: "Debug", dependencies: &[] },
    DeriveCompletion { label: "Default", dependencies: &[] },
    DeriveCompletion { label: "Hash", dependencies: &[] },
    DeriveCompletion { label: "PartialEq", dependencies: &[] },
    DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
    DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
    DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
];

#[cfg(test)]
mod tests {
    use expect::{expect, Expect};

    use crate::completion::{test_utils::completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Attribute);
        expect.assert_eq(&actual);
    }

    #[test]
    fn empty_derive_completion() {
        check(
            r#"
#[derive(<|>)]
struct Test {}
        "#,
            expect![[r#"
                at Clone
                at Copy, Clone
                at Debug
                at Default
                at Eq, PartialEq
                at Hash
                at Ord, PartialOrd, Eq, PartialEq
                at PartialEq
                at PartialOrd, PartialEq
            "#]],
        );
    }

    #[test]
    fn no_completion_for_incorrect_derive() {
        check(
            r#"
#[derive{<|>)]
struct Test {}
"#,
            expect![[r#""#]],
        )
    }

    #[test]
    fn derive_with_input_completion() {
        check(
            r#"
#[derive(serde::Serialize, PartialEq, <|>)]
struct Test {}
"#,
            expect![[r#"
                at Clone
                at Copy, Clone
                at Debug
                at Default
                at Eq
                at Hash
                at Ord, PartialOrd, Eq
                at PartialOrd
            "#]],
        )
    }

    #[test]
    fn test_attribute_completion() {
        check(
            r#"#[<|>]"#,
            expect![[r#"
                at allow(…)
                at cfg(…)
                at cfg_attr(…)
                at deny(…)
                at deprecated = "…"
                at derive(…)
                at doc = "…"
                at forbid(…)
                at ignore(…)
                at inline(…)
                at link
                at link_name = "…"
                at macro_export
                at macro_use
                at must_use = "…"
                at no_mangle
                at non_exhaustive
                at path = "…"
                at proc_macro
                at proc_macro_attribute
                at proc_macro_derive(…)
                at repr(…)
                at should_panic(…)
                at target_feature = "…"
                at test
                at used
                at warn(…)
            "#]],
        )
    }

    #[test]
    fn test_attribute_completion_inside_nested_attr() {
        check(r#"#[allow(<|>)]"#, expect![[]])
    }

    #[test]
    fn test_inner_attribute_completion() {
        check(
            r"#![<|>]",
            expect![[r#"
                at allow(…)
                at cfg(…)
                at cfg_attr(…)
                at deny(…)
                at deprecated = "…"
                at derive(…)
                at doc = "…"
                at feature(…)
                at forbid(…)
                at global_allocator
                at ignore(…)
                at inline(…)
                at link
                at link_name = "…"
                at macro_export
                at macro_use
                at must_use = "…"
                at no_mangle
                at no_std
                at non_exhaustive
                at panic_handler
                at path = "…"
                at proc_macro
                at proc_macro_attribute
                at proc_macro_derive(…)
                at recursion_limit = …
                at repr(…)
                at should_panic(…)
                at target_feature = "…"
                at test
                at used
                at warn(…)
                at windows_subsystem = "…"
            "#]],
        );
    }
}
<commit_msg>Fix ignore attribute autocompletion<commit_after>//! Completion for attributes
//!
//! This module uses a bit of static metadata to provide completions
//! for built-in attributes.

use ra_syntax::{ast, AstNode, SyntaxKind};
use rustc_hash::FxHashSet;

use crate::completion::{
    completion_context::CompletionContext,
    completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
};

pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let attribute = ctx.attribute_under_caret.as_ref()?;

    match (attribute.path(), attribute.input()) {
        (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
            if path.to_string() == "derive" =>
        {
            complete_derive(acc, ctx, token_tree)
        }
        (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {}
        _ => complete_attribute_start(acc, ctx, attribute),
    }
    Some(())
}

fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
    for attr_completion in ATTRIBUTES {
        let mut item = CompletionItem::new(
            CompletionKind::Attribute,
            ctx.source_range(),
            attr_completion.label,
        )
        .kind(CompletionItemKind::Attribute);

        if let Some(lookup) = attr_completion.lookup {
            item = item.lookup_by(lookup);
        }

        match (attr_completion.snippet, ctx.config.snippet_cap) {
            (Some(snippet), Some(cap)) => {
                item = item.insert_snippet(cap, snippet);
            }
            _ => {}
        }

        if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
            acc.add(item);
        }
    }
}

struct AttrCompletion {
    label: &'static str,
    lookup: Option<&'static str>,
    snippet: Option<&'static str>,
    prefer_inner: bool,
}

impl AttrCompletion {
    const fn prefer_inner(self) -> AttrCompletion {
        AttrCompletion { prefer_inner: true, ..self }
    }
}

const fn attr(
    label: &'static str,
    lookup: Option<&'static str>,
    snippet: Option<&'static str>,
) -> AttrCompletion {
    AttrCompletion { label, lookup, snippet, prefer_inner: false }
}

const ATTRIBUTES: &[AttrCompletion] = &[
    attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
    attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
    attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
    attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
    attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
    attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
    attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
    attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
    attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
    // FIXME: resolve through macro resolution?
    attr("global_allocator", None, None).prefer_inner(),
    attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
    attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
    attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
    attr("link", None, None),
    attr("macro_export", None, None),
    attr("macro_use", None, None),
    attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
    attr("no_mangle", None, None),
    attr("no_std", None, None).prefer_inner(),
    attr("non_exhaustive", None, None),
    attr("panic_handler", None, None).prefer_inner(),
    attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
    attr("proc_macro", None, None),
    attr("proc_macro_attribute", None, None),
    attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
    attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
        .prefer_inner(),
    attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
    attr(
        "should_panic(…)",
        Some("should_panic"),
        Some(r#"should_panic(expected = "${0:reason}")"#),
    ),
    attr(
        r#"target_feature = "…""#,
        Some("target_feature"),
        Some("target_feature = \"${0:feature}\""),
    ),
    attr("test", None, None),
    attr("used", None, None),
    attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
    attr(
        r#"windows_subsystem = "…""#,
        Some("windows_subsystem"),
        Some(r#"windows_subsystem = "${0:subsystem}""#),
    )
    .prefer_inner(),
];

fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
    if let Ok(existing_derives) = parse_derive_input(derive_input) {
        for derive_completion in DEFAULT_DERIVE_COMPLETIONS
            .into_iter()
            .filter(|completion| !existing_derives.contains(completion.label))
        {
            let mut label = derive_completion.label.to_owned();
            for dependency in derive_completion
                .dependencies
                .into_iter()
                .filter(|&&dependency| !existing_derives.contains(dependency))
            {
                label.push_str(", ");
                label.push_str(dependency);
            }
            acc.add(
                CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
                    .kind(CompletionItemKind::Attribute),
            );
        }

        for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
            acc.add(
                CompletionItem::new(
                    CompletionKind::Attribute,
                    ctx.source_range(),
                    custom_derive_name,
                )
                .kind(CompletionItemKind::Attribute),
            );
        }
    }
}

fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
    match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
        (Some(left_paren), Some(right_paren))
            if left_paren.kind() == SyntaxKind::L_PAREN
                && right_paren.kind() == SyntaxKind::R_PAREN =>
        {
            let mut input_derives = FxHashSet::default();
            let mut current_derive = String::new();
            for token in derive_input
                .syntax()
                .children_with_tokens()
                .filter_map(|token| token.into_token())
                .skip_while(|token| token != &left_paren)
                .skip(1)
                .take_while(|token| token != &right_paren)
            {
                if SyntaxKind::COMMA == token.kind() {
                    if !current_derive.is_empty() {
                        input_derives.insert(current_derive);
                        current_derive = String::new();
                    }
                } else {
                    current_derive.push_str(token.to_string().trim());
                }
            }

            if !current_derive.is_empty() {
                input_derives.insert(current_derive);
            }
            Ok(input_derives)
        }
        _ => Err(()),
    }
}

fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
    let mut result = FxHashSet::default();
    ctx.scope.process_all_names(&mut |name, scope_def| {
        if let hir::ScopeDef::MacroDef(mac) = scope_def {
            if mac.is_derive_macro() {
                result.insert(name.to_string());
            }
        }
    });
    result
}

struct DeriveCompletion {
    label: &'static str,
    dependencies: &'static [&'static str],
}

/// Standard Rust derives and the information about their dependencies
/// (the dependencies are needed so that the main derive don't break the compilation when added)
const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
    DeriveCompletion { label: "Clone", dependencies: &[] },
    DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
    DeriveCompletion { label: "Debug", dependencies: &[] },
    DeriveCompletion { label: "Default", dependencies: &[] },
    DeriveCompletion { label: "Hash", dependencies: &[] },
    DeriveCompletion { label: "PartialEq", dependencies: &[] },
    DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
    DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
    DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
];

#[cfg(test)]
mod tests {
    use expect::{expect, Expect};

    use crate::completion::{test_utils::completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Attribute);
        expect.assert_eq(&actual);
    }

    #[test]
    fn empty_derive_completion() {
        check(
            r#"
#[derive(<|>)]
struct Test {}
        "#,
            expect![[r#"
                at Clone
                at Copy, Clone
                at Debug
                at Default
                at Eq, PartialEq
                at Hash
                at Ord, PartialOrd, Eq, PartialEq
                at PartialEq
                at PartialOrd, PartialEq
            "#]],
        );
    }

    #[test]
    fn no_completion_for_incorrect_derive() {
        check(
            r#"
#[derive{<|>)]
struct Test {}
"#,
            expect![[r#""#]],
        )
    }

    #[test]
    fn derive_with_input_completion() {
        check(
            r#"
#[derive(serde::Serialize, PartialEq, <|>)]
struct Test {}
"#,
            expect![[r#"
                at Clone
                at Copy, Clone
                at Debug
                at Default
                at Eq
                at Hash
                at Ord, PartialOrd, Eq
                at PartialOrd
            "#]],
        )
    }

    #[test]
    fn test_attribute_completion() {
        check(
            r#"#[<|>]"#,
            expect![[r#"
                at allow(…)
                at cfg(…)
                at cfg_attr(…)
                at deny(…)
                at deprecated = "…"
                at derive(…)
                at doc = "…"
                at forbid(…)
                at ignore = "…"
                at inline(…)
                at link
                at link_name = "…"
                at macro_export
                at macro_use
                at must_use = "…"
                at no_mangle
                at non_exhaustive
                at path = "…"
                at proc_macro
                at proc_macro_attribute
                at proc_macro_derive(…)
                at repr(…)
                at should_panic(…)
                at target_feature = "…"
                at test
                at used
                at warn(…)
            "#]],
        )
    }

    #[test]
    fn test_attribute_completion_inside_nested_attr() {
        check(r#"#[allow(<|>)]"#, expect![[]])
    }

    #[test]
    fn test_inner_attribute_completion() {
        check(
            r"#![<|>]",
            expect![[r#"
                at allow(…)
                at cfg(…)
                at cfg_attr(…)
                at deny(…)
                at deprecated = "…"
                at derive(…)
                at doc = "…"
                at feature(…)
                at forbid(…)
                at global_allocator
                at ignore = "…"
                at inline(…)
                at link
                at link_name = "…"
                at macro_export
                at macro_use
                at must_use = "…"
                at no_mangle
                at no_std
                at non_exhaustive
                at panic_handler
                at path = "…"
                at proc_macro
                at proc_macro_attribute
                at proc_macro_derive(…)
                at recursion_limit = …
                at repr(…)
                at should_panic(…)
                at target_feature = "…"
                at test
                at used
                at warn(…)
                at windows_subsystem = "…"
            "#]],
        );
    }
}
<|endoftext|>