<commit_before>use rustc::lint::*;
use rustc::hir;
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
use std::collections::HashMap;
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token::InternedString;
use utils::{in_macro, span_lint};

/// **What it does:** This lint checks for unused labels.
///
/// **Why is this bad?** Maybe the label should be used in which case there is an error in the
/// code or it should be removed.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust,ignore
/// fn unused_label() {
///     'label: for i in 1..2 {
///         if i > 4 { continue }
///     }
/// ```
declare_lint! {
    pub UNUSED_LABEL,
    Warn,
    "unused label"
}

pub struct UnusedLabel;

#[derive(Default)]
struct UnusedLabelVisitor {
    labels: HashMap<InternedString, Span>,
}

impl UnusedLabelVisitor {
    pub fn new() -> UnusedLabelVisitor {
        ::std::default::Default::default()
    }
}

impl LintPass for UnusedLabel {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNUSED_LABEL)
    }
}

impl LateLintPass for UnusedLabel {
    fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, body: &hir::Block, span: Span, _: ast::NodeId) {
        if in_macro(cx, span) {
            return;
        }

        let mut v = UnusedLabelVisitor::new();
        walk_fn(&mut v, kind, decl, body, span);

        for (label, span) in v.labels {
            span_lint(cx, UNUSED_LABEL, span, &format!("unused label `{}`", label));
        }
    }
}

impl<'v> Visitor<'v> for UnusedLabelVisitor {
    fn visit_expr(&mut self, expr: &hir::Expr) {
        match expr.node {
            hir::ExprBreak(Some(label)) |
            hir::ExprAgain(Some(label)) => {
                self.labels.remove(&label.node.as_str());
            }
            hir::ExprLoop(_, Some(label)) |
            hir::ExprWhile(_, _, Some(label)) => {
                self.labels.insert(label.node.as_str(), expr.span);
            }
            _ => (),
        }

        walk_expr(self, expr);
    }
}
<commit_msg>Rustup to rustc 1.12.0-nightly (1225e122f 2016-07-30)<commit_after>use rustc::lint::*;
use rustc::hir;
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
use std::collections::HashMap;
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token::InternedString;
use utils::{in_macro, span_lint};

/// **What it does:** This lint checks for unused labels.
///
/// **Why is this bad?** Maybe the label should be used in which case there is an error in the
/// code or it should be removed.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust,ignore
/// fn unused_label() {
///     'label: for i in 1..2 {
///         if i > 4 { continue }
///     }
/// ```
declare_lint! {
    pub UNUSED_LABEL,
    Warn,
    "unused label"
}

pub struct UnusedLabel;

#[derive(Default)]
struct UnusedLabelVisitor {
    labels: HashMap<InternedString, Span>,
}

impl UnusedLabelVisitor {
    pub fn new() -> UnusedLabelVisitor {
        ::std::default::Default::default()
    }
}

impl LintPass for UnusedLabel {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNUSED_LABEL)
    }
}

impl LateLintPass for UnusedLabel {
    fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, body: &hir::Block, span: Span, fn_id: ast::NodeId) {
        if in_macro(cx, span) {
            return;
        }

        let mut v = UnusedLabelVisitor::new();
        walk_fn(&mut v, kind, decl, body, span, fn_id);

        for (label, span) in v.labels {
            span_lint(cx, UNUSED_LABEL, span, &format!("unused label `{}`", label));
        }
    }
}

impl<'v> Visitor<'v> for UnusedLabelVisitor {
    fn visit_expr(&mut self, expr: &hir::Expr) {
        match expr.node {
            hir::ExprBreak(Some(label)) |
            hir::ExprAgain(Some(label)) => {
                self.labels.remove(&label.node.as_str());
            }
            hir::ExprLoop(_, Some(label)) |
            hir::ExprWhile(_, _, Some(label)) => {
                self.labels.insert(label.node.as_str(), expr.span);
            }
            _ => (),
        }

        walk_expr(self, expr);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate mozangle;
extern crate webrender;

use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};

// from glslang
const FRAGMENT_SHADER: u32 = 0x8B30;
const VERTEX_SHADER: u32 = 0x8B31;

struct Shader {
    name: &'static str,
    features: &'static [&'static str],
}

const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n";

const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"];
const CLIP_FEATURES: &[&str] = &["TRANSFORM"];
const CACHE_FEATURES: &[&str] = &[""];
const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ];
const PRIM_FEATURES: &[&str] = &[""];

const SHADERS: &[Shader] = &[
    // Clip mask shaders
    Shader {
        name: "cs_clip_rectangle",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_image",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_box_shadow",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_line",
        features: CLIP_FEATURES,
    },
    // Cache shaders
    Shader {
        name: "cs_blur",
        features: &[ "ALPHA_TARGET", "COLOR_TARGET" ],
    },
    Shader {
        name: "cs_border_segment",
        features: CACHE_FEATURES,
    },
    Shader {
        name: "cs_border_solid",
        features: CACHE_FEATURES,
    },
    // Prim shaders
    Shader {
        name: "ps_split_composite",
        features: PRIM_FEATURES,
    },
    Shader {
        name: "ps_text_run",
        features: &[ "", "GLYPH_TRANSFORM" ],
    },
    // Brush shaders
    Shader {
        name: "brush_yuv_image",
        features: &[
            "",
            "YUV_NV12",
            "YUV_PLANAR",
            "YUV_INTERLEAVED",
            "TEXTURE_2D,YUV_NV12",
            "YUV_NV12,ALPHA_PASS",
        ],
    },
    Shader {
        name: "brush_solid",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_image",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_mix_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_radial_gradient",
        features: GRADIENT_FEATURES,
    },
    Shader {
        name: "brush_linear_gradient",
        features: GRADIENT_FEATURES,
    },
];

const VERSION_STRING: &str = "#version 300 es\n";

#[test]
fn validate_shaders() {
    mozangle::shaders::initialize().unwrap();

    let resources = BuiltInResources::default();
    let vs_validator =
        ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    let fs_validator =
        ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    for shader in SHADERS {
        for config in shader.features {
            let mut features = String::new();
            features.push_str(SHADER_PREFIX);

            for feature in config.split(",") {
                features.push_str(&format!("#define WR_FEATURE_{}\n", feature));
            }

            let (vs, fs) =
                webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None);

            validate(&vs_validator, shader.name, vs);
            validate(&fs_validator, shader.name, fs);
        }
    }
}

fn validate(validator: &ShaderValidator, name: &str, source: String) {
    // Check for each `switch` to have a `default`, see
    // https://github.com/servo/webrender/wiki/Driver-issues#lack-of-default-case-in-a-switch
    assert_eq!(source.matches("switch").count(), source.matches("default:").count(),
        "Shader '{}' doesn't have all `switch` covered with `default` cases", name);
    // Run Angle validator
    match validator.compile_and_translate(&[&source]) {
        Ok(_) => {
            println!("Shader translated succesfully: {}", name);
        }
        Err(_) => {
            panic!(
                "Shader compilation failed: {}\n{}",
                name,
                validator.info_log()
            );
        }
    }
}
<commit_msg>Auto merge of #3163 - zakorgy:patch-3, r=nical<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate mozangle;
extern crate webrender;

use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};

// from glslang
const FRAGMENT_SHADER: u32 = 0x8B30;
const VERTEX_SHADER: u32 = 0x8B31;

struct Shader {
    name: &'static str,
    features: &'static [&'static str],
}

const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n";

const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"];
const CLIP_FEATURES: &[&str] = &[""];
const CACHE_FEATURES: &[&str] = &[""];
const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ];
const PRIM_FEATURES: &[&str] = &[""];

const SHADERS: &[Shader] = &[
    // Clip mask shaders
    Shader {
        name: "cs_clip_rectangle",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_image",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_box_shadow",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_line",
        features: CLIP_FEATURES,
    },
    // Cache shaders
    Shader {
        name: "cs_blur",
        features: &[ "ALPHA_TARGET", "COLOR_TARGET" ],
    },
    Shader {
        name: "cs_border_segment",
        features: CACHE_FEATURES,
    },
    Shader {
        name: "cs_border_solid",
        features: CACHE_FEATURES,
    },
    // Prim shaders
    Shader {
        name: "ps_split_composite",
        features: PRIM_FEATURES,
    },
    Shader {
        name: "ps_text_run",
        features: &[ "", "GLYPH_TRANSFORM" ],
    },
    // Brush shaders
    Shader {
        name: "brush_yuv_image",
        features: &[
            "",
            "YUV_NV12",
            "YUV_PLANAR",
            "YUV_INTERLEAVED",
            "TEXTURE_2D,YUV_NV12",
            "YUV_NV12,ALPHA_PASS",
        ],
    },
    Shader {
        name: "brush_solid",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_image",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_mix_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_radial_gradient",
        features: GRADIENT_FEATURES,
    },
    Shader {
        name: "brush_linear_gradient",
        features: GRADIENT_FEATURES,
    },
];

const VERSION_STRING: &str = "#version 300 es\n";

#[test]
fn validate_shaders() {
    mozangle::shaders::initialize().unwrap();

    let resources = BuiltInResources::default();
    let vs_validator =
        ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    let fs_validator =
        ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    for shader in SHADERS {
        for config in shader.features {
            let mut features = String::new();
            features.push_str(SHADER_PREFIX);

            for feature in config.split(",") {
                features.push_str(&format!("#define WR_FEATURE_{}\n", feature));
            }

            let (vs, fs) =
                webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None);

            validate(&vs_validator, shader.name, vs);
            validate(&fs_validator, shader.name, fs);
        }
    }
}

fn validate(validator: &ShaderValidator, name: &str, source: String) {
    // Check for each `switch` to have a `default`, see
    // https://github.com/servo/webrender/wiki/Driver-issues#lack-of-default-case-in-a-switch
    assert_eq!(source.matches("switch").count(), source.matches("default:").count(),
        "Shader '{}' doesn't have all `switch` covered with `default` cases", name);
    // Run Angle validator
    match validator.compile_and_translate(&[&source]) {
        Ok(_) => {
            println!("Shader translated succesfully: {}", name);
        }
        Err(_) => {
            panic!(
                "Shader compilation failed: {}\n{}",
                name,
                validator.info_log()
            );
        }
    }
}
<|endoftext|>


<commit_before>/*
    Copyright 2017-2019 Andrew Medworth <github@medworth.org.uk>

    This file is part of Dots-and-Boxes Engine.

    Dots-and-Boxes Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Dots-and-Boxes Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Dots-and-Boxes Engine.  If not, see <http://www.gnu.org/licenses/>.
*/

use game::{Move, Position, SimplePosition, Side};
use nimstring;
use eval;

use std::io::{self, BufRead};
use std::fs::File;
use regex::{Regex, Captures};
use time;

#[derive(PartialEq)]
#[derive(Debug)]
enum Command {
    MakeMove(Move),
    UndoMove(Move),
    CalcNimstringValue,
    Evaluate,
    PrintHelp,
    Quit,
}

impl Command {
    fn execute(self: &Command, pos: &mut SimplePosition) {
        match self {
            &Command::MakeMove(m) => {
                if pos.is_legal_move(m) {
                    pos.make_move(m);
                }
                else {
                    println!("Not a legal move: {}", m);
                }
            },
            &Command::UndoMove(m) => { pos.undo_move(m); },
            &Command::CalcNimstringValue => {
                let (val, per_move) = nimstring::calc_value_with_moves(pos);
                println!("Position value is {}", val);
                let mut moves: Vec<&Move> = per_move.keys().collect();
                moves.sort_by(|a, b| a.y.cmp(&b.y).then(a.x.cmp(&b.x)).then(a.side.cmp(&b.side)));
                for &m in &moves {
                    println!("{} {}", m, per_move.get(m).unwrap());
                }
            },
            &Command::Evaluate => {
                let (val, best_move) = eval::eval(pos);
                if let Some(best_move) = best_move {
                    println!("V(P) = {}, best move {}", val, best_move);
                } else {
                    println!("V(P) = {}", val);
                }
            },
            &Command::PrintHelp => { print_help(); },
            &Command::Quit => { println!("Bye bye!"); },
        }
    }
}

fn print_help() {
    println!("Available commands:");
    println!("x y t/l/b/r - make move (x,y) top/left/bottom/right");
    println!("u x y t/l/b/r - undo move (x,y) top/left/bottom/right");
    println!("nv - calculate Nimstring value of current position");
    println!("eval - evaluate the current position");
    println!("help - print this help message");
    println!("quit/exit - exit program");
}

fn parse_side(side_s: &str) -> Option<Side> {
    if side_s == "l" || side_s == "left" { Some(Side::Left) }
    else if side_s == "r" || side_s == "right" { Some(Side::Right) }
    else if side_s == "t" || side_s == "top" { Some(Side::Top) }
    else if side_s == "b" || side_s == "bottom" { Some(Side::Bottom) }
    else { None }
}

fn parse_move(caps: Captures) -> Result<Move, String> {
    let x = caps[1].parse::<usize>().unwrap();
    let y = caps[2].parse::<usize>().unwrap();
    let side_s = caps[3].to_string();
    match parse_side(&side_s) {
        Some(side) => Ok(Move{x: x, y: y, side: side}),
        None => Err(format!("Unrecognised side: [{}]", side_s))
    }
}

fn parse_command(input: &str) -> Result<Command, String> {
    let input = input.to_lowercase();
    let move_re = Regex::new(r"^(\d+) (\d+) ([a-zA-Z]+)$").unwrap();
    if let Some(caps) = move_re.captures(&input) {
        match parse_move(caps) {
            Ok(m) => return Ok(Command::MakeMove(m)),
            Err(e) => return Err(format!("Cannot extract move from [{}]: {}", input, e))
        }
    }
    let undo_move_re = Regex::new(r"^[uU] (\d+) (\d+) ([a-zA-Z]+)$").unwrap();
    if let Some(caps) = undo_move_re.captures(&input) {
        match parse_move(caps) {
            Ok(m) => return Ok(Command::UndoMove(m)),
            Err(e) => return Err(format!("Cannot extract move from [{}]: {}", input, e))
        }
    }
    if "nv" == input {
        return Ok(Command::CalcNimstringValue);
    }
    if "eval" == input {
        return Ok(Command::Evaluate);
    }
    if "help" == input {
        return Ok(Command::PrintHelp);
    }
    if "exit" == input || "quit" == input {
        return Ok(Command::Quit);
    }
    Err("Unsupported command".to_string())
}

fn get_next_command() -> Command {
    loop {
        let mut input = String::new();
        if let Err(error) = io::stdin().read_line(&mut input) {
            println!("Error reading from standard input: {}", error);
            continue;
        }
        let input = input.trim();
        match parse_command(&input) {
            Ok(command) => return command,
            Err(error) => {
                println!("Cannot execute [{}]: {}", input, error);
                println!("For help, try 'help'");
            }
        }
    }
}

fn main_loop_from(pos: &mut SimplePosition) {
    loop {
        println!("{}", pos);
        let command = get_next_command();
        let start_time = time::precise_time_s();
        command.execute(pos);
        let end_time = time::precise_time_s();
        if command == Command::Quit {
            break;
        }
        let elapsed = end_time - start_time;
        if elapsed >= 0.1 {
            println!("({:.1} seconds)", end_time - start_time);
        }
        println!();
    }
}

// Enter the main loop of the CLI from the start of the game
pub fn main_loop_start(width: usize, height: usize) {
    main_loop_from(&mut SimplePosition::new_game(width, height));
}

// Execute a given file of commands (which must have the dimensions of the position on the first line)
// and then enter the CLI main loop.
pub fn main_loop_file(filename: &str) {
    let f = File::open(filename).expect(&format!("Could not open file [{}]", filename));
    let reader = io::BufReader::new(f);
    let mut lines = reader.lines();
    let size_spec = lines.next().expect("Empty file").expect("Could not read board size from first line");
    let size_re = Regex::new(r"^(\d+) (\d+)$").unwrap();
    let size_caps = size_re.captures(&size_spec).expect(&format!("Could not read board size from [{}]", size_spec));
    let width = size_caps[1].parse::<usize>().unwrap();
    let height = size_caps[2].parse::<usize>().unwrap();
    let mut pos = SimplePosition::new_game(width, height);
    for line in lines {
        let line = line.expect("Could not read line from file");
        if line.trim().len() == 0 {
            continue;
        }
        match parse_command(&line) {
            Ok(command) => command.execute(&mut pos),
            Err(error) => panic!("Cannot execute [{}]: {}", line, error),
        }
    }
    main_loop_from(&mut pos);
}

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

    #[test]
    fn parse_make_move_cmd() {
        assert_eq!(Command::MakeMove(Move{x: 3, y: 5, side: Side::Bottom}), parse_command("3 5 b").unwrap());
        assert_eq!(Command::MakeMove(Move{x: 3, y: 5, side: Side::Bottom}), parse_command("3 5 Bottom").unwrap());
    }

    #[test]
    fn parse_undo_move_cmd() {
        assert_eq!(Command::UndoMove(Move{x: 8, y: 6, side: Side::Left}), parse_command("u 8 6 l").unwrap());
        assert_eq!(Command::UndoMove(Move{x: 8, y: 6, side: Side::Left}), parse_command("u 8 6 Left").unwrap());
    }

    #[test]
    fn parse_nimstring_value_cmd() {
        assert_eq!(Command::CalcNimstringValue, parse_command("nv").unwrap());
    }

    #[test]
    fn parse_evaluate_cmd() {
        assert_eq!(Command::Evaluate, parse_command("eval").unwrap());
    }

    #[test]
    fn parse_help_cmd() {
        assert_eq!(Command::PrintHelp, parse_command("help").unwrap());
    }

    #[test]
    fn parse_exit_cmd() {
        assert_eq!(Command::Quit, parse_command("quit").unwrap());
        assert_eq!(Command::Quit, parse_command("exit").unwrap());
    }
}
<commit_msg>Extract parse_position function<commit_after>/*
    Copyright 2017-2019 Andrew Medworth <github@medworth.org.uk>

    This file is part of Dots-and-Boxes Engine.

    Dots-and-Boxes Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Dots-and-Boxes Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Dots-and-Boxes Engine.  If not, see <http://www.gnu.org/licenses/>.
*/

use game::{Move, Position, SimplePosition, Side};
use nimstring;
use eval;

use std::io::{self, BufRead};
use std::fs::File;
use regex::{Regex, Captures};
use time;

#[derive(PartialEq)]
#[derive(Debug)]
enum Command {
    MakeMove(Move),
    UndoMove(Move),
    CalcNimstringValue,
    Evaluate,
    PrintHelp,
    Quit,
}

impl Command {
    fn execute(self: &Command, pos: &mut SimplePosition) {
        match self {
            &Command::MakeMove(m) => {
                if pos.is_legal_move(m) {
                    pos.make_move(m);
                }
                else {
                    println!("Not a legal move: {}", m);
                }
            },
            &Command::UndoMove(m) => { pos.undo_move(m); },
            &Command::CalcNimstringValue => {
                let (val, per_move) = nimstring::calc_value_with_moves(pos);
                println!("Position value is {}", val);
                let mut moves: Vec<&Move> = per_move.keys().collect();
                moves.sort_by(|a, b| a.y.cmp(&b.y).then(a.x.cmp(&b.x)).then(a.side.cmp(&b.side)));
                for &m in &moves {
                    println!("{} {}", m, per_move.get(m).unwrap());
                }
            },
            &Command::Evaluate => {
                let (val, best_move) = eval::eval(pos);
                if let Some(best_move) = best_move {
                    println!("V(P) = {}, best move {}", val, best_move);
                } else {
                    println!("V(P) = {}", val);
                }
            },
            &Command::PrintHelp => { print_help(); },
            &Command::Quit => { println!("Bye bye!"); },
        }
    }
}

fn print_help() {
    println!("Available commands:");
    println!("x y t/l/b/r - make move (x,y) top/left/bottom/right");
    println!("u x y t/l/b/r - undo move (x,y) top/left/bottom/right");
    println!("nv - calculate Nimstring value of current position");
    println!("eval - evaluate the current position");
    println!("help - print this help message");
    println!("quit/exit - exit program");
}

fn parse_side(side_s: &str) -> Option<Side> {
    if side_s == "l" || side_s == "left" { Some(Side::Left) }
    else if side_s == "r" || side_s == "right" { Some(Side::Right) }
    else if side_s == "t" || side_s == "top" { Some(Side::Top) }
    else if side_s == "b" || side_s == "bottom" { Some(Side::Bottom) }
    else { None }
}

fn parse_move(caps: Captures) -> Result<Move, String> {
    let x = caps[1].parse::<usize>().unwrap();
    let y = caps[2].parse::<usize>().unwrap();
    let side_s = caps[3].to_string();
    match parse_side(&side_s) {
        Some(side) => Ok(Move{x: x, y: y, side: side}),
        None => Err(format!("Unrecognised side: [{}]", side_s))
    }
}

fn parse_command(input: &str) -> Result<Command, String> {
    let input = input.to_lowercase();
    let move_re = Regex::new(r"^(\d+) (\d+) ([a-zA-Z]+)$").unwrap();
    if let Some(caps) = move_re.captures(&input) {
        match parse_move(caps) {
            Ok(m) => return Ok(Command::MakeMove(m)),
            Err(e) => return Err(format!("Cannot extract move from [{}]: {}", input, e))
        }
    }
    let undo_move_re = Regex::new(r"^[uU] (\d+) (\d+) ([a-zA-Z]+)$").unwrap();
    if let Some(caps) = undo_move_re.captures(&input) {
        match parse_move(caps) {
            Ok(m) => return Ok(Command::UndoMove(m)),
            Err(e) => return Err(format!("Cannot extract move from [{}]: {}", input, e))
        }
    }
    if "nv" == input {
        return Ok(Command::CalcNimstringValue);
    }
    if "eval" == input {
        return Ok(Command::Evaluate);
    }
    if "help" == input {
        return Ok(Command::PrintHelp);
    }
    if "exit" == input || "quit" == input {
        return Ok(Command::Quit);
    }
    Err("Unsupported command".to_string())
}

fn get_next_command() -> Command {
    loop {
        let mut input = String::new();
        if let Err(error) = io::stdin().read_line(&mut input) {
            println!("Error reading from standard input: {}", error);
            continue;
        }
        let input = input.trim();
        match parse_command(&input) {
            Ok(command) => return command,
            Err(error) => {
                println!("Cannot execute [{}]: {}", input, error);
                println!("For help, try 'help'");
            }
        }
    }
}

fn main_loop_from(pos: &mut SimplePosition) {
    loop {
        println!("{}", pos);
        let command = get_next_command();
        let start_time = time::precise_time_s();
        command.execute(pos);
        let end_time = time::precise_time_s();
        if command == Command::Quit {
            break;
        }
        let elapsed = end_time - start_time;
        if elapsed >= 0.1 {
            println!("({:.1} seconds)", end_time - start_time);
        }
        println!();
    }
}

fn parse_position<R: BufRead>(reader: R) -> SimplePosition {
    let mut lines = reader.lines();
    let size_spec = lines.next().expect("Empty file").expect("Could not read board size from first line");
    let size_re = Regex::new(r"^(\d+) (\d+)$").unwrap();
    let size_caps = size_re.captures(&size_spec).expect(&format!("Could not read board size from [{}]", size_spec));
    let width = size_caps[1].parse::<usize>().unwrap();
    let height = size_caps[2].parse::<usize>().unwrap();
    let mut pos = SimplePosition::new_game(width, height);
    for line in lines {
        let line = line.expect("Could not read line from file");
        if line.trim().len() == 0 {
            continue;
        }
        match parse_command(&line) {
            Ok(command) => command.execute(&mut pos),
            Err(error) => panic!("Cannot execute [{}]: {}", line, error),
        }
    }
    pos
}

// Enter the main loop of the CLI from the start of the game
pub fn main_loop_start(width: usize, height: usize) {
    main_loop_from(&mut SimplePosition::new_game(width, height));
}

// Execute a given file of commands (which must have the dimensions of the position on the first line)
// and then enter the CLI main loop.
pub fn main_loop_file(filename: &str) {
    let f = File::open(filename).expect(&format!("Could not open file [{}]", filename));
    let reader = io::BufReader::new(f);
    let mut pos = parse_position(reader);
    main_loop_from(&mut pos);
}

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

    #[test]
    fn parse_make_move_cmd() {
        assert_eq!(Command::MakeMove(Move{x: 3, y: 5, side: Side::Bottom}), parse_command("3 5 b").unwrap());
        assert_eq!(Command::MakeMove(Move{x: 3, y: 5, side: Side::Bottom}), parse_command("3 5 Bottom").unwrap());
    }

    #[test]
    fn parse_undo_move_cmd() {
        assert_eq!(Command::UndoMove(Move{x: 8, y: 6, side: Side::Left}), parse_command("u 8 6 l").unwrap());
        assert_eq!(Command::UndoMove(Move{x: 8, y: 6, side: Side::Left}), parse_command("u 8 6 Left").unwrap());
    }

    #[test]
    fn parse_nimstring_value_cmd() {
        assert_eq!(Command::CalcNimstringValue, parse_command("nv").unwrap());
    }

    #[test]
    fn parse_evaluate_cmd() {
        assert_eq!(Command::Evaluate, parse_command("eval").unwrap());
    }

    #[test]
    fn parse_help_cmd() {
        assert_eq!(Command::PrintHelp, parse_command("help").unwrap());
    }

    #[test]
    fn parse_exit_cmd() {
        assert_eq!(Command::Quit, parse_command("quit").unwrap());
        assert_eq!(Command::Quit, parse_command("exit").unwrap());
    }
}
<|endoftext|>


<commit_before>use clap::{Arg, App};
use log::{info};
use std::path::Path;
use std::ffi::OsStr;
use regex::Regex;

use inkwell::AddressSpace;
use inkwell::memory_buffer::MemoryBuffer;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::module::Linkage;
use inkwell::values::{FunctionValue, GlobalValue, PointerValue};
use inkwell::values::{AnyValue, BasicValue, BasicValueEnum};
use inkwell::types::{AnyType, FunctionType};

fn main() {
    // Command line argument parsing (using clap)
    let matches = App::new("Test inkwell")
        // .version("0.1.0")
        // .author("")
        // .about("")
        .arg(Arg::with_name("initializers")
             .short("i")
             .help("Call initializers from main"))
        .arg(Arg::with_name("seahorn")
             .short("s")
             .help("SeaHorn preparation (conflicts with --initializers)"))
        .arg(Arg::with_name("verbosity")
             .short("v")
             .multiple(true)
             .help("Increase message verbosity"))
        .arg(Arg::with_name("INPUT")
             .help("Input file name")
             .required(true)
             .index(1))
        .arg(Arg::with_name("OUTPUT")
             .help("Output file name")
             .short("o")
             .long("output")
             .takes_value(true)
             .default_value("out"))
        .get_matches();

    stderrlog::new()
        .verbosity(matches.occurrences_of("verbosity") as usize)
        .init()
        .unwrap();

    let path_in  = matches.value_of("INPUT")
        .expect("ERROR: missing input file name argument.");
    let path_in  = Path::new(path_in);

    let path_out = matches.value_of("OUTPUT")
        .expect("ERROR: missing output file name argument.");
    let path_out = Path::new(path_out);

    // Read the input file
    info!("Reading input from {}", path_in.to_str().unwrap());
    let memory_buffer = MemoryBuffer::create_from_file(path_in)
        .expect("ERROR: failed to open file.");

    let context = Context::create();
    let mut module = context.create_module_from_ir(memory_buffer)
        .expect("ERROR: failed to create module.");

    if matches.is_present("initializers") {
        handle_initializers(&context, &mut module);
    }

    if matches.is_present("seahorn") {
        handle_main(&module);

        handle_panic(&module);

        replace_def_with_dec(&module, &Regex::new(r"^_ZN3std2io5stdio7_eprint17h[a-f0-9]{16}E$").unwrap());
        replace_def_with_dec(&module, &Regex::new(r"^_ZN3std2io5stdio6_print17h[a-f0-9]{16}E$").unwrap());
    }

    // Write output file
    info!("Writing output to {}", path_out.to_str().unwrap());
    if path_out.extension() == Some(OsStr::new("bc")) {
        // output bitcode
        // TODO: this function returns bool but the doc doesn't say anything about it.
        module.write_bitcode_to_path(path_out);
    } else {
        // output disassembled bitcode
        module.print_to_file(path_out)
            .expect("ERROR: failed to write to file.");
    }
}

////////////////////////////////////////////////////////////////
// Transformations associated with initializers
////////////////////////////////////////////////////////////////

fn handle_initializers(context: &Context, module: &mut Module) {
    if let Some(initializer) = collect_initializers(context, module, ".init_array", "__init_function") {
        info!("Combined .init_array* initializers into '{}'", initializer.get_name().to_str().unwrap());

        let main = module.get_function("main").expect("Unable to find 'main' function");
        let mut args = get_fn_args(main);
        assert!(args.len() == 2); // We expect "i32 @main(i32 %0, i8** %1)"
        let i8_type = context.i8_type();
        let pi8_type = i8_type.ptr_type(AddressSpace::Generic);
        let ppi8_type = pi8_type.ptr_type(AddressSpace::Generic);
        args.push(ppi8_type.const_null().as_basic_value_enum());
        insert_call_at_head(context, initializer, args, main);
        info!("Inserted call to '{}' into 'main'", initializer.get_name().to_str().unwrap())
    } else {
        info!("No initializers to handle")
    }
}

/// Collect all the initializers in a section (whose name starts with 'prefix')
/// into a single function that calls all the initializers.
fn collect_initializers<'a>(context: &Context, module: &mut Module<'a>, prefix: &str, nm: &str) -> Option<FunctionValue<'a>> {
    let vs = collect_variables_in_section(module, prefix);
    for v in &vs {
        info!("Found initializer {:?}", v.get_name().to_str().unwrap());
    }

    let fps : Vec<PointerValue> = vs.iter().map(get_initializer_function).collect();

    if ! fps.is_empty() {
        let fp = fps[0];

        // dereference the pointer type
        let fp_type = fp.get_type().get_element_type().into_function_type();
        info!("Initializer type {:?}", fp_type.print_to_string());

        Some(build_fanout(context, module, nm, fp_type, fps))
    } else {
        None
    }
}

/// Collect variables that are assigned to a section whose name matches prefix
fn collect_variables_in_section<'a>(module: &Module<'a>, prefix: &str) -> Vec<GlobalValue<'a>> {
    let mut vs = Vec::new();
    // todo: should implement an iterator for global values so that the following loop
    // becomes just iter().filter_map().filter_map().filter()
    let mut og = module.get_first_global();
    while let Some(g) = og {
        if let Some(s) = g.get_section() {
            if let Ok(s) = s.to_str() {
                if s.starts_with(prefix) {
                    vs.push(g)
                }
            }
        }
        og = g.get_next_global();
    }
    vs
}

/// Convert the contents of an initializer section to a function pointer.
///
/// Initializer sections contain structs where the first field is a function pointer cast to
/// some other type.
fn get_initializer_function<'a>(v: &GlobalValue<'a>) -> PointerValue<'a> {
        let i = v.get_initializer().unwrap().into_struct_value();
        assert!(i.get_num_operands() == 2); // expecting two fields in struct
        let i = i.get_operand(0).unwrap();
        assert!(i.get_num_operands() == 1); // expecting bitcast
        let fp = i.get_operand(0).unwrap();
        fp.into_pointer_value()
}

/// Given a list of functions of type 'ty', build a function that calls each function in order
///
/// Assumes (without checking) that return type is void
///
///     define void @fanout(i32 %0, i32 %1) {
///     entry:
///       call void @f1(i32 %0, i32 %1)
///       call void @f2(i32 %0, i32 %1)
///       call void @f3(i32 %0, i32 %1)
///       ret void
///     }
///
fn build_fanout<'a>(context: &Context, module: &mut Module<'a>, nm: &str, ty: FunctionType<'a>, fps: Vec<PointerValue<'a>>) -> FunctionValue<'a> {
    let function = module.add_function(nm, ty, None);
    let args = get_fn_args(function);
    let basic_block = context.append_basic_block(function, "entry");
    let builder = context.create_builder();
    builder.position_at_end(basic_block);

    for fp in fps {
        builder.build_call(fp, &args, "");
        builder.build_return(None);
        // println!("Built function {:?}", function)
    }

    function
}

fn get_fn_args<'a>(function: FunctionValue<'a>) -> Vec<BasicValueEnum<'a>> {
    (0 .. function.count_params()).map(|i| function.get_nth_param(i).unwrap()).collect()
}

fn insert_call_at_head<'a>(context: &Context, f: FunctionValue<'a>, args: Vec<BasicValueEnum<'a>>, insertee: FunctionValue<'a>) {
    let bb = insertee.get_first_basic_block().expect("Unable to find function to insert function call into");
    let first_instruction = bb.get_first_instruction().expect("Unable to find where to insert function call into function");
    let builder = context.create_builder();
    builder.position_before(&first_instruction);
    builder.build_call(f, &args, "");
}

////////////////////////////////////////////////////////////////
// Transformations associated with SeaHorn
////////////////////////////////////////////////////////////////

fn handle_main(module: &Module) {
    // Remove the main function rustc generates.
    if let Some(main) = module.get_function("main") {
        unsafe { main.delete(); }
    }

    // Change the linkage of mangled main function from internal to external.
    if let Some(main) = get_function(module, &Regex::new(r"4main17h[a-f0-9]{16}E$").unwrap()) {
        // main.set_linkage(Linkage::External);
        println!("MAIN: {}", main.get_name().to_str().unwrap());
    }
}

fn get_function<'ctx>(module: &'ctx Module, re: &Regex) -> Option<FunctionValue<'ctx>> {
    let mut op_fun = module.get_first_function();
    while let Some(fun) = op_fun {
        if re.is_match(fun.get_name().to_str()
                       .expect("ERROR: function name is not in valid UTF-8")) {
            return Some(fun);
        }
        op_fun = fun.get_next_function();
    }
    None
}

fn handle_panic(module: &Module) {
    // TODO: make "spanic" a CL arg.
    if let Some(spanic) = module.get_function("spanic") {
        if let Some(unwind) = module.get_function("rust_begin_unwind") {
            unwind.replace_all_uses_with(spanic);
        }
    }
}

fn replace_def_with_dec(module: &Module, re: &Regex) {
    if let Some(fun) = get_function(module, re) {
        for bb in fun.get_basic_blocks() {
            unsafe { bb.delete().unwrap(); }
        }
        fun.remove_personality_function();
        fun.set_linkage(Linkage::External);
    }
}

////////////////////////////////////////////////////////////////
// End
////////////////////////////////////////////////////////////////
<commit_msg>Add long arguments and annotate the conflict between -i and -s.<commit_after>use clap::{Arg, App};
use log::{info};
use std::path::Path;
use std::ffi::OsStr;
use regex::Regex;

use inkwell::AddressSpace;
use inkwell::memory_buffer::MemoryBuffer;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::module::Linkage;
use inkwell::values::{FunctionValue, GlobalValue, PointerValue};
use inkwell::values::{AnyValue, BasicValue, BasicValueEnum};
use inkwell::types::{AnyType, FunctionType};

fn main() {
    // Command line argument parsing (using clap)
    let matches = App::new("Test inkwell")
        // .version("0.1.0")
        // .author("")
        // .about("")
        .arg(Arg::with_name("initializers")
             .short("i")
             .long("initializers")
             .help("Call initializers from main"))
        .arg(Arg::with_name("seahorn")
             .short("s")
             .long("seahorn")
             .conflicts_with("initializers")
             .help("SeaHorn preparation (conflicts with --initializers)"))
        .arg(Arg::with_name("verbosity")
             .short("v")
             .long("verbosity")
             .multiple(true)
             .help("Increase message verbosity"))
        .arg(Arg::with_name("INPUT")
             .help("Input file name")
             .required(true)
             .index(1))
        .arg(Arg::with_name("OUTPUT")
             .help("Output file name")
             .short("o")
             .long("output")
             .takes_value(true)
             .default_value("out"))
        .get_matches();

    stderrlog::new()
        .verbosity(matches.occurrences_of("verbosity") as usize)
        .init()
        .unwrap();

    let path_in  = matches.value_of("INPUT")
        .expect("ERROR: missing input file name argument.");
    let path_in  = Path::new(path_in);

    let path_out = matches.value_of("OUTPUT")
        .expect("ERROR: missing output file name argument.");
    let path_out = Path::new(path_out);

    // Read the input file
    info!("Reading input from {}", path_in.to_str().unwrap());
    let memory_buffer = MemoryBuffer::create_from_file(path_in)
        .expect("ERROR: failed to open file.");

    let context = Context::create();
    let mut module = context.create_module_from_ir(memory_buffer)
        .expect("ERROR: failed to create module.");

    if matches.is_present("initializers") {
        handle_initializers(&context, &mut module);
    }

    if matches.is_present("seahorn") {
        handle_main(&module);

        handle_panic(&module);

        replace_def_with_dec(&module, &Regex::new(r"^_ZN3std2io5stdio7_eprint17h[a-f0-9]{16}E$").unwrap());
        replace_def_with_dec(&module, &Regex::new(r"^_ZN3std2io5stdio6_print17h[a-f0-9]{16}E$").unwrap());
    }

    // Write output file
    info!("Writing output to {}", path_out.to_str().unwrap());
    if path_out.extension() == Some(OsStr::new("bc")) {
        // output bitcode
        // TODO: this function returns bool but the doc doesn't say anything about it.
        module.write_bitcode_to_path(path_out);
    } else {
        // output disassembled bitcode
        module.print_to_file(path_out)
            .expect("ERROR: failed to write to file.");
    }
}

////////////////////////////////////////////////////////////////
// Transformations associated with initializers
////////////////////////////////////////////////////////////////

fn handle_initializers(context: &Context, module: &mut Module) {
    if let Some(initializer) = collect_initializers(context, module, ".init_array", "__init_function") {
        info!("Combined .init_array* initializers into '{}'", initializer.get_name().to_str().unwrap());

        let main = module.get_function("main").expect("Unable to find 'main' function");
        let mut args = get_fn_args(main);
        assert!(args.len() == 2); // We expect "i32 @main(i32 %0, i8** %1)"
        let i8_type = context.i8_type();
        let pi8_type = i8_type.ptr_type(AddressSpace::Generic);
        let ppi8_type = pi8_type.ptr_type(AddressSpace::Generic);
        args.push(ppi8_type.const_null().as_basic_value_enum());
        insert_call_at_head(context, initializer, args, main);
        info!("Inserted call to '{}' into 'main'", initializer.get_name().to_str().unwrap())
    } else {
        info!("No initializers to handle")
    }
}

/// Collect all the initializers in a section (whose name starts with 'prefix')
/// into a single function that calls all the initializers.
fn collect_initializers<'a>(context: &Context, module: &mut Module<'a>, prefix: &str, nm: &str) -> Option<FunctionValue<'a>> {
    let vs = collect_variables_in_section(module, prefix);
    for v in &vs {
        info!("Found initializer {:?}", v.get_name().to_str().unwrap());
    }

    let fps : Vec<PointerValue> = vs.iter().map(get_initializer_function).collect();

    if ! fps.is_empty() {
        let fp = fps[0];

        // dereference the pointer type
        let fp_type = fp.get_type().get_element_type().into_function_type();
        info!("Initializer type {:?}", fp_type.print_to_string());

        Some(build_fanout(context, module, nm, fp_type, fps))
    } else {
        None
    }
}

/// Collect variables that are assigned to a section whose name matches prefix
fn collect_variables_in_section<'a>(module: &Module<'a>, prefix: &str) -> Vec<GlobalValue<'a>> {
    let mut vs = Vec::new();
    // todo: should implement an iterator for global values so that the following loop
    // becomes just iter().filter_map().filter_map().filter()
    let mut og = module.get_first_global();
    while let Some(g) = og {
        if let Some(s) = g.get_section() {
            if let Ok(s) = s.to_str() {
                if s.starts_with(prefix) {
                    vs.push(g)
                }
            }
        }
        og = g.get_next_global();
    }
    vs
}

/// Convert the contents of an initializer section to a function pointer.
///
/// Initializer sections contain structs where the first field is a function pointer cast to
/// some other type.
fn get_initializer_function<'a>(v: &GlobalValue<'a>) -> PointerValue<'a> {
        let i = v.get_initializer().unwrap().into_struct_value();
        assert!(i.get_num_operands() == 2); // expecting two fields in struct
        let i = i.get_operand(0).unwrap();
        assert!(i.get_num_operands() == 1); // expecting bitcast
        let fp = i.get_operand(0).unwrap();
        fp.into_pointer_value()
}

/// Given a list of functions of type 'ty', build a function that calls each function in order
///
/// Assumes (without checking) that return type is void
///
///     define void @fanout(i32 %0, i32 %1) {
///     entry:
///       call void @f1(i32 %0, i32 %1)
///       call void @f2(i32 %0, i32 %1)
///       call void @f3(i32 %0, i32 %1)
///       ret void
///     }
///
fn build_fanout<'a>(context: &Context, module: &mut Module<'a>, nm: &str, ty: FunctionType<'a>, fps: Vec<PointerValue<'a>>) -> FunctionValue<'a> {
    let function = module.add_function(nm, ty, None);
    let args = get_fn_args(function);
    let basic_block = context.append_basic_block(function, "entry");
    let builder = context.create_builder();
    builder.position_at_end(basic_block);

    for fp in fps {
        builder.build_call(fp, &args, "");
        builder.build_return(None);
        // println!("Built function {:?}", function)
    }

    function
}

fn get_fn_args<'a>(function: FunctionValue<'a>) -> Vec<BasicValueEnum<'a>> {
    (0 .. function.count_params()).map(|i| function.get_nth_param(i).unwrap()).collect()
}

fn insert_call_at_head<'a>(context: &Context, f: FunctionValue<'a>, args: Vec<BasicValueEnum<'a>>, insertee: FunctionValue<'a>) {
    let bb = insertee.get_first_basic_block().expect("Unable to find function to insert function call into");
    let first_instruction = bb.get_first_instruction().expect("Unable to find where to insert function call into function");
    let builder = context.create_builder();
    builder.position_before(&first_instruction);
    builder.build_call(f, &args, "");
}

////////////////////////////////////////////////////////////////
// Transformations associated with SeaHorn
////////////////////////////////////////////////////////////////

fn handle_main(module: &Module) {
    // Remove the main function rustc generates.
    if let Some(main) = module.get_function("main") {
        unsafe { main.delete(); }
    }

    // Change the linkage of mangled main function from internal to external.
    if let Some(main) = get_function(module, &Regex::new(r"4main17h[a-f0-9]{16}E$").unwrap()) {
        // main.set_linkage(Linkage::External);
        println!("MAIN: {}", main.get_name().to_str().unwrap());
    }
}

fn get_function<'ctx>(module: &'ctx Module, re: &Regex) -> Option<FunctionValue<'ctx>> {
    let mut op_fun = module.get_first_function();
    while let Some(fun) = op_fun {
        if re.is_match(fun.get_name().to_str()
                       .expect("ERROR: function name is not in valid UTF-8")) {
            return Some(fun);
        }
        op_fun = fun.get_next_function();
    }
    None
}

fn handle_panic(module: &Module) {
    // TODO: make "spanic" a CL arg.
    if let Some(spanic) = module.get_function("spanic") {
        if let Some(unwind) = module.get_function("rust_begin_unwind") {
            unwind.replace_all_uses_with(spanic);
        }
    }
}

fn replace_def_with_dec(module: &Module, re: &Regex) {
    if let Some(fun) = get_function(module, re) {
        for bb in fun.get_basic_blocks() {
            unsafe { bb.delete().unwrap(); }
        }
        fun.remove_personality_function();
        fun.set_linkage(Linkage::External);
    }
}

////////////////////////////////////////////////////////////////
// End
////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>//! MIPS

mod msa;
pub use self::msa::*;

#[cfg(test)]
use stdarch_test::assert_instr;

/// Generates the trap instruction `BREAK`
#[cfg_attr(test, assert_instr(break))]
#[inline]
pub unsafe fn break_() -> ! {
    crate::intrinsics::abort()
}
<commit_msg>Fix building libcore for the Sony PSP<commit_after>//! MIPS

// Building this module (even if unused) for non-fp64 targets such as the Sony
// PSP fails with an LLVM error. There doesn't seem to be a good way to detect
// fp64 support as it is sometimes implied by the target cpu, so
// `#[cfg(target_feature = "fp64")]` will unfortunately not work. This is a
// fairly conservative workaround that only disables MSA intrinsics for the PSP.
#[cfg(not(target_os = "psp"))] mod msa;
#[cfg(not(target_os = "psp"))] pub use self::msa::*;

#[cfg(test)]
use stdarch_test::assert_instr;

/// Generates the trap instruction `BREAK`
#[cfg_attr(test, assert_instr(break))]
#[inline]
pub unsafe fn break_() -> ! {
    crate::intrinsics::abort()
}
<|endoftext|>


<commit_before>//! This module contains functions for editing syntax trees. As the trees are
//! immutable, all function here return a fresh copy of the tree, instead of
//! doing an in-place modification.
use std::{iter, ops::RangeInclusive};

use arrayvec::ArrayVec;
use rustc_hash::FxHashMap;

use crate::{
    algo,
    ast::{
        self,
        make::{self, tokens},
        AstNode, TypeBoundsOwner,
    },
    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::{ATTR, COMMENT, WHITESPACE},
    SyntaxNode, SyntaxToken, T,
};

impl ast::BinExpr {
    #[must_use]
    pub fn replace_op(&self, op: SyntaxKind) -> Option<ast::BinExpr> {
        let op_node: SyntaxElement = self.op_details()?.0.into();
        let to_insert: Option<SyntaxElement> = Some(tokens::op(op).into());
        let replace_range = RangeInclusive::new(op_node.clone(), op_node);
        Some(replace_children(self, replace_range, to_insert.into_iter()))
    }
}

impl ast::FnDef {
    #[must_use]
    pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
            old_body.syntax().clone().into()
        } else if let Some(semi) = self.semicolon_token() {
            to_insert.push(make::tokens::single_space().into());
            semi.into()
        } else {
            to_insert.push(make::tokens::single_space().into());
            to_insert.push(body.syntax().clone().into());
            return insert_children(self, InsertPosition::Last, to_insert.into_iter());
        };
        to_insert.push(body.syntax().clone().into());
        let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi);
        replace_children(self, replace_range, to_insert.into_iter())
    }
}

impl ast::ItemList {
    #[must_use]
    pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList {
        let mut res = self.clone();
        if !self.syntax().text().contains_char('\n') {
            res = res.make_multiline();
        }
        items.for_each(|it| res = res.append_item(it));
        res
    }

    #[must_use]
    pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList {
        let (indent, position) = match self.impl_items().last() {
            Some(it) => (
                leading_indent(it.syntax()).unwrap_or_default().to_string(),
                InsertPosition::After(it.syntax().clone().into()),
            ),
            None => match self.l_curly() {
                Some(it) => (
                    "    ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
                    InsertPosition::After(it),
                ),
                None => return self.clone(),
            },
        };
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert: ArrayVec<[SyntaxElement; 2]> =
            [ws.ws().into(), item.syntax().clone().into()].into();
        insert_children(self, position, to_insert.into_iter())
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }

    fn make_multiline(&self) -> ast::ItemList {
        let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
            Some(it) => it,
            None => return self.clone(),
        };
        let sibling = match l_curly.next_sibling_or_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let existing_ws = match sibling.as_token() {
            None => None,
            Some(tok) if tok.kind() != WHITESPACE => None,
            Some(ws) => {
                if ws.text().contains('\n') {
                    return self.clone();
                }
                Some(ws.clone())
            }
        };

        let indent = leading_indent(self.syntax()).unwrap_or("".into());
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert = iter::once(ws.ws().into());
        match existing_ws {
            None => insert_children(self, InsertPosition::After(l_curly), to_insert),
            Some(ws) => {
                replace_children(self, RangeInclusive::new(ws.clone().into(), ws.into()), to_insert)
            }
        }
    }
}

impl ast::RecordFieldList {
    #[must_use]
    pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList {
        self.insert_field(InsertPosition::Last, field)
    }

    #[must_use]
    pub fn insert_field(
        &self,
        position: InsertPosition<&'_ ast::RecordField>,
        field: &ast::RecordField,
    ) -> ast::RecordFieldList {
        let is_multiline = self.syntax().text().contains_char('\n');
        let ws;
        let space = if is_multiline {
            ws = tokens::WsBuilder::new(&format!(
                "\n{}    ",
                leading_indent(self.syntax()).unwrap_or("".into())
            ));
            ws.ws()
        } else {
            tokens::single_space()
        };

        let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
        to_insert.push(space.into());
        to_insert.push(field.syntax().clone().into());
        to_insert.push(tokens::comma().into());

        macro_rules! after_l_curly {
            () => {{
                let anchor = match self.l_curly() {
                    Some(it) => it,
                    None => return self.clone(),
                };
                InsertPosition::After(anchor)
            }};
        }

        macro_rules! after_field {
            ($anchor:expr) => {
                if let Some(comma) = $anchor
                    .syntax()
                    .siblings_with_tokens(Direction::Next)
                    .find(|it| it.kind() == T![,])
                {
                    InsertPosition::After(comma)
                } else {
                    to_insert.insert(0, tokens::comma().into());
                    InsertPosition::After($anchor.syntax().clone().into())
                }
            };
        };

        let position = match position {
            InsertPosition::First => after_l_curly!(),
            InsertPosition::Last => {
                if !is_multiline {
                    // don't insert comma before curly
                    to_insert.pop();
                }
                match self.fields().last() {
                    Some(it) => after_field!(it),
                    None => after_l_curly!(),
                }
            }
            InsertPosition::Before(anchor) => {
                InsertPosition::Before(anchor.syntax().clone().into())
            }
            InsertPosition::After(anchor) => after_field!(anchor),
        };

        insert_children(self, position, to_insert.iter().cloned())
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }
}

impl ast::TypeParam {
    #[must_use]
    pub fn remove_bounds(&self) -> ast::TypeParam {
        let colon = match self.colon_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let end = match self.type_bound_list() {
            Some(it) => it.syntax().clone().into(),
            None => colon.clone().into(),
        };
        replace_children(self, RangeInclusive::new(colon.into(), end), iter::empty())
    }
}

#[must_use]
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
    N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
}

fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode {
    while let Some(start) =
        node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT)
    {
        let end = match &start.next_sibling_or_token() {
            Some(el) if el.kind() == WHITESPACE => el.clone(),
            Some(_) | None => start.clone(),
        };
        node = algo::replace_children(&node, RangeInclusive::new(start, end), &mut iter::empty());
    }
    node
}

#[must_use]
pub fn replace_descendants<N: AstNode, D: AstNode>(
    parent: &N,
    replacement_map: impl Iterator<Item = (D, D)>,
) -> N {
    let map = replacement_map
        .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into()))
        .collect::<FxHashMap<_, _>>();
    let new_syntax = algo::replace_descendants(parent.syntax(), &map);
    N::cast(new_syntax).unwrap()
}

#[derive(Debug, Clone, Copy)]
pub struct IndentLevel(pub u8);

impl From<u8> for IndentLevel {
    fn from(level: u8) -> IndentLevel {
        IndentLevel(level)
    }
}

impl IndentLevel {
    pub fn from_node(node: &SyntaxNode) -> IndentLevel {
        let first_token = match node.first_token() {
            Some(it) => it,
            None => return IndentLevel(0),
        };
        for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) {
            let text = ws.syntax().text();
            if let Some(pos) = text.rfind('\n') {
                let level = text[pos + 1..].chars().count() / 4;
                return IndentLevel(level as u8);
            }
        }
        IndentLevel(0)
    }

    pub fn increase_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._increase_indent(node.syntax().clone())).unwrap()
    }

    fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(&format!(
                        "{}{:width$}",
                        ws.syntax().text(),
                        "",
                        width = self.0 as usize * 4
                    ))
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, &replacements)
    }

    pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
    }

    fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(
                        &ws.syntax()
                            .text()
                            .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
                    )
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, &replacements)
    }
}

// FIXME: replace usages with IndentLevel above
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
    for token in prev_tokens(node.first_token()?) {
        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
            let ws_text = ws.text();
            if let Some(pos) = ws_text.rfind('\n') {
                return Some(ws_text[pos + 1..].into());
            }
        }
        if token.text().contains('\n') {
            break;
        }
    }
    None
}

fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
    iter::successors(Some(token), |token| token.prev_token())
}

#[must_use]
fn insert_children<N: AstNode>(
    parent: &N,
    position: InsertPosition<SyntaxElement>,
    mut to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::insert_children(parent.syntax(), position, &mut to_insert);
    N::cast(new_syntax).unwrap()
}

#[must_use]
fn replace_children<N: AstNode>(
    parent: &N,
    to_replace: RangeInclusive<SyntaxElement>,
    mut to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert);
    N::cast(new_syntax).unwrap()
}

#[test]
fn test_increase_indent() {
    let arm_list = {
        let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit());
        make::match_arm_list(vec![arm.clone(), arm])
    };
    assert_eq!(
        arm_list.syntax().to_string(),
        "{
    _ => (),
    _ => (),
}"
    );
    let indented = IndentLevel(2).increase_indent(arm_list);
    assert_eq!(
        indented.syntax().to_string(),
        "{
            _ => (),
            _ => (),
        }"
    );
}
<commit_msg>Use unwrap_or_default<commit_after>//! This module contains functions for editing syntax trees. As the trees are
//! immutable, all function here return a fresh copy of the tree, instead of
//! doing an in-place modification.
use std::{iter, ops::RangeInclusive};

use arrayvec::ArrayVec;
use rustc_hash::FxHashMap;

use crate::{
    algo,
    ast::{
        self,
        make::{self, tokens},
        AstNode, TypeBoundsOwner,
    },
    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::{ATTR, COMMENT, WHITESPACE},
    SyntaxNode, SyntaxToken, T,
};

impl ast::BinExpr {
    #[must_use]
    pub fn replace_op(&self, op: SyntaxKind) -> Option<ast::BinExpr> {
        let op_node: SyntaxElement = self.op_details()?.0.into();
        let to_insert: Option<SyntaxElement> = Some(tokens::op(op).into());
        let replace_range = RangeInclusive::new(op_node.clone(), op_node);
        Some(replace_children(self, replace_range, to_insert.into_iter()))
    }
}

impl ast::FnDef {
    #[must_use]
    pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
            old_body.syntax().clone().into()
        } else if let Some(semi) = self.semicolon_token() {
            to_insert.push(make::tokens::single_space().into());
            semi.into()
        } else {
            to_insert.push(make::tokens::single_space().into());
            to_insert.push(body.syntax().clone().into());
            return insert_children(self, InsertPosition::Last, to_insert.into_iter());
        };
        to_insert.push(body.syntax().clone().into());
        let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi);
        replace_children(self, replace_range, to_insert.into_iter())
    }
}

impl ast::ItemList {
    #[must_use]
    pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList {
        let mut res = self.clone();
        if !self.syntax().text().contains_char('\n') {
            res = res.make_multiline();
        }
        items.for_each(|it| res = res.append_item(it));
        res
    }

    #[must_use]
    pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList {
        let (indent, position) = match self.impl_items().last() {
            Some(it) => (
                leading_indent(it.syntax()).unwrap_or_default().to_string(),
                InsertPosition::After(it.syntax().clone().into()),
            ),
            None => match self.l_curly() {
                Some(it) => (
                    "    ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
                    InsertPosition::After(it),
                ),
                None => return self.clone(),
            },
        };
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert: ArrayVec<[SyntaxElement; 2]> =
            [ws.ws().into(), item.syntax().clone().into()].into();
        insert_children(self, position, to_insert.into_iter())
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }

    fn make_multiline(&self) -> ast::ItemList {
        let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
            Some(it) => it,
            None => return self.clone(),
        };
        let sibling = match l_curly.next_sibling_or_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let existing_ws = match sibling.as_token() {
            None => None,
            Some(tok) if tok.kind() != WHITESPACE => None,
            Some(ws) => {
                if ws.text().contains('\n') {
                    return self.clone();
                }
                Some(ws.clone())
            }
        };

        let indent = leading_indent(self.syntax()).unwrap_or_default();
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert = iter::once(ws.ws().into());
        match existing_ws {
            None => insert_children(self, InsertPosition::After(l_curly), to_insert),
            Some(ws) => {
                replace_children(self, RangeInclusive::new(ws.clone().into(), ws.into()), to_insert)
            }
        }
    }
}

impl ast::RecordFieldList {
    #[must_use]
    pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList {
        self.insert_field(InsertPosition::Last, field)
    }

    #[must_use]
    pub fn insert_field(
        &self,
        position: InsertPosition<&'_ ast::RecordField>,
        field: &ast::RecordField,
    ) -> ast::RecordFieldList {
        let is_multiline = self.syntax().text().contains_char('\n');
        let ws;
        let space = if is_multiline {
            ws = tokens::WsBuilder::new(&format!(
                "\n{}    ",
                leading_indent(self.syntax()).unwrap_or_default()
            ));
            ws.ws()
        } else {
            tokens::single_space()
        };

        let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
        to_insert.push(space.into());
        to_insert.push(field.syntax().clone().into());
        to_insert.push(tokens::comma().into());

        macro_rules! after_l_curly {
            () => {{
                let anchor = match self.l_curly() {
                    Some(it) => it,
                    None => return self.clone(),
                };
                InsertPosition::After(anchor)
            }};
        }

        macro_rules! after_field {
            ($anchor:expr) => {
                if let Some(comma) = $anchor
                    .syntax()
                    .siblings_with_tokens(Direction::Next)
                    .find(|it| it.kind() == T![,])
                {
                    InsertPosition::After(comma)
                } else {
                    to_insert.insert(0, tokens::comma().into());
                    InsertPosition::After($anchor.syntax().clone().into())
                }
            };
        };

        let position = match position {
            InsertPosition::First => after_l_curly!(),
            InsertPosition::Last => {
                if !is_multiline {
                    // don't insert comma before curly
                    to_insert.pop();
                }
                match self.fields().last() {
                    Some(it) => after_field!(it),
                    None => after_l_curly!(),
                }
            }
            InsertPosition::Before(anchor) => {
                InsertPosition::Before(anchor.syntax().clone().into())
            }
            InsertPosition::After(anchor) => after_field!(anchor),
        };

        insert_children(self, position, to_insert.iter().cloned())
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }
}

impl ast::TypeParam {
    #[must_use]
    pub fn remove_bounds(&self) -> ast::TypeParam {
        let colon = match self.colon_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let end = match self.type_bound_list() {
            Some(it) => it.syntax().clone().into(),
            None => colon.clone().into(),
        };
        replace_children(self, RangeInclusive::new(colon.into(), end), iter::empty())
    }
}

#[must_use]
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
    N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
}

fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode {
    while let Some(start) =
        node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT)
    {
        let end = match &start.next_sibling_or_token() {
            Some(el) if el.kind() == WHITESPACE => el.clone(),
            Some(_) | None => start.clone(),
        };
        node = algo::replace_children(&node, RangeInclusive::new(start, end), &mut iter::empty());
    }
    node
}

#[must_use]
pub fn replace_descendants<N: AstNode, D: AstNode>(
    parent: &N,
    replacement_map: impl Iterator<Item = (D, D)>,
) -> N {
    let map = replacement_map
        .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into()))
        .collect::<FxHashMap<_, _>>();
    let new_syntax = algo::replace_descendants(parent.syntax(), &map);
    N::cast(new_syntax).unwrap()
}

#[derive(Debug, Clone, Copy)]
pub struct IndentLevel(pub u8);

impl From<u8> for IndentLevel {
    fn from(level: u8) -> IndentLevel {
        IndentLevel(level)
    }
}

impl IndentLevel {
    pub fn from_node(node: &SyntaxNode) -> IndentLevel {
        let first_token = match node.first_token() {
            Some(it) => it,
            None => return IndentLevel(0),
        };
        for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) {
            let text = ws.syntax().text();
            if let Some(pos) = text.rfind('\n') {
                let level = text[pos + 1..].chars().count() / 4;
                return IndentLevel(level as u8);
            }
        }
        IndentLevel(0)
    }

    pub fn increase_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._increase_indent(node.syntax().clone())).unwrap()
    }

    fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(&format!(
                        "{}{:width$}",
                        ws.syntax().text(),
                        "",
                        width = self.0 as usize * 4
                    ))
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, &replacements)
    }

    pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
    }

    fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(
                        &ws.syntax()
                            .text()
                            .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
                    )
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, &replacements)
    }
}

// FIXME: replace usages with IndentLevel above
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
    for token in prev_tokens(node.first_token()?) {
        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
            let ws_text = ws.text();
            if let Some(pos) = ws_text.rfind('\n') {
                return Some(ws_text[pos + 1..].into());
            }
        }
        if token.text().contains('\n') {
            break;
        }
    }
    None
}

fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
    iter::successors(Some(token), |token| token.prev_token())
}

#[must_use]
fn insert_children<N: AstNode>(
    parent: &N,
    position: InsertPosition<SyntaxElement>,
    mut to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::insert_children(parent.syntax(), position, &mut to_insert);
    N::cast(new_syntax).unwrap()
}

#[must_use]
fn replace_children<N: AstNode>(
    parent: &N,
    to_replace: RangeInclusive<SyntaxElement>,
    mut to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert);
    N::cast(new_syntax).unwrap()
}

#[test]
fn test_increase_indent() {
    let arm_list = {
        let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit());
        make::match_arm_list(vec![arm.clone(), arm])
    };
    assert_eq!(
        arm_list.syntax().to_string(),
        "{
    _ => (),
    _ => (),
}"
    );
    let indented = IndentLevel(2).increase_indent(arm_list);
    assert_eq!(
        indented.syntax().to_string(),
        "{
            _ => (),
            _ => (),
        }"
    );
}
<|endoftext|>


<commit_before>#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
#![feature(or_patterns)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_span)]
#![feature(try_blocks)]

#[macro_use]
extern crate rustc_macros;

extern crate proc_macro as pm;

mod placeholders;
mod proc_macro_server;

pub use mbe::macro_rules::compile_declarative_macro;
crate use rustc_span::hygiene;
pub mod base;
pub mod build;
#[macro_use]
pub mod config;
pub mod expand;
pub mod module;
pub mod proc_macro;

crate mod mbe;

// HACK(Centril, #64197): These shouldn't really be here.
// Rather, they should be with their respective modules which are defined in other crates.
// However, since for now constructing a `ParseSess` sorta requires `config` from this crate,
// these tests will need to live here in the iterim.

#[cfg(test)]
mod tests;
#[cfg(test)]
mod parse {
    mod tests;
}
#[cfg(test)]
mod tokenstream {
    mod tests;
}
#[cfg(test)]
mod mut_visit {
    mod tests;
}
<commit_msg>Remove redundant bool_to_option feature gate<commit_after>#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
#![feature(or_patterns)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_span)]
#![feature(try_blocks)]

#[macro_use]
extern crate rustc_macros;

extern crate proc_macro as pm;

mod placeholders;
mod proc_macro_server;

pub use mbe::macro_rules::compile_declarative_macro;
crate use rustc_span::hygiene;
pub mod base;
pub mod build;
#[macro_use]
pub mod config;
pub mod expand;
pub mod module;
pub mod proc_macro;

crate mod mbe;

// HACK(Centril, #64197): These shouldn't really be here.
// Rather, they should be with their respective modules which are defined in other crates.
// However, since for now constructing a `ParseSess` sorta requires `config` from this crate,
// these tests will need to live here in the iterim.

#[cfg(test)]
mod tests;
#[cfg(test)]
mod parse {
    mod tests;
}
#[cfg(test)]
mod tokenstream {
    mod tests;
}
#[cfg(test)]
mod mut_visit {
    mod tests;
}
<|endoftext|>