<commit_before>use super::Hash256;
use super::TransitionError;

pub fn get_signed_parent_hashes(
    cycle_length: &u8,
    block_slot: &u64,
    attestation_slot: &u64,
    current_hashes: &Vec<Hash256>,
    oblique_hashes: &Vec<Hash256>)
    -> Result<Vec<Hash256>, TransitionError>
{
    // This cast places a limit on cycle_length. If you change it, check math
    // for overflow.
    let cycle_length: u64 = *cycle_length as u64;

    if current_hashes.len() as u64 != (cycle_length * 2) {
        return Err(TransitionError::InvalidInput(String::from(
                    "current_hashes.len() must equal cycle_length * 2")));
    }
    if attestation_slot >= block_slot {
        return Err(TransitionError::InvalidInput(String::from(
                    "attestation_slot must be less than block_slot")));
    }
    if oblique_hashes.len() as u64 > cycle_length {
        return Err(TransitionError::InvalidInput(String::from(
                    "oblique_hashes.len() must be <= cycle_length * 2")));
    }

    /*
     * Cannot underflow as block_slot cannot be less
     * than attestation_slot.
     */
    let attestation_distance = block_slot - attestation_slot;

    if attestation_distance > cycle_length {
        return Err(TransitionError::InvalidInput(String::from(
                    "attestation_slot must be withing one cycle of block_slot")));
    }

    /*
     * Cannot underflow because attestation_distance cannot
     * be larger than cycle_length.
     */
    let start = cycle_length - attestation_distance;

    /*
     * Overflow is potentially impossible, but proof is complicated
     * enough to just use checked math.
     *
     * Arithmetic is:
     * start + cycle_length - oblique_hashes.len()
     */
    let end = start.checked_add(cycle_length)
        .and_then(|x| x.checked_sub(oblique_hashes.len() as u64))
        .ok_or(TransitionError::IntWrapping)?;

    let mut hashes = Vec::new();
    hashes.extend_from_slice(
        &current_hashes[(start as usize)..(end as usize)]);
    hashes.append(&mut oblique_hashes.clone());

    Ok(hashes)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn get_range_of_hashes(from: usize, to: usize) -> Vec<Hash256> {
        (from..to).map(|i| get_hash(&vec![i as u8])).collect()
    }

    fn get_hash(value: &[u8]) -> Hash256 {
        Hash256::from_slice(value)
    }

    #[test]
    fn test_get_signed_hashes_oblique_scenario_1() {
        /*
         * Two oblique hashes.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = get_range_of_hashes(100, 102);
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);

        let mut expected_result = get_range_of_hashes(7, 13);
        expected_result.append(&mut get_range_of_hashes(100, 102));
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_oblique_scenario_2() {
        /*
         * All oblique hashes.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = get_range_of_hashes(100, 108);
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);

        let expected_result = get_range_of_hashes(100, 108);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_1() {
        /*
         * Google Slides example.
         * https://tinyurl.com/ybzn2spw
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);
        let expected_result = get_range_of_hashes(7, 15);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_2() {
        /*
         * Block 1, attestation 0.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 1;
        let attestation_slot: u64 = 0;
        let current_hashes = get_range_of_hashes(0, 16);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);
        let expected_result = get_range_of_hashes(7, 15);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_3() {
        /*
         * attestation_slot too large
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 100;
        let attestation_slot: u64 = 100;
        let current_hashes = get_range_of_hashes(0, 16);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_err());
    }

    #[test]
    fn test_get_signed_hashes_scenario_4() {
        /*
         * Current hashes too small
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 100;
        let attestation_slot: u64 = 99;
        let current_hashes = get_range_of_hashes(0, 15);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_err());
    }
}
<commit_msg>Add comment to get_signed_parent_hashes<commit_after>use super::Hash256;
use super::TransitionError;

/// This function is used to select the hashes used in
/// the signing of an AttestationRecord.
///
/// It either returns Result with a vector of length `cycle_length,` or
/// returns an Error.
///
/// See this slide for more information:
/// https://tinyurl.com/ybzn2spw
pub fn get_signed_parent_hashes(
    cycle_length: &u8,
    block_slot: &u64,
    attestation_slot: &u64,
    current_hashes: &Vec<Hash256>,
    oblique_hashes: &Vec<Hash256>)
    -> Result<Vec<Hash256>, TransitionError>
{
    // This cast places a limit on cycle_length. If you change it, check math
    // for overflow.
    let cycle_length: u64 = *cycle_length as u64;

    if current_hashes.len() as u64 != (cycle_length * 2) {
        return Err(TransitionError::InvalidInput(String::from(
                    "current_hashes.len() must equal cycle_length * 2")));
    }
    if attestation_slot >= block_slot {
        return Err(TransitionError::InvalidInput(String::from(
                    "attestation_slot must be less than block_slot")));
    }
    if oblique_hashes.len() as u64 > cycle_length {
        return Err(TransitionError::InvalidInput(String::from(
                    "oblique_hashes.len() must be <= cycle_length * 2")));
    }

    /*
     * Cannot underflow as block_slot cannot be less
     * than attestation_slot.
     */
    let attestation_distance = block_slot - attestation_slot;

    if attestation_distance > cycle_length {
        return Err(TransitionError::InvalidInput(String::from(
                    "attestation_slot must be withing one cycle of block_slot")));
    }

    /*
     * Cannot underflow because attestation_distance cannot
     * be larger than cycle_length.
     */
    let start = cycle_length - attestation_distance;

    /*
     * Overflow is potentially impossible, but proof is complicated
     * enough to just use checked math.
     *
     * Arithmetic is:
     * start + cycle_length - oblique_hashes.len()
     */
    let end = start.checked_add(cycle_length)
        .and_then(|x| x.checked_sub(oblique_hashes.len() as u64))
        .ok_or(TransitionError::IntWrapping)?;

    let mut hashes = Vec::new();
    hashes.extend_from_slice(
        &current_hashes[(start as usize)..(end as usize)]);
    hashes.append(&mut oblique_hashes.clone());

    Ok(hashes)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn get_range_of_hashes(from: usize, to: usize) -> Vec<Hash256> {
        (from..to).map(|i| get_hash(&vec![i as u8])).collect()
    }

    fn get_hash(value: &[u8]) -> Hash256 {
        Hash256::from_slice(value)
    }

    #[test]
    fn test_get_signed_hashes_oblique_scenario_1() {
        /*
         * Two oblique hashes.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = get_range_of_hashes(100, 102);
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);

        let mut expected_result = get_range_of_hashes(7, 13);
        expected_result.append(&mut get_range_of_hashes(100, 102));
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_oblique_scenario_2() {
        /*
         * All oblique hashes.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = get_range_of_hashes(100, 108);
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);

        let expected_result = get_range_of_hashes(100, 108);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_1() {
        /*
         * Google Slides example.
         * https://tinyurl.com/ybzn2spw
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 19;
        let attestation_slot: u64 = 15;
        let current_hashes = get_range_of_hashes(3, 19);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);
        let expected_result = get_range_of_hashes(7, 15);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_2() {
        /*
         * Block 1, attestation 0.
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 1;
        let attestation_slot: u64 = 0;
        let current_hashes = get_range_of_hashes(0, 16);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_ok());
        let result = result.unwrap();
        assert_eq!(result.len(), cycle_length as usize);
        let expected_result = get_range_of_hashes(7, 15);
        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_get_signed_hashes_scenario_3() {
        /*
         * attestation_slot too large
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 100;
        let attestation_slot: u64 = 100;
        let current_hashes = get_range_of_hashes(0, 16);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_err());
    }

    #[test]
    fn test_get_signed_hashes_scenario_4() {
        /*
         * Current hashes too small
         */
        let cycle_length: u8 = 8;
        let block_slot: u64 = 100;
        let attestation_slot: u64 = 99;
        let current_hashes = get_range_of_hashes(0, 15);
        let oblique_hashes = vec![];
        let result = get_signed_parent_hashes(
            &cycle_length,
            &block_slot,
            &attestation_slot,
            &current_hashes,
            &oblique_hashes);
        assert!(result.is_err());
    }
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{self, AstNode},
    SyntaxElement, SyntaxKind, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    let is_leaf = macro_call.syntax().next_sibling().is_none();
    Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) {
        format!("({})", macro_text_in_brackets)
    } else {
        macro_text_in_brackets.to_string()
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    if contents_between_brackets.is_empty() {
        None
    } else {
        match (first_child.kind(), last_child.kind()) {
            (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
                Some(contents_between_brackets)
            }
            _ => None,
        }
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut unpaired_brackets_in_contents = Vec::new();
    for element in macro_contents {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    // paths
                    && symbol_kind != SyntaxKind::COLON
                    // field/method access
                    && symbol_kind != SyntaxKind::DOT
                    // try operator
                    && symbol_kind != SyntaxKind::QUESTION
                    && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW)
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!<|>(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = <|>dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }

    #[test]
    fn test_remove_dbg_try_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(result?).foo();"#,
            r#"let res = result?.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_await_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(fut.await).foo();"#,
            r#"let res = fut.await.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_as_cast() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(3 as usize).foo();"#,
            r#"let res = (3 as usize).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_index_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(array[3]).foo();"#,
            r#"let res = array[3].foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(tuple.3).foo();"#,
            r#"let res = tuple.3.foo();"#,
        );
    }

    #[test]
    #[ignore] // FIXME: we encounter SyntaxKind::DOT instead of SyntaxKind::DOT2 causing this to fail
    fn test_remove_dbg_range_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..bar).foo();"#,
            r#"let res = (foo..bar).foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..=bar).foo();"#,
            r#"let res = (foo..=bar).foo();"#,
        );
    }
}
<commit_msg>Keep parens around in remove-dbg for range expressions<commit_after>use syntax::{
    ast::{self, AstNode},
    SyntaxElement, SyntaxKind, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    let is_leaf = macro_call.syntax().next_sibling().is_none();
    Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) {
        format!("({})", macro_text_in_brackets)
    } else {
        macro_text_in_brackets.to_string()
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    if contents_between_brackets.is_empty() {
        None
    } else {
        match (first_child.kind(), last_child.kind()) {
            (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
                Some(contents_between_brackets)
            }
            _ => None,
        }
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut macro_contents = macro_contents.into_iter().peekable();
    let mut unpaired_brackets_in_contents = Vec::new();
    while let Some(element) = macro_contents.next() {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    && symbol_kind != SyntaxKind::COLON // paths
                    && (symbol_kind != SyntaxKind::DOT // field/method access
                        || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
                            .peek()
                            .map(|element| element.kind() == SyntaxKind::DOT)
                            .unwrap_or(false))
                    && symbol_kind != SyntaxKind::QUESTION // try operator
                    && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW)
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!<|>(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = <|>dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }

    #[test]
    fn test_remove_dbg_try_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(result?).foo();"#,
            r#"let res = result?.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_await_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(fut.await).foo();"#,
            r#"let res = fut.await.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_as_cast() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(3 as usize).foo();"#,
            r#"let res = (3 as usize).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_index_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(array[3]).foo();"#,
            r#"let res = array[3].foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(tuple.3).foo();"#,
            r#"let res = tuple.3.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_range_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..bar).foo();"#,
            r#"let res = (foo..bar).foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..=bar).foo();"#,
            r#"let res = (foo..=bar).foo();"#,
        );
    }
}
<|endoftext|>


<commit_before>//! Shadowsocks Service Network Utilities

pub use self::{flow::FlowStat, mon_socket::MonProxySocket, mon_stream::MonProxyStream};

pub mod flow;
pub mod mon_socket;
pub mod mon_stream;
pub mod packet_window;
pub mod utils;

/// Packet size for all UDP associations' send queue
///
/// This value is set by test result of `perf3` locally running 6.4Gbps bitrates with lost-rate lower than 0.5%
pub const UDP_ASSOCIATION_SEND_CHANNEL_SIZE: usize = 51200;

/// Keep-alive channel size for UDP associations' manager
pub const UDP_ASSOCIATION_KEEP_ALIVE_CHANNEL_SIZE: usize = 64;
<commit_msg>shrink udp association send queue size<commit_after>//! Shadowsocks Service Network Utilities

pub use self::{flow::FlowStat, mon_socket::MonProxySocket, mon_stream::MonProxyStream};

pub mod flow;
pub mod mon_socket;
pub mod mon_stream;
pub mod packet_window;
pub mod utils;

/// Packet size for all UDP associations' send queue
pub const UDP_ASSOCIATION_SEND_CHANNEL_SIZE: usize = 1024;

/// Keep-alive channel size for UDP associations' manager
pub const UDP_ASSOCIATION_KEEP_ALIVE_CHANNEL_SIZE: usize = 64;
<|endoftext|>


<commit_before>pub mod client;
pub mod server;
#[cfg(feature="tls")]
pub mod tls;

use std::ascii::AsciiExt;
use std::str::from_utf8;

use base64;
use bytes::Buf;
use httparse;
use httparse::Status;
use sha1::Sha1;

use error::Result;

// Limit the number of header lines.
const MAX_HEADERS: usize = 124;

/// A handshake state.
pub trait Handshake: Sized {
    /// Resulting stream of this handshake.
    type Stream;
    /// Perform a single handshake round.
    fn handshake(self) -> Result<HandshakeResult<Self>>;
    /// Perform handshake to the end in a blocking mode.
    fn handshake_wait(self) -> Result<Self::Stream> {
        let mut hs = self;
        loop {
            hs = match hs.handshake()? {
                HandshakeResult::Done(stream) => return Ok(stream),
                HandshakeResult::Incomplete(s) => s,
            }
        }
    }
}

/// A handshake result.
pub enum HandshakeResult<H: Handshake> {
    /// Handshake is done, a WebSocket stream is ready.
    Done(H::Stream),
    /// Handshake is not done, call handshake() again.
    Incomplete(H),
}

impl<H: Handshake> HandshakeResult<H> {
    pub fn map<R, F>(self, func: F) -> HandshakeResult<R>
    where R: Handshake<Stream = H::Stream>,
          F: FnOnce(H) -> R,
    {
        match self {
            HandshakeResult::Done(s) => HandshakeResult::Done(s),
            HandshakeResult::Incomplete(h) => HandshakeResult::Incomplete(func(h)),
        }
    }
}

/// Turns a Sec-WebSocket-Key into a Sec-WebSocket-Accept.
fn convert_key(input: &[u8]) -> Result<String> {
    // ... field is constructed by concatenating /key/ ...
    // ... with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (RFC 6455)
    const WS_GUID: &'static [u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    let mut sha1 = Sha1::new();
    sha1.update(input);
    sha1.update(WS_GUID);
    Ok(base64::encode(&sha1.digest().bytes()))
}

/// HTTP request or response headers.
#[derive(Debug)]
pub struct Headers {
    data: Vec<(String, Box<[u8]>)>,
}

impl Headers {

    /// Get first header with the given name, if any.
    pub fn find_first(&self, name: &str) -> Option<&[u8]> {
        self.data.iter()
                .find(|&&(ref n, _)| n.eq_ignore_ascii_case(name))
                .map(|&(_, ref v)| v.as_ref())
    }

    /// Check if the given header has the given value.
    pub fn header_is(&self, name: &str, value: &str) -> bool {
        self.find_first(name)
            .map(|v| v == value.as_bytes())
            .unwrap_or(false)
    }

    /// Check if the given header has the given value (case-insensitive).
    pub fn header_is_ignore_case(&self, name: &str, value: &str) -> bool {
        self.find_first(name).ok_or(())
            .and_then(|val_raw| from_utf8(val_raw).map_err(|_| ()))
            .map(|val| val.eq_ignore_ascii_case(value))
            .unwrap_or(false)
    }

    /// Try to parse data and return headers, if any.
    fn parse<B: Buf>(input: &mut B) -> Result<Option<Headers>> {
        Headers::parse_http(input)
    }

}

/// Trait to read HTTP parseable objects.
trait Httparse: Sized {
    fn httparse(buf: &[u8]) -> Result<Option<(usize, Self)>>;
    fn parse_http<B: Buf>(input: &mut B) -> Result<Option<Self>> {
        Ok(match Self::httparse(input.bytes())? {
            Some((size, obj)) => {
                input.advance(size);
                Some(obj)
            },
            None => None,
        })
    }
}

/// Trait to convert raw objects into HTTP parseables.
trait FromHttparse<T>: Sized {
    fn from_httparse(raw: T) -> Result<Self>;
}

impl Httparse for Headers {
    fn httparse(buf: &[u8]) -> Result<Option<(usize, Self)>> {
        let mut hbuffer = [httparse::EMPTY_HEADER; MAX_HEADERS];
        Ok(match httparse::parse_headers(buf, &mut hbuffer)? {
            Status::Partial => None,
            Status::Complete((size, hdr)) => Some((size, Headers::from_httparse(hdr)?)),
        })
    }
}

impl<'b: 'h, 'h> FromHttparse<&'b [httparse::Header<'h>]> for Headers {
    fn from_httparse(raw: &'b [httparse::Header<'h>]) -> Result<Self> {
        Ok(Headers {
            data: raw.iter()
                     .map(|h| (h.name.into(), Vec::from(h.value).into_boxed_slice()))
                     .collect(),
        })
    }
}

#[cfg(test)]
mod tests {

    use super::{Headers, convert_key};

    use std::io::Cursor;

    #[test]
    fn key_conversion() {
        // example from RFC 6455
        assert_eq!(convert_key(b"dGhlIHNhbXBsZSBub25jZQ==").unwrap(),
                               "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
    }

    #[test]
    fn headers() {
        const data: &'static [u8] =
            b"Host: foo.com\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n";
        let mut inp = Cursor::new(data);
        let hdr = Headers::parse(&mut inp).unwrap().unwrap();
        assert_eq!(hdr.find_first("Host"), Some(&b"foo.com"[..]));
        assert_eq!(hdr.find_first("Upgrade"), Some(&b"websocket"[..]));
        assert_eq!(hdr.find_first("Connection"), Some(&b"Upgrade"[..]));

        assert!(hdr.header_is("upgrade", "websocket"));
        assert!(!hdr.header_is("upgrade", "Websocket"));
        assert!(hdr.header_is_ignore_case("upgrade", "Websocket"));
    }

    #[test]
    fn headers_incomplete() {
        const data: &'static [u8] =
            b"Host: foo.com\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n";
        let mut inp = Cursor::new(data);
        let hdr = Headers::parse(&mut inp).unwrap();
        assert!(hdr.is_none());
    }

}
<commit_msg>handshake: add the ability to iterate over headers.<commit_after>pub mod client;
pub mod server;
#[cfg(feature="tls")]
pub mod tls;

use std::ascii::AsciiExt;
use std::str::from_utf8;
use std::slice;

use base64;
use bytes::Buf;
use httparse;
use httparse::Status;
use sha1::Sha1;

use error::Result;

// Limit the number of header lines.
const MAX_HEADERS: usize = 124;

/// A handshake state.
pub trait Handshake: Sized {
    /// Resulting stream of this handshake.
    type Stream;
    /// Perform a single handshake round.
    fn handshake(self) -> Result<HandshakeResult<Self>>;
    /// Perform handshake to the end in a blocking mode.
    fn handshake_wait(self) -> Result<Self::Stream> {
        let mut hs = self;
        loop {
            hs = match hs.handshake()? {
                HandshakeResult::Done(stream) => return Ok(stream),
                HandshakeResult::Incomplete(s) => s,
            }
        }
    }
}

/// A handshake result.
pub enum HandshakeResult<H: Handshake> {
    /// Handshake is done, a WebSocket stream is ready.
    Done(H::Stream),
    /// Handshake is not done, call handshake() again.
    Incomplete(H),
}

impl<H: Handshake> HandshakeResult<H> {
    pub fn map<R, F>(self, func: F) -> HandshakeResult<R>
    where R: Handshake<Stream = H::Stream>,
          F: FnOnce(H) -> R,
    {
        match self {
            HandshakeResult::Done(s) => HandshakeResult::Done(s),
            HandshakeResult::Incomplete(h) => HandshakeResult::Incomplete(func(h)),
        }
    }
}

/// Turns a Sec-WebSocket-Key into a Sec-WebSocket-Accept.
fn convert_key(input: &[u8]) -> Result<String> {
    // ... field is constructed by concatenating /key/ ...
    // ... with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (RFC 6455)
    const WS_GUID: &'static [u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    let mut sha1 = Sha1::new();
    sha1.update(input);
    sha1.update(WS_GUID);
    Ok(base64::encode(&sha1.digest().bytes()))
}

/// HTTP request or response headers.
#[derive(Debug)]
pub struct Headers {
    data: Vec<(String, Box<[u8]>)>,
}

impl Headers {

    /// Get first header with the given name, if any.
    pub fn find_first(&self, name: &str) -> Option<&[u8]> {
        self.find(name).next()
    }

    /// Iterate over all headers with the given name.
    pub fn find<'headers, 'name>(&'headers self, name: &'name str) -> HeadersIter<'name, 'headers> {
        HeadersIter {
            name: name,
            iter: self.data.iter()
        }
    }

    /// Check if the given header has the given value.
    pub fn header_is(&self, name: &str, value: &str) -> bool {
        self.find_first(name)
            .map(|v| v == value.as_bytes())
            .unwrap_or(false)
    }

    /// Check if the given header has the given value (case-insensitive).
    pub fn header_is_ignore_case(&self, name: &str, value: &str) -> bool {
        self.find_first(name).ok_or(())
            .and_then(|val_raw| from_utf8(val_raw).map_err(|_| ()))
            .map(|val| val.eq_ignore_ascii_case(value))
            .unwrap_or(false)
    }

    /// Try to parse data and return headers, if any.
    fn parse<B: Buf>(input: &mut B) -> Result<Option<Headers>> {
        Headers::parse_http(input)
    }

}

/// The iterator over headers.
pub struct HeadersIter<'name, 'headers> {
    name: &'name str,
    iter: slice::Iter<'headers, (String, Box<[u8]>)>,
}

impl<'name, 'headers> Iterator for HeadersIter<'name, 'headers> {
    type Item = &'headers [u8];
    fn next(&mut self) -> Option<Self::Item> {
        while let Some(&(ref name, ref value)) = self.iter.next() {
            if name.eq_ignore_ascii_case(self.name) {
                return Some(value)
            }
        }
        None
    }
}

/// Trait to read HTTP parseable objects.
trait Httparse: Sized {
    fn httparse(buf: &[u8]) -> Result<Option<(usize, Self)>>;
    fn parse_http<B: Buf>(input: &mut B) -> Result<Option<Self>> {
        Ok(match Self::httparse(input.bytes())? {
            Some((size, obj)) => {
                input.advance(size);
                Some(obj)
            },
            None => None,
        })
    }
}

/// Trait to convert raw objects into HTTP parseables.
trait FromHttparse<T>: Sized {
    fn from_httparse(raw: T) -> Result<Self>;
}

impl Httparse for Headers {
    fn httparse(buf: &[u8]) -> Result<Option<(usize, Self)>> {
        let mut hbuffer = [httparse::EMPTY_HEADER; MAX_HEADERS];
        Ok(match httparse::parse_headers(buf, &mut hbuffer)? {
            Status::Partial => None,
            Status::Complete((size, hdr)) => Some((size, Headers::from_httparse(hdr)?)),
        })
    }
}

impl<'b: 'h, 'h> FromHttparse<&'b [httparse::Header<'h>]> for Headers {
    fn from_httparse(raw: &'b [httparse::Header<'h>]) -> Result<Self> {
        Ok(Headers {
            data: raw.iter()
                     .map(|h| (h.name.into(), Vec::from(h.value).into_boxed_slice()))
                     .collect(),
        })
    }
}

#[cfg(test)]
mod tests {

    use super::{Headers, convert_key};

    use std::io::Cursor;

    #[test]
    fn key_conversion() {
        // example from RFC 6455
        assert_eq!(convert_key(b"dGhlIHNhbXBsZSBub25jZQ==").unwrap(),
                               "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
    }

    #[test]
    fn headers() {
        const data: &'static [u8] =
            b"Host: foo.com\r\n\
             Connection: Upgrade\r\n\
             Upgrade: websocket\r\n\
             \r\n";
        let mut inp = Cursor::new(data);
        let hdr = Headers::parse(&mut inp).unwrap().unwrap();
        assert_eq!(hdr.find_first("Host"), Some(&b"foo.com"[..]));
        assert_eq!(hdr.find_first("Upgrade"), Some(&b"websocket"[..]));
        assert_eq!(hdr.find_first("Connection"), Some(&b"Upgrade"[..]));

        assert!(hdr.header_is("upgrade", "websocket"));
        assert!(!hdr.header_is("upgrade", "Websocket"));
        assert!(hdr.header_is_ignore_case("upgrade", "Websocket"));
    }

    #[test]
    fn headers_iter() {
        const data: &'static [u8] =
            b"Host: foo.com\r\n\
              Sec-WebSocket-Extensions: permessage-deflate\r\n\
              Connection: Upgrade\r\n\
              Sec-WebSocket-ExtenSIONS: permessage-unknown\r\n\
              Upgrade: websocket\r\n\
              \r\n";
        let mut inp = Cursor::new(data);
        let hdr = Headers::parse(&mut inp).unwrap().unwrap();
        let mut iter = hdr.find("Sec-WebSocket-Extensions");
        assert_eq!(iter.next(), Some(&b"permessage-deflate"[..]));
        assert_eq!(iter.next(), Some(&b"permessage-unknown"[..]));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn headers_incomplete() {
        const data: &'static [u8] =
            b"Host: foo.com\r\n\
              Connection: Upgrade\r\n\
              Upgrade: websocket\r\n";
        let mut inp = Cursor::new(data);
        let hdr = Headers::parse(&mut inp).unwrap();
        assert!(hdr.is_none());
    }

}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use address_allocator::AddressAllocator;
use gpu_allocator::{self, GpuMemoryAllocator};
use sys_util::pagesize;

/// Manages allocating system resources such as address space and interrupt numbers.
///
/// # Example - Use the `SystemAddress` builder.
///
/// ```
/// # use sys_util::AddressRanges;
///   if let Some(mut a) = AddressRanges::new()
///           .add_io_addresses(0x1000, 0x10000)
///           .add_device_addresses(0x10000000, 0x10000000)
///           .add_mmio_addresses(0x30000000, 0x10000)
///           .create_allocator(5) {
///       assert_eq!(a.allocate_irq(), Some(5));
///       assert_eq!(a.allocate_irq(), Some(6));
///       assert_eq!(a.allocate_device_addresses(0x100), Some(0x10000000));
///   }
/// ```
pub struct SystemAllocator {
    io_address_space: Option<AddressAllocator>,
    device_address_space: AddressAllocator,
    mmio_address_space: AddressAllocator,
    gpu_allocator: Option<Box<GpuMemoryAllocator>>,
    next_irq: u32,
}

impl SystemAllocator {
    /// Creates a new `SystemAllocator` for managing addresses and irq numvers.
    /// Can return `None` if `base` + `size` overflows a u64 or if alignment isn't a power
    /// of two.
    ///
    /// * `io_base` - The starting address of IO memory.
    /// * `io_size` - The size of IO memory.
    /// * `dev_base` - The starting address of device memory.
    /// * `dev_size` - The size of device memory.
    /// * `mmio_base` - The starting address of MMIO space.
    /// * `mmio_size` - The size of MMIO space.
    /// * `create_gpu_allocator` - If true, enable gpu memory allocation.
    /// * `first_irq` - The first irq number to give out.
    fn new(io_base: Option<u64>, io_size: Option<u64>,
           dev_base: u64, dev_size: u64,
           mmio_base: u64, mmio_size: u64,
           create_gpu_allocator: bool,
           first_irq: u32) -> Option<Self> {
        let page_size = pagesize() as u64;
        Some(SystemAllocator {
            io_address_space: if let (Some(b), Some(s)) = (io_base, io_size) {
                Some(AddressAllocator::new(b, s, Some(0x400))?)
            } else {
                None
            },
            device_address_space: AddressAllocator::new(dev_base, dev_size, Some(page_size))?,
            mmio_address_space: AddressAllocator::new(mmio_base, mmio_size, Some(page_size))?,
            gpu_allocator: if create_gpu_allocator {
                gpu_allocator::create_gpu_memory_allocator().ok()?
            } else {
                None
            },
            next_irq: first_irq,
        })
    }

    /// Reserves the next available system irq number.
    pub fn allocate_irq(&mut self) -> Option<u32> {
        if let Some(irq_num) = self.next_irq.checked_add(1) {
            self.next_irq = irq_num;
            Some(irq_num - 1)
        } else {
            None
        }
    }

    /// Reserves a section of `size` bytes of IO address space.
    pub fn allocate_io_addresses(&mut self, size: u64) -> Option<u64> {
        self.io_address_space.as_mut()?.allocate(size)
    }

    /// Reserves a section of `size` bytes of device address space.
    pub fn allocate_device_addresses(&mut self, size: u64) -> Option<u64> {
        self.device_address_space.allocate(size)
    }

    /// Reserves a section of `size` bytes of device address space.
    pub fn allocate_mmio_addresses(&mut self, size: u64) -> Option<u64> {
        self.mmio_address_space.allocate(size)
    }

    /// Gets an allocator to be used for GPU memory.
    pub fn gpu_memory_allocator(&self) -> Option<&GpuMemoryAllocator> {
        self.gpu_allocator.as_ref().map(|v| v.as_ref())
    }
}

/// Used to build a system address map for use in creating a `SystemAllocator`.
pub struct AddressRanges {
    io_base: Option<u64>,
    io_size: Option<u64>,
    mmio_base: Option<u64>,
    mmio_size: Option<u64>,
    device_base: Option<u64>,
    device_size: Option<u64>,
}

impl AddressRanges {
    pub fn new() -> Self {
        AddressRanges {
            io_base: None,
            io_size: None,
            mmio_base: None,
            mmio_size: None,
            device_base: None,
            device_size: None,
        }
    }

    pub fn add_io_addresses(mut self, base: u64, size: u64) -> Self {
        self.io_base = Some(base);
        self.io_size = Some(size);
        self
    }

    pub fn add_mmio_addresses(mut self, base: u64, size: u64) -> Self {
        self.mmio_base = Some(base);
        self.mmio_size = Some(size);
        self
    }

    pub fn add_device_addresses(mut self, base: u64, size: u64) -> Self {
        self.device_base = Some(base);
        self.device_size = Some(size);
        self
    }

    pub fn create_allocator(&self, first_irq: u32,
                            gpu_allocation: bool) -> Option<SystemAllocator> {
        SystemAllocator::new(self.io_base, self.io_size,
                             self.device_base?, self.device_size?,
                             self.mmio_base?, self.mmio_size?,
                             gpu_allocation,
                             first_irq)
    }
}
<commit_msg>resources: fix allocator doc tests<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use address_allocator::AddressAllocator;
use gpu_allocator::{self, GpuMemoryAllocator};
use sys_util::pagesize;

/// Manages allocating system resources such as address space and interrupt numbers.
///
/// # Example - Use the `SystemAddress` builder.
///
/// ```
/// # use resources::AddressRanges;
///   if let Some(mut a) = AddressRanges::new()
///           .add_io_addresses(0x1000, 0x10000)
///           .add_device_addresses(0x10000000, 0x10000000)
///           .add_mmio_addresses(0x30000000, 0x10000)
///           .create_allocator(5, false) {
///       assert_eq!(a.allocate_irq(), Some(5));
///       assert_eq!(a.allocate_irq(), Some(6));
///       assert_eq!(a.allocate_device_addresses(0x100), Some(0x10000000));
///   }
/// ```
pub struct SystemAllocator {
    io_address_space: Option<AddressAllocator>,
    device_address_space: AddressAllocator,
    mmio_address_space: AddressAllocator,
    gpu_allocator: Option<Box<GpuMemoryAllocator>>,
    next_irq: u32,
}

impl SystemAllocator {
    /// Creates a new `SystemAllocator` for managing addresses and irq numvers.
    /// Can return `None` if `base` + `size` overflows a u64 or if alignment isn't a power
    /// of two.
    ///
    /// * `io_base` - The starting address of IO memory.
    /// * `io_size` - The size of IO memory.
    /// * `dev_base` - The starting address of device memory.
    /// * `dev_size` - The size of device memory.
    /// * `mmio_base` - The starting address of MMIO space.
    /// * `mmio_size` - The size of MMIO space.
    /// * `create_gpu_allocator` - If true, enable gpu memory allocation.
    /// * `first_irq` - The first irq number to give out.
    fn new(io_base: Option<u64>, io_size: Option<u64>,
           dev_base: u64, dev_size: u64,
           mmio_base: u64, mmio_size: u64,
           create_gpu_allocator: bool,
           first_irq: u32) -> Option<Self> {
        let page_size = pagesize() as u64;
        Some(SystemAllocator {
            io_address_space: if let (Some(b), Some(s)) = (io_base, io_size) {
                Some(AddressAllocator::new(b, s, Some(0x400))?)
            } else {
                None
            },
            device_address_space: AddressAllocator::new(dev_base, dev_size, Some(page_size))?,
            mmio_address_space: AddressAllocator::new(mmio_base, mmio_size, Some(page_size))?,
            gpu_allocator: if create_gpu_allocator {
                gpu_allocator::create_gpu_memory_allocator().ok()?
            } else {
                None
            },
            next_irq: first_irq,
        })
    }

    /// Reserves the next available system irq number.
    pub fn allocate_irq(&mut self) -> Option<u32> {
        if let Some(irq_num) = self.next_irq.checked_add(1) {
            self.next_irq = irq_num;
            Some(irq_num - 1)
        } else {
            None
        }
    }

    /// Reserves a section of `size` bytes of IO address space.
    pub fn allocate_io_addresses(&mut self, size: u64) -> Option<u64> {
        self.io_address_space.as_mut()?.allocate(size)
    }

    /// Reserves a section of `size` bytes of device address space.
    pub fn allocate_device_addresses(&mut self, size: u64) -> Option<u64> {
        self.device_address_space.allocate(size)
    }

    /// Reserves a section of `size` bytes of device address space.
    pub fn allocate_mmio_addresses(&mut self, size: u64) -> Option<u64> {
        self.mmio_address_space.allocate(size)
    }

    /// Gets an allocator to be used for GPU memory.
    pub fn gpu_memory_allocator(&self) -> Option<&GpuMemoryAllocator> {
        self.gpu_allocator.as_ref().map(|v| v.as_ref())
    }
}

/// Used to build a system address map for use in creating a `SystemAllocator`.
pub struct AddressRanges {
    io_base: Option<u64>,
    io_size: Option<u64>,
    mmio_base: Option<u64>,
    mmio_size: Option<u64>,
    device_base: Option<u64>,
    device_size: Option<u64>,
}

impl AddressRanges {
    pub fn new() -> Self {
        AddressRanges {
            io_base: None,
            io_size: None,
            mmio_base: None,
            mmio_size: None,
            device_base: None,
            device_size: None,
        }
    }

    pub fn add_io_addresses(mut self, base: u64, size: u64) -> Self {
        self.io_base = Some(base);
        self.io_size = Some(size);
        self
    }

    pub fn add_mmio_addresses(mut self, base: u64, size: u64) -> Self {
        self.mmio_base = Some(base);
        self.mmio_size = Some(size);
        self
    }

    pub fn add_device_addresses(mut self, base: u64, size: u64) -> Self {
        self.device_base = Some(base);
        self.device_size = Some(size);
        self
    }

    pub fn create_allocator(&self, first_irq: u32,
                            gpu_allocation: bool) -> Option<SystemAllocator> {
        SystemAllocator::new(self.io_base, self.io_size,
                             self.device_base?, self.device_size?,
                             self.mmio_base?, self.mmio_size?,
                             gpu_allocation,
                             first_irq)
    }
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};

pub const PIF_ROM_START: u32 = 0x0000;
pub const PIF_ROM_END: u32 = 0x07bf;
pub const PIF_RAM_SIZE: usize = 0x40;
pub const PIF_RAM_START: u32 = 0x07c0;
pub const PIF_RAM_END: u32 = PIF_RAM_START + (PIF_RAM_SIZE as u32) - 1;

pub struct Pif {
    rom: Box<[u8]>,
    ram: Box<[u8]>,
}

impl Pif {
    pub fn new(pifrom: Box<[u8]>) -> Pif {
        Pif {
            rom: pifrom,
            ram: vec![0u8; PIF_RAM_SIZE].into_boxed_slice(),
        }
    }

    pub fn read(&self, addr: u32) -> u32 {
        match addr {
            PIF_ROM_START...PIF_ROM_END => {
                BigEndian::read_u32(&self.rom[(addr - PIF_ROM_START) as usize..])
            }
            PIF_RAM_START...PIF_RAM_END => {
                BigEndian::read_u32(&self.ram[(addr - PIF_RAM_START) as usize..])
            }
            _ => panic!("Address out of range"),
        }
    }

    pub fn write(&mut self, addr: u32, value: u32) {
        match addr {
            PIF_ROM_START...PIF_ROM_END => {
                panic!("Cannot write to PIF ROM");
            }
            PIF_RAM_START...PIF_RAM_END => {
                BigEndian::write_u32(&mut self.ram[(PIF_RAM_START) as usize..], value);
            }
            _ => {
                panic!("Address out of range");
            }
        }
    }
}
<commit_msg>Fix error in writing to pif ram<commit_after>use byteorder::{BigEndian, ByteOrder};

pub const PIF_ROM_START: u32 = 0x0000;
pub const PIF_ROM_END: u32 = 0x07bf;
pub const PIF_RAM_SIZE: usize = 0x40;
pub const PIF_RAM_START: u32 = 0x07c0;
pub const PIF_RAM_END: u32 = PIF_RAM_START + (PIF_RAM_SIZE as u32) - 1;

pub struct Pif {
    rom: Box<[u8]>,
    ram: Box<[u8]>,
}

impl Pif {
    pub fn new(pifrom: Box<[u8]>) -> Pif {
        Pif {
            rom: pifrom,
            ram: vec![0u8; PIF_RAM_SIZE].into_boxed_slice(),
        }
    }

    pub fn read(&self, addr: u32) -> u32 {
        match addr {
            PIF_ROM_START...PIF_ROM_END => {
                BigEndian::read_u32(&self.rom[(addr - PIF_ROM_START) as usize..])
            }
            PIF_RAM_START...PIF_RAM_END => {
                BigEndian::read_u32(&self.ram[(addr - PIF_RAM_START) as usize..])
            }
            _ => panic!("Address out of range"),
        }
    }

    pub fn write(&mut self, addr: u32, value: u32) {
        match addr {
            PIF_ROM_START...PIF_ROM_END => {
                panic!("Cannot write to PIF ROM");
            }
            PIF_RAM_START...PIF_RAM_END => {
                BigEndian::write_u32(&mut self.ram[(addr - PIF_RAM_START) as usize..], value);
            }
            _ => {
                panic!("Address out of range");
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::discovery::Discovery;
use crate::rpc::{RPCEvent, RPCMessage, RPC};
use crate::GossipTopic;
use crate::{error, NetworkConfig};
use crate::{Topic, TopicHash};
use enr::Enr;
use futures::prelude::*;
use libp2p::{
    core::identity::Keypair,
    discv5::Discv5Event,
    gossipsub::{Gossipsub, GossipsubEvent, MessageId},
    identify::{Identify, IdentifyEvent},
    ping::{Ping, PingConfig, PingEvent},
    swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use lru::LruCache;
use slog::{debug, o};
use std::num::NonZeroU32;
use std::time::Duration;

const MAX_IDENTIFY_ADDRESSES: usize = 20;

/// Builds the network behaviour that manages the core protocols of eth2.
/// This core behaviour is managed by `Behaviour` which adds peer management to all core
/// behaviours.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    /// The Eth2 RPC specified in the wire-0 protocol.
    eth2_rpc: RPC<TSubstream>,
    /// Keep regular connection to peers and disconnect if absent.
    // TODO: Remove Libp2p ping in favour of discv5 ping.
    ping: Ping<TSubstream>,
    // TODO: Using id for initial interop. This will be removed by mainnet.
    /// Provides IP addresses and peer information.
    identify: Identify<TSubstream>,
    /// Discovery behaviour.
    discovery: Discovery<TSubstream>,
    /// The events generated by this behaviour to be consumed in the swarm poll.
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    /// A cache of recently seen gossip messages. This is used to filter out any possible
    /// duplicates that may still be seen over gossipsub.
    #[behaviour(ignore)]
    seen_gossip_messages: LruCache<PubsubMessage, ()>,
    /// Logger for behaviour actions.
    #[behaviour(ignore)]
    log: slog::Logger,
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(
        local_key: &Keypair,
        net_conf: &NetworkConfig,
        log: &slog::Logger,
    ) -> error::Result<Self> {
        let local_peer_id = local_key.public().into_peer_id();
        let behaviour_log = log.new(o!());

        let ping_config = PingConfig::new()
            .with_timeout(Duration::from_secs(30))
            .with_interval(Duration::from_secs(20))
            .with_max_failures(NonZeroU32::new(2).expect("2 != 0"))
            .with_keep_alive(false);

        let identify = Identify::new(
            "lighthouse/libp2p".into(),
            version::version(),
            local_key.public(),
        );

        Ok(Behaviour {
            eth2_rpc: RPC::new(log.clone()),
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            discovery: Discovery::new(local_key, net_conf, log)?,
            ping: Ping::new(ping_config),
            identify,
            seen_gossip_messages: LruCache::new(256),
            events: Vec::new(),
            log: behaviour_log,
        })
    }

    pub fn discovery(&self) -> &Discovery<TSubstream> {
        &self.discovery
    }

    pub fn gs(&self) -> &Gossipsub<TSubstream> {
        &self.gossipsub
    }
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(propagation_source, id, gs_msg) => {
                let msg = PubsubMessage::from_topics(&gs_msg.topics, gs_msg.data);

                // Note: We are keeping track here of the peer that sent us the message, not the
                // peer that originally published the message.
                if self.seen_gossip_messages.put(msg.clone(), ()).is_none() {
                    // if this message isn't a duplicate, notify the network
                    self.events.push(BehaviourEvent::GossipMessage {
                        id,
                        source: propagation_source,
                        topics: gs_msg.topics,
                        message: msg,
                    });
                } else {
                    debug!(self.log, "A duplicate message was received"; "message" => format!("{:?}", msg));
                }
            }
            GossipsubEvent::Subscribed { peer_id, topic } => {
                self.events
                    .push(BehaviourEvent::PeerSubscribed(peer_id, topic));
            }
            GossipsubEvent::Unsubscribed { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::PeerDisconnected(peer_id) => {
                self.events.push(BehaviourEvent::PeerDisconnected(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: PingEvent) {
        // not interested in ping responses at the moment.
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Received {
                peer_id,
                mut info,
                observed_addr,
            } => {
                if info.listen_addrs.len() > MAX_IDENTIFY_ADDRESSES {
                    debug!(
                        self.log,
                        "More than 20 addresses have been identified, truncating"
                    );
                    info.listen_addrs.truncate(MAX_IDENTIFY_ADDRESSES);
                }
                debug!(self.log, "Identified Peer"; "peer" => format!("{}", peer_id),
                "protocol_version" => info.protocol_version,
                "agent_version" => info.agent_version,
                "listening_ addresses" => format!("{:?}", info.listen_addrs),
                "observed_address" => format!("{:?}", observed_addr),
                "protocols" => format!("{:?}", info.protocols)
                );
            }
            IdentifyEvent::Sent { .. } => {}
            IdentifyEvent::Error { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<Discv5Event>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: Discv5Event) {
        // discv5 has no events to inject
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /* Pubsub behaviour functions */

    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Unsubscribe from a gossipsub topic.
    pub fn unsubscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.unsubscribe(topic)
    }

    /// Publishes a message on the pubsub (gossipsub) behaviour.
    pub fn publish(&mut self, topics: &[Topic], message: PubsubMessage) {
        let message_data = message.into_data();
        for topic in topics {
            self.gossipsub.publish(topic, message_data.clone());
        }
    }

    /// Forwards a message that is waiting in gossipsub's mcache. Messages are only propagated
    /// once validated by the beacon chain.
    pub fn propagate_message(&mut self, propagation_source: &PeerId, message_id: MessageId) {
        self.gossipsub
            .propagate_message(&message_id, propagation_source);
    }

    /* Eth2 RPC behaviour functions */

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.eth2_rpc.send_rpc(peer_id, rpc_event);
    }

    /* Discovery / Peer management functions */
    /// Return the list of currently connected peers.
    pub fn connected_peers(&self) -> usize {
        self.discovery.connected_peers()
    }

    /// Notify discovery that the peer has been banned.
    pub fn peer_banned(&mut self, peer_id: PeerId) {
        self.discovery.peer_banned(peer_id);
    }

    /// Notify discovery that the peer has been unbanned.
    pub fn peer_unbanned(&mut self, peer_id: &PeerId) {
        self.discovery.peer_unbanned(peer_id);
    }

    /// Returns an iterator over all enr entries in the DHT.
    pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> {
        self.discovery.enr_entries()
    }

    /// Add an ENR to the routing table of the discovery mechanism.
    pub fn add_enr(&mut self, enr: Enr) {
        self.discovery.add_enr(enr);
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    /// A received RPC event and the peer that it was received from.
    RPC(PeerId, RPCEvent),
    /// We have completed an initial connection to a new peer.
    PeerDialed(PeerId),
    /// A peer has disconnected.
    PeerDisconnected(PeerId),
    /// A gossipsub message has been received.
    GossipMessage {
        /// The gossipsub message id. Used when propagating blocks after validation.
        id: MessageId,
        /// The peer from which we received this message, not the peer that published it.
        source: PeerId,
        /// The topics that this message was sent on.
        topics: Vec<TopicHash>,
        /// The message itself.
        message: PubsubMessage,
    },
    /// Subscribed to peer for given topic
    PeerSubscribed(PeerId, TopicHash),
}

/// Messages that are passed to and from the pubsub (Gossipsub) behaviour. These are encoded and
/// decoded upstream.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PubsubMessage {
    /// Gossipsub message providing notification of a new block.
    Block(Vec<u8>),
    /// Gossipsub message providing notification of a new attestation.
    Attestation(Vec<u8>),
    /// Gossipsub message providing notification of a voluntary exit.
    VoluntaryExit(Vec<u8>),
    /// Gossipsub message providing notification of a new proposer slashing.
    ProposerSlashing(Vec<u8>),
    /// Gossipsub message providing notification of a new attester slashing.
    AttesterSlashing(Vec<u8>),
    /// Gossipsub message from an unknown topic.
    Unknown(Vec<u8>),
}

impl PubsubMessage {
    /* Note: This is assuming we are not hashing topics. If we choose to hash topics, these will
     * need to be modified.
     *
     * Also note that a message can be associated with many topics. As soon as one of the topics is
     * known we match. If none of the topics are known we return an unknown state.
     */
    fn from_topics(topics: &[TopicHash], data: Vec<u8>) -> Self {
        for topic in topics {
            match GossipTopic::from(topic.as_str()) {
                GossipTopic::BeaconBlock => return PubsubMessage::Block(data),
                GossipTopic::BeaconAttestation => return PubsubMessage::Attestation(data),
                GossipTopic::VoluntaryExit => return PubsubMessage::VoluntaryExit(data),
                GossipTopic::ProposerSlashing => return PubsubMessage::ProposerSlashing(data),
                GossipTopic::AttesterSlashing => return PubsubMessage::AttesterSlashing(data),
                GossipTopic::Shard => return PubsubMessage::Unknown(data),
                GossipTopic::Unknown(_) => continue,
            }
        }
        PubsubMessage::Unknown(data)
    }

    fn into_data(self) -> Vec<u8> {
        match self {
            PubsubMessage::Block(data)
            | PubsubMessage::Attestation(data)
            | PubsubMessage::VoluntaryExit(data)
            | PubsubMessage::ProposerSlashing(data)
            | PubsubMessage::AttesterSlashing(data)
            | PubsubMessage::Unknown(data) => data,
        }
    }
}
<commit_msg>Prevent duplicate gossipsub loops/message amplification (#832)<commit_after>use crate::discovery::Discovery;
use crate::rpc::{RPCEvent, RPCMessage, RPC};
use crate::GossipTopic;
use crate::{error, NetworkConfig};
use crate::{Topic, TopicHash};
use enr::Enr;
use futures::prelude::*;
use libp2p::{
    core::identity::Keypair,
    discv5::Discv5Event,
    gossipsub::{Gossipsub, GossipsubEvent, MessageId},
    identify::{Identify, IdentifyEvent},
    ping::{Ping, PingConfig, PingEvent},
    swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use lru::LruCache;
use slog::{debug, o};
use std::num::NonZeroU32;
use std::time::Duration;

const MAX_IDENTIFY_ADDRESSES: usize = 20;

/// Builds the network behaviour that manages the core protocols of eth2.
/// This core behaviour is managed by `Behaviour` which adds peer management to all core
/// behaviours.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    /// The Eth2 RPC specified in the wire-0 protocol.
    eth2_rpc: RPC<TSubstream>,
    /// Keep regular connection to peers and disconnect if absent.
    // TODO: Remove Libp2p ping in favour of discv5 ping.
    ping: Ping<TSubstream>,
    // TODO: Using id for initial interop. This will be removed by mainnet.
    /// Provides IP addresses and peer information.
    identify: Identify<TSubstream>,
    /// Discovery behaviour.
    discovery: Discovery<TSubstream>,
    /// The events generated by this behaviour to be consumed in the swarm poll.
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    /// A cache of recently seen gossip messages. This is used to filter out any possible
    /// duplicates that may still be seen over gossipsub.
    #[behaviour(ignore)]
    seen_gossip_messages: LruCache<MessageId, ()>,
    /// Logger for behaviour actions.
    #[behaviour(ignore)]
    log: slog::Logger,
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(
        local_key: &Keypair,
        net_conf: &NetworkConfig,
        log: &slog::Logger,
    ) -> error::Result<Self> {
        let local_peer_id = local_key.public().into_peer_id();
        let behaviour_log = log.new(o!());

        let ping_config = PingConfig::new()
            .with_timeout(Duration::from_secs(30))
            .with_interval(Duration::from_secs(20))
            .with_max_failures(NonZeroU32::new(2).expect("2 != 0"))
            .with_keep_alive(false);

        let identify = Identify::new(
            "lighthouse/libp2p".into(),
            version::version(),
            local_key.public(),
        );

        Ok(Behaviour {
            eth2_rpc: RPC::new(log.clone()),
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            discovery: Discovery::new(local_key, net_conf, log)?,
            ping: Ping::new(ping_config),
            identify,
            seen_gossip_messages: LruCache::new(100_000),
            events: Vec::new(),
            log: behaviour_log,
        })
    }

    pub fn discovery(&self) -> &Discovery<TSubstream> {
        &self.discovery
    }

    pub fn gs(&self) -> &Gossipsub<TSubstream> {
        &self.gossipsub
    }
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(propagation_source, id, gs_msg) => {
                let msg = PubsubMessage::from_topics(&gs_msg.topics, gs_msg.data);

                // Note: We are keeping track here of the peer that sent us the message, not the
                // peer that originally published the message.
                if self.seen_gossip_messages.put(id.clone(), ()).is_none() {
                    // if this message isn't a duplicate, notify the network
                    self.events.push(BehaviourEvent::GossipMessage {
                        id,
                        source: propagation_source,
                        topics: gs_msg.topics,
                        message: msg,
                    });
                } else {
                    debug!(self.log, "A duplicate message was received"; "message" => format!("{:?}", msg));
                }
            }
            GossipsubEvent::Subscribed { peer_id, topic } => {
                self.events
                    .push(BehaviourEvent::PeerSubscribed(peer_id, topic));
            }
            GossipsubEvent::Unsubscribed { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::PeerDisconnected(peer_id) => {
                self.events.push(BehaviourEvent::PeerDisconnected(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: PingEvent) {
        // not interested in ping responses at the moment.
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Received {
                peer_id,
                mut info,
                observed_addr,
            } => {
                if info.listen_addrs.len() > MAX_IDENTIFY_ADDRESSES {
                    debug!(
                        self.log,
                        "More than 20 addresses have been identified, truncating"
                    );
                    info.listen_addrs.truncate(MAX_IDENTIFY_ADDRESSES);
                }
                debug!(self.log, "Identified Peer"; "peer" => format!("{}", peer_id),
                "protocol_version" => info.protocol_version,
                "agent_version" => info.agent_version,
                "listening_ addresses" => format!("{:?}", info.listen_addrs),
                "observed_address" => format!("{:?}", observed_addr),
                "protocols" => format!("{:?}", info.protocols)
                );
            }
            IdentifyEvent::Sent { .. } => {}
            IdentifyEvent::Error { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<Discv5Event>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: Discv5Event) {
        // discv5 has no events to inject
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /* Pubsub behaviour functions */

    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Unsubscribe from a gossipsub topic.
    pub fn unsubscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.unsubscribe(topic)
    }

    /// Publishes a message on the pubsub (gossipsub) behaviour.
    pub fn publish(&mut self, topics: &[Topic], message: PubsubMessage) {
        let message_data = message.into_data();
        for topic in topics {
            self.gossipsub.publish(topic, message_data.clone());
        }
    }

    /// Forwards a message that is waiting in gossipsub's mcache. Messages are only propagated
    /// once validated by the beacon chain.
    pub fn propagate_message(&mut self, propagation_source: &PeerId, message_id: MessageId) {
        self.gossipsub
            .propagate_message(&message_id, propagation_source);
    }

    /* Eth2 RPC behaviour functions */

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.eth2_rpc.send_rpc(peer_id, rpc_event);
    }

    /* Discovery / Peer management functions */
    /// Return the list of currently connected peers.
    pub fn connected_peers(&self) -> usize {
        self.discovery.connected_peers()
    }

    /// Notify discovery that the peer has been banned.
    pub fn peer_banned(&mut self, peer_id: PeerId) {
        self.discovery.peer_banned(peer_id);
    }

    /// Notify discovery that the peer has been unbanned.
    pub fn peer_unbanned(&mut self, peer_id: &PeerId) {
        self.discovery.peer_unbanned(peer_id);
    }

    /// Returns an iterator over all enr entries in the DHT.
    pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> {
        self.discovery.enr_entries()
    }

    /// Add an ENR to the routing table of the discovery mechanism.
    pub fn add_enr(&mut self, enr: Enr) {
        self.discovery.add_enr(enr);
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    /// A received RPC event and the peer that it was received from.
    RPC(PeerId, RPCEvent),
    /// We have completed an initial connection to a new peer.
    PeerDialed(PeerId),
    /// A peer has disconnected.
    PeerDisconnected(PeerId),
    /// A gossipsub message has been received.
    GossipMessage {
        /// The gossipsub message id. Used when propagating blocks after validation.
        id: MessageId,
        /// The peer from which we received this message, not the peer that published it.
        source: PeerId,
        /// The topics that this message was sent on.
        topics: Vec<TopicHash>,
        /// The message itself.
        message: PubsubMessage,
    },
    /// Subscribed to peer for given topic
    PeerSubscribed(PeerId, TopicHash),
}

/// Messages that are passed to and from the pubsub (Gossipsub) behaviour. These are encoded and
/// decoded upstream.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PubsubMessage {
    /// Gossipsub message providing notification of a new block.
    Block(Vec<u8>),
    /// Gossipsub message providing notification of a new attestation.
    Attestation(Vec<u8>),
    /// Gossipsub message providing notification of a voluntary exit.
    VoluntaryExit(Vec<u8>),
    /// Gossipsub message providing notification of a new proposer slashing.
    ProposerSlashing(Vec<u8>),
    /// Gossipsub message providing notification of a new attester slashing.
    AttesterSlashing(Vec<u8>),
    /// Gossipsub message from an unknown topic.
    Unknown(Vec<u8>),
}

impl PubsubMessage {
    /* Note: This is assuming we are not hashing topics. If we choose to hash topics, these will
     * need to be modified.
     *
     * Also note that a message can be associated with many topics. As soon as one of the topics is
     * known we match. If none of the topics are known we return an unknown state.
     */
    fn from_topics(topics: &[TopicHash], data: Vec<u8>) -> Self {
        for topic in topics {
            match GossipTopic::from(topic.as_str()) {
                GossipTopic::BeaconBlock => return PubsubMessage::Block(data),
                GossipTopic::BeaconAttestation => return PubsubMessage::Attestation(data),
                GossipTopic::VoluntaryExit => return PubsubMessage::VoluntaryExit(data),
                GossipTopic::ProposerSlashing => return PubsubMessage::ProposerSlashing(data),
                GossipTopic::AttesterSlashing => return PubsubMessage::AttesterSlashing(data),
                GossipTopic::Shard => return PubsubMessage::Unknown(data),
                GossipTopic::Unknown(_) => continue,
            }
        }
        PubsubMessage::Unknown(data)
    }

    fn into_data(self) -> Vec<u8> {
        match self {
            PubsubMessage::Block(data)
            | PubsubMessage::Attestation(data)
            | PubsubMessage::VoluntaryExit(data)
            | PubsubMessage::ProposerSlashing(data)
            | PubsubMessage::AttesterSlashing(data)
            | PubsubMessage::Unknown(data) => data,
        }
    }
}
<|endoftext|>


<commit_before>//! The Rust parser and macro expander.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       test(attr(deny(warnings))))]

#![deny(rust_2018_idioms)]
#![cfg_attr(not(stage0), deny(internal))]

#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
#![feature(nll)]
#![feature(rustc_attrs)]
#![feature(rustc_diagnostic_macros)]
#![feature(step_trait)]
#![feature(try_trait)]
#![feature(unicode_internals)]

#![recursion_limit="256"]

#[allow(unused_extern_crates)]
extern crate serialize as rustc_serialize; // used by deriving

pub use errors;
use rustc_data_structures::sync::Lock;
use rustc_data_structures::bit_set::GrowableBitSet;
pub use rustc_data_structures::thin_vec::ThinVec;
use ast::AttrId;

// A variant of 'try!' that panics on an Err. This is used as a crutch on the
// way towards a non-panic!-prone parser. It should be used for fatal parsing
// errors; eventually we plan to convert all code using panictry to just use
// normal try.
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use errors::FatalError;
        match $e {
            Ok(e) => e,
            Err(mut e) => {
                e.emit();
                FatalError.raise()
            }
        }
    })
}

// A variant of 'panictry!' that works on a Vec<Diagnostic> instead of a single DiagnosticBuilder.
macro_rules! panictry_buffer {
    ($handler:expr, $e:expr) => ({
        use std::result::Result::{Ok, Err};
        use errors::{FatalError, DiagnosticBuilder};
        match $e {
            Ok(e) => e,
            Err(errs) => {
                for e in errs {
                    DiagnosticBuilder::new_diagnostic($handler, e).emit();
                }
                FatalError.raise()
            }
        }
    })
}

#[macro_export]
macro_rules! unwrap_or {
    ($opt:expr, $default:expr) => {
        match $opt {
            Some(x) => x,
            None => $default,
        }
    }
}

pub struct Globals {
    used_attrs: Lock<GrowableBitSet<AttrId>>,
    known_attrs: Lock<GrowableBitSet<AttrId>>,
    syntax_pos_globals: syntax_pos::Globals,
}

impl Globals {
    fn new() -> Globals {
        Globals {
            // We have no idea how many attributes their will be, so just
            // initiate the vectors with 0 bits. We'll grow them as necessary.
            used_attrs: Lock::new(GrowableBitSet::new_empty()),
            known_attrs: Lock::new(GrowableBitSet::new_empty()),
            syntax_pos_globals: syntax_pos::Globals::new(),
        }
    }
}

pub fn with_globals<F, R>(f: F) -> R
    where F: FnOnce() -> R
{
    let globals = Globals::new();
    GLOBALS.set(&globals, || {
        syntax_pos::GLOBALS.set(&globals.syntax_pos_globals, f)
    })
}

scoped_tls::scoped_thread_local!(pub static GLOBALS: Globals);

#[macro_use]
pub mod diagnostics {
    #[macro_use]
    pub mod macros;
    pub mod plugin;
    pub mod metadata;
}

// N.B., this module needs to be declared first so diagnostics are
// registered before they are used.
pub mod diagnostic_list;

pub mod util {
    pub mod lev_distance;
    pub mod node_count;
    pub mod parser;
    #[cfg(test)]
    pub mod parser_testing;
    pub mod map_in_place;
}

pub mod json;

pub mod syntax {
    pub use crate::ext;
    pub use crate::parse;
    pub use crate::ast;
}

pub mod ast;
pub mod attr;
pub mod source_map;
#[macro_use]
pub mod config;
pub mod entry;
pub mod feature_gate;
pub mod mut_visit;
pub mod parse;
pub mod ptr;
pub mod show_span;
pub mod std_inject;
pub use syntax_pos::edition;
pub use syntax_pos::symbol;
pub mod test;
pub mod tokenstream;
pub mod visit;

pub mod print {
    pub mod pp;
    pub mod pprust;
}

pub mod ext {
    pub use syntax_pos::hygiene;
    pub mod base;
    pub mod build;
    pub mod derive;
    pub mod expand;
    pub mod placeholders;
    pub mod source_util;

    pub mod tt {
        pub mod transcribe;
        pub mod macro_parser;
        pub mod macro_rules;
        pub mod quoted;
    }
}

pub mod early_buffered_lints;

#[cfg(test)]
mod test_snippet;

__build_diagnostic_array! { libsyntax, DIAGNOSTICS }
<commit_msg>Rename module<commit_after>//! The Rust parser and macro expander.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       test(attr(deny(warnings))))]

#![deny(rust_2018_idioms)]
#![cfg_attr(not(stage0), deny(internal))]

#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
#![feature(nll)]
#![feature(rustc_attrs)]
#![feature(rustc_diagnostic_macros)]
#![feature(step_trait)]
#![feature(try_trait)]
#![feature(unicode_internals)]

#![recursion_limit="256"]

#[allow(unused_extern_crates)]
extern crate serialize as rustc_serialize; // used by deriving

pub use errors;
use rustc_data_structures::sync::Lock;
use rustc_data_structures::bit_set::GrowableBitSet;
pub use rustc_data_structures::thin_vec::ThinVec;
use ast::AttrId;

// A variant of 'try!' that panics on an Err. This is used as a crutch on the
// way towards a non-panic!-prone parser. It should be used for fatal parsing
// errors; eventually we plan to convert all code using panictry to just use
// normal try.
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use errors::FatalError;
        match $e {
            Ok(e) => e,
            Err(mut e) => {
                e.emit();
                FatalError.raise()
            }
        }
    })
}

// A variant of 'panictry!' that works on a Vec<Diagnostic> instead of a single DiagnosticBuilder.
macro_rules! panictry_buffer {
    ($handler:expr, $e:expr) => ({
        use std::result::Result::{Ok, Err};
        use errors::{FatalError, DiagnosticBuilder};
        match $e {
            Ok(e) => e,
            Err(errs) => {
                for e in errs {
                    DiagnosticBuilder::new_diagnostic($handler, e).emit();
                }
                FatalError.raise()
            }
        }
    })
}

#[macro_export]
macro_rules! unwrap_or {
    ($opt:expr, $default:expr) => {
        match $opt {
            Some(x) => x,
            None => $default,
        }
    }
}

pub struct Globals {
    used_attrs: Lock<GrowableBitSet<AttrId>>,
    known_attrs: Lock<GrowableBitSet<AttrId>>,
    syntax_pos_globals: syntax_pos::Globals,
}

impl Globals {
    fn new() -> Globals {
        Globals {
            // We have no idea how many attributes their will be, so just
            // initiate the vectors with 0 bits. We'll grow them as necessary.
            used_attrs: Lock::new(GrowableBitSet::new_empty()),
            known_attrs: Lock::new(GrowableBitSet::new_empty()),
            syntax_pos_globals: syntax_pos::Globals::new(),
        }
    }
}

pub fn with_globals<F, R>(f: F) -> R
    where F: FnOnce() -> R
{
    let globals = Globals::new();
    GLOBALS.set(&globals, || {
        syntax_pos::GLOBALS.set(&globals.syntax_pos_globals, f)
    })
}

scoped_tls::scoped_thread_local!(pub static GLOBALS: Globals);

#[macro_use]
pub mod diagnostics {
    #[macro_use]
    pub mod macros;
    pub mod plugin;
    pub mod metadata;
}

// N.B., this module needs to be declared first so diagnostics are
// registered before they are used.
pub mod error_codes;

pub mod util {
    pub mod lev_distance;
    pub mod node_count;
    pub mod parser;
    #[cfg(test)]
    pub mod parser_testing;
    pub mod map_in_place;
}

pub mod json;

pub mod syntax {
    pub use crate::ext;
    pub use crate::parse;
    pub use crate::ast;
}

pub mod ast;
pub mod attr;
pub mod source_map;
#[macro_use]
pub mod config;
pub mod entry;
pub mod feature_gate;
pub mod mut_visit;
pub mod parse;
pub mod ptr;
pub mod show_span;
pub mod std_inject;
pub use syntax_pos::edition;
pub use syntax_pos::symbol;
pub mod test;
pub mod tokenstream;
pub mod visit;

pub mod print {
    pub mod pp;
    pub mod pprust;
}

pub mod ext {
    pub use syntax_pos::hygiene;
    pub mod base;
    pub mod build;
    pub mod derive;
    pub mod expand;
    pub mod placeholders;
    pub mod source_util;

    pub mod tt {
        pub mod transcribe;
        pub mod macro_parser;
        pub mod macro_rules;
        pub mod quoted;
    }
}

pub mod early_buffered_lints;

#[cfg(test)]
mod test_snippet;

__build_diagnostic_array! { libsyntax, DIAGNOSTICS }
<|endoftext|>


<commit_before>use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::span_lint_and_help;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `as` conversions.
    ///
    /// **Why is this bad?** `as` conversions will perform many kinds of
    /// conversions, including silently lossy conversions and dangerous coercions.
    /// There are cases when it makes sense to use `as`, so the lint is
    /// Allow by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a: u32;
    /// ...
    /// f(a as u16);
    /// ```
    ///
    /// Usually better represents the semantics you expect:
    /// ```rust,ignore
    /// f(a.try_into()?);
    /// ```
    /// or
    /// ```rust,ignore
    /// f(a.try_into().expect("Unexpected u16 overflow in f"));
    /// ```
    ///
    pub AS_CONVERSIONS,
    restriction,
    "using a potentially dangerous silent `as` conversion"
}

declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);

impl EarlyLintPass for AsConversions {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess(), expr.span) {
            return;
        }

        if let ExprKind::Cast(_, _) = expr.kind {
            span_lint_and_help(
                cx,
                AS_CONVERSIONS,
                expr.span,
                "using a potentially dangerous silent `as` conversion",
                None,
                "consider using a safe wrapper for this conversion",
            );
        }
    }
}
<commit_msg>Add a note to `as_conversions`<commit_after>use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::span_lint_and_help;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `as` conversions.
    ///
    /// Note that this lint is specialized in linting *every single* use of `as`
    /// regardless of whether good alternatives exist or not.
    /// If you want more precise lints for `as`, please consider using these separate lints:
    /// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
    /// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
    /// There is a good explanation the reason why this lint should work in this way and how it is useful
    /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
    ///
    /// **Why is this bad?** `as` conversions will perform many kinds of
    /// conversions, including silently lossy conversions and dangerous coercions.
    /// There are cases when it makes sense to use `as`, so the lint is
    /// Allow by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a: u32;
    /// ...
    /// f(a as u16);
    /// ```
    ///
    /// Usually better represents the semantics you expect:
    /// ```rust,ignore
    /// f(a.try_into()?);
    /// ```
    /// or
    /// ```rust,ignore
    /// f(a.try_into().expect("Unexpected u16 overflow in f"));
    /// ```
    ///
    pub AS_CONVERSIONS,
    restriction,
    "using a potentially dangerous silent `as` conversion"
}

declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);

impl EarlyLintPass for AsConversions {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess(), expr.span) {
            return;
        }

        if let ExprKind::Cast(_, _) = expr.kind {
            span_lint_and_help(
                cx,
                AS_CONVERSIONS,
                expr.span,
                "using a potentially dangerous silent `as` conversion",
                None,
                "consider using a safe wrapper for this conversion",
            );
        }
    }
}
<|endoftext|>