1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
macro_rules! check_mods {
    ($errors:ident, $context:expr, $actual:ident, [$($allowed:ident),*] ) => {{
        let allowed_mods = [$(Keyword::$allowed),*];
        $errors.extend_from_slice(
            &check_allowed_modifier(&$actual, &allowed_mods, $context)
        );
    }}
}


pub mod java8;

use ast;
use lex;
use base::diag::Report;
use base::code::{Span, BytePos};

type Mods = [ast::Spanned<lex::Keyword>];

// some helper functions
fn check_allowed_modifier(actual: &Mods, allowed: &[lex::Keyword], ctx: &str)
    -> Vec<Report>
{
    let mut seen: Vec<ast::Spanned<lex::Keyword>> = Vec::new();
    actual.iter().flat_map(|&m| {
        // collect all reports
        let mut reps = Vec::new();

        // check for duplicate modifiers
        let dupe = seen
            .iter()
            .find(|hay| hay.inner == m.inner)
            .map(|m| m.span);
        if let Some(prev_span) = dupe {
            reps.push(Report::simple_error(
                format!("duplicate `{:?}` modifier", m.inner),
                m.span,
            ).with_span_note(
                format!("the first `{:?}` modifier is already here", m.inner),
                prev_span,
            ));
        } else {
            seen.push(m);
        }

        // check if modifier is valid
        if !allowed.contains(&m.inner) {
            let msg = format!("modifier `{}` is illegal in this context", m.inner);
            let note = format!("valid `{}` modifier are: {:?}", ctx, allowed);
            reps.push(Report::simple_error(msg, m.span).with_note(note));
        }
        reps.into_iter()
    }).collect()
}

fn get_visibility(mods: &Mods, errors: &mut Vec<Report>)
    -> Option<ast::Visibility>
{
    use lex::Keyword;

    let mut vis = None;
    let mut first_vis = None;

    for &ast::Spanned { inner: modifier, span } in mods {
        match modifier {
            Keyword::Public | Keyword::Private | Keyword::Protected => {
                // check if there was a visibility modifier before this one
                if let Some(prev_span) = first_vis {
                    let e = Report::simple_error(
                        "duplicate visibility modifier",
                        span
                    ).with_span_note(
                        "the first visibility modifier is already here",
                        prev_span
                    );
                    errors.push(e);
                } else {
                    first_vis = Some(span);
                    vis = Some(match modifier {
                        Keyword::Public => ast::Visibility::Public,
                        Keyword::Private => ast::Visibility::Private,
                        Keyword::Protected => ast::Visibility::Protected,
                        _ => unreachable!(),
                    });
                }
            },
            _ => {},
        }
    }

    vis
}

macro_rules! gen_finder {
    ($name:ident, $keyword:ident) => {
        /// Returns if the modifier for the given property is in the list of
        /// modifiers. Does NOT check duplicate modifier.
        fn $name(mods: &Mods) -> Option<Span> {
            mods.iter()
                .find(|m| m.inner == lex::Keyword::$keyword)
                .map(|m| m.span)
        }
    }
}

gen_finder!(get_abstract, Abstract);
gen_finder!(get_default, Default);
gen_finder!(get_final, Final);
gen_finder!(get_native, Native);
gen_finder!(get_static, Static);
gen_finder!(get_strictfp, Strictfp);
gen_finder!(get_synchronized, Synchronized);

fn unwrap_keyword(tok: &lex::Token) -> lex::Keyword {
    if let &lex::Token::KeyW(kw) = tok { kw } else { unreachable!() }
}