From 06d401948ba7451d8ed10b4a58e1e2d08c0094f7 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos <SeanPatrickSantos@gmail.com> Date: Thu, 15 Jan 2015 13:49:23 -0700 Subject: [PATCH 01/33] For issue 15149 test, don't execute from tmpfs, and wait to see if the child panics before passing. --- src/test/run-pass/issue-15149.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs index 57dc6fd75f0..59b1bb287fa 100644 --- a/src/test/run-pass/issue-15149.rs +++ b/src/test/run-pass/issue-15149.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io::{TempDir, Command, fs}; +use std::io::{Command, fs, USER_RWX}; use std::os; fn main() { @@ -22,19 +22,23 @@ fn main() { } fn test() { - // If we're the parent, copy our own binary to a tempr directory, and then - // make it executable. - let dir = TempDir::new("mytest").unwrap(); - let me = os::self_exe_name().unwrap(); - let dest = dir.path().join(format!("mytest{}", os::consts::EXE_SUFFIX)); - fs::copy(&me, &dest).unwrap(); + // If we're the parent, copy our own binary to a new directory. + let my_path = os::self_exe_name().unwrap(); + let my_dir = my_path.dir_path(); - // Append the temp directory to our own PATH. + let child_dir = Path::new(my_dir.join("issue-15149-child")); + drop(fs::mkdir(&child_dir, USER_RWX)); + + let child_path = child_dir.join(format!("mytest{}", + os::consts::EXE_SUFFIX)); + fs::copy(&my_path, &child_path).unwrap(); + + // Append the new directory to our own PATH. let mut path = os::split_paths(os::getenv("PATH").unwrap_or(String::new())); - path.push(dir.path().clone()); + path.push(child_dir.clone()); let path = os::join_paths(path.as_slice()).unwrap(); - Command::new("mytest").env("PATH", path.as_slice()) - .arg("child") - .spawn().unwrap(); + assert!(Command::new("mytest").env("PATH", path.as_slice()) + .arg("child") + .status().unwrap().success()); } From f39297f9918625ff95fdd0b771902037adf069f5 Mon Sep 17 00:00:00 2001 From: Brian Leibig <brian@brianleibig.com> Date: Tue, 20 Jan 2015 18:46:44 -0800 Subject: [PATCH 02/33] Add a LALR grammar for Rust with testing support --- configure | 2 + mk/grammar.mk | 48 + src/grammar/lexer.l | 342 ++++++ src/grammar/parser-lalr-main.c | 203 ++++ src/grammar/parser-lalr.y | 1912 ++++++++++++++++++++++++++++++++ src/grammar/testparser.py | 65 ++ src/grammar/tokens.h | 91 ++ 7 files changed, 2663 insertions(+) create mode 100644 src/grammar/lexer.l create mode 100644 src/grammar/parser-lalr-main.c create mode 100644 src/grammar/parser-lalr.y create mode 100755 src/grammar/testparser.py create mode 100644 src/grammar/tokens.h diff --git a/configure b/configure index 712db849039..fa6a4579f19 100755 --- a/configure +++ b/configure @@ -647,6 +647,8 @@ probe CFG_ISCC iscc probe CFG_JAVAC javac probe CFG_ANTLR4 antlr4 probe CFG_GRUN grun +probe CFG_FLEX flex +probe CFG_BISON bison probe CFG_PANDOC pandoc probe CFG_PDFLATEX pdflatex probe CFG_XELATEX xelatex diff --git a/mk/grammar.mk b/mk/grammar.mk index 93e40302f51..d9c66e282bc 100644 --- a/mk/grammar.mk +++ b/mk/grammar.mk @@ -14,6 +14,11 @@ B = $(CFG_BUILD_DIR)/$(CFG_BUILD)/stage2/ L = $(B)lib/rustlib/$(CFG_BUILD)/lib LD = $(CFG_BUILD)/stage2/lib/rustlib/$(CFG_BUILD)/lib/ RUSTC = $(STAGE2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) +ifeq ($(CFG_OSTYPE),apple-darwin) + FLEX_LDFLAGS=-ll +else + FLEX_LDFLAGS=-lfl +endif # Run the reference lexer against libsyntax and compare the tokens and spans. # If "// ignore-lexer-test" is present in the file, it will be ignored. @@ -67,3 +72,46 @@ $(info cfg: javac not available, skipping lexer test...) check-lexer: endif + +$(BG)lex.yy.c: $(SG)lexer.l $(BG) + @$(call E, flex: $@) + $(Q)$(CFG_FLEX) -o $@ $< + +$(BG)lexer-lalr.o: $(BG)lex.yy.c $(BG)parser-lalr.tab.h + @$(call E, cc: $@) + $(Q)$(CFG_CC) -include $(BG)parser-lalr.tab.h -c -o $@ $< + +$(BG)parser-lalr.tab.c $(BG)parser-lalr.tab.h: $(SG)parser-lalr.y + @$(call E, bison: $@) + $(Q)$(CFG_BISON) $< --output=$(BG)parser-lalr.tab.c --defines=$(BG)parser-lalr.tab.h \ + --name-prefix=rs --warnings=error=all + +$(BG)parser-lalr.o: $(BG)parser-lalr.tab.c + @$(call E, cc: $@) + $(Q)$(CFG_CC) -c -o $@ $< + +$(BG)parser-lalr-main.o: $(SG)parser-lalr-main.c + @$(call E, cc: $@) + $(Q)$(CFG_CC) -std=c99 -c -o $@ $< + +$(BG)parser-lalr: $(BG)parser-lalr.o $(BG)parser-lalr-main.o $(BG)lexer-lalr.o + @$(call E, cc: $@) + $(Q)$(CFG_CC) -o $@ $^ $(FLEX_LDFLAGS) + + +ifdef CFG_FLEX +ifdef CFG_BISON +check-grammar: $(BG) $(BG)parser-lalr + $(info Verifying grammar ...) + $(SG)testparser.py -p $(BG)parser-lalr -s $(S)src + +else +$(info cfg: bison not available, skipping parser test...) +check-grammar: + +endif +else +$(info cfg: flex not available, skipping parser test...) +check-grammar: + +endif diff --git a/src/grammar/lexer.l b/src/grammar/lexer.l new file mode 100644 index 00000000000..58202a08c37 --- /dev/null +++ b/src/grammar/lexer.l @@ -0,0 +1,342 @@ +%{ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#include <stdio.h> +#include <ctype.h> + +static int num_hashes; +static int end_hashes; +static int saw_non_hash; + +%} + +%option stack +%option yylineno + +%x str +%x rawstr +%x rawstr_esc_begin +%x rawstr_esc_body +%x rawstr_esc_end +%x byte +%x bytestr +%x rawbytestr +%x rawbytestr_nohash +%x pound +%x shebang_or_attr +%x ltorchar +%x linecomment +%x doc_line +%x blockcomment +%x doc_block +%x suffix + +ident [a-zA-Z\x80-\xff_][a-zA-Z0-9\x80-\xff_]* + +%% + +<suffix>{ident} { BEGIN(INITIAL); } +<suffix>(.|\n) { yyless(0); BEGIN(INITIAL); } + +[ \n\t\r] { } + +\xef\xbb\xbf { + // UTF-8 byte order mark (BOM), ignore if in line 1, error otherwise + if (yyget_lineno() != 1) { + return -1; + } +} + +\/\/(\/|\!) { BEGIN(doc_line); yymore(); } +<doc_line>\n { BEGIN(INITIAL); + yyleng--; + yytext[yyleng] = 0; + return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT); + } +<doc_line>[^\n]* { yymore(); } + +\/\/|\/\/\/\/ { BEGIN(linecomment); } +<linecomment>\n { BEGIN(INITIAL); } +<linecomment>[^\n]* { } + +\/\*(\*|\!)[^*] { yy_push_state(INITIAL); yy_push_state(doc_block); yymore(); } +<doc_block>\/\* { yy_push_state(doc_block); yymore(); } +<doc_block>\*\/ { + yy_pop_state(); + if (yy_top_state() == doc_block) { + yymore(); + } else { + return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT); + } +} +<doc_block>(.|\n) { yymore(); } + +\/\* { yy_push_state(blockcomment); } +<blockcomment>\/\* { yy_push_state(blockcomment); } +<blockcomment>\*\/ { yy_pop_state(); } +<blockcomment>(.|\n) { } + +_ { return UNDERSCORE; } +as { return AS; } +box { return BOX; } +break { return BREAK; } +const { return CONST; } +continue { return CONTINUE; } +crate { return CRATE; } +else { return ELSE; } +enum { return ENUM; } +extern { return EXTERN; } +false { return FALSE; } +fn { return FN; } +for { return FOR; } +if { return IF; } +impl { return IMPL; } +in { return IN; } +let { return LET; } +loop { return LOOP; } +match { return MATCH; } +mod { return MOD; } +move { return MOVE; } +mut { return MUT; } +priv { return PRIV; } +proc { return PROC; } +pub { return PUB; } +ref { return REF; } +return { return RETURN; } +self { return SELF; } +static { return STATIC; } +struct { return STRUCT; } +trait { return TRAIT; } +true { return TRUE; } +type { return TYPE; } +typeof { return TYPEOF; } +unsafe { return UNSAFE; } +use { return USE; } +where { return WHERE; } +while { return WHILE; } + +{ident} { return IDENT; } + +0x[0-9a-fA-F_]+ { BEGIN(suffix); return LIT_INTEGER; } +0o[0-8_]+ { BEGIN(suffix); return LIT_INTEGER; } +0b[01_]+ { BEGIN(suffix); return LIT_INTEGER; } +[0-9][0-9_]* { BEGIN(suffix); return LIT_INTEGER; } +[0-9][0-9_]*\.(\.|[a-zA-Z]) { yyless(yyleng - 2); BEGIN(suffix); return LIT_INTEGER; } + +[0-9][0-9_]*\.[0-9_]*([eE][-\+]?[0-9_]+)? { BEGIN(suffix); return LIT_FLOAT; } +[0-9][0-9_]*(\.[0-9_]*)?[eE][-\+]?[0-9_]+ { BEGIN(suffix); return LIT_FLOAT; } + +; { return ';'; } +, { return ','; } +\.\.\. { return DOTDOTDOT; } +\.\. { return DOTDOT; } +\. { return '.'; } +\( { return '('; } +\) { return ')'; } +\{ { return '{'; } +\} { return '}'; } +\[ { return '['; } +\] { return ']'; } +@ { return '@'; } +# { BEGIN(pound); yymore(); } +<pound>\! { BEGIN(shebang_or_attr); yymore(); } +<shebang_or_attr>\[ { + BEGIN(INITIAL); + yyless(2); + return SHEBANG; +} +<shebang_or_attr>[^\[\n]*\n { + // Since the \n was eaten as part of the token, yylineno will have + // been incremented to the value 2 if the shebang was on the first + // line. This yyless undoes that, setting yylineno back to 1. + yyless(yyleng - 1); + if (yyget_lineno() == 1) { + BEGIN(INITIAL); + return SHEBANG_LINE; + } else { + BEGIN(INITIAL); + yyless(2); + return SHEBANG; + } +} +<pound>. { BEGIN(INITIAL); yyless(1); return '#'; } + +\~ { return '~'; } +:: { return MOD_SEP; } +: { return ':'; } +\$ { return '$'; } +\? { return '?'; } + +== { return EQEQ; } +=> { return FAT_ARROW; } += { return '='; } +\!= { return NE; } +\! { return '!'; } +\<= { return LE; } +\<\< { return SHL; } +\<\<= { return SHLEQ; } +\< { return '<'; } +\>= { return GE; } +\>\> { return SHR; } +\>\>= { return SHREQ; } +\> { return '>'; } + +\x27 { BEGIN(ltorchar); yymore(); } +<ltorchar>static { BEGIN(INITIAL); return STATIC_LIFETIME; } +<ltorchar>{ident} { BEGIN(INITIAL); return LIFETIME; } +<ltorchar>\\[nrt\\\x27\x220]\x27 { BEGIN(suffix); return LIT_CHAR; } +<ltorchar>\\x[0-9a-fA-F]{2}\x27 { BEGIN(suffix); return LIT_CHAR; } +<ltorchar>\\u\{[0-9a-fA-F]?{6}\}\x27 { BEGIN(suffix); return LIT_CHAR; } +<ltorchar>.\x27 { BEGIN(suffix); return LIT_CHAR; } +<ltorchar>[\x80-\xff]{2,4}\x27 { BEGIN(suffix); return LIT_CHAR; } +<ltorchar><<EOF>> { BEGIN(INITIAL); return -1; } + +b\x22 { BEGIN(bytestr); yymore(); } +<bytestr>\x22 { BEGIN(suffix); return LIT_BINARY; } + +<bytestr><<EOF>> { return -1; } +<bytestr>\\[n\nrt\\\x27\x220] { yymore(); } +<bytestr>\\x[0-9a-fA-F]{2} { yymore(); } +<bytestr>\\u\{[0-9a-fA-F]?{6}\} { yymore(); } +<bytestr>\\[^n\nrt\\\x27\x220] { return -1; } +<bytestr>(.|\n) { yymore(); } + +br\x22 { BEGIN(rawbytestr_nohash); yymore(); } +<rawbytestr_nohash>\x22 { BEGIN(suffix); return LIT_BINARY_RAW; } +<rawbytestr_nohash>(.|\n) { yymore(); } +<rawbytestr_nohash><<EOF>> { return -1; } + +br/# { + BEGIN(rawbytestr); + yymore(); + num_hashes = 0; + saw_non_hash = 0; + end_hashes = 0; +} +<rawbytestr># { + if (!saw_non_hash) { + num_hashes++; + } else if (end_hashes != 0) { + end_hashes++; + if (end_hashes == num_hashes) { + BEGIN(INITIAL); + return LIT_BINARY_RAW; + } + } + yymore(); +} +<rawbytestr>\x22# { + end_hashes = 1; + if (end_hashes == num_hashes) { + BEGIN(INITIAL); + return LIT_BINARY_RAW; + } + yymore(); +} +<rawbytestr>(.|\n) { + if (!saw_non_hash) { + saw_non_hash = 1; + } + if (end_hashes != 0) { + end_hashes = 0; + } + yymore(); +} +<rawbytestr><<EOF>> { return -1; } + +b\x27 { BEGIN(byte); yymore(); } +<byte>\\[nrt\\\x27\x220]\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<byte>\\x[0-9a-fA-F]{2}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<byte>\\u[0-9a-fA-F]{4}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<byte>\\U[0-9a-fA-F]{8}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<byte>.\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<byte><<EOF>> { BEGIN(INITIAL); return -1; } + +r\x22 { BEGIN(rawstr); yymore(); } +<rawstr>\x22 { BEGIN(suffix); return LIT_STR_RAW; } +<rawstr>(.|\n) { yymore(); } +<rawstr><<EOF>> { return -1; } + +r/# { + BEGIN(rawstr_esc_begin); + yymore(); + num_hashes = 0; + saw_non_hash = 0; + end_hashes = 0; +} + +<rawstr_esc_begin># { + num_hashes++; + yymore(); +} +<rawstr_esc_begin>\x22 { + BEGIN(rawstr_esc_body); + yymore(); +} +<rawstr_esc_begin>(.|\n) { return -1; } + +<rawstr_esc_body>\x22/# { + BEGIN(rawstr_esc_end); + yymore(); + } +<rawstr_esc_body>(.|\n) { + yymore(); + } + +<rawstr_esc_end># { + end_hashes++; + if (end_hashes == num_hashes) { + BEGIN(INITIAL); + return LIT_STR_RAW; + } + yymore(); + } +<rawstr_esc_end>[^#] { + end_hashes = 0; + BEGIN(rawstr_esc_body); + yymore(); + } + +<rawstr_esc_begin,rawstr_esc_body,rawstr_esc_end><<EOF>> { return -1; } + +\x22 { BEGIN(str); yymore(); } +<str>\x22 { BEGIN(suffix); return LIT_STR; } + +<str><<EOF>> { return -1; } +<str>\\[n\nrt\\\x27\x220] { yymore(); } +<str>\\x[0-9a-fA-F]{2} { yymore(); } +<str>\\u\{[0-9a-fA-F]?{6}\} { yymore(); } +<str>\\[^n\nrt\\\x27\x220] { return -1; } +<str>(.|\n) { yymore(); } + +-\> { return RARROW; } +- { return '-'; } +-= { return MINUSEQ; } +&& { return ANDAND; } +& { return '&'; } +&= { return ANDEQ; } +\|\| { return OROR; } +\| { return '|'; } +\|= { return OREQ; } +\+ { return '+'; } +\+= { return PLUSEQ; } +\* { return '*'; } +\*= { return STAREQ; } +\/ { return '/'; } +\/= { return SLASHEQ; } +\^ { return '^'; } +\^= { return CARETEQ; } +% { return '%'; } +%= { return PERCENTEQ; } + +<<EOF>> { return 0; } + +%% diff --git a/src/grammar/parser-lalr-main.c b/src/grammar/parser-lalr-main.c new file mode 100644 index 00000000000..db88a1f2999 --- /dev/null +++ b/src/grammar/parser-lalr-main.c @@ -0,0 +1,203 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +extern int yylex(); +extern int rsparse(); + +#define PUSHBACK_LEN 4 + +static char pushback[PUSHBACK_LEN]; +static int verbose; + +void print(const char* format, ...) { + va_list args; + va_start(args, format); + if (verbose) { + vprintf(format, args); + } + va_end(args); +} + +// If there is a non-null char at the head of the pushback queue, +// dequeue it and shift the rest of the queue forwards. Otherwise, +// return the token from calling yylex. +int rslex() { + if (pushback[0] == '\0') { + return yylex(); + } else { + char c = pushback[0]; + memmove(pushback, pushback + 1, PUSHBACK_LEN - 1); + pushback[PUSHBACK_LEN - 1] = '\0'; + return c; + } +} + +// Note: this does nothing if the pushback queue is full. As long as +// there aren't more than PUSHBACK_LEN consecutive calls to push_back +// in an action, this shouldn't be a problem. +void push_back(char c) { + for (int i = 0; i < PUSHBACK_LEN; ++i) { + if (pushback[i] == '\0') { + pushback[i] = c; + break; + } + } +} + +extern int rsdebug; + +struct node { + struct node *next; + struct node *prev; + int own_string; + char const *name; + int n_elems; + struct node *elems[]; +}; + +struct node *nodes = NULL; +int n_nodes; + +struct node *mk_node(char const *name, int n, ...) { + va_list ap; + int i = 0; + unsigned sz = sizeof(struct node) + (n * sizeof(struct node *)); + struct node *nn, *nd = (struct node *)malloc(sz); + + print("# New %d-ary node: %s = %p\n", n, name, nd); + + nd->own_string = 0; + nd->prev = NULL; + nd->next = nodes; + if (nodes) { + nodes->prev = nd; + } + nodes = nd; + + nd->name = name; + nd->n_elems = n; + + va_start(ap, n); + while (i < n) { + nn = va_arg(ap, struct node *); + print("# arg[%d]: %p\n", i, nn); + print("# (%s ...)\n", nn->name); + nd->elems[i++] = nn; + } + va_end(ap); + n_nodes++; + return nd; +} + +struct node *mk_atom(char *name) { + struct node *nd = mk_node((char const *)strdup(name), 0); + nd->own_string = 1; + return nd; +} + +struct node *mk_none() { + return mk_atom("<none>"); +} + +struct node *ext_node(struct node *nd, int n, ...) { + va_list ap; + int i = 0, c = nd->n_elems + n; + unsigned sz = sizeof(struct node) + (c * sizeof(struct node *)); + struct node *nn; + + print("# Extending %d-ary node by %d nodes: %s = %p", + nd->n_elems, c, nd->name, nd); + + if (nd->next) { + nd->next->prev = nd->prev; + } + if (nd->prev) { + nd->prev->next = nd->next; + } + nd = realloc(nd, sz); + nd->prev = NULL; + nd->next = nodes; + nodes->prev = nd; + nodes = nd; + + print(" ==> %p\n", nd); + + va_start(ap, n); + while (i < n) { + nn = va_arg(ap, struct node *); + print("# arg[%d]: %p\n", i, nn); + print("# (%s ...)\n", nn->name); + nd->elems[nd->n_elems++] = nn; + ++i; + } + va_end(ap); + return nd; +} + +int const indent_step = 4; + +void print_indent(int depth) { + while (depth) { + if (depth-- % indent_step == 0) { + print("|"); + } else { + print(" "); + } + } +} + +void print_node(struct node *n, int depth) { + int i = 0; + print_indent(depth); + if (n->n_elems == 0) { + print("%s\n", n->name); + } else { + print("(%s\n", n->name); + for (i = 0; i < n->n_elems; ++i) { + print_node(n->elems[i], depth + indent_step); + } + print_indent(depth); + print(")\n"); + } +} + +int main(int argc, char **argv) { + if (argc == 2 && strcmp(argv[1], "-v") == 0) { + verbose = 1; + } else { + verbose = 0; + } + int ret = 0; + struct node *tmp; + memset(pushback, '\0', PUSHBACK_LEN); + ret = rsparse(); + print("--- PARSE COMPLETE: ret:%d, n_nodes:%d ---\n", ret, n_nodes); + if (nodes) { + print_node(nodes, 0); + } + while (nodes) { + tmp = nodes; + nodes = tmp->next; + if (tmp->own_string) { + free((void*)tmp->name); + } + free(tmp); + } + return ret; +} + +void rserror(char const *s) { + fprintf(stderr, "%s\n", s); +} diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y new file mode 100644 index 00000000000..24185ed65d5 --- /dev/null +++ b/src/grammar/parser-lalr.y @@ -0,0 +1,1912 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +%{ +#define YYERROR_VERBOSE +#define YYSTYPE struct node * +struct node; +extern int yylex(); +extern void yyerror(char const *s); +extern struct node *mk_node(char const *name, int n, ...); +extern struct node *mk_atom(char *text); +extern struct node *mk_none(); +extern struct node *ext_node(struct node *nd, int n, ...); +extern void push_back(char c); +extern char *yytext; +%} +%debug + +%token SHL +%token SHR +%token LE +%token EQEQ +%token NE +%token GE +%token ANDAND +%token OROR +%token SHLEQ +%token SHREQ +%token MINUSEQ +%token ANDEQ +%token OREQ +%token PLUSEQ +%token STAREQ +%token SLASHEQ +%token CARETEQ +%token PERCENTEQ +%token DOTDOT +%token DOTDOTDOT +%token MOD_SEP +%token RARROW +%token FAT_ARROW +%token LIT_BYTE +%token LIT_CHAR +%token LIT_INTEGER +%token LIT_FLOAT +%token LIT_STR +%token LIT_STR_RAW +%token LIT_BINARY +%token LIT_BINARY_RAW +%token IDENT +%token UNDERSCORE +%token LIFETIME + +// keywords +%token SELF +%token STATIC +%token AS +%token BREAK +%token CRATE +%token ELSE +%token ENUM +%token EXTERN +%token FALSE +%token FN +%token FOR +%token IF +%token IMPL +%token IN +%token LET +%token LOOP +%token MATCH +%token MOD +%token MOVE +%token MUT +%token PRIV +%token PUB +%token REF +%token RETURN +%token STRUCT +%token TRUE +%token TRAIT +%token TYPE +%token UNSAFE +%token USE +%token WHILE +%token CONTINUE +%token PROC +%token BOX +%token CONST +%token WHERE +%token TYPEOF +%token INNER_DOC_COMMENT +%token OUTER_DOC_COMMENT + +%token SHEBANG +%token SHEBANG_LINE +%token STATIC_LIFETIME + + /* + Quoting from the Bison manual: + + "Finally, the resolution of conflicts works by comparing the precedence + of the rule being considered with that of the lookahead token. If the + token's precedence is higher, the choice is to shift. If the rule's + precedence is higher, the choice is to reduce. If they have equal + precedence, the choice is made based on the associativity of that + precedence level. The verbose output file made by ‘-v’ (see Invoking + Bison) says how each conflict was resolved" + */ + +// We expect no shift/reduce or reduce/reduce conflicts in this grammar; +// all potential ambiguities are scrutinized and eliminated manually. +%expect 0 + +// fake-precedence symbol to cause '|' bars in lambda context to parse +// at low precedence, permit things like |x| foo = bar, where '=' is +// otherwise lower-precedence than '|'. Also used for proc() to cause +// things like proc() a + b to parse as proc() { a + b }. +%precedence LAMBDA + +%precedence SELF + +// MUT should be lower precedence than IDENT so that in the pat rule, +// "& MUT pat" has higher precedence than "binding_mode ident [@ pat]" +%precedence MUT + +// IDENT needs to be lower than '{' so that 'foo {' is shifted when +// trying to decide if we've got a struct-construction expr (esp. in +// contexts like 'if foo { .') +// +// IDENT also needs to be lower precedence than '<' so that '<' in +// 'foo:bar . <' is shifted (in a trait reference occurring in a +// bounds list), parsing as foo:(bar<baz>) rather than (foo:bar)<baz>. +%precedence IDENT + +// A couple fake-precedence symbols to use in rules associated with + +// and < in trailing type contexts. These come up when you have a type +// in the RHS of operator-AS, such as "foo as bar<baz>". The "<" there +// has to be shifted so the parser keeps trying to parse a type, even +// though it might well consider reducing the type "bar" and then +// going on to "<" as a subsequent binop. The "+" case is with +// trailing type-bounds ("foo as bar:A+B"), for the same reason. +%precedence SHIFTPLUS + +%precedence MOD_SEP +%precedence RARROW ':' + +// Binops & unops, and their precedences +%precedence BOX +%precedence BOXPLACE +%nonassoc DOTDOT + +// RETURN needs to be lower-precedence than tokens that start +// prefix_exprs +%precedence RETURN + +%left '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ +%left OROR +%left ANDAND +%left EQEQ NE +%left '<' '>' LE GE +%left '|' +%left '^' +%left '&' +%left SHL SHR +%left '+' '-' +%precedence AS +%left '*' '/' '%' +%precedence '!' + +%precedence '{' '[' '(' '.' + +%start crate + +%% + +//////////////////////////////////////////////////////////////////////// +// Part 1: Items and attributes +//////////////////////////////////////////////////////////////////////// + +crate +: maybe_shebang inner_attrs maybe_mod_items { mk_node("crate", 2, $2, $3); } +| maybe_shebang maybe_mod_items { mk_node("crate", 1, $2); } +; + +maybe_shebang +: SHEBANG_LINE +| %empty +; + +maybe_inner_attrs +: inner_attrs +| %empty { $$ = mk_none(); } +; + +inner_attrs +: inner_attr { $$ = mk_node("InnerAttrs", 1, $1); } +| inner_attrs inner_attr { $$ = ext_node($1, 1, $2); } +; + +inner_attr +: SHEBANG '[' meta_item ']' { $$ = mk_node("InnerAttr", 1, $3); } +| INNER_DOC_COMMENT { $$ = mk_node("InnerAttr", 1, mk_node("doc-comment", 1, mk_atom(yytext))); } +; + +maybe_outer_attrs +: outer_attrs +| %empty { $$ = mk_none(); } +; + +outer_attrs +: outer_attr { $$ = mk_node("OuterAttrs", 1, $1); } +| outer_attrs outer_attr { $$ = ext_node($1, 1, $2); } +; + +outer_attr +: '#' '[' meta_item ']' { $$ = $3; } +| OUTER_DOC_COMMENT { $$ = mk_node("doc-comment", 1, mk_atom(yytext)); } +; + +meta_item +: ident { $$ = mk_node("MetaWord", 1, $1); } +| ident '=' lit { $$ = mk_node("MetaNameValue", 2, $1, $3); } +| ident '(' meta_seq ')' { $$ = mk_node("MetaList", 2, $1, $3); } +| ident '(' meta_seq ',' ')' { $$ = mk_node("MetaList", 2, $1, $3); } +; + +meta_seq +: %empty { $$ = mk_none(); } +| meta_item { $$ = mk_node("MetaItems", 1, $1); } +| meta_seq ',' meta_item { $$ = ext_node($1, 1, $3); } +; + +maybe_mod_items +: mod_items +| %empty { $$ = mk_none(); } +; + +mod_items +: mod_item { $$ = mk_node("Items", 1, $1); } +| mod_items mod_item { $$ = ext_node($1, 1, $2); } +; + +attrs_and_vis +: maybe_outer_attrs visibility { $$ = mk_node("AttrsAndVis", 2, $1, $2); } +; + +mod_item +: attrs_and_vis item { $$ = mk_node("Item", 2, $1, $2); } +; + +// items that can appear outside of a fn block +item +: item_static +| item_const +| item_type +| block_item +| view_item +| item_macro +; + +// items that can appear in "stmts" +stmt_item +: item_static +| item_const +| item_type +| block_item +| use_item +| extern_fn_item +; + +item_static +: STATIC ident ':' ty '=' expr ';' { $$ = mk_node("ItemStatic", 3, $2, $4, $6); } +| STATIC MUT ident ':' ty '=' expr ';' { $$ = mk_node("ItemStatic", 3, $3, $5, $7); } +; + +item_const +: CONST ident ':' ty '=' expr ';' { $$ = mk_node("ItemConst", 3, $2, $4, $6); } +; + +item_macro +: path_expr '!' maybe_ident parens_delimited_token_trees ';' +| path_expr '!' maybe_ident braces_delimited_token_trees +| path_expr '!' maybe_ident brackets_delimited_token_trees ';' +; + +view_item +: use_item +| extern_fn_item +| EXTERN CRATE ident ';' { $$ = mk_node("ViewItemExternCrate", 1, $3); } +| EXTERN CRATE ident '=' str ';' { $$ = mk_node("ViewItemExternCrate", 2, $3, $5); } +| EXTERN CRATE str AS ident ';' { $$ = mk_node("ViewItemExternCrate", 2, $3, $5); } +; + +extern_fn_item +: EXTERN maybe_abi item_fn { $$ = mk_node("ViewItemExternFn", 2, $2, $3); } +; + +use_item +: USE view_path ';' { $$ = mk_node("ViewItemUse", 1, $2); } +; + +view_path +: path_no_types_allowed { $$ = mk_node("ViewPathSimple", 1, $1); } +| path_no_types_allowed MOD_SEP '{' '}' { $$ = mk_node("ViewPathList", 2, $1, mk_atom("ViewPathListEmpty")); } +| path_no_types_allowed MOD_SEP '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 2, $1, $4); } +| path_no_types_allowed MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 2, $1, $4); } +| path_no_types_allowed MOD_SEP '*' { $$ = mk_node("ViewPathGlob", 1, $1); } +| '{' '}' { $$ = mk_atom("ViewPathListEmpty"); } +| '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 1, $2); } +| '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $2); } +| path_no_types_allowed AS ident { $$ = mk_node("ViewPathSimple", 2, $1, $3); } +; + +block_item +: item_fn +| item_unsafe_fn +| item_mod +| item_foreign_mod { $$ = mk_node("ItemForeignMod", 1, $1); } +| item_struct +| item_enum +| item_trait +| item_impl +; + +maybe_ty_ascription +: ':' ty { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +maybe_init_expr +: '=' expr { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +// structs +item_struct +: STRUCT ident generic_params maybe_where_clause struct_decl_args +{ + $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5); +} +| STRUCT ident generic_params struct_tuple_args maybe_where_clause ';' +{ + $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5); +} +| STRUCT ident generic_params maybe_where_clause ';' +{ + $$ = mk_node("ItemStruct", 3, $2, $3, $4); +} +; + +struct_decl_args +: '{' struct_decl_fields '}' { $$ = $2; } +| '{' struct_decl_fields ',' '}' { $$ = $2; } +; + +struct_tuple_args +: '(' struct_tuple_fields ')' { $$ = $2; } +| '(' struct_tuple_fields ',' ')' { $$ = $2; } +; + +struct_decl_fields +: struct_decl_field { $$ = mk_node("StructFields", 1, $1); } +| struct_decl_fields ',' struct_decl_field { $$ = ext_node($1, 1, $3); } +| %empty { $$ = mk_none(); } +; + +struct_decl_field +: attrs_and_vis ident ':' ty_sum { $$ = mk_node("StructField", 3, $1, $2, $4); } +; + +struct_tuple_fields +: struct_tuple_field { $$ = mk_node("StructFields", 1, $1); } +| struct_tuple_fields ',' struct_tuple_field { $$ = ext_node($1, 1, $3); } +; + +struct_tuple_field +: attrs_and_vis ty_sum { $$ = mk_node("StructField", 2, $1, $2); } +; + +// enums +item_enum +: ENUM ident generic_params maybe_where_clause '{' enum_defs '}' { $$ = mk_node("ItemEnum", 0); } +| ENUM ident generic_params maybe_where_clause '{' enum_defs ',' '}' { $$ = mk_node("ItemEnum", 0); } +; + +enum_defs +: enum_def { $$ = mk_node("EnumDefs", 1, $1); } +| enum_defs ',' enum_def { $$ = ext_node($1, 1, $3); } +| %empty { $$ = mk_none(); } +; + +enum_def +: attrs_and_vis ident enum_args { $$ = mk_node("EnumDef", 3, $1, $2, $3); } +; + +enum_args +: '{' struct_decl_fields '}' { $$ = mk_node("EnumArgs", 1, $2); } +| '{' struct_decl_fields ',' '}' { $$ = mk_node("EnumArgs", 1, $2); } +| '(' maybe_ty_sums ')' { $$ = mk_node("EnumArgs", 1, $2); } +| '=' expr { $$ = mk_node("EnumArgs", 1, $2); } +| %empty { $$ = mk_none(); } +; + +item_mod +: MOD ident ';' { $$ = mk_node("ItemMod", 1, $2); } +| MOD ident '{' maybe_mod_items '}' { $$ = mk_node("ItemMod", 2, $2, $4); } +| MOD ident '{' inner_attrs maybe_mod_items '}' { $$ = mk_node("ItemMod", 3, $2, $4, $5); } +; + +item_foreign_mod +: EXTERN maybe_abi '{' maybe_foreign_items '}' { $$ = mk_node("ItemForeignMod", 1, $4); } +| EXTERN maybe_abi '{' inner_attrs maybe_foreign_items '}' { $$ = mk_node("ItemForeignMod", 2, $4, $5); } +; + +maybe_abi +: str +| %empty { $$ = mk_none(); } +; + +maybe_foreign_items +: foreign_items +| %empty { $$ = mk_none(); } +; + +foreign_items +: foreign_item { $$ = mk_node("ForeignItems", 1, $1); } +| foreign_items foreign_item { $$ = ext_node($1, 1, $2); } +; + +foreign_item +: attrs_and_vis STATIC item_foreign_static { $$ = mk_node("ForeignItem", 2, $1, $3); } +| attrs_and_vis item_foreign_fn { $$ = mk_node("ForeignItem", 2, $1, $2); } +| attrs_and_vis UNSAFE item_foreign_fn { $$ = mk_node("ForeignItem", 2, $1, $3); } +; + +item_foreign_static +: maybe_mut ident ':' ty ';' { $$ = mk_node("StaticItem", 3, $1, $2, $4); } +; + +item_foreign_fn +: FN ident generic_params fn_decl_allow_variadic maybe_where_clause ';' { $$ = mk_node("ForeignFn", 4, $2, $3, $4, $5); } +; + +fn_decl_allow_variadic +: fn_params_allow_variadic ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } +; + +fn_params_allow_variadic +: '(' ')' { $$ = mk_none(); } +| '(' params ')' { $$ = $2; } +| '(' params ',' ')' { $$ = $2; } +| '(' params ',' DOTDOTDOT ')' { $$ = $2; } +; + +visibility +: PUB { $$ = mk_atom("Public"); } +| %empty { $$ = mk_atom("Inherited"); } +; + +idents_or_self +: ident_or_self { $$ = mk_node("IdentsOrSelf", 1, $1); } +| idents_or_self ',' ident_or_self { $$ = ext_node($1, 1, $3); } +; + +ident_or_self +: ident +| SELF { $$ = mk_atom(yytext); } +; + +item_type +: TYPE ident generic_params maybe_where_clause '=' ty_sum ';' { $$ = mk_node("ItemTy", 4, $2, $3, $4, $6); } +; + +for_sized +: FOR '?' ident { $$ = mk_node("ForSized", 1, $3); } +| FOR ident '?' { $$ = mk_node("ForSized", 1, $2); } +| %empty { $$ = mk_none(); } +; + +item_trait +: maybe_unsafe TRAIT ident generic_params for_sized maybe_ty_param_bounds maybe_where_clause '{' maybe_trait_items '}' +{ + $$ = mk_node("ItemTrait", 7, $1, $3, $4, $5, $6, $7, $9); +} +; + +maybe_trait_items +: trait_items +| %empty { $$ = mk_none(); } +; + +trait_items +: trait_item { $$ = mk_node("TraitItems", 1, $1); } +| trait_items trait_item { $$ = ext_node($1, 1, $2); } +; + +trait_item +: trait_type +| trait_method +; + +trait_type +: maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); } +; + +maybe_unsafe +: UNSAFE { $$ = mk_atom("Unsafe"); } +| %empty { $$ = mk_none(); } +; + +trait_method +: type_method { $$ = mk_node("Required", 1, $1); } +| method { $$ = mk_node("Provided", 1, $1); } +; + +type_method +: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' +{ + $$ = mk_node("TypeMethod", 6, $1, $2, $4, $5, $6, $7); +} +| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' +{ + $$ = mk_node("TypeMethod", 7, $1, $2, $4, $6, $7, $8, $9); +} +; + +method +: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8); +} +| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); +} +; + +impl_method +: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8); +} +| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); +} +; + +// There are two forms of impl: +// +// impl (<...>)? TY { ... } +// impl (<...>)? TRAIT for TY { ... } +// +// Unfortunately since TY can begin with '<' itself -- as part of a +// TyQualifiedPath type -- there's an s/r conflict when we see '<' after IMPL: +// should we reduce one of the early rules of TY (such as maybe_once) +// or shall we continue shifting into the generic_params list for the +// impl? +// +// The production parser disambiguates a different case here by +// permitting / requiring the user to provide parens around types when +// they are ambiguous with traits. We do the same here, regrettably, +// by splitting ty into ty and ty_prim. +item_impl +: maybe_unsafe IMPL generic_params ty_prim_sum maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' +{ + $$ = mk_node("ItemImpl", 6, $1, $3, $4, $5, $7, $8); +} +| maybe_unsafe IMPL generic_params '(' ty ')' maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' +{ + $$ = mk_node("ItemImpl", 6, $1, $3, 5, $6, $9, $10); +} +| maybe_unsafe IMPL generic_params trait_ref FOR ty maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' +{ + $$ = mk_node("ItemImpl", 6, $3, $4, $6, $7, $9, $10); +} +| maybe_unsafe IMPL generic_params '!' trait_ref FOR ty maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' +{ + $$ = mk_node("ItemImplNeg", 7, $1, $3, $5, $7, $8, $10, $11); +} +; + +maybe_impl_items +: impl_items +| %empty { $$ = mk_none(); } +; + +impl_items +: impl_item { $$ = mk_node("ImplItems", 1, $1); } +| impl_item impl_items { $$ = ext_node($1, 1, $2); } +; + +impl_item +: impl_method +| item_macro +| trait_type +; + +item_fn +: FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("ItemFn", 5, $2, $3, $4, $5, $6); +} +; + +item_unsafe_fn +: UNSAFE FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("ItemUnsafeFn", 5, $3, $4, $5, $6, $7); +} +| UNSAFE EXTERN maybe_abi FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("ItemUnsafeFn", 6, $3, $5, $6, $7, $8, $9); +} +; + +fn_decl +: fn_params ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } +; + +fn_decl_with_self +: fn_params_with_self ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } +; + +fn_decl_with_self_allow_anon_params +: fn_anon_params_with_self ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } +; + +fn_params +: '(' maybe_params ')' { $$ = $2; } +; + +fn_anon_params +: '(' anon_param anon_params_allow_variadic_tail ')' { $$ = ext_node($2, 1, $3); } +| '(' ')' { $$ = mk_none(); } +; + +fn_params_with_self +: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfValue", 3, $2, $4, $5); } +| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfRegion", 3, $3, $5, $6); } +| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); } +| '(' maybe_params ')' { $$ = mk_node("SelfStatic", 1, $2); } +; + +fn_anon_params_with_self +: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfValue", 3, $2, $4, $5); } +| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfRegion", 3, $3, $5, $6); } +| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); } +| '(' maybe_anon_params ')' { $$ = mk_node("SelfStatic", 1, $2); } +; + +maybe_params +: params +| params ',' +| %empty { $$ = mk_none(); } +; + +params +: param { $$ = mk_node("Args", 1, $1); } +| params ',' param { $$ = ext_node($1, 1, $3); } +; + +param +: pat ':' ty { $$ = mk_node("Arg", 2, $1, $3); } +; + +inferrable_params +: inferrable_param { $$ = mk_node("InferrableParams", 1, $1); } +| inferrable_params ',' inferrable_param { $$ = ext_node($1, 1, $3); } +; + +inferrable_param +: pat maybe_ty_ascription { $$ = mk_node("InferrableParam", 2, $1, $2); } +; + +maybe_unboxed_closure_kind +: %empty +| ':' +| '&' maybe_mut ':' +; + +maybe_comma_params +: ',' { $$ = mk_none(); } +| ',' params { $$ = $2; } +| ',' params ',' { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +maybe_comma_anon_params +: ',' { $$ = mk_none(); } +| ',' anon_params { $$ = $2; } +| ',' anon_params ',' { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +maybe_anon_params +: anon_params +| anon_params ',' +| %empty { $$ = mk_none(); } +; + +anon_params +: anon_param { $$ = mk_node("Args", 1, $1); } +| anon_params ',' anon_param { $$ = ext_node($1, 1, $3); } +; + +// anon means it's allowed to be anonymous (type-only), but it can +// still have a name +anon_param +: named_arg ':' ty { $$ = mk_node("Arg", 2, $1, $3); } +| ty +; + +anon_params_allow_variadic_tail +: ',' DOTDOTDOT { $$ = mk_none(); } +| ',' anon_param anon_params_allow_variadic_tail { $$ = mk_node("Args", 2, $2, $3); } +| %empty { $$ = mk_none(); } +; + +named_arg +: ident +| UNDERSCORE { $$ = mk_atom("PatWild"); } +| '&' ident { $$ = $2; } +| '&' UNDERSCORE { $$ = mk_atom("PatWild"); } +| ANDAND ident { $$ = $2; } +| ANDAND UNDERSCORE { $$ = mk_atom("PatWild"); } +| MUT ident { $$ = $2; } +; + +ret_ty +: RARROW '!' { $$ = mk_none(); } +| RARROW ty { $$ = mk_node("ret-ty", 1, $2); } +| %prec IDENT %empty { $$ = mk_none(); } +; + +generic_params +: '<' lifetimes '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } +| '<' lifetimes ',' '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } +| '<' lifetimes SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } +| '<' lifetimes ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } +| '<' lifetimes ',' ty_params '>' { $$ = mk_node("Generics", 2, $2, $4); } +| '<' lifetimes ',' ty_params ',' '>' { $$ = mk_node("Generics", 2, $2, $4); } +| '<' lifetimes ',' ty_params SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); } +| '<' lifetimes ',' ty_params ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); } +| '<' ty_params '>' { $$ = mk_node("Generics", 2, mk_none(), $2); } +| '<' ty_params ',' '>' { $$ = mk_node("Generics", 2, mk_none(), $2); } +| '<' ty_params SHR { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); } +| '<' ty_params ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); } +| %empty { $$ = mk_none(); } +; + +maybe_where_clause +: %empty { $$ = mk_none(); } +| where_clause +; + +where_clause +: WHERE where_predicates { $$ = mk_node("WhereClause", 1, $2); } +| WHERE where_predicates ',' { $$ = mk_node("WhereClause", 1, $2); } +; + +where_predicates +: where_predicate { $$ = mk_node("WherePredicates", 1, $1); } +| where_predicates ',' where_predicate { $$ = ext_node($1, 1, $3); } +; + +where_predicate +: lifetime ':' bounds { $$ = mk_node("WherePredicate", 2, $1, $3); } +| ty ':' ty_param_bounds { $$ = mk_node("WherePredicate", 2, $1, $3); } +; + +ty_params +: ty_param { $$ = mk_node("TyParams", 1, $1); } +| ty_params ',' ty_param { $$ = ext_node($1, 1, $3); } +; + +// A path with no type parameters; e.g. `foo::bar::Baz` +// +// These show up in 'use' view-items, because these are processed +// without respect to types. +path_no_types_allowed +: ident { $$ = mk_node("ViewPath", 1, $1); } +| MOD_SEP ident { $$ = mk_node("ViewPath", 1, $2); } +| SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } +| MOD_SEP SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } +| path_no_types_allowed MOD_SEP ident { $$ = ext_node($1, 1, $3); } +; + +// A path with a lifetime and type parameters, with no double colons +// before the type parameters; e.g. `foo::bar<'a>::Baz<T>` +// +// These show up in "trait references", the components of +// type-parameter bounds lists, as well as in the prefix of the +// path_generic_args_and_bounds rule, which is the full form of a +// named typed expression. +// +// They do not have (nor need) an extra '::' before '<' because +// unlike in expr context, there are no "less-than" type exprs to +// be ambiguous with. +path_generic_args_without_colons +: %prec IDENT + ident { $$ = mk_node("components", 1, $1); } +| %prec IDENT + ident generic_args { $$ = mk_node("components", 2, $1, $2); } +| %prec IDENT + ident '(' maybe_ty_sums ')' ret_ty { $$ = mk_node("components", 2, $1, $3); } +| %prec IDENT + path_generic_args_without_colons MOD_SEP ident { $$ = ext_node($1, 1, $3); } +| %prec IDENT + path_generic_args_without_colons MOD_SEP ident generic_args { $$ = ext_node($1, 2, $3, $4); } +| %prec IDENT + path_generic_args_without_colons MOD_SEP ident '(' maybe_ty_sums ')' ret_ty { $$ = ext_node($1, 2, $3, $5); } +; + +generic_args +: '<' generic_values '>' { $$ = $2; } +| '<' generic_values SHR { push_back('>'); $$ = $2; } +| '<' generic_values GE { push_back('='); $$ = $2; } +| '<' generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; } +// If generic_args starts with "<<", the first arg must be a +// TyQualifiedPath because that's the only type that can start with a +// '<'. This rule parses that as the first ty_sum and then continues +// with the rest of generic_values. +| SHL ty_qualified_path_and_generic_values '>' { $$ = $2; } +| SHL ty_qualified_path_and_generic_values SHR { push_back('>'); $$ = $2; } +| SHL ty_qualified_path_and_generic_values GE { push_back('='); $$ = $2; } +| SHL ty_qualified_path_and_generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; } +; + +generic_values +: maybe_lifetimes maybe_ty_sums_and_or_bindings { $$ = mk_node("GenericValues", 2, $1, $2); } +; + +maybe_ty_sums_and_or_bindings +: ty_sums +| ty_sums ',' +| ty_sums ',' bindings { $$ = mk_node("TySumsAndBindings", 2, $1, $3); } +| bindings +| bindings ',' +| %empty { $$ = mk_none(); } +; + +maybe_bindings +: ',' bindings { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +//////////////////////////////////////////////////////////////////////// +// Part 2: Patterns +//////////////////////////////////////////////////////////////////////// + +pat +: UNDERSCORE { $$ = mk_atom("PatWild"); } +| '&' pat { $$ = mk_node("PatRegion", 1, $2); } +| '&' MUT pat { $$ = mk_node("PatRegion", 1, $3); } +| ANDAND pat { $$ = mk_node("PatRegion", 1, mk_node("PatRegion", 1, $2)); } +| '(' ')' { $$ = mk_atom("PatUnit"); } +| '(' pat_tup ')' { $$ = mk_node("PatTup", 1, $2); } +| '(' pat_tup ',' ')' { $$ = mk_node("PatTup", 1, $2); } +| '[' pat_vec ']' { $$ = mk_node("PatVec", 1, $2); } +| lit_or_path +| lit_or_path DOTDOTDOT lit_or_path { $$ = mk_node("PatRange", 2, $1, $3); } +| path_expr '{' pat_struct '}' { $$ = mk_node("PatStruct", 2, $1, $3); } +| path_expr '(' DOTDOT ')' { $$ = mk_node("PatEnum", 1, $1); } +| path_expr '(' pat_tup ')' { $$ = mk_node("PatEnum", 2, $1, $3); } +| path_expr '!' maybe_ident delimited_token_trees { $$ = mk_node("PatMac", 3, $1, $3, $4); } +| binding_mode ident { $$ = mk_node("PatIdent", 2, $1, $2); } +| ident '@' pat { $$ = mk_node("PatIdent", 3, mk_node("BindByValue", 1, mk_atom("MutImmutable")), $1, $3); } +| binding_mode ident '@' pat { $$ = mk_node("PatIdent", 3, $1, $2, $4); } +| BOX pat { $$ = mk_node("PatUniq", 1, $2); } +; + +pats_or +: pat { $$ = mk_node("Pats", 1, $1); } +| pats_or '|' pat { $$ = ext_node($1, 1, $3); } +; + +binding_mode +: REF { $$ = mk_node("BindByRef", 1, mk_atom("MutImmutable")); } +| REF MUT { $$ = mk_node("BindByRef", 1, mk_atom("MutMutable")); } +| MUT { $$ = mk_node("BindByValue", 1, mk_atom("MutMutable")); } +; + +lit_or_path +: path_expr { $$ = mk_node("PatLit", 1, $1); } +| lit { $$ = mk_node("PatLit", 1, $1); } +| '-' lit { $$ = mk_node("PatLit", 1, $2); } +; + +pat_field +: ident { $$ = mk_node("PatField", 1, $1); } +| binding_mode ident { $$ = mk_node("PatField", 2, $1, $2); } +| BOX ident { $$ = mk_node("PatField", 2, mk_atom("box"), $2); } +| BOX binding_mode ident { $$ = mk_node("PatField", 3, mk_atom("box"), $2, $3); } +| ident ':' pat { $$ = mk_node("PatField", 2, $1, $3); } +| binding_mode ident ':' pat { $$ = mk_node("PatField", 3, $1, $2, $4); } +; + +pat_fields +: pat_field { $$ = mk_node("PatFields", 1, $1); } +| pat_fields ',' pat_field { $$ = ext_node($1, 1, $3); } +; + +pat_struct +: pat_fields { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); } +| pat_fields ',' { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); } +| pat_fields ',' DOTDOT { $$ = mk_node("PatStruct", 2, $1, mk_atom("true")); } +| DOTDOT { $$ = mk_node("PatStruct", 1, mk_atom("true")); } +; + +pat_tup +: pat { $$ = mk_node("pat_tup", 1, $1); } +| pat_tup ',' pat { $$ = ext_node($1, 1, $3); } +; + +pat_vec +: pat_vec_elts { $$ = mk_node("PatVec", 2, $1, mk_none()); } +| pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, mk_none()); } +| pat_vec_elts DOTDOT { $$ = mk_node("PatVec", 2, $1, mk_none()); } +| pat_vec_elts ',' DOTDOT { $$ = mk_node("PatVec", 2, $1, mk_none()); } +| pat_vec_elts DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, $1, $4); } +| pat_vec_elts DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, $4); } +| pat_vec_elts ',' DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, $1, $5); } +| pat_vec_elts ',' DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, $5); } +| DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, mk_none(), $3); } +| DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, mk_none(), $3); } +| DOTDOT { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); } +| %empty { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); } +; + +pat_vec_elts +: pat { $$ = mk_node("PatVecElts", 1, $1); } +| pat_vec_elts ',' pat { $$ = ext_node($1, 1, $3); } +; + +//////////////////////////////////////////////////////////////////////// +// Part 3: Types +//////////////////////////////////////////////////////////////////////// + +ty +: ty_prim +| ty_closure +| '<' ty_sum AS trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, $2, $4, $7); } +| SHL ty_sum AS trait_ref '>' MOD_SEP ident AS trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, mk_node("TyQualifiedPath", 3, $2, $4, $7), $9, $12); } +| '(' ty_sums ')' { $$ = mk_node("TyTup", 1, $2); } +| '(' ty_sums ',' ')' { $$ = mk_node("TyTup", 1, $2); } +| '(' ')' { $$ = mk_atom("TyNil"); } +; + +ty_prim +: %prec IDENT path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("false")), $1); } +| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("true")), $2); } +| %prec IDENT SELF MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("self", 1, mk_atom("true")), $3); } +| BOX ty { $$ = mk_node("TyBox", 1, $2); } +| '*' maybe_mut_or_const ty { $$ = mk_node("TyPtr", 2, $2, $3); } +| '&' ty { $$ = mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2); } +| '&' MUT ty { $$ = mk_node("TyRptr", 2, mk_atom("MutMutable"), $3); } +| ANDAND ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2)); } +| ANDAND MUT ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutMutable"), $3)); } +| '&' lifetime maybe_mut ty { $$ = mk_node("TyRptr", 3, $2, $3, $4); } +| ANDAND lifetime maybe_mut ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 3, $2, $3, $4)); } +| '[' ty ']' { $$ = mk_node("TyVec", 1, $2); } +| '[' ty ',' DOTDOT expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $5); } +| '[' ty ';' expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $4); } +| TYPEOF '(' expr ')' { $$ = mk_node("TyTypeof", 1, $3); } +| UNDERSCORE { $$ = mk_atom("TyInfer"); } +| ty_bare_fn +| ty_proc +| for_in_type +; + +ty_bare_fn +: FN ty_fn_decl { $$ = $2; } +| UNSAFE FN ty_fn_decl { $$ = $3; } +| EXTERN maybe_abi FN ty_fn_decl { $$ = $4; } +| UNSAFE EXTERN maybe_abi FN ty_fn_decl { $$ = $5; } +; + +ty_fn_decl +: generic_params fn_anon_params ret_ty { $$ = mk_node("TyFnDecl", 3, $1, $2, $3); } +; + +ty_closure +: UNSAFE '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $3, $5, $6); } +| '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $2, $4, $5); } +| UNSAFE OROR maybe_bounds ret_ty { $$ = mk_node("TyClosure", 2, $3, $4); } +| OROR maybe_bounds ret_ty { $$ = mk_node("TyClosure", 2, $2, $3); } +; + +ty_proc +: PROC generic_params fn_params maybe_bounds ret_ty { $$ = mk_node("TyProc", 4, $2, $3, $4, $5); } +; + +for_in_type +: FOR '<' maybe_lifetimes '>' for_in_type_suffix { $$ = mk_node("ForInType", 2, $3, $5); } +; + +for_in_type_suffix +: ty_proc +| ty_bare_fn +| trait_ref +| ty_closure +; + +maybe_mut +: MUT { $$ = mk_atom("MutMutable"); } +| %prec MUT %empty { $$ = mk_atom("MutImmutable"); } +; + +maybe_mut_or_const +: MUT { $$ = mk_atom("MutMutable"); } +| CONST { $$ = mk_atom("MutImmutable"); } +| %empty { $$ = mk_atom("MutImmutable"); } +; + +ty_qualified_path_and_generic_values +: ty_qualified_path maybe_bindings +{ + $$ = mk_node("GenericValues", 3, mk_none(), mk_node("TySums", 1, mk_node("TySum", 1, $1)), $2); +} +| ty_qualified_path ',' ty_sums maybe_bindings +{ + $$ = mk_node("GenericValues", 3, mk_none(), ext_node(mk_node("TySums", 1, $1), 1, $3), $4); } +; + +ty_qualified_path +: ty_sum AS trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); } +| ty_sum AS trait_ref '>' MOD_SEP ident '+' ty_param_bounds { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); } +; + +maybe_ty_sums +: ty_sums +| ty_sums ',' +| %empty { $$ = mk_none(); } +; + +ty_sums +: ty_sum { $$ = mk_node("TySums", 1, $1); } +| ty_sums ',' ty_sum { $$ = ext_node($1, 1, $3); } +; + +ty_sum +: ty { $$ = mk_node("TySum", 1, $1); } +| ty '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); } +; + +ty_prim_sum +: ty_prim { $$ = mk_node("TySum", 1, $1); } +| ty_prim '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); } +; + +maybe_ty_param_bounds +: ':' ty_param_bounds { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +ty_param_bounds +: boundseq +| %empty { $$ = mk_none(); } +; + +boundseq +: polybound +| boundseq '+' polybound { $$ = ext_node($1, 1, $3); } +; + +polybound +: FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $3, $5); } +| bound +| '?' bound { $$ = $2; } +; + +bindings +: binding { $$ = mk_node("Bindings", 1, $1); } +| bindings ',' binding { $$ = ext_node($1, 1, $3); } +; + +binding +: ident '=' ty { mk_node("Binding", 2, $1, $3); } +; + +ty_param +: ident maybe_ty_param_bounds maybe_ty_default { $$ = mk_node("TyParam", 3, $1, $2, $3); } +| ident '?' ident maybe_ty_param_bounds maybe_ty_default { $$ = mk_node("TyParam", 4, $1, $3, $4, $5); } +; + +maybe_bounds +: %prec SHIFTPLUS + ':' bounds { $$ = $2; } +| %prec SHIFTPLUS %empty { $$ = mk_none(); } +; + +bounds +: bound { $$ = mk_node("bounds", 1, $1); } +| bounds '+' bound { $$ = ext_node($1, 1, $3); } +; + +bound +: lifetime +| trait_ref +; + +maybe_ltbounds +: %prec SHIFTPLUS + ':' ltbounds { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +ltbounds +: lifetime { $$ = mk_node("ltbounds", 1, $1); } +| ltbounds '+' lifetime { $$ = ext_node($1, 1, $3); } +; + +maybe_ty_default +: '=' ty_sum { $$ = mk_node("TyDefault", 1, $2); } +| %empty { $$ = mk_none(); } +; + +maybe_lifetimes +: lifetimes +| lifetimes ',' +| %empty { $$ = mk_none(); } +; + +lifetimes +: lifetime_and_bounds { $$ = mk_node("Lifetimes", 1, $1); } +| lifetimes ',' lifetime_and_bounds { $$ = ext_node($1, 1, $3); } +; + +lifetime_and_bounds +: LIFETIME maybe_ltbounds { $$ = mk_node("lifetime", 2, mk_atom(yytext), $2); } +| STATIC_LIFETIME { $$ = mk_atom("static_lifetime"); } +; + +lifetime +: LIFETIME { $$ = mk_node("lifetime", 1, mk_atom(yytext)); } +| STATIC_LIFETIME { $$ = mk_atom("static_lifetime"); } +; + +trait_ref +: %prec IDENT path_generic_args_without_colons +| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = $2; } +; + +//////////////////////////////////////////////////////////////////////// +// Part 4: Blocks, statements, and expressions +//////////////////////////////////////////////////////////////////////// + +inner_attrs_and_block +: '{' maybe_inner_attrs maybe_stmts '}' { $$ = mk_node("ExprBlock", 2, $2, $3); } +; + +block +: '{' maybe_stmts '}' { $$ = mk_node("ExprBlock", 1, $2); } +; + +maybe_stmts +: stmts +| stmts nonblock_expr { $$ = ext_node($1, 1, $2); } +| nonblock_expr +| %empty { $$ = mk_none(); } +; + +// There are two sub-grammars within a "stmts: exprs" derivation +// depending on whether each stmt-expr is a block-expr form; this is to +// handle the "semicolon rule" for stmt sequencing that permits +// writing +// +// if foo { bar } 10 +// +// as a sequence of two stmts (one if-expr stmt, one lit-10-expr +// stmt). Unfortunately by permitting juxtaposition of exprs in +// sequence like that, the non-block expr grammar has to have a +// second limited sub-grammar that excludes the prefix exprs that +// are ambiguous with binops. That is to say: +// +// {10} - 1 +// +// should parse as (progn (progn 10) (- 1)) not (- (progn 10) 1), that +// is to say, two statements rather than one, at least according to +// the mainline rust parser. +// +// So we wind up with a 3-way split in exprs that occur in stmt lists: +// block, nonblock-prefix, and nonblock-nonprefix. +// +// In non-stmts contexts, expr can relax this trichotomy. +// +// There are also two other expr subtypes: first, nonparen_expr +// disallows exprs surrounded by parens (including tuple expressions), +// this is neccesary for BOX (place) expressions, so a parens expr +// following the BOX is always parsed as the place. There is also +// expr_norange used in index_expr, which disallows '..' in +// expressions as that has special meaning inside of brackets. + +stmts +: stmt { $$ = mk_node("stmts", 1, $1); } +| stmts stmt { $$ = ext_node($1, 1, $2); } +; + +stmt +: let +| stmt_item +| PUB stmt_item { $$ = $2; } +| outer_attrs stmt_item { $$ = $2; } +| outer_attrs PUB stmt_item { $$ = $3; } +| full_block_expr +| block +| nonblock_expr ';' +| ';' { $$ = mk_none(); } +; + +maybe_exprs +: exprs +| exprs ',' +| %empty { $$ = mk_none(); } +; + +maybe_expr +: expr +| %empty { $$ = mk_none(); } +; + +exprs +: expr { $$ = mk_node("exprs", 1, $1); } +| exprs ',' expr { $$ = ext_node($1, 1, $3); } +; + +path_expr +: path_generic_args_with_colons +| MOD_SEP path_generic_args_with_colons { $$ = $2; } +| SELF MOD_SEP path_generic_args_with_colons { $$ = mk_node("SelfPath", 1, $3); } +; + +// A path with a lifetime and type parameters with double colons before +// the type parameters; e.g. `foo::bar::<'a>::Baz::<T>` +// +// These show up in expr context, in order to disambiguate from "less-than" +// expressions. +path_generic_args_with_colons +: ident { $$ = mk_node("components", 1, $1); } +| path_generic_args_with_colons MOD_SEP ident { $$ = ext_node($1, 1, $3); } +| path_generic_args_with_colons MOD_SEP generic_args { $$ = ext_node($1, 1, $3); } +; + +// the braces-delimited macro is a block_expr so it doesn't appear here +macro_expr +: path_expr '!' maybe_ident parens_delimited_token_trees { $$ = mk_node("MacroExpr", 3, $1, $3, $4); } +| path_expr '!' maybe_ident brackets_delimited_token_trees { $$ = mk_node("MacroExpr", 3, $1, $3, $4); } +; + +nonblock_expr +: lit { $$ = mk_node("ExprLit", 1, $1); } +| %prec IDENT + path_expr { $$ = mk_node("ExprPath", 1, $1); } +| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } +| macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| nonblock_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| nonblock_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| nonblock_expr '[' index_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } +| nonblock_expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } +| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } +| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } +| CONTINUE { $$ = mk_node("ExprAgain", 0); } +| CONTINUE lifetime { $$ = mk_node("ExprAgain", 1, $2); } +| RETURN { $$ = mk_node("ExprRet", 0); } +| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } +| BREAK { $$ = mk_node("ExprBreak", 0); } +| BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); } +| nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } +| nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } +| nonblock_expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } +| nonblock_expr MINUSEQ expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } +| nonblock_expr ANDEQ expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } +| nonblock_expr OREQ expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } +| nonblock_expr PLUSEQ expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } +| nonblock_expr STAREQ expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } +| nonblock_expr SLASHEQ expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } +| nonblock_expr CARETEQ expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } +| nonblock_expr PERCENTEQ expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } +| nonblock_expr OROR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } +| nonblock_expr ANDAND expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } +| nonblock_expr EQEQ expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } +| nonblock_expr NE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } +| nonblock_expr '<' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } +| nonblock_expr '>' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } +| nonblock_expr LE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } +| nonblock_expr GE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } +| nonblock_expr '|' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } +| nonblock_expr '^' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } +| nonblock_expr '&' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } +| nonblock_expr SHL expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } +| nonblock_expr SHR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } +| nonblock_expr '+' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } +| nonblock_expr '-' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } +| nonblock_expr '*' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } +| nonblock_expr '/' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } +| nonblock_expr '%' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } +| nonblock_expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } +| nonblock_expr DOTDOT expr { $$ = mk_node("ExprRange", 2, $1, $3); } +| DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } +| nonblock_expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } +| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } +| %prec BOXPLACE BOX '(' maybe_expr ')' nonblock_expr { $$ = mk_node("ExprBox", 2, $3, $5); } +| nonblock_prefix_expr +; + +expr +: lit { $$ = mk_node("ExprLit", 1, $1); } +| %prec IDENT + path_expr { $$ = mk_node("ExprPath", 1, $1); } +| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } +| macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| expr '[' index_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } +| expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } +| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } +| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } +| CONTINUE { $$ = mk_node("ExprAgain", 0); } +| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } +| RETURN { $$ = mk_node("ExprRet", 0); } +| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } +| BREAK { $$ = mk_node("ExprBreak", 0); } +| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } +| expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } +| expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } +| expr MINUSEQ expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } +| expr ANDEQ expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } +| expr OREQ expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } +| expr PLUSEQ expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } +| expr STAREQ expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } +| expr SLASHEQ expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } +| expr CARETEQ expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } +| expr PERCENTEQ expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } +| expr OROR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } +| expr ANDAND expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } +| expr EQEQ expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } +| expr NE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } +| expr '<' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } +| expr '>' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } +| expr LE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } +| expr GE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } +| expr '|' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } +| expr '^' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } +| expr '&' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } +| expr SHL expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } +| expr SHR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } +| expr '+' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } +| expr '-' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } +| expr '*' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } +| expr '/' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } +| expr '%' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } +| expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } +| expr DOTDOT expr { $$ = mk_node("ExprRange", 2, $1, $3); } +| DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } +| expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } +| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } +| %prec BOXPLACE BOX '(' maybe_expr ')' expr { $$ = mk_node("ExprBox", 2, $3, $5); } +| block_expr +| block +| nonblock_prefix_expr +; + +nonparen_expr +: lit { $$ = mk_node("ExprLit", 1, $1); } +| %prec IDENT + path_expr { $$ = mk_node("ExprPath", 1, $1); } +| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } +| macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| nonparen_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| nonparen_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| nonparen_expr '[' index_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } +| nonparen_expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } +| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } +| CONTINUE { $$ = mk_node("ExprAgain", 0); } +| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } +| RETURN { $$ = mk_node("ExprRet", 0); } +| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } +| BREAK { $$ = mk_node("ExprBreak", 0); } +| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| nonparen_expr '=' nonparen_expr { $$ = mk_node("ExprAssign", 2, $1, $3); } +| nonparen_expr SHLEQ nonparen_expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } +| nonparen_expr SHREQ nonparen_expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } +| nonparen_expr MINUSEQ nonparen_expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } +| nonparen_expr ANDEQ nonparen_expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } +| nonparen_expr OREQ nonparen_expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } +| nonparen_expr PLUSEQ nonparen_expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } +| nonparen_expr STAREQ nonparen_expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } +| nonparen_expr SLASHEQ nonparen_expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } +| nonparen_expr CARETEQ nonparen_expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } +| nonparen_expr PERCENTEQ nonparen_expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } +| nonparen_expr OROR nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } +| nonparen_expr ANDAND nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } +| nonparen_expr EQEQ nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } +| nonparen_expr NE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } +| nonparen_expr '<' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } +| nonparen_expr '>' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } +| nonparen_expr LE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } +| nonparen_expr GE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } +| nonparen_expr '|' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } +| nonparen_expr '^' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } +| nonparen_expr '&' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } +| nonparen_expr SHL nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } +| nonparen_expr SHR nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } +| nonparen_expr '+' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } +| nonparen_expr '-' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } +| nonparen_expr '*' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } +| nonparen_expr '/' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } +| nonparen_expr '%' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } +| nonparen_expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } +| nonparen_expr DOTDOT nonparen_expr { $$ = mk_node("ExprRange", 2, $1, $3); } +| DOTDOT nonparen_expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } +| nonparen_expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } +| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } +| %prec BOXPLACE BOX '(' maybe_expr ')' expr { $$ = mk_node("ExprBox", 1, $3, $5); } +| block_expr +| block +| nonblock_prefix_expr +; + +expr_norange +: lit { $$ = mk_node("ExprLit", 1, $1); } +| %prec IDENT + path_expr { $$ = mk_node("ExprPath", 1, $1); } +| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } +| macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| expr_norange '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| expr_norange '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| expr_norange '[' index_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } +| expr_norange '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } +| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } +| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } +| CONTINUE { $$ = mk_node("ExprAgain", 0); } +| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } +| RETURN { $$ = mk_node("ExprRet", 0); } +| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } +| BREAK { $$ = mk_node("ExprBreak", 0); } +| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| expr_norange '=' expr_norange { $$ = mk_node("ExprAssign", 2, $1, $3); } +| expr_norange SHLEQ expr_norange { $$ = mk_node("ExprAssignShl", 2, $1, $3); } +| expr_norange SHREQ expr_norange { $$ = mk_node("ExprAssignShr", 2, $1, $3); } +| expr_norange MINUSEQ expr_norange { $$ = mk_node("ExprAssignSub", 2, $1, $3); } +| expr_norange ANDEQ expr_norange { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } +| expr_norange OREQ expr_norange { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } +| expr_norange PLUSEQ expr_norange { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } +| expr_norange STAREQ expr_norange { $$ = mk_node("ExprAssignMul", 2, $1, $3); } +| expr_norange SLASHEQ expr_norange { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } +| expr_norange CARETEQ expr_norange { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } +| expr_norange PERCENTEQ expr_norange { $$ = mk_node("ExprAssignRem", 2, $1, $3); } +| expr_norange OROR expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } +| expr_norange ANDAND expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } +| expr_norange EQEQ expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } +| expr_norange NE expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } +| expr_norange '<' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } +| expr_norange '>' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } +| expr_norange LE expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } +| expr_norange GE expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } +| expr_norange '|' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } +| expr_norange '^' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } +| expr_norange '&' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } +| expr_norange SHL expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } +| expr_norange SHR expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } +| expr_norange '+' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } +| expr_norange '-' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } +| expr_norange '*' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } +| expr_norange '/' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } +| expr_norange '%' expr_norange { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } +| expr_norange AS ty { $$ = mk_node("Expr_NorangeCast", 2, $1, $3); } +| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } +| %prec BOXPLACE BOX '(' maybe_expr ')' expr_norange { $$ = mk_node("ExprBox", 2, $3, $5); } +| block_expr +| block +| nonblock_prefix_expr +; + +expr_nostruct +: lit { $$ = mk_node("ExprLit", 1, $1); } +| %prec IDENT + path_expr { $$ = mk_node("ExprPath", 1, $1); } +| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } +| macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| expr_nostruct '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| expr_nostruct '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| expr_nostruct '[' index_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } +| expr_nostruct '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } +| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } +| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } +| CONTINUE { $$ = mk_node("ExprAgain", 0); } +| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } +| RETURN { $$ = mk_node("ExprRet", 0); } +| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } +| BREAK { $$ = mk_node("ExprBreak", 0); } +| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); } +| expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); } +| expr_nostruct SHREQ expr_nostruct { $$ = mk_node("ExprAssignShr", 2, $1, $3); } +| expr_nostruct MINUSEQ expr_nostruct { $$ = mk_node("ExprAssignSub", 2, $1, $3); } +| expr_nostruct ANDEQ expr_nostruct { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } +| expr_nostruct OREQ expr_nostruct { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } +| expr_nostruct PLUSEQ expr_nostruct { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } +| expr_nostruct STAREQ expr_nostruct { $$ = mk_node("ExprAssignMul", 2, $1, $3); } +| expr_nostruct SLASHEQ expr_nostruct { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } +| expr_nostruct CARETEQ expr_nostruct { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } +| expr_nostruct PERCENTEQ expr_nostruct { $$ = mk_node("ExprAssignRem", 2, $1, $3); } +| expr_nostruct OROR expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } +| expr_nostruct ANDAND expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } +| expr_nostruct EQEQ expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } +| expr_nostruct NE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } +| expr_nostruct '<' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } +| expr_nostruct '>' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } +| expr_nostruct LE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } +| expr_nostruct GE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } +| expr_nostruct '|' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } +| expr_nostruct '^' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } +| expr_nostruct '&' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } +| expr_nostruct SHL expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } +| expr_nostruct SHR expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } +| expr_nostruct '+' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } +| expr_nostruct '-' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } +| expr_nostruct '*' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } +| expr_nostruct '/' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } +| expr_nostruct '%' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } +| expr_nostruct DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } +| expr_nostruct DOTDOT expr_nostruct { $$ = mk_node("ExprRange", 2, $1, $3); } +| DOTDOT expr_nostruct { $$ = mk_node("ExprRange", 2, mk_none(), $2); } +| expr_nostruct AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } +| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } +| %prec BOXPLACE BOX '(' maybe_expr ')' expr_nostruct { $$ = mk_node("ExprBox", 1, $3, $5); } +| block_expr +| block +| nonblock_prefix_expr_nostruct +; + +nonblock_prefix_expr_nostruct +: '-' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); } +| '!' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); } +| '*' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); } +| '&' maybe_mut expr_nostruct { $$ = mk_node("ExprAddrOf", 2, $2, $3); } +| ANDAND maybe_mut expr_nostruct { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } +| lambda_expr_nostruct +| MOVE lambda_expr_nostruct { $$ = $2; } +| proc_expr_nostruct +; + +nonblock_prefix_expr +: '-' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); } +| '!' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); } +| '*' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); } +| '&' maybe_mut expr { $$ = mk_node("ExprAddrOf", 2, $2, $3); } +| ANDAND maybe_mut expr { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } +| lambda_expr +| MOVE lambda_expr { $$ = $2; } +| proc_expr +; + +lambda_expr +: %prec LAMBDA + OROR ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } +| %prec LAMBDA + '|' maybe_unboxed_closure_kind '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $4, $5); } +| %prec LAMBDA + '|' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $2, $4, $5); } +| %prec LAMBDA + '|' '&' maybe_mut ':' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $5, $7, $8); } +| %prec LAMBDA + '|' ':' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $3, $5, $6); } +; + +lambda_expr_nostruct +: %prec LAMBDA + OROR expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $2); } +| %prec LAMBDA + '|' maybe_unboxed_closure_kind '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $4); } +| %prec LAMBDA + '|' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $2, $4); } +| %prec LAMBDA + '|' '&' maybe_mut ':' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $5, $7); } +| %prec LAMBDA + '|' ':' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $3, $5); } + +; + +proc_expr +: %prec LAMBDA + PROC '(' ')' expr { $$ = mk_node("ExprProc", 2, mk_none(), $4); } +| %prec LAMBDA + PROC '(' inferrable_params ')' expr { $$ = mk_node("ExprProc", 2, $3, $5); } +; + +proc_expr_nostruct +: %prec LAMBDA + PROC '(' ')' expr_nostruct { $$ = mk_node("ExprProc", 2, mk_none(), $4); } +| %prec LAMBDA + PROC '(' inferrable_params ')' expr_nostruct { $$ = mk_node("ExprProc", 2, $3, $5); } +; + +vec_expr +: maybe_exprs +| exprs ';' expr { $$ = mk_node("VecRepeat", 2, $1, $3); } +; + +index_expr +: expr_norange { $$ = mk_node("Index", 1, $1); } +| expr_norange DOTDOT { $$ = mk_node("SliceToEnd", 1, $1); } +| DOTDOT expr_norange { $$ = mk_node("SliceFromBeginning", 1, $2); } +| expr_norange DOTDOT expr_norange { $$ = mk_node("Slice", 2, $1, $3); } +| %empty { $$ = mk_none(); } +; + +struct_expr_fields +: field_inits +| field_inits ',' +| maybe_field_inits default_field_init { $$ = ext_node($1, 1, $2); } +; + +maybe_field_inits +: field_inits +| field_inits ',' +| %empty { $$ = mk_none(); } +; + +field_inits +: field_init { $$ = mk_node("FieldInits", 1, $1); } +| field_inits ',' field_init { $$ = ext_node($1, 1, $3); } +; + +field_init +: ident ':' expr { $$ = mk_node("FieldInit", 2, $1, $3); } +; + +default_field_init +: DOTDOT expr { $$ = mk_node("DefaultFieldInit", 1, $2); } +; + +block_expr +: expr_match +| expr_if +| expr_if_let +| expr_while +| expr_while_let +| expr_loop +| expr_for +| UNSAFE block { $$ = mk_node("UnsafeBlock", 1, $2); } +| path_expr '!' maybe_ident braces_delimited_token_trees { $$ = mk_node("Macro", 3, $1, $3, $4); } +; + +full_block_expr +: block_expr +| full_block_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } +| full_block_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +; + +expr_match +: MATCH expr_nostruct '{' '}' { $$ = mk_node("ExprMatch", 1, $2); } +| MATCH expr_nostruct '{' match_clauses '}' { $$ = mk_node("ExprMatch", 2, $2, $4); } +| MATCH expr_nostruct '{' match_clauses nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, ext_node($4, 1, $5)); } +| MATCH expr_nostruct '{' nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, mk_node("Arms", 1, $4)); } +; + +match_clauses +: match_clause { $$ = mk_node("Arms", 1, $1); } +| match_clauses match_clause { $$ = ext_node($1, 1, $2); } +; + +match_clause +: nonblock_match_clause ',' +| block_match_clause +| block_match_clause ',' +; + +nonblock_match_clause +: maybe_outer_attrs pats_or maybe_guard FAT_ARROW nonblock_expr { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } +| maybe_outer_attrs pats_or maybe_guard FAT_ARROW full_block_expr { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } +; + +block_match_clause +: maybe_outer_attrs pats_or maybe_guard FAT_ARROW block { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } +; + +maybe_guard +: IF expr_nostruct { $$ = $2; } +| %empty { $$ = mk_none(); } +; + +expr_if +: IF expr_nostruct block { $$ = mk_node("ExprIf", 2, $2, $3); } +| IF expr_nostruct block ELSE block_or_if { $$ = mk_node("ExprIf", 3, $2, $3, $5); } +; + +expr_if_let +: IF LET pat '=' expr_nostruct block { $$ = mk_node("ExprIfLet", 3, $3, $5, $6); } +| IF LET pat '=' expr_nostruct block ELSE block_or_if { $$ = mk_node("ExprIfLet", 4, $3, $5, $6, $8); } +; + +block_or_if +: block +| expr_if +| expr_if_let +; + +expr_while +: maybe_label WHILE expr_nostruct block { $$ = mk_node("ExprWhile", 3, $1, $3, $4); } +; + +expr_while_let +: maybe_label WHILE LET pat '=' expr_nostruct block { $$ = mk_node("ExprWhileLet", 4, $1, $4, $6, $7); } +; + +expr_loop +: maybe_label LOOP block { $$ = mk_node("ExprLoop", 2, $1, $3); } +; + +expr_for +: maybe_label FOR pat IN expr_nostruct block { $$ = mk_node("ExprForLoop", 4, $1, $3, $5, $6); } +; + +maybe_label +: lifetime ':' +| %empty { $$ = mk_none(); } +; + +let +: LET pat maybe_ty_ascription maybe_init_expr ';' { $$ = mk_node("DeclLocal", 3, $2, $3, $4); } +; + +//////////////////////////////////////////////////////////////////////// +// Part 5: Macros and misc. rules +//////////////////////////////////////////////////////////////////////// + +lit +: LIT_BYTE { $$ = mk_node("LitByte", 1, mk_atom(yytext)); } +| LIT_CHAR { $$ = mk_node("LitChar", 1, mk_atom(yytext)); } +| LIT_INTEGER { $$ = mk_node("LitInteger", 1, mk_atom(yytext)); } +| LIT_FLOAT { $$ = mk_node("LitFloat", 1, mk_atom(yytext)); } +| TRUE { $$ = mk_node("LitBool", 1, mk_atom(yytext)); } +| FALSE { $$ = mk_node("LitBool", 1, mk_atom(yytext)); } +| str +; + +str +: LIT_STR { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("CookedStr")); } +| LIT_STR_RAW { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("RawStr")); } +| LIT_BINARY { $$ = mk_node("LitBinary", 1, mk_atom(yytext), mk_atom("BinaryStr")); } +| LIT_BINARY_RAW { $$ = mk_node("LitBinary", 1, mk_atom(yytext), mk_atom("RawBinaryStr")); } +; + +maybe_ident +: %empty { $$ = mk_none(); } +| ident +; + +ident +: IDENT { $$ = mk_node("ident", 1, mk_atom(yytext)); } +; + +unpaired_token +: SHL { $$ = mk_atom(yytext); } +| SHR { $$ = mk_atom(yytext); } +| LE { $$ = mk_atom(yytext); } +| EQEQ { $$ = mk_atom(yytext); } +| NE { $$ = mk_atom(yytext); } +| GE { $$ = mk_atom(yytext); } +| ANDAND { $$ = mk_atom(yytext); } +| OROR { $$ = mk_atom(yytext); } +| SHLEQ { $$ = mk_atom(yytext); } +| SHREQ { $$ = mk_atom(yytext); } +| MINUSEQ { $$ = mk_atom(yytext); } +| ANDEQ { $$ = mk_atom(yytext); } +| OREQ { $$ = mk_atom(yytext); } +| PLUSEQ { $$ = mk_atom(yytext); } +| STAREQ { $$ = mk_atom(yytext); } +| SLASHEQ { $$ = mk_atom(yytext); } +| CARETEQ { $$ = mk_atom(yytext); } +| PERCENTEQ { $$ = mk_atom(yytext); } +| DOTDOT { $$ = mk_atom(yytext); } +| DOTDOTDOT { $$ = mk_atom(yytext); } +| MOD_SEP { $$ = mk_atom(yytext); } +| RARROW { $$ = mk_atom(yytext); } +| FAT_ARROW { $$ = mk_atom(yytext); } +| LIT_BYTE { $$ = mk_atom(yytext); } +| LIT_CHAR { $$ = mk_atom(yytext); } +| LIT_INTEGER { $$ = mk_atom(yytext); } +| LIT_FLOAT { $$ = mk_atom(yytext); } +| LIT_STR { $$ = mk_atom(yytext); } +| LIT_STR_RAW { $$ = mk_atom(yytext); } +| LIT_BINARY { $$ = mk_atom(yytext); } +| LIT_BINARY_RAW { $$ = mk_atom(yytext); } +| IDENT { $$ = mk_atom(yytext); } +| UNDERSCORE { $$ = mk_atom(yytext); } +| LIFETIME { $$ = mk_atom(yytext); } +| SELF { $$ = mk_atom(yytext); } +| STATIC { $$ = mk_atom(yytext); } +| AS { $$ = mk_atom(yytext); } +| BREAK { $$ = mk_atom(yytext); } +| CRATE { $$ = mk_atom(yytext); } +| ELSE { $$ = mk_atom(yytext); } +| ENUM { $$ = mk_atom(yytext); } +| EXTERN { $$ = mk_atom(yytext); } +| FALSE { $$ = mk_atom(yytext); } +| FN { $$ = mk_atom(yytext); } +| FOR { $$ = mk_atom(yytext); } +| IF { $$ = mk_atom(yytext); } +| IMPL { $$ = mk_atom(yytext); } +| IN { $$ = mk_atom(yytext); } +| LET { $$ = mk_atom(yytext); } +| LOOP { $$ = mk_atom(yytext); } +| MATCH { $$ = mk_atom(yytext); } +| MOD { $$ = mk_atom(yytext); } +| MOVE { $$ = mk_atom(yytext); } +| MUT { $$ = mk_atom(yytext); } +| PRIV { $$ = mk_atom(yytext); } +| PUB { $$ = mk_atom(yytext); } +| REF { $$ = mk_atom(yytext); } +| RETURN { $$ = mk_atom(yytext); } +| STRUCT { $$ = mk_atom(yytext); } +| TRUE { $$ = mk_atom(yytext); } +| TRAIT { $$ = mk_atom(yytext); } +| TYPE { $$ = mk_atom(yytext); } +| UNSAFE { $$ = mk_atom(yytext); } +| USE { $$ = mk_atom(yytext); } +| WHILE { $$ = mk_atom(yytext); } +| CONTINUE { $$ = mk_atom(yytext); } +| PROC { $$ = mk_atom(yytext); } +| BOX { $$ = mk_atom(yytext); } +| CONST { $$ = mk_atom(yytext); } +| WHERE { $$ = mk_atom(yytext); } +| TYPEOF { $$ = mk_atom(yytext); } +| INNER_DOC_COMMENT { $$ = mk_atom(yytext); } +| OUTER_DOC_COMMENT { $$ = mk_atom(yytext); } +| SHEBANG { $$ = mk_atom(yytext); } +| STATIC_LIFETIME { $$ = mk_atom(yytext); } +| ';' { $$ = mk_atom(yytext); } +| ',' { $$ = mk_atom(yytext); } +| '.' { $$ = mk_atom(yytext); } +| '@' { $$ = mk_atom(yytext); } +| '#' { $$ = mk_atom(yytext); } +| '~' { $$ = mk_atom(yytext); } +| ':' { $$ = mk_atom(yytext); } +| '$' { $$ = mk_atom(yytext); } +| '=' { $$ = mk_atom(yytext); } +| '?' { $$ = mk_atom(yytext); } +| '!' { $$ = mk_atom(yytext); } +| '<' { $$ = mk_atom(yytext); } +| '>' { $$ = mk_atom(yytext); } +| '-' { $$ = mk_atom(yytext); } +| '&' { $$ = mk_atom(yytext); } +| '|' { $$ = mk_atom(yytext); } +| '+' { $$ = mk_atom(yytext); } +| '*' { $$ = mk_atom(yytext); } +| '/' { $$ = mk_atom(yytext); } +| '^' { $$ = mk_atom(yytext); } +| '%' { $$ = mk_atom(yytext); } +; + +token_trees +: %empty { $$ = mk_node("TokenTrees", 0); } +| token_trees token_tree { $$ = ext_node($1, 1, $2); } +; + +token_tree +: delimited_token_trees +| unpaired_token { $$ = mk_node("TTTok", 1, $1); } +; + +delimited_token_trees +: parens_delimited_token_trees +| braces_delimited_token_trees +| brackets_delimited_token_trees +; + +parens_delimited_token_trees +: '(' token_trees ')' +{ + $$ = mk_node("TTDelim", 3, + mk_node("TTTok", 1, mk_atom("(")), + $2, + mk_node("TTTok", 1, mk_atom(")"))); +} +; + +braces_delimited_token_trees +: '{' token_trees '}' +{ + $$ = mk_node("TTDelim", 3, + mk_node("TTTok", 1, mk_atom("{")), + $2, + mk_node("TTTok", 1, mk_atom("}"))); +} +; + +brackets_delimited_token_trees +: '[' token_trees ']' +{ + $$ = mk_node("TTDelim", 3, + mk_node("TTTok", 1, mk_atom("[")), + $2, + mk_node("TTTok", 1, mk_atom("]"))); +} +; diff --git a/src/grammar/testparser.py b/src/grammar/testparser.py new file mode 100755 index 00000000000..38e57be288b --- /dev/null +++ b/src/grammar/testparser.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. +import sys + +import os +import subprocess +import argparse + +# usage: testparser.py [-h] [-p PARSER [PARSER ...]] -s SOURCE_DIR + +# Parsers should read from stdin and return exit status 0 for a +# successful parse, and nonzero for an unsuccessful parse + +parser = argparse.ArgumentParser() +parser.add_argument('-p', '--parser', nargs='+') +parser.add_argument('-s', '--source-dir', nargs=1, required=True) +args = parser.parse_args(sys.argv[1:]) + +total = 0 +ok = {} +bad = {} +for parser in args.parser: + ok[parser] = 0 + bad[parser] = [] +devnull = open(os.devnull, 'w') +print "\n" + +for base, dirs, files in os.walk(args.source_dir[0]): + for f in filter(lambda p: p.endswith('.rs'), files): + p = os.path.join(base, f) + compile_fail = 'compile-fail' in p + ignore = any('ignore-test' in line or 'ignore-lexer-test' in line + for line in open(p).readlines()) + if compile_fail or ignore: + continue + total += 1 + for parser in args.parser: + if subprocess.call(parser, stdin=open(p), stderr=subprocess.STDOUT, stdout=devnull) == 0: + ok[parser] += 1 + else: + bad[parser].append(p) + parser_stats = ', '.join(['{}: {}'.format(parser, ok[parser]) for parser in args.parser]) + sys.stdout.write("\033[K\r total: {}, {}, scanned {}" + .format(total, os.path.relpath(parser_stats), os.path.relpath(p))) + +devnull.close() + +print "\n" + +for parser in args.parser: + filename = os.path.basename(parser) + '.bad' + print("writing {} files that failed to parse with {} to {}".format(len(bad[parser]), parser, filename)) + with open(filename, "w") as f: + for p in bad[parser]: + f.write(p) + f.write("\n") diff --git a/src/grammar/tokens.h b/src/grammar/tokens.h new file mode 100644 index 00000000000..4457e3f8b5a --- /dev/null +++ b/src/grammar/tokens.h @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Token { + SHL = 257, // Parser generators reserve 0-256 for char literals + SHR, + LE, + EQEQ, + NE, + GE, + ANDAND, + OROR, + SHLEQ, + SHREQ, + MINUSEQ, + ANDEQ, + OREQ, + PLUSEQ, + STAREQ, + SLASHEQ, + CARETEQ, + PERCENTEQ, + DOTDOT, + DOTDOTDOT, + MOD_SEP, + RARROW, + FAT_ARROW, + LIT_BYTE, + LIT_CHAR, + LIT_INTEGER, + LIT_FLOAT, + LIT_STR, + LIT_STR_RAW, + LIT_BINARY, + LIT_BINARY_RAW, + IDENT, + UNDERSCORE, + LIFETIME, + + // keywords + SELF, + STATIC, + AS, + BREAK, + CRATE, + ELSE, + ENUM, + EXTERN, + FALSE, + FN, + FOR, + IF, + IMPL, + IN, + LET, + LOOP, + MATCH, + MOD, + MOVE, + MUT, + PRIV, + PUB, + REF, + RETURN, + STRUCT, + TRUE, + TRAIT, + TYPE, + UNSAFE, + USE, + WHILE, + CONTINUE, + PROC, + BOX, + CONST, + WHERE, + TYPEOF, + INNER_DOC_COMMENT, + OUTER_DOC_COMMENT, + + SHEBANG, + SHEBANG_LINE, + STATIC_LIFETIME +}; From be138ed2bd6a4c4bf1c80287569709a9905e4891 Mon Sep 17 00:00:00 2001 From: P1start <rewi-github@whanau.org> Date: Wed, 21 Jan 2015 11:04:17 +1300 Subject: [PATCH 03/33] Add `#[rustc_on_unimplemented]` annotations to more traits --- src/libcore/fmt/mod.rs | 4 ++++ src/libcore/iter.rs | 2 ++ src/libcore/marker.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0e8d31a62ee..60403d1c011 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -243,6 +243,8 @@ pub trait Show { /// Format trait for the `:?` format. Useful for debugging, most all types /// should implement this. #[unstable = "I/O and core have yet to be reconciled"] +#[rustc_on_unimplemented = "`{Self}` cannot be formatted using `:?`; if it is defined in your \ + crate, add `#[derive(Debug)]` or manually implement it"] pub trait Debug { /// Formats the value using the given formatter. fn fmt(&self, &mut Formatter) -> Result; @@ -266,6 +268,8 @@ pub trait String { /// When a value can be semantically expressed as a String, this trait may be /// used. It corresponds to the default format, `{}`. #[unstable = "I/O and core have yet to be reconciled"] +#[rustc_on_unimplemented = "`{Self}` cannot be formatted with the default formatter; try using \ + `:?` instead if you are using a format string"] pub trait Display { /// Formats the value using the given formatter. fn fmt(&self, &mut Formatter) -> Result; diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 773ac99b0de..c31ad56fe5b 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -82,6 +82,8 @@ use uint; /// else. #[lang="iterator"] #[stable] +#[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling `.iter()` or a similar \ + method"] pub trait Iterator { #[stable] type Item; diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index a987a0a5068..f32552f300c 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -30,6 +30,7 @@ use clone::Clone; /// Types able to be transferred across task boundaries. #[unstable = "will be overhauled with new lifetime rules; see RFC 458"] #[lang="send"] +#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] pub unsafe trait Send: 'static { // empty. } @@ -37,6 +38,7 @@ pub unsafe trait Send: 'static { /// Types with a constant size known at compile-time. #[stable] #[lang="sized"] +#[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"] pub trait Sized { // Empty. } @@ -193,6 +195,7 @@ pub trait Copy { /// `transmute`-ing from `&T` to `&mut T` is illegal). #[unstable = "will be overhauled with new lifetime rules; see RFC 458"] #[lang="sync"] +#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"] pub unsafe trait Sync { // Empty } From 87db16cecd165ab8ed57dfa308a429868716fdc9 Mon Sep 17 00:00:00 2001 From: P1start <rewi-github@whanau.org> Date: Thu, 22 Jan 2015 22:22:16 +1300 Subject: [PATCH 04/33] core::marker: s/task/thread/ --- src/libcore/marker.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index f32552f300c..688f0484401 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -27,7 +27,7 @@ use clone::Clone; -/// Types able to be transferred across task boundaries. +/// Types able to be transferred across thread boundaries. #[unstable = "will be overhauled with new lifetime rules; see RFC 458"] #[lang="send"] #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] @@ -148,11 +148,11 @@ pub trait Copy { // Empty. } -/// Types that can be safely shared between tasks when aliased. +/// Types that can be safely shared between threads when aliased. /// /// The precise definition is: a type `T` is `Sync` if `&T` is /// thread-safe. In other words, there is no possibility of data races -/// when passing `&T` references between tasks. +/// when passing `&T` references between threads. /// /// As one would expect, primitive types like `u8` and `f64` are all /// `Sync`, and so are simple aggregate types containing them (like From 3dbac7882e27d680112cf172b789f9741c7ab25d Mon Sep 17 00:00:00 2001 From: blackbeam <aikorsky@gmail.com> Date: Thu, 22 Jan 2015 12:29:49 +0300 Subject: [PATCH 05/33] libsyntax: fix for `has_test_signature` --- src/libsyntax/test.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index e5d8e4e5143..8933d3a9669 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -295,6 +295,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool { &ast::ItemFn(ref decl, _, _, ref generics, _) => { let no_output = match decl.output { ast::DefaultReturn(..) => true, + ast::Return(ref t) if t.node == ast::TyTup(vec![]) => true, _ => false }; if decl.inputs.is_empty() @@ -331,6 +332,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool { let input_cnt = decl.inputs.len(); let no_output = match decl.output { ast::DefaultReturn(..) => true, + ast::Return(ref t) if t.node == ast::TyTup(vec![]) => true, _ => false }; let tparm_cnt = generics.ty_params.len(); From fd029209ec4901580b1cc0180098e0b4024bee5b Mon Sep 17 00:00:00 2001 From: blackbeam <aikorsky@gmail.com> Date: Thu, 22 Jan 2015 21:38:00 +0300 Subject: [PATCH 06/33] Add test for `has_test_signature` for explicit return --- ...e-verification-for-explicit-return-type.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs diff --git a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs new file mode 100644 index 00000000000..65a6ed2b332 --- /dev/null +++ b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --test +extern crate test; + +#[bench] +fn bench_explicit_return_type(_: &mut ::test::Bencher) -> () {} + +#[test] +fn test_explicit_return_type() -> () {} + From 4cfb70026c91a338518a17579bcd62a32a0a413d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= <john.kare.alsaker@gmail.com> Date: Thu, 22 Jan 2015 19:43:39 +0100 Subject: [PATCH 07/33] Better inline assembly errors --- src/librustc_llvm/diagnostic.rs | 33 +++++++++++++++++++++- src/librustc_llvm/lib.rs | 4 +++ src/librustc_trans/back/write.rs | 47 +++++++++++++++++++------------- src/rustllvm/RustWrapper.cpp | 16 +++++++++++ 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/librustc_llvm/diagnostic.rs b/src/librustc_llvm/diagnostic.rs index db2a569cdef..5d0ae9d1f92 100644 --- a/src/librustc_llvm/diagnostic.rs +++ b/src/librustc_llvm/diagnostic.rs @@ -13,7 +13,7 @@ pub use self::OptimizationDiagnosticKind::*; pub use self::Diagnostic::*; -use libc::c_char; +use libc::{c_char, c_uint}; use std::ptr; use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef}; @@ -69,9 +69,37 @@ impl OptimizationDiagnostic { } } +pub struct InlineAsmDiagnostic { + pub cookie: c_uint, + pub message: TwineRef, + pub instruction: ValueRef, +} + +impl Copy for InlineAsmDiagnostic {} + +impl InlineAsmDiagnostic { + unsafe fn unpack(di: DiagnosticInfoRef) + -> InlineAsmDiagnostic { + + let mut opt = InlineAsmDiagnostic { + cookie: 0, + message: ptr::null_mut(), + instruction: ptr::null_mut(), + }; + + super::LLVMUnpackInlineAsmDiagnostic(di, + &mut opt.cookie, + &mut opt.message, + &mut opt.instruction); + + opt + } +} + #[derive(Copy)] pub enum Diagnostic { Optimization(OptimizationDiagnostic), + InlineAsm(InlineAsmDiagnostic), /// LLVM has other types that we do not wrap here. UnknownDiagnostic(DiagnosticInfoRef), @@ -82,6 +110,9 @@ impl Diagnostic { let kind = super::LLVMGetDiagInfoKind(di); match kind { + super::DK_InlineAsm + => InlineAsm(InlineAsmDiagnostic::unpack(di)), + super::DK_OptimizationRemark => Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)), diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index eded88bb62e..8c651cf839b 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -2049,6 +2049,10 @@ extern { function_out: *mut ValueRef, debugloc_out: *mut DebugLocRef, message_out: *mut TwineRef); + pub fn LLVMUnpackInlineAsmDiagnostic(DI: DiagnosticInfoRef, + cookie_out: *mut c_uint, + message_out: *mut TwineRef, + instruction_out: *mut ValueRef); pub fn LLVMWriteDiagnosticInfoToString(DI: DiagnosticInfoRef, s: RustStringRef); pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity; diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b07c2060e69..5e48ce384be 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -336,30 +336,36 @@ struct HandlerFreeVars<'a> { cgcx: &'a CodegenContext<'a>, } +unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, + msg: &'b str, + cookie: c_uint) { + use syntax::codemap::ExpnId; + + match cgcx.lto_ctxt { + Some((sess, _)) => { + sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info { + Some(ei) => sess.span_err(ei.call_site, msg), + None => sess.err(msg), + }); + } + + None => { + cgcx.handler.err(msg); + cgcx.handler.note("build without -C codegen-units for more exact errors"); + } + } +} + unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, user: *const c_void, cookie: c_uint) { - use syntax::codemap::ExpnId; - let HandlerFreeVars { cgcx, .. } = *mem::transmute::<_, *const HandlerFreeVars>(user); let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s)) .expect("non-UTF8 SMDiagnostic"); - match cgcx.lto_ctxt { - Some((sess, _)) => { - sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info { - Some(ei) => sess.span_err(ei.call_site, &msg[]), - None => sess.err(&msg[]), - }); - } - - None => { - cgcx.handler.err(&msg[]); - cgcx.handler.note("build without -C codegen-units for more exact errors"); - } - } + report_inline_asm(cgcx, &msg[], cookie); } unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { @@ -367,6 +373,12 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo = *mem::transmute::<_, *const HandlerFreeVars>(user); match llvm::diagnostic::Diagnostic::unpack(info) { + llvm::diagnostic::InlineAsm(inline) => { + report_inline_asm(cgcx, + llvm::twine_to_string(inline.message).as_slice(), + inline.cookie); + } + llvm::diagnostic::Optimization(opt) => { let pass_name = str::from_utf8(ffi::c_str_to_bytes(&opt.pass_name)) .ok() @@ -407,10 +419,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, let fv = &fv as *const HandlerFreeVars as *mut c_void; llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); - - if !cgcx.remark.is_empty() { - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); - } + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); if config.emit_no_opt_bc { let ext = format!("{}.no-opt.bc", name_extra); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index bce73a27699..728ff1461fc 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -894,6 +894,22 @@ LLVMUnpackOptimizationDiagnostic( *message_out = wrap(&opt->getMsg()); } +extern "C" void +LLVMUnpackInlineAsmDiagnostic( + LLVMDiagnosticInfoRef di, + unsigned *cookie_out, + LLVMTwineRef *message_out, + LLVMValueRef *instruction_out) +{ + // Undefined to call this not on an inline assembly diagnostic! + llvm::DiagnosticInfoInlineAsm *ia + = static_cast<llvm::DiagnosticInfoInlineAsm*>(unwrap(di)); + + *cookie_out = ia->getLocCookie(); + *message_out = wrap(&ia->getMsgStr()); + *instruction_out = wrap(ia->getInstruction()); +} + extern "C" void LLVMWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef di, RustStringRef str) { raw_rust_string_ostream os(str); DiagnosticPrinterRawOStream dp(os); From 918dd3488f450383c5ce27f0a27f45fa3fc9d0d6 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos <SeanPatrickSantos@gmail.com> Date: Thu, 22 Jan 2015 11:54:45 -0700 Subject: [PATCH 08/33] Attempt fix for assertion on Windows, and add extra output for debugging. --- src/test/run-pass/issue-15149.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs index 59b1bb287fa..4b345f639e4 100644 --- a/src/test/run-pass/issue-15149.rs +++ b/src/test/run-pass/issue-15149.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::path::BytesContainer; use std::io::{Command, fs, USER_RWX}; use std::os; @@ -15,7 +16,8 @@ fn main() { // If we're the child, make sure we were invoked correctly let args = os::args(); if args.len() > 1 && args[1].as_slice() == "child" { - return assert_eq!(args[0].as_slice(), "mytest"); + return assert_eq!(args[0], + format!("mytest{}", os::consts::EXE_SUFFIX)); } test(); @@ -38,7 +40,12 @@ fn test() { path.push(child_dir.clone()); let path = os::join_paths(path.as_slice()).unwrap(); - assert!(Command::new("mytest").env("PATH", path.as_slice()) - .arg("child") - .status().unwrap().success()); + let child_output = Command::new("mytest").env("PATH", path.as_slice()) + .arg("child") + .output().unwrap(); + + assert!(child_output.status.success(), + format!("child assertion failed\n child stdout:\n {}\n child stderr:\n {}", + child_output.output.container_as_str().unwrap(), + child_output.error.container_as_str().unwrap())); } From ea6dc180fe145176e20ae7ecd6b9b0b7afffe67c Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Thu, 22 Jan 2015 18:59:33 -0500 Subject: [PATCH 09/33] remove weird sentence Fixes #21531 --- src/doc/reference.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 9ec4708eb2f..e5fefc9e4c2 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2640,9 +2640,7 @@ The currently implemented features of the reference compiler are: declare a `static` as being unique per-thread leveraging LLVM's implementation which works in concert with the kernel loader and dynamic linker. This is not necessarily available - on all platforms, and usage of it is discouraged (rust - focuses more on thread-local data instead of thread-local - data). + on all platforms, and usage of it is discouraged. * `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty hack that will certainly be removed. From 7f45dc9e68ffe80d399560644af48383a25427fd Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos <SeanPatrickSantos@gmail.com> Date: Thu, 22 Jan 2015 18:34:58 -0700 Subject: [PATCH 10/33] Add a random number string to the end of the issue-15149 test's child directory's name, and remove the directory after a successful test. --- src/test/run-pass/issue-15149.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs index 4b345f639e4..5d3571e4d74 100644 --- a/src/test/run-pass/issue-15149.rs +++ b/src/test/run-pass/issue-15149.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::path::BytesContainer; use std::io::{Command, fs, USER_RWX}; use std::os; +use std::path::BytesContainer; +use std::rand::random; fn main() { // If we're the child, make sure we were invoked correctly @@ -28,8 +29,10 @@ fn test() { let my_path = os::self_exe_name().unwrap(); let my_dir = my_path.dir_path(); - let child_dir = Path::new(my_dir.join("issue-15149-child")); - drop(fs::mkdir(&child_dir, USER_RWX)); + let random_u32: u32 = random(); + let child_dir = Path::new(my_dir.join(format!("issue-15149-child-{}", + random_u32))); + fs::mkdir(&child_dir, USER_RWX).unwrap(); let child_path = child_dir.join(format!("mytest{}", os::consts::EXE_SUFFIX)); @@ -48,4 +51,7 @@ fn test() { format!("child assertion failed\n child stdout:\n {}\n child stderr:\n {}", child_output.output.container_as_str().unwrap(), child_output.error.container_as_str().unwrap())); + + fs::rmdir_recursive(&child_dir).unwrap(); + } From a80807028a027c0a677777c8120237c5b3047993 Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Thu, 22 Jan 2015 22:35:28 -0500 Subject: [PATCH 11/33] Language tweak in configure Fixes #13082 --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index b2f8c33380c..5d26d4e7ed5 100755 --- a/configure +++ b/configure @@ -28,8 +28,8 @@ need_ok() { need_cmd() { if command -v $1 >/dev/null 2>&1 - then msg "found $1" - else err "need $1" + then msg "found program $1" + else err "need program $1" fi } From 7aa3ed1ee8b04df11ed42aeea64495cc1190aaf5 Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Thu, 22 Jan 2015 22:44:43 -0500 Subject: [PATCH 12/33] Check for make in configure Fixes #13179 --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index b2f8c33380c..d3867b85074 100755 --- a/configure +++ b/configure @@ -340,6 +340,7 @@ need_cmd date need_cmd tr need_cmd sed need_cmd file +need_cmd make msg "inspecting environment" From fc5bbdf70f8a9b0bfd7e377cc7df732b119bdac7 Mon Sep 17 00:00:00 2001 From: Kevin Yap <me@kevinyap.ca> Date: Thu, 22 Jan 2015 17:54:49 -0800 Subject: [PATCH 13/33] Make `make tidy` Python scripts more idiomatic Also makes errorck.py and tidy.py compatible with Python 3. --- src/etc/errorck.py | 39 ++++++++++++++------------------------- src/etc/licenseck.py | 12 ++++++------ src/etc/tidy.py | 2 +- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/etc/errorck.py b/src/etc/errorck.py index 17659309d3b..952e299265d 100644 --- a/src/etc/errorck.py +++ b/src/etc/errorck.py @@ -14,11 +14,10 @@ import sys, os, re src_dir = sys.argv[1] - -errcode_map = { } +errcode_map = {} +error_re = re.compile("(E\d\d\d\d)") for (dirpath, dirnames, filenames) in os.walk(src_dir): - if "src/test" in dirpath or "src/llvm" in dirpath: # Short circuit for fast continue @@ -28,15 +27,12 @@ for (dirpath, dirnames, filenames) in os.walk(src_dir): continue path = os.path.join(dirpath, filename) - line_num = 1 + with open(path, 'r') as f: - for line in f: - - p = re.compile("(E\d\d\d\d)") - m = p.search(line) - if not m is None: - errcode = m.group(1) - + for line_num, line in enumerate(f, start=1): + match = error_re.search(line) + if match: + errcode = match.group(1) new_record = [(errcode, path, line_num, line)] existing = errcode_map.get(errcode) if existing is not None: @@ -45,26 +41,19 @@ for (dirpath, dirnames, filenames) in os.walk(src_dir): else: errcode_map[errcode] = new_record - line_num += 1 - errors = False all_errors = [] -for errcode in errcode_map: - entries = errcode_map[errcode] - all_errors += [entries[0][0]] + +for errcode, entries in errcode_map.items(): + all_errors.append(entries[0][0]) if len(entries) > 1: - print "error: duplicate error code " + errcode + print("error: duplicate error code " + errcode) for entry in entries: - print entry[1] + ": " + str(entry[2]) - print entry[3] + print("{1}: {2}\n{3}".format(*entry)) errors = True -print str(len(errcode_map)) + " error codes" - -all_errors.sort() -all_errors.reverse() - -print "highest error code: " + all_errors[0] +print("{0} error codes".format(len(errcode_map))) +print("highest error code: " + max(all_errors)) if errors: sys.exit(1) diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 9ac0acc38a7..44a50efcd09 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -57,18 +57,18 @@ exceptions = [ def check_license(name, contents): # Whitelist check - for exception in exceptions: - if name.endswith(exception): - return True + if any(name.endswith(e) for e in exceptions): + return True # Xfail check firstlineish = contents[:100] - if firstlineish.find("ignore-license") != -1: + if "ignore-license" in firstlineish: return True # License check boilerplate = contents[:500] - if (boilerplate.find(license1) == -1 or boilerplate.find(license2) == -1) and \ - (boilerplate.find(license3) == -1 or boilerplate.find(license4) == -1): + if (license1 not in boilerplate or license2 not in boilerplate) and \ + (license3 not in boilerplate or license4 not in boilerplate): return False + return True diff --git a/src/etc/tidy.py b/src/etc/tidy.py index 536ab7f30b9..c65b762e517 100644 --- a/src/etc/tidy.py +++ b/src/etc/tidy.py @@ -113,7 +113,7 @@ try: if current_name != "": do_license_check(current_name, current_contents) -except UnicodeDecodeError, e: +except UnicodeDecodeError as e: report_err("UTF-8 decoding error " + str(e)) From 76c279a4cfc9e54327bcc668a895ec19185139e9 Mon Sep 17 00:00:00 2001 From: Kevin Yap <me@kevinyap.ca> Date: Thu, 22 Jan 2015 20:40:15 -0800 Subject: [PATCH 14/33] Use a regex to perform license check --- src/etc/licenseck.py | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 44a50efcd09..f38583ee1fb 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -8,29 +8,18 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. -license1 = """// Copyright """ -license2 = """ The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -""" +import re -license3 = """# Copyright """ -license4 = """ The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -""" +license_re = re.compile( +u"""(#|//) Copyright .* The Rust Project Developers. See the COPYRIGHT +\\1 file at the top-level directory of this distribution and at +\\1 http://rust-lang.org/COPYRIGHT. +\\1 +\\1 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +\\1 http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +\\1 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +\\1 option. This file may not be copied, modified, or distributed +\\1 except according to those terms.""") exceptions = [ "rt/rust_android_dummy.cpp", # BSD, chromium @@ -67,8 +56,4 @@ def check_license(name, contents): # License check boilerplate = contents[:500] - if (license1 not in boilerplate or license2 not in boilerplate) and \ - (license3 not in boilerplate or license4 not in boilerplate): - return False - - return True + return bool(license_re.search(boilerplate)) From 06714c2bceb1ec7803e1a3c52e613fa003722fbc Mon Sep 17 00:00:00 2001 From: Flavio Percoco <flaper87@gmail.com> Date: Fri, 23 Jan 2015 15:21:11 +0100 Subject: [PATCH 15/33] Fix compile test for stage0 --- src/compiletest/common.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index c21785c45a3..eb8c013de0e 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -13,6 +13,20 @@ use std::fmt; use std::str::FromStr; use regex::Regex; +#[cfg(stage0)] // NOTE: remove impl after snapshot +#[derive(Clone, PartialEq, Show)] +pub enum Mode { + CompileFail, + RunFail, + RunPass, + RunPassValgrind, + Pretty, + DebugInfoGdb, + DebugInfoLldb, + Codegen +} + +#[cfg(not(stage0))] // NOTE: remove cfg after snapshot #[derive(Clone, PartialEq, Debug)] pub enum Mode { CompileFail, @@ -25,6 +39,7 @@ pub enum Mode { Codegen } + impl Copy for Mode {} impl FromStr for Mode { From aca793966a84dfec785c3e300e62588dad35dfeb Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Tue, 13 Jan 2015 15:00:49 -0500 Subject: [PATCH 16/33] Soup up 'method syntax' chapter of the Book Fixes #16969 --- src/doc/trpl/method-syntax.md | 114 +++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/src/doc/trpl/method-syntax.md b/src/doc/trpl/method-syntax.md index 3d8de00991c..e6570c2ee74 100644 --- a/src/doc/trpl/method-syntax.md +++ b/src/doc/trpl/method-syntax.md @@ -18,6 +18,8 @@ x.foo().bar().baz(); Luckily, as you may have guessed with the leading question, you can! Rust provides the ability to use this *method call syntax* via the `impl` keyword. +## Method calls + Here's how it works: ```{rust} @@ -56,11 +58,56 @@ other parameter. Because we know it's a `Circle`, we can access the `radius` just like we would with any other struct. An import of π and some multiplications later, and we have our area. +## Chaining method calls + +So, now we know how to call a method, such as `foo.bar()`. But what about our +original example, `foo.bar().baz()`? This is called 'method chaining', and we +can do it by returning `self`. + +``` +struct Circle { + x: f64, + y: f64, + radius: f64, +} + +impl Circle { + fn area(&self) -> f64 { + std::f64::consts::PI * (self.radius * self.radius) + } + + fn grow(&self) -> Circle { + Circle { x: self.x, y: self.y, radius: (self.radius * 10.0) } + } +} + +fn main() { + let c = Circle { x: 0.0, y: 0.0, radius: 2.0 }; + println!("{}", c.area()); + + let d = c.grow().area(); + println!("{}", d); +} +``` + +Check the return type: + +``` +# struct Circle; +# impl Circle { +fn grow(&self) -> Circle { +# Circle } } +``` + +We just say we're returning a `Circle`. With this, we can grow a new circle +that's twice as big as the old one. + +## Static methods + You can also define methods that do not take a `self` parameter. Here's a pattern that's very common in Rust code: -```{rust} -# #![allow(non_shorthand_field_patterns)] +``` struct Circle { x: f64, y: f64, @@ -86,3 +133,66 @@ This *static method* builds a new `Circle` for us. Note that static methods are called with the `Struct::method()` syntax, rather than the `ref.method()` syntax. +## Builder Pattern + +Let's say that we want our users to be able to create Circles, but we will +allow them to only set the properties they care about. Otherwise, the `x` +and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't +have method overloading, named arguments, or variable arguments. We employ +the builder pattern instead. It looks like this: + +``` +struct Circle { + x: f64, + y: f64, + radius: f64, +} + +impl Circle { + fn area(&self) -> f64 { + std::f64::consts::PI * (self.radius * self.radius) + } +} + +struct CircleBuilder { + coordinate: f64, + radius: f64, +} + +impl CircleBuilder { + fn new() -> CircleBuilder { + CircleBuilder { coordinate: 0.0, radius: 0.0, } + } + + fn coordinate(&mut self, coordinate: f64) -> &mut CircleBuilder { + self.coordinate = coordinate; + self + } + + fn radius(&mut self, radius: f64) -> &mut CircleBuilder { + self.radius = radius; + self + } + + fn finalize(&self) -> Circle { + Circle { x: self.coordinate, y: self.coordinate, radius: self.radius } + } +} + +fn main() { + let c = CircleBuilder::new() + .coordinate(10.0) + .radius(5.0) + .finalize(); + + + println!("area: {}", c.area()); +} +``` + +What we've done here is make another struct, `CircleBuilder`. We've defined our +builder methods on it. We've also defined our `area()` method on `Circle`. We +also made one more method on `CircleBuilder`: `finalize()`. This method creates +our final `Circle` from the builder. Now, we've used the type system to enforce +our concerns: we can use the methods on `CircleBuilder` to constrain making +`Circle`s in any way we choose. From dc2b3ac889d9841bff3cd9ddc7c63f5955212313 Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Thu, 22 Jan 2015 19:09:59 -0500 Subject: [PATCH 17/33] Remove lang items from the reference. Fixes #19759 --- src/doc/reference.md | 131 +------------------------------------------ 1 file changed, 1 insertion(+), 130 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 9ec4708eb2f..a0d46750097 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2279,136 +2279,7 @@ The name `str_eq` has a special meaning to the Rust compiler, and the presence of this definition means that it will use this definition when generating calls to the string equality function. -A complete list of the built-in language items follows: - -#### Built-in Traits - -* `copy` - : Types that do not move ownership when used by-value. -* `drop` - : Have destructors. -* `send` - : Able to be sent across thread boundaries. -* `sized` - : Has a size known at compile time. -* `sync` - : Able to be safely shared between threads when aliased. - -#### Operators - -These language items are traits: - -* `add` - : Elements can be added (for example, integers and floats). -* `sub` - : Elements can be subtracted. -* `mul` - : Elements can be multiplied. -* `div` - : Elements have a division operation. -* `rem` - : Elements have a remainder operation. -* `neg` - : Elements can be negated arithmetically. -* `not` - : Elements can be negated logically. -* `bitxor` - : Elements have an exclusive-or operation. -* `bitand` - : Elements have a bitwise `and` operation. -* `bitor` - : Elements have a bitwise `or` operation. -* `shl` - : Elements have a left shift operation. -* `shr` - : Elements have a right shift operation. -* `index` - : Elements can be indexed. -* `index_mut` - : ___Needs filling in___ -* `eq` - : Elements can be compared for equality. -* `ord` - : Elements have a partial ordering. -* `deref` - : `*` can be applied, yielding a reference to another type. -* `deref_mut` - : `*` can be applied, yielding a mutable reference to another type. - -These are functions: - -* `fn` - : ___Needs filling in___ -* `fn_mut` - : ___Needs filling in___ -* `fn_once` - : ___Needs filling in___ -* `str_eq` - : Compare two strings (`&str`) for equality. -* `strdup_uniq` - : Return a new unique string - containing a copy of the contents of a unique string. - -#### Types - -* `type_id` - : The type returned by the `type_id` intrinsic. -* `unsafe` - : A type whose contents can be mutated through an immutable reference. - -#### Marker types - -These types help drive the compiler's analysis - -* `begin_unwind` - : ___Needs filling in___ -* `no_copy_bound` - : This type does not implement "copy", even if eligible. -* `eh_personality` - : ___Needs filling in___ -* `exchange_free` - : Free memory that was allocated on the exchange heap. -* `exchange_malloc` - : Allocate memory on the exchange heap. -* `closure_exchange_malloc` - : ___Needs filling in___ -* `panic` - : Abort the program with an error. -* `fail_bounds_check` - : Abort the program with a bounds check error. -* `free` - : Free memory that was allocated on the managed heap. -* `gc` - : ___Needs filling in___ -* `exchange_heap` - : ___Needs filling in___ -* `iterator` - : ___Needs filling in___ -* `contravariant_lifetime` - : The lifetime parameter should be considered contravariant. -* `covariant_lifetime` - : The lifetime parameter should be considered covariant. -* `invariant_lifetime` - : The lifetime parameter should be considered invariant. -* `malloc` - : Allocate memory on the managed heap. -* `owned_box` - : ___Needs filling in___ -* `stack_exhausted` - : ___Needs filling in___ -* `start` - : ___Needs filling in___ -* `contravariant_type` - : The type parameter should be considered contravariant. -* `covariant_type` - : The type parameter should be considered covariant. -* `invariant_type` - : The type parameter should be considered invariant. -* `ty_desc` - : ___Needs filling in___ - -> **Note:** This list is likely to become out of date. We should auto-generate -> it from `librustc/middle/lang_items.rs`. +A complete list of the built-in language items will be added in the future. ### Inline attributes From 91037a417ed9bd19608f3f06fd6214fcc0daf9cf Mon Sep 17 00:00:00 2001 From: Steve Klabnik <steve@steveklabnik.com> Date: Fri, 23 Jan 2015 13:53:19 -0500 Subject: [PATCH 18/33] remove discuss link from the book --- src/doc/trpl/installing-rust.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/trpl/installing-rust.md b/src/doc/trpl/installing-rust.md index 5893b51a420..80288c4c3d9 100644 --- a/src/doc/trpl/installing-rust.md +++ b/src/doc/trpl/installing-rust.md @@ -83,7 +83,6 @@ If not, there are a number of places where you can get help. The easiest is you can access through [Mibbit](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust). Click that link, and you'll be chatting with other Rustaceans (a silly nickname we -call ourselves), and we can help you out. Other great resources include [our -forum](http://discuss.rust-lang.org/), [the /r/rust -subreddit](http://www.reddit.com/r/rust), and [Stack +call ourselves), and we can help you out. Other great resources include [the +/r/rust subreddit](http://www.reddit.com/r/rust), and [Stack Overflow](http://stackoverflow.com/questions/tagged/rust). From 08246520c0fef902b169233e26e15cf58ef1cd8b Mon Sep 17 00:00:00 2001 From: Alex Crichton <alex@alexcrichton.com> Date: Fri, 23 Jan 2015 10:38:50 -0800 Subject: [PATCH 19/33] std: Relax Result::unwrap() to Debug This commit relaxes the bound on `Result::unwrap` and `Result::unwrap_err` from the `Display` trait to the `Debug` trait for generating an error message about the unwrapping operation. This commit is a breaking change and any breakage should be mitigated by ensuring that `Debug` is implemented on the relevant type. [breaking-change] --- src/libcore/result.rs | 10 +++++----- src/libstd/sync/mpsc/mod.rs | 21 +++++++++++++++++++-- src/libstd/sync/poison.rs | 19 +++++++++++++++++-- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c3d49e24978..118f29ccfa4 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -229,7 +229,7 @@ use self::Result::{Ok, Err}; use clone::Clone; -use fmt::Display; +use fmt::Debug; use iter::{Iterator, IteratorExt, DoubleEndedIterator, FromIterator, ExactSizeIterator}; use ops::{FnMut, FnOnce}; use option::Option::{self, None, Some}; @@ -714,7 +714,7 @@ impl<T, E> Result<T, E> { } #[stable] -impl<T, E: Display> Result<T, E> { +impl<T, E: Debug> Result<T, E> { /// Unwraps a result, yielding the content of an `Ok`. /// /// # Panics @@ -739,13 +739,13 @@ impl<T, E: Display> Result<T, E> { match self { Ok(t) => t, Err(e) => - panic!("called `Result::unwrap()` on an `Err` value: {}", e) + panic!("called `Result::unwrap()` on an `Err` value: {:?}", e) } } } #[stable] -impl<T: Display, E> Result<T, E> { +impl<T: Debug, E> Result<T, E> { /// Unwraps a result, yielding the content of an `Err`. /// /// # Panics @@ -769,7 +769,7 @@ impl<T: Display, E> Result<T, E> { pub fn unwrap_err(self) -> E { match self { Ok(t) => - panic!("called `Result::unwrap_err()` on an `Ok` value: {}", t), + panic!("called `Result::unwrap_err()` on an `Ok` value: {:?}", t), Err(e) => e } } diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 062bdf346cd..893f353b1a7 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -382,7 +382,7 @@ impl<T> !Sync for SyncSender<T> {} /// A `send` operation can only fail if the receiving end of a channel is /// disconnected, implying that the data could never be received. The error /// contains the data being sent as a payload so it can be recovered. -#[derive(PartialEq, Eq, Show)] +#[derive(PartialEq, Eq)] #[stable] pub struct SendError<T>(pub T); @@ -412,7 +412,7 @@ pub enum TryRecvError { /// This enumeration is the list of the possible error outcomes for the /// `SyncSender::try_send` method. -#[derive(PartialEq, Clone, Show)] +#[derive(PartialEq, Clone)] #[stable] pub enum TrySendError<T> { /// The data could not be sent on the channel because it would require that @@ -961,6 +961,13 @@ impl<T: Send> Drop for Receiver<T> { } } +#[stable] +impl<T> fmt::Debug for SendError<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "SendError(..)".fmt(f) + } +} + #[stable] impl<T> fmt::Display for SendError<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -968,6 +975,16 @@ impl<T> fmt::Display for SendError<T> { } } +#[stable] +impl<T> fmt::Debug for TrySendError<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TrySendError::Full(..) => "Full(..)".fmt(f), + TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), + } + } +} + #[stable] impl<T> fmt::Display for TrySendError<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs index c97fcf7cefb..d7fcee3ae2e 100644 --- a/src/libstd/sync/poison.rs +++ b/src/libstd/sync/poison.rs @@ -53,7 +53,6 @@ pub struct Guard { /// is held. The precise semantics for when a lock is poisoned is documented on /// each lock, but once a lock is poisoned then all future acquisitions will /// return this error. -#[derive(Show)] #[stable] pub struct PoisonError<T> { guard: T, @@ -61,7 +60,6 @@ pub struct PoisonError<T> { /// An enumeration of possible errors which can occur while calling the /// `try_lock` method. -#[derive(Show)] #[stable] pub enum TryLockError<T> { /// The lock could not be acquired because another task failed while holding @@ -92,6 +90,13 @@ pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>; #[stable] pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>; +#[stable] +impl<T> fmt::Debug for PoisonError<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "PoisonError { inner: .. }".fmt(f) + } +} + #[stable] impl<T> fmt::Display for PoisonError<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -133,6 +138,16 @@ impl<T> FromError<PoisonError<T>> for TryLockError<T> { } } +#[stable] +impl<T> fmt::Debug for TryLockError<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), + TryLockError::WouldBlock => "WouldBlock".fmt(f) + } + } +} + #[stable] impl<T> fmt::Display for TryLockError<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From 6f7c0b16d3bb0be438a0394595c74904ab868770 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio <japaricious@gmail.com> Date: Fri, 23 Jan 2015 19:35:10 -0500 Subject: [PATCH 20/33] add test for issue 19660 closes #19660 --- src/test/compile-fail/issue-19660.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/test/compile-fail/issue-19660.rs diff --git a/src/test/compile-fail/issue-19660.rs b/src/test/compile-fail/issue-19660.rs new file mode 100644 index 00000000000..f83037d47bb --- /dev/null +++ b/src/test/compile-fail/issue-19660.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: requires `copy` lang_item + +#![feature(lang_items, start)] +#![no_std] + +#[lang = "sized"] +trait Sized {} + +#[start] +fn main(_: int, _: *const *const u8) -> int { + 0 +} From 000dc07f71baafc31da74e392ad5530fb6de9757 Mon Sep 17 00:00:00 2001 From: Huon Wilson <dbau.pp+github@gmail.com> Date: Thu, 1 Jan 2015 03:03:14 +1100 Subject: [PATCH 21/33] Store a method-from-trait's impl in some cases when it is known. This allows one to look at an `ExprMethodCall` `foo.bar()` where `bar` is a method in some trait and (sometimes) extract the `impl` that `bar` is defined in, e.g. trait Foo { fn bar(&self); } impl Foo for uint { // <A> fn bar(&self) {} } fn main() { 1u.bar(); // impl_def_id == Some(<A>) } This definitely doesn't handle all cases, but is correct when it is known, meaning it should only be used for certain linting/heuristic purposes; no safety analysis. --- src/librustc/middle/astencode.rs | 22 +++++++++++++++++++++ src/librustc/middle/ty.rs | 7 ++++++- src/librustc/middle/ty_fold.rs | 3 ++- src/librustc_trans/trans/meth.rs | 3 ++- src/librustc_typeck/check/method/confirm.rs | 9 ++++++--- src/librustc_typeck/check/method/mod.rs | 3 ++- 6 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 537a2b3f545..430b63f81c8 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -627,6 +627,7 @@ impl<'tcx> tr for MethodOrigin<'tcx> { // def-id is already translated when we read it out trait_ref: mp.trait_ref.clone(), method_num: mp.method_num, + impl_def_id: mp.impl_def_id.tr(dcx), } ) } @@ -879,6 +880,16 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { try!(this.emit_struct_field("method_num", 0, |this| { this.emit_uint(p.method_num) })); + try!(this.emit_struct_field("impl_def_id", 0, |this| { + this.emit_option(|this| { + match p.impl_def_id { + None => this.emit_option_none(), + Some(did) => this.emit_option_some(|this| { + Ok(this.emit_def_id(did)) + }) + } + }) + })); Ok(()) }) }) @@ -1452,6 +1463,17 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { this.read_struct_field("method_num", 1, |this| { this.read_uint() }).unwrap() + }, + impl_def_id: { + this.read_struct_field("impl_def_id", 2, |this| { + this.read_option(|this, b| { + if b { + Ok(Some(this.read_def_id(dcx))) + } else { + Ok(None) + } + }) + }).unwrap() } })) }).unwrap() diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 83bbdf14e4a..5b5b53d1246 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -453,9 +453,14 @@ pub struct MethodParam<'tcx> { // never contains bound regions; those regions should have been // instantiated with fresh variables at this point. pub trait_ref: Rc<ty::TraitRef<'tcx>>, - // index of uint in the list of methods for the trait pub method_num: uint, + + /// The impl for the trait from which the method comes. This + /// should only be used for certain linting/heuristic purposes + /// since there is no guarantee that this is Some in every + /// situation that it could/should be. + pub impl_def_id: Option<ast::DefId>, } // details for a method invoked with a receiver whose type is an object diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index b4e6cff954b..0b8c7786015 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -310,7 +310,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::MethodOrigin<'tcx> { ty::MethodTypeParam(ref param) => { ty::MethodTypeParam(ty::MethodParam { trait_ref: param.trait_ref.fold_with(folder), - method_num: param.method_num + method_num: param.method_num, + impl_def_id: param.impl_def_id, }) } ty::MethodTraitObject(ref object) => { diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index c2f19670e4f..9356be1b9b4 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -132,7 +132,8 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ty::MethodTypeParam(ty::MethodParam { ref trait_ref, - method_num + method_num, + impl_def_id: _ }) => { let trait_ref = ty::Binder(bcx.monomorphize(trait_ref)); let span = bcx.tcx().map.span(method_call.expr_id); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 79460774859..4aa0a211221 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -256,7 +256,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { &impl_polytype.substs, &ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap()); let origin = MethodTypeParam(MethodParam { trait_ref: impl_trait_ref.clone(), - method_num: method_num }); + method_num: method_num, + impl_def_id: Some(impl_def_id) }); (impl_trait_ref.substs.clone(), origin) } @@ -275,7 +276,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, self.tcx().mk_substs(substs.clone()))); let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref, - method_num: method_num }); + method_num: method_num, + impl_def_id: None }); (substs, origin) } @@ -285,7 +287,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { let trait_ref = self.replace_late_bound_regions_with_fresh_var(&*poly_trait_ref); let substs = trait_ref.substs.clone(); let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref, - method_num: method_num }); + method_num: method_num, + impl_def_id: None }); (substs, origin) } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 345bc5fd2aa..d92cc1dfc1e 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -287,7 +287,8 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let callee = MethodCallee { origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(), - method_num: method_num}), + method_num: method_num, + impl_def_id: None}), ty: fty, substs: trait_ref.substs.clone() }; From 2595780e2690364f081c4c33a78286e7ddaa1953 Mon Sep 17 00:00:00 2001 From: Brian Anderson <banderson@mozilla.com> Date: Fri, 23 Jan 2015 21:13:35 -0800 Subject: [PATCH 22/33] Fix beta naming --- mk/main.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/main.mk b/mk/main.mk index 8c910f47596..0e52f168f59 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -30,8 +30,8 @@ CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM) CFG_DISABLE_UNSTABLE_FEATURES=1 endif ifeq ($(CFG_RELEASE_CHANNEL),beta) -CFG_RELEASE=$(CFG_RELEASE_NUM)-beta(CFG_PRERELEASE_VERSION) -CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-beta(CFG_PRERELEASE_VERSION) +CFG_RELEASE=$(CFG_RELEASE_NUM)-beta$(CFG_PRERELEASE_VERSION) +CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-beta$(CFG_PRERELEASE_VERSION) CFG_DISABLE_UNSTABLE_FEATURES=1 endif ifeq ($(CFG_RELEASE_CHANNEL),nightly) From ec88426ea8dbee3a4647039d65deea4de31fb69a Mon Sep 17 00:00:00 2001 From: Daniel Griffen <daniel@dgriffen.com> Date: Mon, 5 Jan 2015 23:30:58 -0600 Subject: [PATCH 23/33] Implement winsize() for unix. --- src/libstd/sys/unix/tty.rs | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/tty.rs b/src/libstd/sys/unix/tty.rs index bee3d440a16..d20469f5106 100644 --- a/src/libstd/sys/unix/tty.rs +++ b/src/libstd/sys/unix/tty.rs @@ -11,14 +11,22 @@ use prelude::v1::*; use sys::fs::FileDesc; -use libc::{self, c_int}; +use libc::{self, c_int, c_ulong, funcs}; use io::{self, IoResult, IoError}; +use sys::c; use sys_common; pub struct TTY { pub fd: FileDesc, } +#[cfg(any(target_os = "macos", + target_os = "freebsd"))] +const TIOCGWINSZ: c_ulong = 0x40087468; + +#[cfg(any(target_os = "linux", target_os = "android"))] +const TIOCGWINSZ: c_ulong = 0x00005413; + impl TTY { pub fn new(fd: c_int) -> IoResult<TTY> { if unsafe { libc::isatty(fd) } != 0 { @@ -41,8 +49,39 @@ impl TTY { pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> { Err(sys_common::unimpl()) } + + #[cfg(any(target_os = "linux", + target_os = "android", + target_os = "macos", + target_os = "freebsd"))] + pub fn get_winsize(&mut self) -> IoResult<(int, int)> { + unsafe { + #[repr(C)] + struct winsize { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16 + } + + let mut size = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 }; + if c::ioctl(self.fd.fd(), TIOCGWINSZ, &mut size) == -1 { + Err(IoError { + kind: io::OtherIoError, + desc: "Size of terminal could not be determined", + detail: None, + }) + } else { + Ok((size.ws_col as int, size.ws_row as int)) + } + } + } + + #[cfg(any(target_os = "ios", + target_os = "dragonfly"))] pub fn get_winsize(&mut self) -> IoResult<(int, int)> { Err(sys_common::unimpl()) } + pub fn isatty(&self) -> bool { false } } From fbef241709093b3fb18cb12500fd33e4ab964b62 Mon Sep 17 00:00:00 2001 From: Huon Wilson <dbau.pp+github@gmail.com> Date: Thu, 1 Jan 2015 03:06:38 +1100 Subject: [PATCH 24/33] Add a lint to detect unconditional recursion. E.g. `fn foo() { foo() }`, or, more subtlely impl Foo for Box<Foo+'static> { fn bar(&self) { self.bar(); } } The compiler will warn and point out the points where recursion occurs, if it determines that the function cannot return without calling itself. Closes #17899. --- src/librustc/lint/builtin.rs | 192 +++++++++++++++++- src/librustc/lint/context.rs | 1 + .../lint-unconditional-recursion.rs | 66 ++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/lint-unconditional-recursion.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index fef1017b782..531bdc2941e 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -32,10 +32,12 @@ use middle::subst::Substs; use middle::ty::{self, Ty}; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, const_int, const_uint}; +use middle::cfg; use util::ppaux::{ty_to_string}; use util::nodemap::{FnvHashMap, NodeSet}; -use lint::{Context, LintPass, LintArray, Lint}; +use lint::{Level, Context, LintPass, LintArray, Lint}; +use std::collections::BitvSet; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::num::SignedInt; use std::{cmp, slice}; @@ -1788,6 +1790,194 @@ impl LintPass for Stability { } } +declare_lint! { + pub UNCONDITIONAL_RECURSION, + Warn, + "functions that cannot return without calling themselves" +} + +#[derive(Copy)] +pub struct UnconditionalRecursion; + + +impl LintPass for UnconditionalRecursion { + fn get_lints(&self) -> LintArray { + lint_array![UNCONDITIONAL_RECURSION] + } + + fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl, + blk: &ast::Block, sp: Span, id: ast::NodeId) { + type F = for<'tcx> fn(&ty::ctxt<'tcx>, + ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool; + + let (name, checker) = match fn_kind { + visit::FkItemFn(name, _, _, _) => (name, id_refers_to_this_fn as F), + visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F), + // closures can't recur, so they don't matter. + visit::FkFnBlock => return + }; + + let impl_def_id = ty::impl_of_method(cx.tcx, ast_util::local_def(id)) + .unwrap_or(ast_util::local_def(ast::DUMMY_NODE_ID)); + assert!(ast_util::is_local(impl_def_id)); + let impl_node_id = impl_def_id.node; + + // Walk through this function (say `f`) looking to see if + // every possible path references itself, i.e. the function is + // called recursively unconditionally. This is done by trying + // to find a path from the entry node to the exit node that + // *doesn't* call `f` by traversing from the entry while + // pretending that calls of `f` are sinks (i.e. ignoring any + // exit edges from them). + // + // NB. this has an edge case with non-returning statements, + // like `loop {}` or `panic!()`: control flow never reaches + // the exit node through these, so one can have a function + // that never actually calls itselfs but is still picked up by + // this lint: + // + // fn f(cond: bool) { + // if !cond { panic!() } // could come from `assert!(cond)` + // f(false) + // } + // + // In general, functions of that form may be able to call + // itself a finite number of times and then diverge. The lint + // considers this to be an error for two reasons, (a) it is + // easier to implement, and (b) it seems rare to actually want + // to have behaviour like the above, rather than + // e.g. accidentally recurring after an assert. + + let cfg = cfg::CFG::new(cx.tcx, blk); + + let mut work_queue = vec![cfg.entry]; + let mut reached_exit_without_self_call = false; + let mut self_call_spans = vec![]; + let mut visited = BitvSet::new(); + + while let Some(idx) = work_queue.pop() { + let cfg_id = idx.node_id(); + if idx == cfg.exit { + // found a path! + reached_exit_without_self_call = true; + break + } else if visited.contains(&cfg_id) { + // already done + continue + } + visited.insert(cfg_id); + let node_id = cfg.graph.node_data(idx).id; + + // is this a recursive call? + if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) { + + self_call_spans.push(cx.tcx.map.span(node_id)); + // this is a self call, so we shouldn't explore past + // this node in the CFG. + continue + } + // add the successors of this node to explore the graph further. + cfg.graph.each_outgoing_edge(idx, |_, edge| { + let target_idx = edge.target(); + let target_cfg_id = target_idx.node_id(); + if !visited.contains(&target_cfg_id) { + work_queue.push(target_idx) + } + true + }); + } + + // check the number of sell calls because a function that + // doesn't return (e.g. calls a `-> !` function or `loop { /* + // no break */ }`) shouldn't be linted unless it actually + // recurs. + if !reached_exit_without_self_call && self_call_spans.len() > 0 { + cx.span_lint(UNCONDITIONAL_RECURSION, sp, + "function cannot return without recurring"); + + // FIXME #19668: these could be span_lint_note's instead of this manual guard. + if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow { + let sess = cx.sess(); + // offer some help to the programmer. + for call in self_call_spans.iter() { + sess.span_note(*call, "recursive call site") + } + sess.span_help(sp, "a `loop` may express intention better if this is on purpose") + } + } + + // all done + return; + + // Functions for identifying if the given NodeId `id` + // represents a call to the function `fn_id`/method + // `method_id`. + + fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>, + _: ast::NodeId, + fn_id: ast::NodeId, + _: ast::Ident, + id: ast::NodeId) -> bool { + tcx.def_map.borrow().get(&id) + .map_or(false, |def| { + let did = def.def_id(); + ast_util::is_local(did) && did.node == fn_id + }) + } + + // check if the method call `id` refers to method `method_id` + // (with name `method_name` contained in impl `impl_id`). + fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>, + impl_id: ast::NodeId, + method_id: ast::NodeId, + method_name: ast::Ident, + id: ast::NodeId) -> bool { + let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) { + None => return false, + Some(m) => match m.origin { + // There's no way to know if a method call via a + // vtable is recursion, so we assume it's not. + ty::MethodTraitObject(_) => return false, + + // This `did` refers directly to the method definition. + ty::MethodStatic(did) | ty::MethodStaticUnboxedClosure(did) => did, + + // MethodTypeParam are methods from traits: + + // The `impl ... for ...` of this method call + // isn't known, e.g. it might be a default method + // in a trait, so we get the def-id of the trait + // method instead. + ty::MethodTypeParam( + ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => { + ty::trait_item(tcx, trait_ref.def_id, method_num).def_id() + } + + // The `impl` is known, so we check that with a + // special case: + ty::MethodTypeParam( + ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => { + + let name = match tcx.map.expect_expr(id).node { + ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node, + _ => tcx.sess.span_bug( + tcx.map.span(id), + "non-method call expr behaving like a method call?") + }; + // it matches if it comes from the same impl, + // and has the same method name. + return ast_util::is_local(impl_def_id) + && impl_def_id.node == impl_id + && method_name.name == name.name + } + } + }; + + ast_util::is_local(did) && did.node == method_id + } + } +} + declare_lint! { pub UNUSED_IMPORTS, Warn, diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 4cbfcf7e91a..3728e6f4980 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -211,6 +211,7 @@ impl LintStore { UnusedAllocation, MissingCopyImplementations, UnstableFeatures, + UnconditionalRecursion, ); add_builtin_with_new!(sess, diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs new file mode 100644 index 00000000000..0c3d1c6adea --- /dev/null +++ b/src/test/compile-fail/lint-unconditional-recursion.rs @@ -0,0 +1,66 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(unconditional_recursion)] +#![allow(dead_code)] +fn foo() { //~ ERROR function cannot return without recurring + foo(); //~ NOTE recursive call site +} + +fn bar() { + if true { + bar() + } +} + +fn baz() { //~ ERROR function cannot return without recurring + if true { + baz() //~ NOTE recursive call site + } else { + baz() //~ NOTE recursive call site + } +} + +fn qux() { + loop {} +} + +fn quz() -> bool { //~ ERROR function cannot return without recurring + if true { + while quz() {} //~ NOTE recursive call site + true + } else { + loop { quz(); } //~ NOTE recursive call site + } +} + +trait Foo { + fn bar(&self) { //~ ERROR function cannot return without recurring + self.bar() //~ NOTE recursive call site + } +} + +impl Foo for Box<Foo+'static> { + fn bar(&self) { //~ ERROR function cannot return without recurring + loop { + self.bar() //~ NOTE recursive call site + } + } + +} + +struct Baz; +impl Baz { + fn qux(&self) { //~ ERROR function cannot return without recurring + self.qux(); //~ NOTE recursive call site + } +} + +fn main() {} From 0684c8ebf92933e6382d1ac13a77999296411dac Mon Sep 17 00:00:00 2001 From: Huon Wilson <dbau.pp+github@gmail.com> Date: Sun, 25 Jan 2015 00:05:42 +1100 Subject: [PATCH 25/33] Fix infinite recursion in the compiler. This was detected by the unconditional_recursion lint. --- src/librustc/util/ppaux.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index c5aced4eb86..266048fce51 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -544,7 +544,7 @@ impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for Option<T> { impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for P<T> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { - (*self).repr(tcx) + (**self).repr(tcx) } } From 2e888d0341e81de1744b257c25b012c2c148f0ba Mon Sep 17 00:00:00 2001 From: Huon Wilson <dbau.pp+github@gmail.com> Date: Tue, 13 Jan 2015 14:24:37 +1100 Subject: [PATCH 26/33] Add the span of the operator itself to ast::BinOp. --- src/librustc/lint/builtin.rs | 14 +++++++------- src/librustc/middle/cfg/construct.rs | 2 +- src/librustc/middle/const_eval.rs | 12 ++++++------ src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/liveness.rs | 4 ++-- src/librustc/middle/region.rs | 6 +++--- src/librustc/middle/ty.rs | 2 +- src/librustc_back/svh.rs | 8 ++++---- src/librustc_trans/trans/base.rs | 10 +++++----- src/librustc_trans/trans/consts.rs | 2 +- src/librustc_trans/trans/expr.rs | 8 ++++---- src/librustc_typeck/check/mod.rs | 18 +++++++++--------- src/librustc_typeck/check/regionck.rs | 2 +- src/libsyntax/ast.rs | 6 ++++-- src/libsyntax/ast_util.rs | 12 ++++++------ src/libsyntax/ext/build.rs | 6 +++--- src/libsyntax/ext/deriving/generic/mod.rs | 2 +- src/libsyntax/parse/parser.rs | 12 +++++++----- src/libsyntax/parse/token.rs | 2 +- src/libsyntax/print/pprust.rs | 4 ++-- 20 files changed, 69 insertions(+), 65 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index fef1017b782..9a7b7e0eb94 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -44,7 +44,7 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; use syntax::{abi, ast, ast_map}; use syntax::ast_util::is_shift_binop; use syntax::attr::{self, AttrMetaMethods}; -use syntax::codemap::{Span, DUMMY_SP}; +use syntax::codemap::{self, Span, DUMMY_SP}; use syntax::parse::token; use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64}; use syntax::ast_util; @@ -185,7 +185,7 @@ impl LintPass for TypeLimits { "comparison is useless due to type limits"); } - if is_shift_binop(binop) { + if is_shift_binop(binop.node) { let opt_ty_bits = match ty::expr_ty(cx.tcx, &**l).sty { ty::ty_int(t) => Some(int_ty_bits(t, cx.sess().target.int_type)), ty::ty_uint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)), @@ -272,7 +272,7 @@ impl LintPass for TypeLimits { fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T, min: T, max: T) -> bool { - match binop { + match binop.node { ast::BiLt => v > min && v <= max, ast::BiLe => v >= min && v < max, ast::BiGt => v >= min && v < max, @@ -283,13 +283,13 @@ impl LintPass for TypeLimits { } fn rev_binop(binop: ast::BinOp) -> ast::BinOp { - match binop { + codemap::respan(binop.span, match binop.node { ast::BiLt => ast::BiGt, ast::BiLe => ast::BiGe, ast::BiGt => ast::BiLt, ast::BiGe => ast::BiLe, - _ => binop - } + _ => return binop + }) } // for int & uint, be conservative with the warnings, so that the @@ -382,7 +382,7 @@ impl LintPass for TypeLimits { } fn is_comparison(binop: ast::BinOp) -> bool { - match binop { + match binop.node { ast::BiEq | ast::BiLt | ast::BiLe | ast::BiNe | ast::BiGe | ast::BiGt => true, _ => false diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 1a2162b3076..6162f61fde1 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -372,7 +372,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { expr_exit } - ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => { + ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => { // // [pred] // | diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 05dd47f5a36..c2533c1a9c6 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -400,7 +400,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St match (eval_const_expr_partial(tcx, &**a), eval_const_expr_partial(tcx, &**b)) { (Ok(const_float(a)), Ok(const_float(b))) => { - match op { + match op.node { ast::BiAdd => Ok(const_float(a + b)), ast::BiSub => Ok(const_float(a - b)), ast::BiMul => Ok(const_float(a * b)), @@ -416,7 +416,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St } } (Ok(const_int(a)), Ok(const_int(b))) => { - match op { + match op.node { ast::BiAdd => Ok(const_int(a + b)), ast::BiSub => Ok(const_int(a - b)), ast::BiMul => Ok(const_int(a * b)), @@ -443,7 +443,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St } } (Ok(const_uint(a)), Ok(const_uint(b))) => { - match op { + match op.node { ast::BiAdd => Ok(const_uint(a + b)), ast::BiSub => Ok(const_uint(a - b)), ast::BiMul => Ok(const_uint(a * b)), @@ -471,21 +471,21 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St } // shifts can have any integral type as their rhs (Ok(const_int(a)), Ok(const_uint(b))) => { - match op { + match op.node { ast::BiShl => Ok(const_int(a << b as uint)), ast::BiShr => Ok(const_int(a >> b as uint)), _ => Err("can't do this op on an int and uint".to_string()) } } (Ok(const_uint(a)), Ok(const_int(b))) => { - match op { + match op.node { ast::BiShl => Ok(const_uint(a << b as uint)), ast::BiShr => Ok(const_uint(a >> b as uint)), _ => Err("can't do this op on a uint and int".to_string()) } } (Ok(const_bool(a)), Ok(const_bool(b))) => { - Ok(const_bool(match op { + Ok(const_bool(match op.node { ast::BiAnd => a && b, ast::BiOr => a || b, ast::BiBitXor => a ^ b, diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index a5f2dc398e9..d9ab86deb3b 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -568,7 +568,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } ast::ExprBinary(op, ref lhs, ref rhs) => { - let pass_args = if ast_util::is_by_value_binop(op) { + let pass_args = if ast_util::is_by_value_binop(op.node) { PassArgs::ByValue } else { PassArgs::ByRef diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 43989d0aadc..c08af95f139 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -504,7 +504,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); visit::walk_expr(ir, expr); } - ast::ExprBinary(op, _, _) if ast_util::lazy_binop(op) => { + ast::ExprBinary(op, _, _) if ast_util::lazy_binop(op.node) => { ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); visit::walk_expr(ir, expr); } @@ -1177,7 +1177,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_exprs(&exprs[], succ) } - ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => { + ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => { let r_succ = self.propagate_through_expr(&**r, succ); let ln = self.live_node(expr.id, expr.span); diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index b4b2e1b63e8..67c0e52d664 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -22,7 +22,7 @@ use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap}; use util::common::can_reach; use std::cell::RefCell; -use syntax::codemap::Span; +use syntax::codemap::{self, Span}; use syntax::{ast, visit}; use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local}; use syntax::ast_util::{stmt_id}; @@ -496,8 +496,8 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) { // scopes, meaning that temporaries cannot outlive them. // This ensures fixed size stacks. - ast::ExprBinary(ast::BiAnd, _, ref r) | - ast::ExprBinary(ast::BiOr, _, ref r) => { + ast::ExprBinary(codemap::Spanned { node: ast::BiAnd, .. }, _, ref r) | + ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. terminating(r.id); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 83bbdf14e4a..bae41b78c08 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5716,7 +5716,7 @@ pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool static opcat_mod: int = 8; fn opcat(op: ast::BinOp) -> int { - match op { + match op.node { ast::BiAdd => opcat_add, ast::BiSub => opcat_sub, ast::BiMul => opcat_mult, diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index ebeaf3e6e22..65a7fbf60a5 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -231,7 +231,7 @@ mod svh_visitor { SawExprCall, SawExprMethodCall, SawExprTup, - SawExprBinary(ast::BinOp), + SawExprBinary(ast::BinOp_), SawExprUnary(ast::UnOp), SawExprLit(ast::Lit_), SawExprCast, @@ -241,7 +241,7 @@ mod svh_visitor { SawExprClosure, SawExprBlock, SawExprAssign, - SawExprAssignOp(ast::BinOp), + SawExprAssignOp(ast::BinOp_), SawExprIndex, SawExprRange, SawExprPath, @@ -262,7 +262,7 @@ mod svh_visitor { ExprCall(..) => SawExprCall, ExprMethodCall(..) => SawExprMethodCall, ExprTup(..) => SawExprTup, - ExprBinary(op, _, _) => SawExprBinary(op), + ExprBinary(op, _, _) => SawExprBinary(op.node), ExprUnary(op, _) => SawExprUnary(op), ExprLit(ref lit) => SawExprLit(lit.node.clone()), ExprCast(..) => SawExprCast, @@ -273,7 +273,7 @@ mod svh_visitor { ExprClosure(..) => SawExprClosure, ExprBlock(..) => SawExprBlock, ExprAssign(..) => SawExprAssign, - ExprAssignOp(op, _, _) => SawExprAssignOp(op), + ExprAssignOp(op, _, _) => SawExprAssignOp(op.node), ExprField(_, id) => SawExprField(content(id.node)), ExprTupField(_, id) => SawExprTupField(id.node), ExprIndex(..) => SawExprIndex, diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 572dfd165ee..4c1fdc6140e 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -540,7 +540,7 @@ pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>, lhs: ValueRef, rhs: ValueRef, t: Ty<'tcx>, - op: ast::BinOp) + op: ast::BinOp_) -> Result<'blk, 'tcx> { let f = |&: a| Result::new(cx, compare_scalar_values(cx, lhs, rhs, a, op)); @@ -561,7 +561,7 @@ pub fn compare_scalar_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, lhs: ValueRef, rhs: ValueRef, nt: scalar_type, - op: ast::BinOp) + op: ast::BinOp_) -> ValueRef { let _icx = push_ctxt("compare_scalar_values"); fn die(cx: Block) -> ! { @@ -635,7 +635,7 @@ pub fn compare_simd_types<'blk, 'tcx>( not supported for floating point SIMD types") }, ty::ty_uint(_) | ty::ty_int(_) => { - let cmp = match op { + let cmp = match op.node { ast::BiEq => llvm::IntEQ, ast::BiNe => llvm::IntNE, ast::BiLt => llvm::IntSLT, @@ -823,7 +823,7 @@ pub fn cast_shift_rhs<F, G>(op: ast::BinOp, G: FnOnce(ValueRef, Type) -> ValueRef, { // Shifts may have any size int on the rhs - if ast_util::is_shift_binop(op) { + if ast_util::is_shift_binop(op.node) { let mut rhs_llty = val_ty(rhs); let mut lhs_llty = val_ty(lhs); if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() } @@ -852,7 +852,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>( rhs: ValueRef, rhs_t: Ty<'tcx>) -> Block<'blk, 'tcx> { - let (zero_text, overflow_text) = if divrem == ast::BiDiv { + let (zero_text, overflow_text) = if divrem.node == ast::BiDiv { ("attempted to divide by zero", "attempted to divide with overflow") } else { diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 29cf9f72ef8..ba3af53a916 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -310,7 +310,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { let ty = ty::expr_ty(cx.tcx(), &**e1); let is_float = ty::type_is_fp(ty); let signed = ty::type_is_signed(ty); - return match b { + return match b.node { ast::BiAdd => { if is_float { llvm::LLVMConstFAdd(te1, te2) } else { llvm::LLVMConstAdd(te1, te2) } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 4ebaf91d111..f0b491bdea8 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1132,7 +1132,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let rhs_datum = unpack_datum!(bcx, trans(bcx, &**rhs)); trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs, vec![(rhs_datum, rhs.id)], Some(dest), - !ast_util::is_by_value_binop(op)).bcx + !ast_util::is_by_value_binop(op.node)).bcx } ast::ExprUnary(op, ref subexpr) => { // if not overloaded, would be RvalueDatumExpr @@ -1676,7 +1676,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let binop_debug_loc = binop_expr.debug_loc(); let mut bcx = bcx; - let val = match op { + let val = match op.node { ast::BiAdd => { if is_float { FAdd(bcx, lhs, rhs, binop_debug_loc) @@ -1739,7 +1739,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { if ty::type_is_scalar(rhs_t) { - unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op)) + unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op.node)) } else if is_simd { base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op) } else { @@ -1811,7 +1811,7 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // if overloaded, would be RvalueDpsExpr assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); - match op { + match op.node { ast::BiAnd => { trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5f8ae09b5bd..a9f81d3a266 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2859,7 +2859,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let lhs_t = structurally_resolved_type(fcx, lhs.span, fcx.expr_ty(&*lhs)); - if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) { + if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) { // Shift is a special case: rhs must be uint, no matter what lhs is check_expr(fcx, &**rhs); let rhs_ty = fcx.expr_ty(&**rhs); @@ -2887,7 +2887,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, demand::suptype(fcx, expr.span, tvar, lhs_t); check_expr_has_type(fcx, &**rhs, tvar); - let result_t = match op { + let result_t = match op.node { ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe | ast::BiGt => { if ty::type_is_simd(tcx, lhs_t) { @@ -2898,7 +2898,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, operation `{}` not \ supported for floating \ point SIMD vector `{}`", - ast_util::binop_to_string(op), + ast_util::binop_to_string(op.node), actual) }, lhs_t, @@ -2919,7 +2919,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, return; } - if op == ast::BiOr || op == ast::BiAnd { + if op.node == ast::BiOr || op.node == ast::BiAnd { // This is an error; one of the operands must have the wrong // type fcx.write_error(expr.id); @@ -2928,7 +2928,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, |actual| { format!("binary operation `{}` cannot be applied \ to type `{}`", - ast_util::binop_to_string(op), + ast_util::binop_to_string(op.node), actual) }, lhs_t, @@ -2945,7 +2945,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, operation `{}=` \ cannot be applied to \ type `{}`", - ast_util::binop_to_string(op), + ast_util::binop_to_string(op.node), actual) }, lhs_t, @@ -2968,7 +2968,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, rhs: &P<ast::Expr>) -> Ty<'tcx> { let tcx = fcx.ccx.tcx; let lang = &tcx.lang_items; - let (name, trait_did) = match op { + let (name, trait_did) = match op.node { ast::BiAdd => ("add", lang.add_trait()), ast::BiSub => ("sub", lang.sub_trait()), ast::BiMul => ("mul", lang.mul_trait()), @@ -2994,10 +2994,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, trait_did, lhs_expr, Some(rhs), || { fcx.type_error_message(ex.span, |actual| { format!("binary operation `{}` cannot be applied to type `{}`", - ast_util::binop_to_string(op), + ast_util::binop_to_string(op.node), actual) }, lhs_resolved_t, None) - }, if ast_util::is_by_value_binop(op) { AutorefArgs::No } else { AutorefArgs::Yes }) + }, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes }) } fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 56b700663d4..af5250805b7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -564,7 +564,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { }, ast::ExprBinary(op, ref lhs, ref rhs) if has_method_map => { - let implicitly_ref_args = !ast_util::is_by_value_binop(op); + let implicitly_ref_args = !ast_util::is_by_value_binop(op.node); // As `expr_method_call`, but the call is via an // overloaded op. Note that we (sadly) currently use an diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7111fe3af1f..25a30b5e8e2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -13,7 +13,7 @@ pub use self::AsmDialect::*; pub use self::AttrStyle::*; pub use self::BindingMode::*; -pub use self::BinOp::*; +pub use self::BinOp_::*; pub use self::BlockCheckMode::*; pub use self::CaptureClause::*; pub use self::Decl_::*; @@ -582,7 +582,7 @@ pub enum Mutability { } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)] -pub enum BinOp { +pub enum BinOp_ { BiAdd, BiSub, BiMul, @@ -603,6 +603,8 @@ pub enum BinOp { BiGt, } +pub type BinOp = Spanned<BinOp_>; + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)] pub enum UnOp { UnUniq, diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index cf0aac5bf4a..5aeea47ac60 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -46,7 +46,7 @@ pub fn stmt_id(s: &Stmt) -> NodeId { } } -pub fn binop_to_string(op: BinOp) -> &'static str { +pub fn binop_to_string(op: BinOp_) -> &'static str { match op { BiAdd => "+", BiSub => "-", @@ -69,7 +69,7 @@ pub fn binop_to_string(op: BinOp) -> &'static str { } } -pub fn lazy_binop(b: BinOp) -> bool { +pub fn lazy_binop(b: BinOp_) -> bool { match b { BiAnd => true, BiOr => true, @@ -77,7 +77,7 @@ pub fn lazy_binop(b: BinOp) -> bool { } } -pub fn is_shift_binop(b: BinOp) -> bool { +pub fn is_shift_binop(b: BinOp_) -> bool { match b { BiShl => true, BiShr => true, @@ -85,7 +85,7 @@ pub fn is_shift_binop(b: BinOp) -> bool { } } -pub fn is_comparison_binop(b: BinOp) -> bool { +pub fn is_comparison_binop(b: BinOp_) -> bool { match b { BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true, _ => false @@ -93,7 +93,7 @@ pub fn is_comparison_binop(b: BinOp) -> bool { } /// Returns `true` if the binary operator takes its arguments by value -pub fn is_by_value_binop(b: BinOp) -> bool { +pub fn is_by_value_binop(b: BinOp_) -> bool { match b { BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => { true @@ -319,7 +319,7 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility { } /// Maps a binary operator to its precedence -pub fn operator_prec(op: ast::BinOp) -> usize { +pub fn operator_prec(op: ast::BinOp_) -> usize { match op { // 'as' sits here with 12 BiMul | BiDiv | BiRem => 11us, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 92619cf42e4..2b3a7212683 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -106,7 +106,7 @@ pub trait AstBuilder { fn expr_ident(&self, span: Span, id: ast::Ident) -> P<ast::Expr>; fn expr_self(&self, span: Span) -> P<ast::Expr>; - fn expr_binary(&self, sp: Span, op: ast::BinOp, + fn expr_binary(&self, sp: Span, op: ast::BinOp_, lhs: P<ast::Expr>, rhs: P<ast::Expr>) -> P<ast::Expr>; fn expr_deref(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr>; fn expr_unary(&self, sp: Span, op: ast::UnOp, e: P<ast::Expr>) -> P<ast::Expr>; @@ -561,9 +561,9 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr_ident(span, special_idents::self_) } - fn expr_binary(&self, sp: Span, op: ast::BinOp, + fn expr_binary(&self, sp: Span, op: ast::BinOp_, lhs: P<ast::Expr>, rhs: P<ast::Expr>) -> P<ast::Expr> { - self.expr(sp, ast::ExprBinary(op, lhs, rhs)) + self.expr(sp, ast::ExprBinary(Spanned { node: op, span: sp }, lhs, rhs)) } fn expr_deref(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr> { diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 272b0464010..f8812f4d28d 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -1449,7 +1449,7 @@ pub fn cs_same_method_fold<F>(use_foldl: bool, /// Use a given binop to combine the result of calling the derived method /// on all the fields. #[inline] -pub fn cs_binop(binop: ast::BinOp, base: P<Expr>, +pub fn cs_binop(binop: ast::BinOp_, base: P<Expr>, enum_nonmatch_f: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, trait_span: Span, substructure: &Substructure) -> P<Expr> { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e59dbe52b76..15254988ce0 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2840,6 +2840,7 @@ impl<'a> Parser<'a> { self.expected_tokens.push(TokenType::Operator); + let cur_op_span = self.span; let cur_opt = self.token.to_binop(); match cur_opt { Some(cur_op) => { @@ -2853,7 +2854,7 @@ impl<'a> Parser<'a> { let rhs = self.parse_more_binops(expr, cur_prec + 1); let lhs_span = lhs.span; let rhs_span = rhs.span; - let binary = self.mk_binary(cur_op, lhs, rhs); + let binary = self.mk_binary(codemap::respan(cur_op_span, cur_op), lhs, rhs); let bin = self.mk_expr(lhs_span.lo, rhs_span.hi, binary); self.parse_more_binops(bin, min_prec) } else { @@ -2877,14 +2878,14 @@ impl<'a> Parser<'a> { /// Produce an error if comparison operators are chained (RFC #558). /// We only need to check lhs, not rhs, because all comparison ops /// have same precedence and are left-associative - fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp) { + fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp_) { debug_assert!(ast_util::is_comparison_binop(outer_op)); match lhs.node { - ExprBinary(op, _, _) if ast_util::is_comparison_binop(op) => { + ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => { let op_span = self.span; self.span_err(op_span, "Chained comparison operators require parentheses"); - if op == BiLt && outer_op == BiGt { + if op.node == BiLt && outer_op == BiGt { self.span_help(op_span, "use ::< instead of < if you meant to specify type arguments"); } @@ -2919,6 +2920,7 @@ impl<'a> Parser<'a> { pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> P<Expr> { let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL; + let op_span = self.span; match self.token { token::Eq => { self.bump(); @@ -2942,7 +2944,7 @@ impl<'a> Parser<'a> { }; let rhs_span = rhs.span; let span = lhs.span; - let assign_op = self.mk_assign_op(aop, lhs, rhs); + let assign_op = self.mk_assign_op(codemap::respan(op_span, aop), lhs, rhs); self.mk_expr(span.lo, rhs_span.hi, assign_op) } // A range expression, either `expr..expr` or `expr..`. diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a129fd19d94..ac694afac6b 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -249,7 +249,7 @@ impl Token { } /// Maps a token to its corresponding binary operator. - pub fn to_binop(&self) -> Option<ast::BinOp> { + pub fn to_binop(&self) -> Option<ast::BinOp_> { match *self { BinOp(Star) => Some(ast::BiMul), BinOp(Slash) => Some(ast::BiDiv), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 42f156d6a11..1026f4cb323 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1618,7 +1618,7 @@ impl<'a> State<'a> { rhs: &ast::Expr) -> IoResult<()> { try!(self.print_expr(lhs)); try!(space(&mut self.s)); - try!(self.word_space(ast_util::binop_to_string(op))); + try!(self.word_space(ast_util::binop_to_string(op.node))); self.print_expr(rhs) } @@ -1786,7 +1786,7 @@ impl<'a> State<'a> { ast::ExprAssignOp(op, ref lhs, ref rhs) => { try!(self.print_expr(&**lhs)); try!(space(&mut self.s)); - try!(word(&mut self.s, ast_util::binop_to_string(op))); + try!(word(&mut self.s, ast_util::binop_to_string(op.node))); try!(self.word_space("=")); try!(self.print_expr(&**rhs)); } From ec790d6fcc426e13431cbb7510c62864515a3ef8 Mon Sep 17 00:00:00 2001 From: Huon Wilson <dbau.pp+github@gmail.com> Date: Tue, 13 Jan 2015 15:18:55 +1100 Subject: [PATCH 27/33] Tweak chained comparison errors. Lower case and give a more precise span: from operator to operator, not just the last one. --- src/libsyntax/parse/parser.rs | 7 ++++--- .../compile-fail/require-parens-for-chained-comparison.rs | 8 ++++---- src/test/compile-fail/unsized2.rs | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 15254988ce0..759e5e8229a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2882,12 +2882,13 @@ impl<'a> Parser<'a> { debug_assert!(ast_util::is_comparison_binop(outer_op)); match lhs.node { ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => { - let op_span = self.span; + // respan to include both operators + let op_span = mk_sp(op.span.lo, self.span.hi); self.span_err(op_span, - "Chained comparison operators require parentheses"); + "chained comparison operators require parentheses"); if op.node == BiLt && outer_op == BiGt { self.span_help(op_span, - "use ::< instead of < if you meant to specify type arguments"); + "use `::<...>` instead of `<...>` if you meant to specify type arguments"); } } _ => {} diff --git a/src/test/compile-fail/require-parens-for-chained-comparison.rs b/src/test/compile-fail/require-parens-for-chained-comparison.rs index f5d8c574814..f2705f58331 100644 --- a/src/test/compile-fail/require-parens-for-chained-comparison.rs +++ b/src/test/compile-fail/require-parens-for-chained-comparison.rs @@ -12,12 +12,12 @@ fn f<T>() {} fn main() { false == false == false; - //~^ ERROR: Chained comparison operators require parentheses + //~^ ERROR: chained comparison operators require parentheses false == 0 < 2; - //~^ ERROR: Chained comparison operators require parentheses + //~^ ERROR: chained comparison operators require parentheses f<X>(); - //~^ ERROR: Chained comparison operators require parentheses - //~^^ HELP: use ::< instead of < if you meant to specify type arguments + //~^ ERROR: chained comparison operators require parentheses + //~^^ HELP: use `::<...>` instead of `<...>` } diff --git a/src/test/compile-fail/unsized2.rs b/src/test/compile-fail/unsized2.rs index a47d81e38cc..b2eb2064aeb 100644 --- a/src/test/compile-fail/unsized2.rs +++ b/src/test/compile-fail/unsized2.rs @@ -15,6 +15,6 @@ fn f<X>() {} pub fn main() { f<type>(); //~^ ERROR expected identifier, found keyword `type` - //~^^ ERROR: Chained comparison operators require parentheses - //~^^^ HELP: use ::< instead of < if you meant to specify type arguments + //~^^ ERROR: chained comparison + //~^^^ HELP: use `::< } From 612ded78e3275332e4b68391701a2866ca3689cc Mon Sep 17 00:00:00 2001 From: Michael Woerister <michaelwoerister@posteo> Date: Thu, 8 Jan 2015 14:55:13 +0100 Subject: [PATCH 28/33] debuginfo: Extend option-like-enum test case to contain nested discriminants. --- src/test/debuginfo/option-like-enum.rs | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/test/debuginfo/option-like-enum.rs b/src/test/debuginfo/option-like-enum.rs index 71c235c878c..fdfbcda7421 100644 --- a/src/test/debuginfo/option-like-enum.rs +++ b/src/test/debuginfo/option-like-enum.rs @@ -36,6 +36,12 @@ // gdb-command:print void_droid_gdb->internals // gdb-check:$6 = (isize *) 0x0 +// gdb-command:print nested_non_zero_yep +// gdb-check:$7 = {RUST$ENCODED$ENUM$1$2$Nope = {10.5, {a = 10, b = 20, c = [...]}}} + +// gdb-command:print nested_non_zero_nope +// gdb-check:$8 = {RUST$ENCODED$ENUM$1$2$Nope = {[...], {a = [...], b = [...], c = 0x0}}} + // gdb-command:continue @@ -67,6 +73,12 @@ // lldb-command:print none_str // lldb-check:[...]$7 = None +// lldb-command:print nested_non_zero_yep +// lldb-check:[...]$8 = Yep(10.5, NestedNonZeroField { a: 10, b: 20, c: &[...] }) + +// lldb-command:print nested_non_zero_nope +// lldb-check:[...]$9 = Nope + #![omit_gdb_pretty_printer_section] @@ -102,6 +114,17 @@ struct NamedFieldsRepr<'a> { internals: &'a isize } +struct NestedNonZeroField<'a> { + a: u16, + b: u32, + c: &'a char, +} + +enum NestedNonZero<'a> { + Yep(f64, NestedNonZeroField<'a>), + Nope +} + fn main() { let some_str: Option<&'static str> = Some("abc"); @@ -124,6 +147,17 @@ fn main() { let void_droid = NamedFields::Void; let void_droid_gdb: &NamedFieldsRepr = unsafe { std::mem::transmute(&NamedFields::Void) }; + let x = 'x'; + let nested_non_zero_yep = NestedNonZero::Yep( + 10.5, + NestedNonZeroField { + a: 10, + b: 20, + c: &x + }); + + let nested_non_zero_nope = NestedNonZero::Nope; + zzz(); // #break } From 75ad1161dd598ba0e61b9215a08216e744dd9f4c Mon Sep 17 00:00:00 2001 From: Michael Woerister <michaelwoerister@posteo> Date: Thu, 8 Jan 2015 16:50:45 +0100 Subject: [PATCH 29/33] debuginfo: Improve DWARF representation of unsized vecs and strings. --- src/librustc_trans/trans/debuginfo.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 39413d63482..b366c7faefa 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -207,7 +207,7 @@ use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet}; use util::ppaux; -use libc::c_uint; +use libc::{c_uint, c_longlong}; use std::ffi::CString; use std::cell::{Cell, RefCell}; use std::ptr; @@ -2764,7 +2764,7 @@ fn create_struct_stub(cx: &CrateContext, fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, unique_type_id: UniqueTypeId, element_type: Ty<'tcx>, - len: uint, + len: Option<u64>, span: Span) -> MetadataCreationResult { let element_type_metadata = type_metadata(cx, element_type, span); @@ -2774,18 +2774,20 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let element_llvm_type = type_of::type_of(cx, element_type); let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); + let (array_size_in_bytes, upper_bound) = match len { + Some(len) => (element_type_size * len, len as c_longlong), + None => (0, -1) + }; + let subrange = unsafe { - llvm::LLVMDIBuilderGetOrCreateSubrange( - DIB(cx), - 0, - len as i64) + llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; let subscripts = create_DIArray(DIB(cx), &[subrange]); let metadata = unsafe { llvm::LLVMDIBuilderCreateArrayType( DIB(cx), - bytes_to_bits(element_type_size * (len as u64)), + bytes_to_bits(array_size_in_bytes), bytes_to_bits(element_type_align), element_type_metadata, subscripts) @@ -2991,12 +2993,12 @@ fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::ty_enum(def_id, _) => { prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx) } - ty::ty_vec(typ, Some(len)) => { - fixed_vec_metadata(cx, unique_type_id, typ, len, usage_site_span) + ty::ty_vec(typ, len) => { + fixed_vec_metadata(cx, unique_type_id, typ, len.map(|x| x as u64), usage_site_span) + } + ty::ty_str => { + fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, None, usage_site_span) } - // FIXME Can we do better than this for unsized vec/str fields? - ty::ty_vec(typ, None) => fixed_vec_metadata(cx, unique_type_id, typ, 0, usage_site_span), - ty::ty_str => fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, 0, usage_site_span), ty::ty_trait(..) => { MetadataCreationResult::new( trait_pointer_metadata(cx, t, None, unique_type_id), From 577c4643dd3f098697c17606e64d92477247d4b5 Mon Sep 17 00:00:00 2001 From: Flavio Percoco <flaper87@gmail.com> Date: Sat, 24 Jan 2015 15:10:37 +0100 Subject: [PATCH 30/33] don't run pretty-rpass for tests using #![main] --- .../test-fn-signature-verification-for-explicit-return-type.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs index 65a6ed2b332..7c99c968e35 100644 --- a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs +++ b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: --test +// no-pretty-expanded extern crate test; #[bench] From c5369ebc7f4791c4e291951751b8964052c7a523 Mon Sep 17 00:00:00 2001 From: Aaron Turon <aturon@mozilla.com> Date: Wed, 21 Jan 2015 15:55:31 -0800 Subject: [PATCH 31/33] Add ffi::OsString and OsStr Per [RFC 517](https://github.com/rust-lang/rfcs/pull/575/), this commit introduces platform-native strings. The API is essentially as described in the RFC. The WTF-8 implementation is adapted from @SimonSapin's [implementation](https://github.com/SimonSapin/rust-wtf8). To make this work, some encodign and decoding functionality in `libcore` is now exported in a "raw" fashion reusable for WTF-8. These exports are *not* reexported in `std`, nor are they stable. --- src/libcore/char.rs | 96 ++- src/libcore/str/mod.rs | 116 +-- src/libstd/ffi/mod.rs | 5 + src/libstd/ffi/os_str.rs | 259 +++++++ src/libstd/sys/common/mod.rs | 15 +- src/libstd/sys/common/wtf8.rs | 1212 ++++++++++++++++++++++++++++++ src/libstd/sys/unix/ext.rs | 35 +- src/libstd/sys/unix/mod.rs | 1 + src/libstd/sys/unix/os_str.rs | 86 +++ src/libstd/sys/windows/ext.rs | 34 +- src/libstd/sys/windows/mod.rs | 1 + src/libstd/sys/windows/os_str.rs | 82 ++ 12 files changed, 1850 insertions(+), 92 deletions(-) create mode 100644 src/libstd/ffi/os_str.rs create mode 100644 src/libstd/sys/common/wtf8.rs create mode 100644 src/libstd/sys/unix/os_str.rs create mode 100644 src/libstd/sys/windows/os_str.rs diff --git a/src/libcore/char.rs b/src/libcore/char.rs index caac894c0da..0e6b634bd11 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -258,49 +258,69 @@ impl CharExt for char { #[inline] #[unstable = "pending decision about Iterator/Writer/Reader"] fn encode_utf8(self, dst: &mut [u8]) -> Option<uint> { - // Marked #[inline] to allow llvm optimizing it away - let code = self as u32; - if code < MAX_ONE_B && dst.len() >= 1 { - dst[0] = code as u8; - Some(1) - } else if code < MAX_TWO_B && dst.len() >= 2 { - dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B; - dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(2) - } else if code < MAX_THREE_B && dst.len() >= 3 { - dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B; - dst[1] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; - dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(3) - } else if dst.len() >= 4 { - dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B; - dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT; - dst[2] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; - dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(4) - } else { - None - } + encode_utf8_raw(self as u32, dst) } #[inline] #[unstable = "pending decision about Iterator/Writer/Reader"] fn encode_utf16(self, dst: &mut [u16]) -> Option<uint> { - // Marked #[inline] to allow llvm optimizing it away - let mut ch = self as u32; - if (ch & 0xFFFF_u32) == ch && dst.len() >= 1 { - // The BMP falls through (assuming non-surrogate, as it should) - dst[0] = ch as u16; - Some(1) - } else if dst.len() >= 2 { - // Supplementary planes break into surrogates. - ch -= 0x1_0000_u32; - dst[0] = 0xD800_u16 | ((ch >> 10) as u16); - dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); - Some(2) - } else { - None - } + encode_utf16_raw(self as u32, dst) + } +} + +/// Encodes a raw u32 value as UTF-8 into the provided byte buffer, +/// and then returns the number of bytes written. +/// +/// If the buffer is not large enough, nothing will be written into it +/// and a `None` will be returned. +#[inline] +#[unstable] +pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> Option<uint> { + // Marked #[inline] to allow llvm optimizing it away + if code < MAX_ONE_B && dst.len() >= 1 { + dst[0] = code as u8; + Some(1) + } else if code < MAX_TWO_B && dst.len() >= 2 { + dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B; + dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(2) + } else if code < MAX_THREE_B && dst.len() >= 3 { + dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B; + dst[1] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(3) + } else if dst.len() >= 4 { + dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B; + dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(4) + } else { + None + } +} + +/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, +/// and then returns the number of `u16`s written. +/// +/// If the buffer is not large enough, nothing will be written into it +/// and a `None` will be returned. +#[inline] +#[unstable] +pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option<uint> { + // Marked #[inline] to allow llvm optimizing it away + if (ch & 0xFFFF_u32) == ch && dst.len() >= 1 { + // The BMP falls through (assuming non-surrogate, as it should) + dst[0] = ch as u16; + Some(1) + } else if dst.len() >= 2 { + // Supplementary planes break into surrogates. + ch -= 0x1_0000_u32; + dst[0] = 0xD800_u16 | ((ch >> 10) as u16); + dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); + Some(2) + } else { + None } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index bdac686cb66..1e01da4e41d 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -305,43 +305,52 @@ fn unwrap_or_0(opt: Option<&u8>) -> u8 { } } +/// Reads the next code point out of a byte iterator (assuming a +/// UTF-8-like encoding). +#[unstable] +pub fn next_code_point(bytes: &mut slice::Iter<u8>) -> Option<u32> { + // Decode UTF-8 + let x = match bytes.next() { + None => return None, + Some(&next_byte) if next_byte < 128 => return Some(next_byte as u32), + Some(&next_byte) => next_byte, + }; + + // Multibyte case follows + // Decode from a byte combination out of: [[[x y] z] w] + // NOTE: Performance is sensitive to the exact formulation here + let init = utf8_first_byte!(x, 2); + let y = unwrap_or_0(bytes.next()); + let mut ch = utf8_acc_cont_byte!(init, y); + if x >= 0xE0 { + // [[x y z] w] case + // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid + let z = unwrap_or_0(bytes.next()); + let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z); + ch = init << 12 | y_z; + if x >= 0xF0 { + // [x y z w] case + // use only the lower 3 bits of `init` + let w = unwrap_or_0(bytes.next()); + ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w); + } + } + + Some(ch) +} + #[stable] impl<'a> Iterator for Chars<'a> { type Item = char; #[inline] fn next(&mut self) -> Option<char> { - // Decode UTF-8, using the valid UTF-8 invariant - let x = match self.iter.next() { - None => return None, - Some(&next_byte) if next_byte < 128 => return Some(next_byte as char), - Some(&next_byte) => next_byte, - }; - - // Multibyte case follows - // Decode from a byte combination out of: [[[x y] z] w] - // NOTE: Performance is sensitive to the exact formulation here - let init = utf8_first_byte!(x, 2); - let y = unwrap_or_0(self.iter.next()); - let mut ch = utf8_acc_cont_byte!(init, y); - if x >= 0xE0 { - // [[x y z] w] case - // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid - let z = unwrap_or_0(self.iter.next()); - let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z); - ch = init << 12 | y_z; - if x >= 0xF0 { - // [x y z w] case - // use only the lower 3 bits of `init` - let w = unwrap_or_0(self.iter.next()); - ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w); + next_code_point(&mut self.iter).map(|ch| { + // str invariant says `ch` is a valid Unicode Scalar Value + unsafe { + mem::transmute(ch) } - } - - // str invariant says `ch` is a valid Unicode Scalar Value - unsafe { - Some(mem::transmute(ch)) - } + }) } #[inline] @@ -1517,25 +1526,8 @@ impl StrExt for str { #[inline] fn char_range_at(&self, i: uint) -> CharRange { - if self.as_bytes()[i] < 128u8 { - return CharRange {ch: self.as_bytes()[i] as char, next: i + 1 }; - } - - // Multibyte case is a fn to allow char_range_at to inline cleanly - fn multibyte_char_range_at(s: &str, i: uint) -> CharRange { - let mut val = s.as_bytes()[i] as u32; - let w = UTF8_CHAR_WIDTH[val as uint] as uint; - assert!((w != 0)); - - val = utf8_first_byte!(val, w); - val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 1]); - if w > 2 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 2]); } - if w > 3 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 3]); } - - return CharRange {ch: unsafe { mem::transmute(val) }, next: i + w}; - } - - return multibyte_char_range_at(self, i); + let (c, n) = char_range_at_raw(self.as_bytes(), i); + CharRange { ch: unsafe { mem::transmute(c) }, next: n } } #[inline] @@ -1653,6 +1645,32 @@ impl StrExt for str { fn parse<T: FromStr>(&self) -> Option<T> { FromStr::from_str(self) } } +/// Pluck a code point out of a UTF-8-like byte slice and return the +/// index of the next code point. +#[inline] +#[unstable] +pub fn char_range_at_raw(bytes: &[u8], i: uint) -> (u32, usize) { + if bytes[i] < 128u8 { + return (bytes[i] as u32, i + 1); + } + + // Multibyte case is a fn to allow char_range_at to inline cleanly + fn multibyte_char_range_at(bytes: &[u8], i: uint) -> (u32, usize) { + let mut val = bytes[i] as u32; + let w = UTF8_CHAR_WIDTH[val as uint] as uint; + assert!((w != 0)); + + val = utf8_first_byte!(val, w); + val = utf8_acc_cont_byte!(val, bytes[i + 1]); + if w > 2 { val = utf8_acc_cont_byte!(val, bytes[i + 2]); } + if w > 3 { val = utf8_acc_cont_byte!(val, bytes[i + 3]); } + + return (val, i + w); + } + + multibyte_char_range_at(bytes, i) +} + #[stable] impl<'a> Default for &'a str { #[stable] diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index cc86f804e3e..95ad6178bab 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -17,4 +17,9 @@ pub use self::c_str::CString; pub use self::c_str::c_str_to_bytes; pub use self::c_str::c_str_to_bytes_with_nul; +pub use self::os_str::OsString; +pub use self::os_str::OsStr; +pub use self::os_str::AsOsStr; + mod c_str; +mod os_str; diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs new file mode 100644 index 00000000000..9c5cf62f853 --- /dev/null +++ b/src/libstd/ffi/os_str.rs @@ -0,0 +1,259 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A type that can represent all platform-native strings, but is cheaply +//! interconvertable with Rust strings. +//! +//! The need for this type arises from the fact that: +//! +//! * On Unix systems, strings are often arbitrary sequences of non-zero +//! bytes, in many cases interpreted as UTF-8. +//! +//! * On Windows, strings are often arbitrary sequences of non-zero 16-bit +//! values, interpreted as UTF-16 when it is valid to do so. +//! +//! * In Rust, strings are always valid UTF-8, but may contain zeros. +//! +//! The types in this module bridge this gap by simultaneously representing Rust +//! and platform-native string values, and in particular allowing a Rust string +//! to be converted into an "OS" string with no cost. +//! +//! **Note**: At the moment, these types are extremely bare-bones, usable only +//! for conversion to/from various other string types. Eventually these types +//! will offer a full-fledged string API. + +#![unstable = "recently added as part of path/io reform"] + +use core::prelude::*; + +use core::borrow::{BorrowFrom, ToOwned}; +use fmt::{self, Debug}; +use mem; +use string::{String, CowString}; +use ops; +use cmp; +use hash::{Hash, Hasher, Writer}; + +use sys::os_str::{Buf, Slice}; +use sys_common::{AsInner, IntoInner, FromInner}; + +/// Owned, mutable OS strings. +#[derive(Clone)] +pub struct OsString { + inner: Buf +} + +/// Slices into OS strings. +pub struct OsStr { + inner: Slice +} + +impl OsString { + /// Constructs an `OsString` at no cost by consuming a `String`. + pub fn from_string(s: String) -> OsString { + OsString { inner: Buf::from_string(s) } + } + + /// Constructs an `OsString` by copying from a `&str` slice. + /// + /// Equivalent to: `OsString::from_string(String::from_str(s))`. + pub fn from_str(s: &str) -> OsString { + OsString { inner: Buf::from_str(s) } + } + + /// Convert the `OsString` into a `String` if it contains valid Unicode data. + /// + /// On failure, ownership of the original `OsString` is returned. + pub fn into_string(self) -> Result<String, OsString> { + self.inner.into_string().map_err(|buf| OsString { inner: buf} ) + } + + /// Extend the string with the given `&OsStr` slice. + pub fn push_os_str(&mut self, s: &OsStr) { + self.inner.push_slice(&s.inner) + } +} + +impl ops::Index<ops::FullRange> for OsString { + type Output = OsStr; + + #[inline] + fn index(&self, _index: &ops::FullRange) -> &OsStr { + unsafe { mem::transmute(self.inner.as_slice()) } + } +} + +impl ops::Deref for OsString { + type Target = OsStr; + + #[inline] + fn deref(&self) -> &OsStr { + &self[] + } +} + +impl Debug for OsString { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt::Debug::fmt(&**self, formatter) + } +} + +impl OsStr { + /// Coerce directly from a `&str` slice to a `&OsStr` slice. + pub fn from_str(s: &str) -> &OsStr { + unsafe { mem::transmute(Slice::from_str(s)) } + } + + /// Yield a `&str` slice if the `OsStr` is valid unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Convert an `OsStr` to a `CowString`. + /// + /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER. + pub fn to_string_lossy(&self) -> CowString { + self.inner.to_string_lossy() + } + + /// Copy the slice into an onwed `OsString`. + pub fn to_os_string(&self) -> OsString { + OsString { inner: self.inner.to_owned() } + } + + /// Get the underlying byte representation. + /// + /// Note: it is *crucial* that this API is private, to avoid + /// revealing the internal, platform-specific encodings. + fn bytes(&self) -> &[u8] { + unsafe { mem::transmute(&self.inner) } + } +} + +impl PartialEq for OsStr { + fn eq(&self, other: &OsStr) -> bool { + self.bytes().eq(other.bytes()) + } +} + +impl PartialEq<str> for OsStr { + fn eq(&self, other: &str) -> bool { + *self == *OsStr::from_str(other) + } +} + +impl PartialEq<OsStr> for str { + fn eq(&self, other: &OsStr) -> bool { + *other == *OsStr::from_str(self) + } +} + +impl Eq for OsStr {} + +impl PartialOrd for OsStr { + #[inline] + fn partial_cmp(&self, other: &OsStr) -> Option<cmp::Ordering> { + self.bytes().partial_cmp(other.bytes()) + } + #[inline] + fn lt(&self, other: &OsStr) -> bool { self.bytes().lt(other.bytes()) } + #[inline] + fn le(&self, other: &OsStr) -> bool { self.bytes().le(other.bytes()) } + #[inline] + fn gt(&self, other: &OsStr) -> bool { self.bytes().gt(other.bytes()) } + #[inline] + fn ge(&self, other: &OsStr) -> bool { self.bytes().ge(other.bytes()) } +} + +impl PartialOrd<str> for OsStr { + #[inline] + fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> { + self.partial_cmp(OsStr::from_str(other)) + } +} + +// FIXME (#19470): cannot provide PartialOrd<OsStr> for str until we +// have more flexible coherence rules. + +impl Ord for OsStr { + #[inline] + fn cmp(&self, other: &OsStr) -> cmp::Ordering { self.bytes().cmp(other.bytes()) } +} + +impl<'a, S: Hasher + Writer> Hash<S> for OsStr { + #[inline] + fn hash(&self, state: &mut S) { + self.bytes().hash(state) + } +} + +impl Debug for OsStr { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.inner.fmt(formatter) + } +} + +impl BorrowFrom<OsString> for OsStr { + fn borrow_from(owned: &OsString) -> &OsStr { &owned[] } +} + +impl ToOwned<OsString> for OsStr { + fn to_owned(&self) -> OsString { self.to_os_string() } +} + +/// Freely convertible to an `&OsStr` slice. +pub trait AsOsStr { + /// Convert to an `&OsStr` slice. + fn as_os_str(&self) -> &OsStr; +} + +impl AsOsStr for OsStr { + fn as_os_str(&self) -> &OsStr { + self + } +} + +impl AsOsStr for OsString { + fn as_os_str(&self) -> &OsStr { + &self[] + } +} + +impl AsOsStr for str { + fn as_os_str(&self) -> &OsStr { + OsStr::from_str(self) + } +} + +impl AsOsStr for String { + fn as_os_str(&self) -> &OsStr { + OsStr::from_str(&self[]) + } +} + +impl FromInner<Buf> for OsString { + fn from_inner(buf: Buf) -> OsString { + OsString { inner: buf } + } +} + +impl IntoInner<Buf> for OsString { + fn into_inner(self) -> Buf { + self.inner + } +} + +impl AsInner<Slice> for OsStr { + fn as_inner(&self) -> &Slice { + &self.inner + } +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index a31dcc9884f..272cf9bd0c0 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -29,6 +29,7 @@ pub mod stack; pub mod thread; pub mod thread_info; pub mod thread_local; +pub mod wtf8; // common error constructors @@ -93,11 +94,21 @@ pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where return (origamt - amt) as i64; } -// A trait for extracting representations from std::io types -pub trait AsInner<Inner> { +/// A trait for viewing representations from std types +pub trait AsInner<Inner: ?Sized> { fn as_inner(&self) -> &Inner; } +/// A trait for extracting representations from std types +pub trait IntoInner<Inner> { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations +pub trait FromInner<Inner> { + fn from_inner(inner: Inner) -> Self; +} + pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> { fn program(&self) -> &CString; fn args(&self) -> &[CString]; diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs new file mode 100644 index 00000000000..bc072155063 --- /dev/null +++ b/src/libstd/sys/common/wtf8.rs @@ -0,0 +1,1212 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). +//! +//! This library uses Rust’s type system to maintain +//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), +//! like the `String` and `&str` types do for UTF-8. +//! +//! Since [WTF-8 must not be used +//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), +//! this library deliberately does not provide access to the underlying bytes +//! of WTF-8 strings, +//! nor can it decode WTF-8 from arbitrary bytes. +//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points. + +use core::prelude::*; + +use core::char::{encode_utf8_raw, encode_utf16_raw}; +use core::str::{char_range_at_raw, next_code_point}; +use core::raw::Slice as RawSlice; + +use borrow::Cow; +use cmp; +use fmt; +use hash::{Hash, Writer, Hasher}; +use iter::FromIterator; +use mem; +use num::Int; +use ops; +use slice; +use str; +use string::{String, CowString}; +use unicode::str::{Utf16Item, utf16_items}; +use vec::Vec; + +static UTF8_REPLACEMENT_CHARACTER: &'static [u8] = b"\xEF\xBF\xBD"; + +/// A Unicode code point: from U+0000 to U+10FFFF. +/// +/// Compare with the `char` type, +/// which represents a Unicode scalar value: +/// a code point that is not a surrogate (U+D800 to U+DFFF). +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub struct CodePoint { + value: u32 +} + +/// Format the code point as `U+` followed by four to six hexadecimal digits. +/// Example: `U+1F4A9` +impl fmt::Debug for CodePoint { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(formatter, "U+{:04X}", self.value) + } +} + +impl CodePoint { + /// Unsafely create a new `CodePoint` without checking the value. + /// + /// Only use when `value` is known to be less than or equal to 0x10FFFF. + #[inline] + pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint { + CodePoint { value: value } + } + + /// Create a new `CodePoint` if the value is a valid code point. + /// + /// Return `None` if `value` is above 0x10FFFF. + #[inline] + pub fn from_u32(value: u32) -> Option<CodePoint> { + match value { + 0 ... 0x10FFFF => Some(CodePoint { value: value }), + _ => None + } + } + + /// Create a new `CodePoint` from a `char`. + /// + /// Since all Unicode scalar values are code points, this always succeds. + #[inline] + pub fn from_char(value: char) -> CodePoint { + CodePoint { value: value as u32 } + } + + /// Return the numeric value of the code point. + #[inline] + pub fn to_u32(&self) -> u32 { + self.value + } + + /// Optionally return a Unicode scalar value for the code point. + /// + /// Return `None` if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char(&self) -> Option<char> { + match self.value { + 0xD800 ... 0xDFFF => None, + _ => Some(unsafe { mem::transmute(self.value) }) + } + } + + /// Return a Unicode scalar value for the code point. + /// + /// Return `'\u{FFFD}'` (the replacement character “�”) + /// if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char_lossy(&self) -> char { + self.to_char().unwrap_or('\u{FFFD}') + } +} + +/// An owned, growable string of well-formed WTF-8 data. +/// +/// Similar to `String`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)] +pub struct Wtf8Buf { + bytes: Vec<u8> +} + +impl ops::Deref for Wtf8Buf { + type Target = Wtf8; + + fn deref(&self) -> &Wtf8 { + self.as_slice() + } +} + +/// Format the string with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8Buf { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl Wtf8Buf { + /// Create an new, empty WTF-8 string. + #[inline] + pub fn new() -> Wtf8Buf { + Wtf8Buf { bytes: Vec::new() } + } + + /// Create an new, empty WTF-8 string with pre-allocated capacity for `n` bytes. + #[inline] + pub fn with_capacity(n: uint) -> Wtf8Buf { + Wtf8Buf { bytes: Vec::with_capacity(n) } + } + + /// Create a WTF-8 string from an UTF-8 `String`. + /// + /// This takes ownership of the `String` and does not copy. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_string(string: String) -> Wtf8Buf { + Wtf8Buf { bytes: string.into_bytes() } + } + + /// Create a WTF-8 string from an UTF-8 `&str` slice. + /// + /// This copies the content of the slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(str: &str) -> Wtf8Buf { + Wtf8Buf { bytes: slice::SliceExt::to_vec(str.as_bytes()) } + } + + /// Create a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + pub fn from_wide(v: &[u16]) -> Wtf8Buf { + let mut string = Wtf8Buf::with_capacity(v.len()); + for item in utf16_items(v) { + match item { + Utf16Item::ScalarValue(c) => string.push_char(c), + Utf16Item::LoneSurrogate(s) => { + // Surrogates are known to be in the code point range. + let code_point = unsafe { CodePoint::from_u32_unchecked(s as u32) }; + // Skip the WTF-8 concatenation check, + // surrogate pairs are already decoded by utf16_items + string.push_code_point_unchecked(code_point) + } + } + } + string + } + + /// Copied from String::push + /// This does **not** include the WTF-8 concatenation check. + fn push_code_point_unchecked(&mut self, code_point: CodePoint) { + let cur_len = self.len(); + // This may use up to 4 bytes. + self.reserve(4); + + unsafe { + // Attempt to not use an intermediate buffer by just pushing bytes + // directly onto this string. + let slice = RawSlice { + data: self.bytes.as_ptr().offset(cur_len as int), + len: 4, + }; + let used = encode_utf8_raw(code_point.value, mem::transmute(slice)) + .unwrap_or(0); + self.bytes.set_len(cur_len + used); + } + } + + #[inline] + pub fn as_slice(&self) -> &Wtf8 { + unsafe { mem::transmute(self.bytes.as_slice()) } + } + + /// Reserves capacity for at least `additional` more bytes to be inserted + /// in the given `Wtf8Buf`. + /// The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `uint`. + #[inline] + pub fn reserve(&mut self, additional: uint) { + self.bytes.reserve(additional) + } + + /// Returns the number of bytes that this string buffer can hold without reallocating. + #[inline] + pub fn capacity(&self) -> uint { + self.bytes.capacity() + } + + /// Append an UTF-8 slice at the end of the string. + #[inline] + pub fn push_str(&mut self, other: &str) { + self.bytes.push_all(other.as_bytes()) + } + + /// Append a WTF-8 slice at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push_wtf8(&mut self, other: &Wtf8) { + match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { + // Replace newly paired surrogates by a supplementary code point. + (Some(lead), Some(trail)) => { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + let other_without_trail_surrogate = &other.bytes[3..]; + // 4 bytes for the supplementary code point + self.bytes.reserve(4 + other_without_trail_surrogate.len()); + self.push_char(decode_surrogate_pair(lead, trail)); + self.bytes.push_all(other_without_trail_surrogate); + } + _ => self.bytes.push_all(&other.bytes) + } + } + + /// Append a Unicode scalar value at the end of the string. + #[inline] + pub fn push_char(&mut self, c: char) { + self.push_code_point_unchecked(CodePoint::from_char(c)) + } + + /// Append a code point at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push(&mut self, code_point: CodePoint) { + match code_point.to_u32() { + trail @ 0xDC00...0xDFFF => { + match (&*self).final_lead_surrogate() { + Some(lead) => { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + self.push_char(decode_surrogate_pair(lead, trail as u16)); + return + } + _ => {} + } + } + _ => {} + } + + // No newly paired surrogates at the boundary. + self.push_code_point_unchecked(code_point) + } + + /// Shortens a string to the specified length. + /// + /// # Panics + /// + /// Panics if `new_len` > current length, + /// or if `new_len` is not a code point boundary. + #[inline] + pub fn truncate(&mut self, new_len: uint) { + assert!(is_code_point_boundary(self.as_slice(), new_len)); + self.bytes.truncate(new_len) + } + + /// Consume the WTF-8 string and try to convert it to UTF-8. + /// + /// This does not copy the data. + /// + /// If the contents are not well-formed UTF-8 + /// (that is, if the string contains surrogates), + /// the original WTF-8 string is returned instead. + pub fn into_string(self) -> Result<String, Wtf8Buf> { + match self.next_surrogate(0) { + None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }), + Some(_) => Err(self), + } + } + + /// Consume the WTF-8 string and convert it lossily to UTF-8. + /// + /// This does not copy the data (but may overwrite parts of it in place). + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) + pub fn into_string_lossy(mut self) -> String { + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + pos = surrogate_pos + 3; + slice::bytes::copy_memory( + &mut self.bytes[surrogate_pos .. pos], + UTF8_REPLACEMENT_CHARACTER + ); + }, + None => return unsafe { String::from_utf8_unchecked(self.bytes) } + } + } + } +} + +/// Create a new WTF-8 string from an iterator of code points. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl FromIterator<CodePoint> for Wtf8Buf { + fn from_iter<T: Iterator<Item=CodePoint>>(iterator: T) -> Wtf8Buf { + let mut string = Wtf8Buf::new(); + string.extend(iterator); + string + } +} + +/// Append code points from an iterator to the string. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl Extend<CodePoint> for Wtf8Buf { + fn extend<T: Iterator<Item=CodePoint>>(&mut self, mut iterator: T) { + let (low, _high) = iterator.size_hint(); + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(low); + for code_point in iterator { + self.push(code_point); + } + } +} + +/// A borrowed slice of well-formed WTF-8 data. +/// +/// Similar to `&str`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +pub struct Wtf8 { + bytes: [u8] +} + +// FIXME: https://github.com/rust-lang/rust/issues/18805 +impl PartialEq for Wtf8 { + fn eq(&self, other: &Wtf8) -> bool { self.bytes.eq(&other.bytes) } +} + +// FIXME: https://github.com/rust-lang/rust/issues/18805 +impl Eq for Wtf8 {} + +// FIXME: https://github.com/rust-lang/rust/issues/18738 +impl PartialOrd for Wtf8 { + #[inline] + fn partial_cmp(&self, other: &Wtf8) -> Option<cmp::Ordering> { + self.bytes.partial_cmp(&other.bytes) + } + #[inline] + fn lt(&self, other: &Wtf8) -> bool { self.bytes.lt(&other.bytes) } + #[inline] + fn le(&self, other: &Wtf8) -> bool { self.bytes.le(&other.bytes) } + #[inline] + fn gt(&self, other: &Wtf8) -> bool { self.bytes.gt(&other.bytes) } + #[inline] + fn ge(&self, other: &Wtf8) -> bool { self.bytes.ge(&other.bytes) } +} + +// FIXME: https://github.com/rust-lang/rust/issues/18738 +impl Ord for Wtf8 { + #[inline] + fn cmp(&self, other: &Wtf8) -> cmp::Ordering { self.bytes.cmp(&other.bytes) } +} + +/// Format the slice with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8 { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + try!(formatter.write_str("\"")); + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + None => break, + Some((surrogate_pos, surrogate)) => { + try!(formatter.write_str(unsafe { + // the data in this slice is valid UTF-8, transmute to &str + mem::transmute(&self.bytes[pos .. surrogate_pos]) + })); + try!(write!(formatter, "\\u{{{:X}}}", surrogate)); + pos = surrogate_pos + 3; + } + } + } + try!(formatter.write_str(unsafe { + // the data in this slice is valid UTF-8, transmute to &str + mem::transmute(&self.bytes[pos..]) + })); + formatter.write_str("\"") + } +} + +impl Wtf8 { + /// Create a WTF-8 slice from a UTF-8 `&str` slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(value: &str) -> &Wtf8 { + unsafe { mem::transmute(value.as_bytes()) } + } + + /// Return the length, in WTF-8 bytes. + #[inline] + pub fn len(&self) -> uint { + self.bytes.len() + } + + /// Return the code point at `position` if it is in the ASCII range, + /// or `b'\xFF' otherwise. + /// + /// # Panics + /// + /// Panics if `position` is beyond the end of the string. + #[inline] + pub fn ascii_byte_at(&self, position: uint) -> u8 { + match self.bytes[position] { + ascii_byte @ 0x00 ... 0x7F => ascii_byte, + _ => 0xFF + } + } + + /// Return the code point at `position`. + /// + /// # Panics + /// + /// Panics if `position` is not at a code point boundary, + /// or is beyond the end of the string. + #[inline] + pub fn code_point_at(&self, position: uint) -> CodePoint { + let (code_point, _) = self.code_point_range_at(position); + code_point + } + + /// Return the code point at `position` + /// and the position of the next code point. + /// + /// # Panics + /// + /// Panics if `position` is not at a code point boundary, + /// or is beyond the end of the string. + #[inline] + pub fn code_point_range_at(&self, position: uint) -> (CodePoint, uint) { + let (c, n) = char_range_at_raw(&self.bytes, position); + (CodePoint { value: c }, n) + } + + /// Return an iterator for the string’s code points. + #[inline] + pub fn code_points(&self) -> Wtf8CodePoints { + Wtf8CodePoints { bytes: self.bytes.iter() } + } + + /// Try to convert the string to UTF-8 and return a `&str` slice. + /// + /// Return `None` if the string contains surrogates. + /// + /// This does not copy the data. + #[inline] + pub fn as_str(&self) -> Option<&str> { + // Well-formed WTF-8 is also well-formed UTF-8 + // if and only if it contains no surrogate. + match self.next_surrogate(0) { + None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some(_) => None, + } + } + + /// Lossily convert the string to UTF-8. + /// Return an UTF-8 `&str` slice if the contents are well-formed in UTF-8. + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). + /// + /// This only copies the data if necessary (if it contains any surrogate). + pub fn to_string_lossy(&self) -> CowString { + let surrogate_pos = match self.next_surrogate(0) { + None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some((pos, _)) => pos, + }; + let wtf8_bytes = &self.bytes; + let mut utf8_bytes = Vec::with_capacity(self.len()); + utf8_bytes.push_all(&wtf8_bytes[..surrogate_pos]); + utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER); + let mut pos = surrogate_pos + 3; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + utf8_bytes.push_all(&wtf8_bytes[pos .. surrogate_pos]); + utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER); + pos = surrogate_pos + 3; + }, + None => { + utf8_bytes.push_all(&wtf8_bytes[pos..]); + return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }) + } + } + } + } + + /// Convert the WTF-8 string to potentially ill-formed UTF-16 + /// and return an iterator of 16-bit code units. + /// + /// This is lossless: + /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units + /// would always return the original WTF-8 string. + #[inline] + pub fn encode_wide(&self) -> EncodeWide { + EncodeWide { code_points: self.code_points(), extra: 0 } + } + + #[inline] + fn next_surrogate(&self, mut pos: uint) -> Option<(uint, u16)> { + let mut iter = self.bytes[pos..].iter(); + loop { + let b = match iter.next() { + None => return None, + Some(&b) => b, + }; + if b < 0x80 { + pos += 1; + } else if b < 0xE0 { + iter.next(); + pos += 2; + } else if b == 0xED { + match (iter.next(), iter.next()) { + (Some(&b2), Some(&b3)) if b2 >= 0xA0 => { + return Some((pos, decode_surrogate(b2, b3))) + } + _ => pos += 3 + } + } else if b < 0xF0 { + iter.next(); + iter.next(); + pos += 3; + } else { + iter.next(); + iter.next(); + iter.next(); + pos += 4; + } + } + } + + #[inline] + fn final_lead_surrogate(&self) -> Option<u16> { + let len = self.len(); + if len < 3 { + return None + } + match &self.bytes[(len - 3)..] { + [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)), + _ => None + } + } + + #[inline] + fn initial_trail_surrogate(&self) -> Option<u16> { + let len = self.len(); + if len < 3 { + return None + } + match &self.bytes[..3] { + [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)), + _ => None + } + } +} + + +/// Return a slice of the given string for the byte range [`begin`..`end`). +/// +/// # Panics +/// +/// Panics when `begin` and `end` do not point to code point boundaries, +/// or point beyond the end of the string. +impl ops::Index<ops::Range<usize>> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::Range<usize>) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if range.start <= range.end && + is_code_point_boundary(self, range.start) && + is_code_point_boundary(self, range.end) { + unsafe { slice_unchecked(self, range.start, range.end) } + } else { + slice_error_fail(self, range.start, range.end) + } + } +} + +/// Return a slice of the given string from byte `begin` to its end. +/// +/// # Panics +/// +/// Panics when `begin` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index<ops::RangeFrom<usize>> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::RangeFrom<usize>) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.start) { + unsafe { slice_unchecked(self, range.start, self.len()) } + } else { + slice_error_fail(self, range.start, self.len()) + } + } +} + +/// Return a slice of the given string from its beginning to byte `end`. +/// +/// # Panics +/// +/// Panics when `end` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index<ops::RangeTo<usize>> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::RangeTo<usize>) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.end) { + unsafe { slice_unchecked(self, 0, range.end) } + } else { + slice_error_fail(self, 0, range.end) + } + } +} + +impl ops::Index<ops::FullRange> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, _range: &ops::FullRange) -> &Wtf8 { + self + } +} + +#[inline] +fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 { + // The first byte is assumed to be 0xED + 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F +} + +#[inline] +fn decode_surrogate_pair(lead: u16, trail: u16) -> char { + let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); + unsafe { mem::transmute(code_point) } +} + +/// Copied from core::str::StrPrelude::is_char_boundary +#[inline] +pub fn is_code_point_boundary(slice: &Wtf8, index: uint) -> bool { + if index == slice.len() { return true; } + match slice.bytes.get(index) { + None => false, + Some(&b) => b < 128u8 || b >= 192u8, + } +} + +/// Copied from core::str::raw::slice_unchecked +#[inline] +pub unsafe fn slice_unchecked(s: &Wtf8, begin: uint, end: uint) -> &Wtf8 { + mem::transmute(RawSlice { + data: s.bytes.as_ptr().offset(begin as int), + len: end - begin, + }) +} + +/// Copied from core::str::raw::slice_error_fail +#[inline(never)] +pub fn slice_error_fail(s: &Wtf8, begin: uint, end: uint) -> ! { + assert!(begin <= end); + panic!("index {} and/or {} in `{:?}` do not lie on character boundary", + begin, end, s); +} + +/// Iterator for the code points of a WTF-8 string. +/// +/// Created with the method `.code_points()`. +#[derive(Clone)] +pub struct Wtf8CodePoints<'a> { + bytes: slice::Iter<'a, u8> +} + +impl<'a> Iterator for Wtf8CodePoints<'a> { + type Item = CodePoint; + + #[inline] + fn next(&mut self) -> Option<CodePoint> { + next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) + } + + #[inline] + fn size_hint(&self) -> (uint, Option<uint>) { + let (len, _) = self.bytes.size_hint(); + (len.saturating_add(3) / 4, Some(len)) + } +} + +#[derive(Clone)] +pub struct EncodeWide<'a> { + code_points: Wtf8CodePoints<'a>, + extra: u16 +} + +// Copied from libunicode/u_str.rs +impl<'a> Iterator for EncodeWide<'a> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option<u16> { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0u16; 2]; + self.code_points.next().map(|code_point| { + let n = encode_utf16_raw(code_point.value, buf.as_mut_slice()) + .unwrap_or(0); + if n == 2 { self.extra = buf[1]; } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (uint, Option<uint>) { + let (low, high) = self.code_points.size_hint(); + // every code point gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +impl<S: Writer + Hasher> Hash<S> for CodePoint { + #[inline] + fn hash(&self, state: &mut S) { + self.value.hash(state) + } +} + +impl<S: Writer + Hasher> Hash<S> for Wtf8Buf { + #[inline] + fn hash(&self, state: &mut S) { + state.write(self.bytes.as_slice()); + 0xfeu8.hash(state) + } +} + +impl<'a, S: Writer + Hasher> Hash<S> for Wtf8 { + #[inline] + fn hash(&self, state: &mut S) { + state.write(&self.bytes); + 0xfeu8.hash(state) + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use borrow::Cow; + use super::*; + use mem::transmute; + use string::CowString; + + #[test] + fn code_point_from_u32() { + assert!(CodePoint::from_u32(0).is_some()); + assert!(CodePoint::from_u32(0xD800).is_some()); + assert!(CodePoint::from_u32(0x10FFFF).is_some()); + assert!(CodePoint::from_u32(0x110000).is_none()); + } + + #[test] + fn code_point_to_u32() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0).to_u32(), 0); + assert_eq!(c(0xD800).to_u32(), 0xD800); + assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF); + } + + #[test] + fn code_point_from_char() { + assert_eq!(CodePoint::from_char('a').to_u32(), 0x61); + assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9); + } + + #[test] + fn code_point_to_string() { + assert_eq!(format!("{:?}", CodePoint::from_char('a')).as_slice(), "U+0061"); + assert_eq!(format!("{:?}", CodePoint::from_char('💩')).as_slice(), "U+1F4A9"); + } + + #[test] + fn code_point_to_char() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0x61).to_char(), Some('a')); + assert_eq!(c(0x1F4A9).to_char(), Some('💩')); + assert_eq!(c(0xD800).to_char(), None); + } + + #[test] + fn code_point_to_char_lossy() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0x61).to_char_lossy(), 'a'); + assert_eq!(c(0x1F4A9).to_char_lossy(), '💩'); + assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}'); + } + + #[test] + fn wtf8buf_new() { + assert_eq!(Wtf8Buf::new().bytes.as_slice(), b""); + } + + #[test] + fn wtf8buf_from_str() { + assert_eq!(Wtf8Buf::from_str("").bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_str("aé 💩").bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_from_string() { + assert_eq!(Wtf8Buf::from_string(String::from_str("")).bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_string(String::from_str("aé 💩")).bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_from_wide() { + assert_eq!(Wtf8Buf::from_wide(&[]).bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_wide( + &[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes.as_slice(), + b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push_str() { + let mut string = Wtf8Buf::new(); + assert_eq!(string.bytes.as_slice(), b""); + string.push_str("aé 💩"); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push_char() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 "); + string.push_char('💩'); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 "); + string.push(CodePoint::from_char('💩')); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0x20)); // not surrogate + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xDBFF)); // lead + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xE000)); // not surrogate + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD7FF)); // not surrogate + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0x61)); // not surrogate, < 3 bytes + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_push_wtf8() { + let mut string = Wtf8Buf::from_str("aé"); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9"); + string.push_wtf8(Wtf8::from_str(" 💩")); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn w(value: &[u8]) -> &Wtf8 { unsafe { transmute(value) } } + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b" ")); // not surrogate + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xED\xAF\xBF")); // lead + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_truncate() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(1); + assert_eq!(string.bytes.as_slice(), b"a"); + } + + #[test] + #[should_fail] + fn wtf8buf_truncate_fail_code_point_boundary() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(2); + } + + #[test] + #[should_fail] + fn wtf8buf_truncate_fail_longer() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(4); + } + + #[test] + fn wtf8buf_into_string() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string(), Ok(String::from_str("aé 💩"))); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string(), Err(string)); + } + + #[test] + fn wtf8buf_into_string_lossy() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩")); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩�")); + } + + #[test] + fn wtf8buf_from_iterator() { + fn f(values: &[u32]) -> Wtf8Buf { + values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>() + }; + assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(f(&[0xD83D, 0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(f(&[0xD800, 0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(f(&[0xD800, 0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(f(&[0xD7FF, 0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(f(&[0x61, 0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80"); + assert_eq!(f(&[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_extend() { + fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf { + fn c(value: &u32) -> CodePoint { CodePoint::from_u32(*value).unwrap() } + let mut string = initial.iter().map(c).collect::<Wtf8Buf>(); + string.extend(extended.iter().map(c)); + string + }; + + assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(e(&[0xD800], &[0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(e(&[0xD800], &[0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(e(&[0x61], &[0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80"); + assert_eq!(e(&[], &[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_show() { + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(format!("{:?}", string).as_slice(), r#""aé 💩\u{D800}""#); + } + + #[test] + fn wtf8buf_as_slice() { + assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé")); + } + + #[test] + fn wtf8_show() { + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(format!("{:?}", string.as_slice()).as_slice(), r#""aé 💩\u{D800}""#); + } + + #[test] + fn wtf8_from_str() { + assert_eq!(&Wtf8::from_str("").bytes, b""); + assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8_len() { + assert_eq!(Wtf8::from_str("").len(), 0); + assert_eq!(Wtf8::from_str("aé 💩").len(), 8); + } + + #[test] + fn wtf8_slice() { + assert_eq!(&Wtf8::from_str("aé 💩")[1.. 4].bytes, b"\xC3\xA9 "); + } + + #[test] + #[should_fail] + fn wtf8_slice_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2.. 4]; + } + + #[test] + fn wtf8_slice_from() { + assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + #[should_fail] + fn wtf8_slice_from_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2..]; + } + + #[test] + fn wtf8_slice_to() { + assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 "); + } + + #[test] + #[should_fail] + fn wtf8_slice_to_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[5..]; + } + + #[test] + fn wtf8_ascii_byte_at() { + let slice = Wtf8::from_str("aé 💩"); + assert_eq!(slice.ascii_byte_at(0), b'a'); + assert_eq!(slice.ascii_byte_at(1), b'\xFF'); + assert_eq!(slice.ascii_byte_at(2), b'\xFF'); + assert_eq!(slice.ascii_byte_at(3), b' '); + assert_eq!(slice.ascii_byte_at(4), b'\xFF'); + } + + #[test] + fn wtf8_code_point_at() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.code_point_at(0), CodePoint::from_char('a')); + assert_eq!(string.code_point_at(1), CodePoint::from_char('é')); + assert_eq!(string.code_point_at(3), CodePoint::from_char(' ')); + assert_eq!(string.code_point_at(4), CodePoint::from_u32(0xD83D).unwrap()); + assert_eq!(string.code_point_at(7), CodePoint::from_char('💩')); + } + + #[test] + fn wtf8_code_point_range_at() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.code_point_range_at(0), (CodePoint::from_char('a'), 1)); + assert_eq!(string.code_point_range_at(1), (CodePoint::from_char('é'), 3)); + assert_eq!(string.code_point_range_at(3), (CodePoint::from_char(' '), 4)); + assert_eq!(string.code_point_range_at(4), (CodePoint::from_u32(0xD83D).unwrap(), 7)); + assert_eq!(string.code_point_range_at(7), (CodePoint::from_char('💩'), 11)); + } + + #[test] + fn wtf8_code_points() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + fn cp(string: &Wtf8Buf) -> Vec<Option<char>> { + string.code_points().map(|c| c.to_char()).collect::<Vec<_>>() + } + let mut string = Wtf8Buf::from_str("é "); + assert_eq!(cp(&string), vec![Some('é'), Some(' ')]); + string.push(c(0xD83D)); + assert_eq!(cp(&string), vec![Some('é'), Some(' '), None]); + string.push(c(0xDCA9)); + assert_eq!(cp(&string), vec![Some('é'), Some(' '), Some('💩')]); + } + + #[test] + fn wtf8_as_str() { + assert_eq!(Wtf8::from_str("").as_str(), Some("")); + assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.as_str(), None); + } + + #[test] + fn wtf8_to_string_lossy() { + assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed("")); + assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + let expected: CowString = Cow::Owned(String::from_str("aé 💩�")); + assert_eq!(string.to_string_lossy(), expected); + } + + #[test] + fn wtf8_encode_wide() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.encode_wide().collect::<Vec<_>>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]); + } +} diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs index 0e4a9d1b307..9c659b56d93 100644 --- a/src/libstd/sys/unix/ext.rs +++ b/src/libstd/sys/unix/ext.rs @@ -31,7 +31,10 @@ #![unstable] -use sys_common::AsInner; +use vec::Vec; +use sys::os_str::Buf; +use sys_common::{AsInner, IntoInner, FromInner}; +use ffi::{OsStr, OsString}; use libc; use io; @@ -99,6 +102,36 @@ impl AsRawFd for io::net::udp::UdpSocket { } } +// Unix-specific extensions to `OsString`. +pub trait OsStringExt { + /// Create an `OsString` from a byte vector. + fn from_vec(vec: Vec<u8>) -> Self; + + /// Yield the underlying byte vector of this `OsString`. + fn into_vec(self) -> Vec<u8>; +} + +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +// Unix-specific extensions to `OsStr`. +pub trait OsStrExt { + fn as_byte_slice(&self) -> &[u8]; +} + +impl OsStrExt for OsStr { + fn as_byte_slice(&self) -> &[u8] { + &self.as_inner().inner + } +} + /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index bb98d1e052a..5493bc20a87 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -44,6 +44,7 @@ pub mod fs; pub mod helper_signal; pub mod mutex; pub mod os; +pub mod os_str; pub mod pipe; pub mod process; pub mod rwlock; diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs new file mode 100644 index 00000000000..3dd89ad0759 --- /dev/null +++ b/src/libstd/sys/unix/os_str.rs @@ -0,0 +1,86 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec<u8>`/`[u8]`. + +use core::prelude::*; + +use fmt::{self, Debug}; +use vec::Vec; +use slice::SliceExt as StdSliceExt; +use str; +use string::{String, CowString}; +use mem; + +#[derive(Clone)] +pub struct Buf { + pub inner: Vec<u8> +} + +pub struct Slice { + pub inner: [u8] +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.to_string_lossy().fmt(formatter) + } +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + pub fn from_str(s: &str) -> Buf { + Buf { inner: s.as_bytes().to_vec() } + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn into_string(self) -> Result<String, Buf> { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_all(&s.inner) + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(s.as_bytes()) } + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> CowString { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } +} diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs index 87ff31ab73c..de37c428288 100644 --- a/src/libstd/sys/windows/ext.rs +++ b/src/libstd/sys/windows/ext.rs @@ -16,7 +16,11 @@ #![unstable] -use sys_common::AsInner; +pub use sys_common::wtf8::{Wtf8Buf, EncodeWide}; + +use sys::os_str::Buf; +use sys_common::{AsInner, FromInner}; +use ffi::{OsStr, OsString}; use libc; use io; @@ -92,9 +96,35 @@ impl AsRawSocket for io::net::udp::UdpSocket { } } +// Windows-specific extensions to `OsString`. +pub trait OsStringExt { + /// Create an `OsString` from a potentially ill-formed UTF-16 slice of 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + fn from_wide(wide: &[u16]) -> Self; +} + +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +// Windows-specific extensions to `OsStr`. +pub trait OsStrExt { + fn encode_wide(&self) -> EncodeWide; +} + +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide { + self.as_inner().inner.encode_wide() + } +} + /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. pub mod prelude { - pub use super::{Socket, Handle, AsRawSocket, AsRawHandle}; + pub use super::{Socket, Handle, AsRawSocket, AsRawHandle, OsStrExt, OsStringExt}; } diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 72fc2f8700d..876159623ac 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -44,6 +44,7 @@ pub mod fs; pub mod helper_signal; pub mod mutex; pub mod os; +pub mod os_str; pub mod pipe; pub mod process; pub mod rwlock; diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs new file mode 100644 index 00000000000..aab2406cef9 --- /dev/null +++ b/src/libstd/sys/windows/os_str.rs @@ -0,0 +1,82 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Windows is a +/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. + +use fmt::{self, Debug}; +use sys_common::wtf8::{Wtf8, Wtf8Buf}; +use string::{String, CowString}; +use result::Result; +use option::Option; +use mem; + +#[derive(Clone)] +pub struct Buf { + pub inner: Wtf8Buf +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +pub struct Slice { + pub inner: Wtf8 +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.inner.fmt(formatter) + } +} + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: Wtf8Buf::from_string(s) } + } + + pub fn from_str(s: &str) -> Buf { + Buf { inner: Wtf8Buf::from_str(s) } + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn into_string(self) -> Result<String, Buf> { + self.inner.into_string().map_err(|buf| Buf { inner: buf }) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_wtf8(&s.inner) + } +} + +impl Slice { + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(Wtf8::from_str(s)) } + } + + pub fn to_str(&self) -> Option<&str> { + self.inner.as_str() + } + + pub fn to_string_lossy(&self) -> CowString { + self.inner.to_string_lossy() + } + + pub fn to_owned(&self) -> Buf { + let mut buf = Wtf8Buf::with_capacity(self.inner.len()); + buf.push_wtf8(&self.inner); + Buf { inner: buf } + } +} From f67e7470b31a38e80c9f1007e4182ff5b56db2f7 Mon Sep 17 00:00:00 2001 From: Alfie John <alfiej@fastmail.fm> Date: Thu, 22 Jan 2015 14:08:56 +0000 Subject: [PATCH 32/33] Moving away from deprecated i/u suffixes in libcore --- src/libcore/atomic.rs | 20 ++++++------- src/libcore/cell.rs | 8 +++--- src/libcore/char.rs | 16 +++++------ src/libcore/cmp.rs | 16 +++++------ src/libcore/default.rs | 20 ++++++------- src/libcore/fmt/float.rs | 6 ++-- src/libcore/fmt/mod.rs | 6 ++-- src/libcore/fmt/num.rs | 2 +- src/libcore/hash/sip.rs | 2 +- src/libcore/iter.rs | 10 +++---- src/libcore/macros.rs | 10 +++---- src/libcore/mem.rs | 14 ++++----- src/libcore/num/f32.rs | 6 ++-- src/libcore/num/f64.rs | 6 ++-- src/libcore/num/mod.rs | 8 +++--- src/libcore/option.rs | 62 ++++++++++++++++++++-------------------- src/libcore/result.rs | 26 ++++++++--------- src/libcore/slice.rs | 8 +++--- src/libcore/str/mod.rs | 10 +++---- 19 files changed, 128 insertions(+), 128 deletions(-) diff --git a/src/libcore/atomic.rs b/src/libcore/atomic.rs index 18f7fff9053..e75481198d4 100644 --- a/src/libcore/atomic.rs +++ b/src/libcore/atomic.rs @@ -589,7 +589,7 @@ impl AtomicUsize { /// ``` /// use std::sync::atomic::AtomicUsize; /// - /// let atomic_forty_two = AtomicUsize::new(42u); + /// let atomic_forty_two = AtomicUsize::new(42); /// ``` #[inline] pub fn new(v: usize) -> AtomicUsize { @@ -765,7 +765,7 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::AtomicPtr; /// - /// let ptr = &mut 5i; + /// let ptr = &mut 5; /// let atomic_ptr = AtomicPtr::new(ptr); /// ``` #[inline] @@ -787,7 +787,7 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let ptr = &mut 5i; + /// let ptr = &mut 5; /// let some_ptr = AtomicPtr::new(ptr); /// /// let value = some_ptr.load(Ordering::Relaxed); @@ -809,10 +809,10 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let ptr = &mut 5i; + /// let ptr = &mut 5; /// let some_ptr = AtomicPtr::new(ptr); /// - /// let other_ptr = &mut 10i; + /// let other_ptr = &mut 10; /// /// some_ptr.store(other_ptr, Ordering::Relaxed); /// ``` @@ -835,10 +835,10 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let ptr = &mut 5i; + /// let ptr = &mut 5; /// let some_ptr = AtomicPtr::new(ptr); /// - /// let other_ptr = &mut 10i; + /// let other_ptr = &mut 10; /// /// let value = some_ptr.swap(other_ptr, Ordering::Relaxed); /// ``` @@ -860,11 +860,11 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let ptr = &mut 5i; + /// let ptr = &mut 5; /// let some_ptr = AtomicPtr::new(ptr); /// - /// let other_ptr = &mut 10i; - /// let another_ptr = &mut 10i; + /// let other_ptr = &mut 10; + /// let another_ptr = &mut 10; /// /// let value = some_ptr.compare_and_swap(other_ptr, another_ptr, Ordering::Relaxed); /// ``` diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 7f73be9eb5f..94307166ad5 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -67,10 +67,10 @@ //! //! fn main() { //! let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new())); -//! shared_map.borrow_mut().insert("africa", 92388i); -//! shared_map.borrow_mut().insert("kyoto", 11837i); -//! shared_map.borrow_mut().insert("piccadilly", 11826i); -//! shared_map.borrow_mut().insert("marbles", 38i); +//! shared_map.borrow_mut().insert("africa", 92388); +//! shared_map.borrow_mut().insert("kyoto", 11837); +//! shared_map.borrow_mut().insert("piccadilly", 11826); +//! shared_map.borrow_mut().insert("marbles", 38); //! } //! ``` //! diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 0e6b634bd11..c17f7cdccba 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -102,7 +102,7 @@ pub fn from_digit(num: uint, radix: uint) -> Option<char> { if num < 10 { Some(transmute(('0' as uint + num) as u32)) } else { - Some(transmute(('a' as uint + num - 10u) as u32)) + Some(transmute(('a' as uint + num - 10) as u32)) } } } else { @@ -208,8 +208,8 @@ impl CharExt for char { } let val = match self { '0' ... '9' => self as uint - ('0' as uint), - 'a' ... 'z' => self as uint + 10u - ('a' as uint), - 'A' ... 'Z' => self as uint + 10u - ('A' as uint), + 'a' ... 'z' => self as uint + 10 - ('a' as uint), + 'A' ... 'Z' => self as uint + 10 - ('A' as uint), _ => return None, }; if val < radix { Some(val) } @@ -241,10 +241,10 @@ impl CharExt for char { fn len_utf8(self) -> uint { let code = self as u32; match () { - _ if code < MAX_ONE_B => 1u, - _ if code < MAX_TWO_B => 2u, - _ if code < MAX_THREE_B => 3u, - _ => 4u, + _ if code < MAX_ONE_B => 1, + _ if code < MAX_TWO_B => 2, + _ if code < MAX_THREE_B => 3, + _ => 4, } } @@ -359,7 +359,7 @@ impl Iterator for EscapeUnicode { Some('u') } EscapeUnicodeState::LeftBrace => { - let mut n = 0u; + let mut n = 0; while (self.c as u32) >> (4 * (n + 1)) != 0 { n += 1; } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index c1f1997df74..6cda9d0e653 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -110,13 +110,13 @@ pub trait Eq: PartialEq<Self> { pub enum Ordering { /// An ordering where a compared value is less [than another]. #[stable] - Less = -1i, + Less = -1, /// An ordering where a compared value is equal [to another]. #[stable] - Equal = 0i, + Equal = 0, /// An ordering where a compared value is greater [than another]. #[stable] - Greater = 1i, + Greater = 1, } impl Ordering { @@ -132,12 +132,12 @@ impl Ordering { /// assert_eq!(Equal.reverse(), Equal); /// assert_eq!(Greater.reverse(), Less); /// - /// let mut data: &mut [_] = &mut [2u, 10, 5, 8]; + /// let mut data: &mut [_] = &mut [2, 10, 5, 8]; /// /// // sort the array from largest to smallest. /// data.sort_by(|a, b| a.cmp(b).reverse()); /// - /// let b: &mut [_] = &mut [10u, 8, 5, 2]; + /// let b: &mut [_] = &mut [10, 8, 5, 2]; /// assert!(data == b); /// ``` #[inline] @@ -174,9 +174,9 @@ pub trait Ord: Eq + PartialOrd<Self> { /// ``` /// use std::cmp::Ordering::{Less, Equal, Greater}; /// - /// assert_eq!( 5u.cmp(&10), Less); // because 5 < 10 - /// assert_eq!(10u.cmp(&5), Greater); // because 10 > 5 - /// assert_eq!( 5u.cmp(&5), Equal); // because 5 == 5 + /// assert_eq!( 5.cmp(&10), Less); // because 5 < 10 + /// assert_eq!(10.cmp(&5), Greater); // because 10 > 5 + /// assert_eq!( 5.cmp(&5), Equal); // because 5 == 5 /// ``` #[stable] fn cmp(&self, other: &Self) -> Ordering; diff --git a/src/libcore/default.rs b/src/libcore/default.rs index 8d4ecf72243..3b1e5e23150 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs @@ -150,17 +150,17 @@ default_impl! { (), () } default_impl! { bool, false } default_impl! { char, '\x00' } -default_impl! { uint, 0u } -default_impl! { u8, 0u8 } -default_impl! { u16, 0u16 } -default_impl! { u32, 0u32 } -default_impl! { u64, 0u64 } +default_impl! { uint, 0 } +default_impl! { u8, 0 } +default_impl! { u16, 0 } +default_impl! { u32, 0 } +default_impl! { u64, 0 } -default_impl! { int, 0i } -default_impl! { i8, 0i8 } -default_impl! { i16, 0i16 } -default_impl! { i32, 0i32 } -default_impl! { i64, 0i64 } +default_impl! { int, 0 } +default_impl! { i8, 0 } +default_impl! { i16, 0 } +default_impl! { i32, 0 } +default_impl! { i64, 0 } default_impl! { f32, 0.0f32 } default_impl! { f64, 0.0f64 } diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index 245dc00d838..50123499eba 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -53,7 +53,7 @@ pub enum SignFormat { SignNeg } -static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; +static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11; /// Converts a number to its string representation as a byte vector. /// This is meant to be a common base implementation for all numeric string @@ -191,7 +191,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>( if deccum != _0 || (limit_digits && exact && digit_count > 0) { buf[end] = b'.'; end += 1; - let mut dig = 0u; + let mut dig = 0; // calculate new digits while // - there is no limit and there are digits left @@ -218,7 +218,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>( // Decrease the deccumulator one fractional digit at a time deccum = deccum.fract(); - dig += 1u; + dig += 1; } // If digits are limited, and that limit has been reached, diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0e8d31a62ee..17eff98d429 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -560,8 +560,8 @@ impl<'a> Formatter<'a> { }; let (pre_pad, post_pad) = match align { - rt::AlignLeft => (0u, padding), - rt::AlignRight | rt::AlignUnknown => (padding, 0u), + rt::AlignLeft => (0, padding), + rt::AlignRight | rt::AlignUnknown => (padding, 0), rt::AlignCenter => (padding / 2, (padding + 1) / 2), }; @@ -846,7 +846,7 @@ macro_rules! tuple { fn fmt(&self, f: &mut Formatter) -> Result { try!(write!(f, "(")); let ($(ref $name,)*) = *self; - let mut n = 0i; + let mut n = 0; $( if n > 0 { try!(write!(f, ", ")); diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index c456b3379e8..a659a9988bb 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -145,7 +145,7 @@ pub struct RadixFmt<T, R>(T, R); /// /// ``` /// use std::fmt::radix; -/// assert_eq!(format!("{}", radix(55i, 36)), "1j".to_string()); +/// assert_eq!(format!("{}", radix(55, 36)), "1j".to_string()); /// ``` #[unstable = "may be renamed or move to a different module"] pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> { diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs index c20fb8457d2..8840eaf5fbc 100644 --- a/src/libcore/hash/sip.rs +++ b/src/libcore/hash/sip.rs @@ -122,7 +122,7 @@ impl Writer for SipHasher { let length = msg.len(); self.length += length; - let mut needed = 0u; + let mut needed = 0; if self.ntail != 0 { needed = 8 - self.ntail; diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 2673cf1af78..591822d6e1b 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -33,7 +33,7 @@ //! translated to the `loop` below. //! //! ``` -//! let values = vec![1i, 2, 3]; +//! let values = vec![1, 2, 3]; //! //! // "Syntactical sugar" taking advantage of an iterator //! for &x in values.iter() { @@ -615,7 +615,7 @@ pub trait IteratorExt: Iterator + Sized { /// # Examples /// /// ``` - /// let a = [1i, 2, 3, 4, 5]; + /// let a = [1, 2, 3, 4, 5]; /// assert!(a.iter().all(|x| *x > 0)); /// assert!(!a.iter().all(|x| *x > 2)); /// ``` @@ -1141,7 +1141,7 @@ pub trait AdditiveIterator<A> { /// ``` /// use std::iter::AdditiveIterator; /// - /// let a = [1i, 2, 3, 4, 5]; + /// let a = [1i32, 2, 3, 4, 5]; /// let mut it = a.iter().map(|&x| x); /// assert!(it.sum() == 15); /// ``` @@ -1183,7 +1183,7 @@ pub trait MultiplicativeIterator<A> { /// use std::iter::{count, MultiplicativeIterator}; /// /// fn factorial(n: usize) -> usize { - /// count(1u, 1).take_while(|&i| i <= n).product() + /// count(1, 1).take_while(|&i| i <= n).product() /// } /// assert!(factorial(0) == 1); /// assert!(factorial(1) == 1); @@ -2526,7 +2526,7 @@ pub struct Range<A> { /// ``` /// let array = [0, 1, 2, 3, 4]; /// -/// for i in range(0, 5u) { +/// for i in range(0, 5) { /// println!("{}", i); /// assert_eq!(i, array[i]); /// } diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 1c37126e8e9..d44eaae820f 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -48,7 +48,7 @@ macro_rules! panic { /// let x = true; /// assert!(x, "x wasn't true!"); /// -/// let a = 3i; let b = 27i; +/// let a = 3; let b = 27; /// assert!(a + b == 30, "a = {}, b = {}", a, b); /// ``` #[macro_export] @@ -74,8 +74,8 @@ macro_rules! assert { /// # Example /// /// ``` -/// let a = 3i; -/// let b = 1i + 2i; +/// let a = 3; +/// let b = 1 + 2; /// assert_eq!(a, b); /// ``` #[macro_export] @@ -141,8 +141,8 @@ macro_rules! debug_assert { /// # Example /// /// ``` -/// let a = 3i; -/// let b = 1i + 2i; +/// let a = 3; +/// let b = 1 + 2; /// debug_assert_eq!(a, b); /// ``` #[macro_export] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 8438c9b206e..608a78325b3 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -187,13 +187,13 @@ pub unsafe fn uninitialized<T>() -> T { /// ``` /// use std::mem; /// -/// let x = &mut 5i; -/// let y = &mut 42i; +/// let x = &mut 5; +/// let y = &mut 42; /// /// mem::swap(x, y); /// -/// assert_eq!(42i, *x); -/// assert_eq!(5i, *y); +/// assert_eq!(42, *x); +/// assert_eq!(5, *y); /// ``` #[inline] #[stable] @@ -277,7 +277,7 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T { /// ``` /// use std::cell::RefCell; /// -/// let x = RefCell::new(1i); +/// let x = RefCell::new(1); /// /// let mut mutable_borrow = x.borrow_mut(); /// *mutable_borrow = 1; @@ -306,9 +306,9 @@ pub fn drop<T>(_x: T) { } /// ``` /// use std::mem; /// -/// let one = unsafe { mem::transmute_copy(&1i) }; +/// let one = unsafe { mem::transmute_copy(&1) }; /// -/// assert_eq!(1u, one); +/// assert_eq!(1, one); /// ``` #[inline] #[stable] diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index f8a85e788e9..2186101bc2f 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -23,12 +23,12 @@ use num::FpCategory as Fp; use option::Option; #[unstable = "pending integer conventions"] -pub const RADIX: uint = 2u; +pub const RADIX: uint = 2; #[unstable = "pending integer conventions"] -pub const MANTISSA_DIGITS: uint = 24u; +pub const MANTISSA_DIGITS: uint = 24; #[unstable = "pending integer conventions"] -pub const DIGITS: uint = 6u; +pub const DIGITS: uint = 6; #[stable] pub const EPSILON: f32 = 1.19209290e-07_f32; diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 840428179cd..a7f0266b095 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -27,11 +27,11 @@ use option::Option; // members of `Bounded` and `Float`. #[unstable = "pending integer conventions"] -pub const RADIX: uint = 2u; +pub const RADIX: uint = 2; -pub const MANTISSA_DIGITS: uint = 53u; +pub const MANTISSA_DIGITS: uint = 53; #[unstable = "pending integer conventions"] -pub const DIGITS: uint = 15u; +pub const DIGITS: uint = 15; #[stable] pub const EPSILON: f64 = 2.2204460492503131e-16_f64; diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 599a5515e3b..910c9e6c670 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -356,7 +356,7 @@ pub trait Int /// ```rust /// use std::num::Int; /// - /// assert_eq!(2i.pow(4), 16); + /// assert_eq!(2.pow(4), 16); /// ``` #[unstable = "pending integer conventions"] #[inline] @@ -1185,7 +1185,7 @@ impl_from_primitive! { f64, to_f64 } /// ``` /// use std::num; /// -/// let twenty: f32 = num::cast(0x14i).unwrap(); +/// let twenty: f32 = num::cast(0x14).unwrap(); /// assert_eq!(twenty, 20f32); /// ``` /// @@ -1571,8 +1571,8 @@ macro_rules! from_str_radix_float_impl { let exp = match exp_info { Some((c, offset)) => { let base = match c { - 'E' | 'e' if radix == 10 => 10u as $T, - 'P' | 'p' if radix == 16 => 2u as $T, + 'E' | 'e' if radix == 10 => 10.0, + 'P' | 'p' if radix == 16 => 2.0, _ => return None, }; diff --git a/src/libcore/option.rs b/src/libcore/option.rs index af7fc875389..3ee2aa678e9 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -254,12 +254,12 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let mut x = Some(2u); + /// let mut x = Some(2); /// match x.as_mut() { /// Some(v) => *v = 42, /// None => {}, /// } - /// assert_eq!(x, Some(42u)); + /// assert_eq!(x, Some(42)); /// ``` #[inline] #[stable] @@ -384,9 +384,9 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let k = 10u; - /// assert_eq!(Some(4u).unwrap_or_else(|| 2 * k), 4u); - /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20u); + /// let k = 10i32; + /// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20); /// ``` #[inline] #[stable] @@ -427,10 +427,10 @@ impl<T> Option<T> { /// /// ``` /// let x = Some("foo"); - /// assert_eq!(x.map_or(42u, |v| v.len()), 3u); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); /// /// let x: Option<&str> = None; - /// assert_eq!(x.map_or(42u, |v| v.len()), 42u); + /// assert_eq!(x.map_or(42, |v| v.len()), 42); /// ``` #[inline] #[stable] @@ -446,13 +446,13 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let k = 21u; + /// let k = 21; /// /// let x = Some("foo"); - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3u); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); /// /// let x: Option<&str> = None; - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42u); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); /// ``` #[inline] #[stable] @@ -470,10 +470,10 @@ impl<T> Option<T> { /// /// ``` /// let x = Some("foo"); - /// assert_eq!(x.ok_or(0i), Ok("foo")); + /// assert_eq!(x.ok_or(0), Ok("foo")); /// /// let x: Option<&str> = None; - /// assert_eq!(x.ok_or(0i), Err(0i)); + /// assert_eq!(x.ok_or(0), Err(0)); /// ``` #[inline] #[unstable] @@ -491,10 +491,10 @@ impl<T> Option<T> { /// /// ``` /// let x = Some("foo"); - /// assert_eq!(x.ok_or_else(|| 0i), Ok("foo")); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); /// /// let x: Option<&str> = None; - /// assert_eq!(x.ok_or_else(|| 0i), Err(0i)); + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); /// ``` #[inline] #[unstable] @@ -514,7 +514,7 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let x = Some(4u); + /// let x = Some(4); /// assert_eq!(x.iter().next(), Some(&4)); /// /// let x: Option<uint> = None; @@ -531,9 +531,9 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let mut x = Some(4u); + /// let mut x = Some(4); /// match x.iter_mut().next() { - /// Some(&mut ref mut v) => *v = 42u, + /// Some(&mut ref mut v) => *v = 42, /// None => {}, /// } /// assert_eq!(x, Some(42)); @@ -575,7 +575,7 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let x = Some(2u); + /// let x = Some(2); /// let y: Option<&str> = None; /// assert_eq!(x.and(y), None); /// @@ -583,7 +583,7 @@ impl<T> Option<T> { /// let y = Some("foo"); /// assert_eq!(x.and(y), None); /// - /// let x = Some(2u); + /// let x = Some(2); /// let y = Some("foo"); /// assert_eq!(x.and(y), Some("foo")); /// @@ -628,17 +628,17 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let x = Some(2u); + /// let x = Some(2); /// let y = None; - /// assert_eq!(x.or(y), Some(2u)); + /// assert_eq!(x.or(y), Some(2)); /// /// let x = None; - /// let y = Some(100u); - /// assert_eq!(x.or(y), Some(100u)); + /// let y = Some(100); + /// assert_eq!(x.or(y), Some(100)); /// - /// let x = Some(2u); - /// let y = Some(100u); - /// assert_eq!(x.or(y), Some(2u)); + /// let x = Some(2); + /// let y = Some(100); + /// assert_eq!(x.or(y), Some(2)); /// /// let x: Option<uint> = None; /// let y = None; @@ -684,7 +684,7 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// let mut x = Some(2u); + /// let mut x = Some(2); /// x.take(); /// assert_eq!(x, None); /// @@ -728,8 +728,8 @@ impl<T: Default> Option<T> { /// let good_year = good_year_from_input.parse().unwrap_or_default(); /// let bad_year = bad_year_from_input.parse().unwrap_or_default(); /// - /// assert_eq!(1909i, good_year); - /// assert_eq!(0i, bad_year); + /// assert_eq!(1909, good_year); + /// assert_eq!(0, bad_year); /// ``` #[inline] #[stable] @@ -894,12 +894,12 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> { /// ```rust /// use std::uint; /// - /// let v = vec!(1u, 2u); + /// let v = vec!(1, 2); /// let res: Option<Vec<uint>> = v.iter().map(|&x: &uint| /// if x == uint::MAX { None } /// else { Some(x + 1) } /// ).collect(); - /// assert!(res == Some(vec!(2u, 3u))); + /// assert!(res == Some(vec!(2, 3))); /// ``` #[inline] #[stable] diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c3d49e24978..4ea3bf8e5bd 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -482,8 +482,8 @@ impl<T, E> Result<T, E> { /// ``` /// fn stringify(x: uint) -> String { format!("error code: {}", x) } /// - /// let x: Result<uint, uint> = Ok(2u); - /// assert_eq!(x.map_err(stringify), Ok(2u)); + /// let x: Result<uint, uint> = Ok(2); + /// assert_eq!(x.map_err(stringify), Ok(2)); /// /// let x: Result<uint, uint> = Err(13); /// assert_eq!(x.map_err(stringify), Err("error code: 13".to_string())); @@ -546,7 +546,7 @@ impl<T, E> Result<T, E> { /// ``` /// let x: Result<uint, &str> = Ok(5); /// let v: Vec<uint> = x.into_iter().collect(); - /// assert_eq!(v, vec![5u]); + /// assert_eq!(v, vec![5]); /// /// let x: Result<uint, &str> = Err("nothing!"); /// let v: Vec<uint> = x.into_iter().collect(); @@ -676,9 +676,9 @@ impl<T, E> Result<T, E> { /// # Example /// /// ``` - /// let optb = 2u; - /// let x: Result<uint, &str> = Ok(9u); - /// assert_eq!(x.unwrap_or(optb), 9u); + /// let optb = 2; + /// let x: Result<uint, &str> = Ok(9); + /// assert_eq!(x.unwrap_or(optb), 9); /// /// let x: Result<uint, &str> = Err("error"); /// assert_eq!(x.unwrap_or(optb), optb); @@ -700,8 +700,8 @@ impl<T, E> Result<T, E> { /// ``` /// fn count(x: &str) -> uint { x.len() } /// - /// assert_eq!(Ok(2u).unwrap_or_else(count), 2u); - /// assert_eq!(Err("foo").unwrap_or_else(count), 3u); + /// assert_eq!(Ok(2).unwrap_or_else(count), 2); + /// assert_eq!(Err("foo").unwrap_or_else(count), 3); /// ``` #[inline] #[stable] @@ -725,8 +725,8 @@ impl<T, E: Display> Result<T, E> { /// # Example /// /// ``` - /// let x: Result<uint, &str> = Ok(2u); - /// assert_eq!(x.unwrap(), 2u); + /// let x: Result<uint, &str> = Ok(2); + /// assert_eq!(x.unwrap(), 2); /// ``` /// /// ```{.should_fail} @@ -756,7 +756,7 @@ impl<T: Display, E> Result<T, E> { /// # Example /// /// ```{.should_fail} - /// let x: Result<uint, &str> = Ok(2u); + /// let x: Result<uint, &str> = Ok(2); /// x.unwrap_err(); // panics with `2` /// ``` /// @@ -897,12 +897,12 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> { /// ```rust /// use std::uint; /// - /// let v = vec!(1u, 2u); + /// let v = vec!(1, 2); /// let res: Result<Vec<uint>, &'static str> = v.iter().map(|&x: &uint| /// if x == uint::MAX { Err("Overflow!") } /// else { Ok(x + 1) } /// ).collect(); - /// assert!(res == Ok(vec!(2u, 3u))); + /// assert!(res == Ok(vec!(2, 3))); /// ``` #[inline] fn from_iter<I: Iterator<Item=Result<A, E>>>(iter: I) -> Result<V, E> { diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index ec43a35248e..f08978db8fe 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -653,7 +653,7 @@ macro_rules! iterator { self.ptr = transmute(self.ptr as uint + 1); // Use a non-null pointer value - Some(transmute(1u)) + Some(&mut *(1 as *mut _)) } else { let old = self.ptr; self.ptr = self.ptr.offset(1); @@ -687,7 +687,7 @@ macro_rules! iterator { self.end = transmute(self.end as uint - 1); // Use a non-null pointer value - Some(transmute(1u)) + Some(&mut *(1 as *mut _)) } else { self.end = self.end.offset(-1); @@ -795,7 +795,7 @@ impl<'a, T> RandomAccessIterator for Iter<'a, T> { if index < self.indexable() { if mem::size_of::<T>() == 0 { // Use a non-null pointer value - Some(transmute(1u)) + Some(&mut *(1 as *mut _)) } else { Some(transmute(self.ptr.offset(index as int))) } @@ -1175,7 +1175,7 @@ impl<'a, T> Iterator for Windows<'a, T> { (0, Some(0)) } else { let x = self.v.len() - self.size; - (x.saturating_add(1), x.checked_add(1u)) + (x.saturating_add(1), x.checked_add(1)) } } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 1e01da4e41d..b0227c749cc 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -198,9 +198,9 @@ pub unsafe fn from_utf8_unchecked<'a>(v: &'a [u8]) -> &'a str { #[deprecated = "use std::ffi::c_str_to_bytes + str::from_utf8"] pub unsafe fn from_c_str(s: *const i8) -> &'static str { let s = s as *const u8; - let mut len = 0u; + let mut len = 0; while *s.offset(len as int) != 0 { - len += 1u; + len += 1; } let v: &'static [u8] = ::mem::transmute(Slice { data: s, len: len }); from_utf8(v).ok().expect("from_c_str passed invalid utf-8 data") @@ -1510,7 +1510,7 @@ impl StrExt for str { None => "", Some(last) => { let next = self.char_range_at(last).next; - unsafe { self.slice_unchecked(0u, next) } + unsafe { self.slice_unchecked(0, next) } } } } @@ -1543,7 +1543,7 @@ impl StrExt for str { fn multibyte_char_range_at_reverse(s: &str, mut i: uint) -> CharRange { // while there is a previous byte == 10...... while i > 0 && s.as_bytes()[i] & !CONT_MASK == TAG_CONT_U8 { - i -= 1u; + i -= 1; } let mut val = s.as_bytes()[i] as u32; @@ -1613,7 +1613,7 @@ impl StrExt for str { if self.is_empty() { None } else { - let CharRange {ch, next} = self.char_range_at(0u); + let CharRange {ch, next} = self.char_range_at(0); let next_s = unsafe { self.slice_unchecked(next, self.len()) }; Some((ch, next_s)) } From 7e83e46556fea9f448e47f7879f5970c400fb24c Mon Sep 17 00:00:00 2001 From: Flavio Percoco <flaper87@gmail.com> Date: Sun, 25 Jan 2015 10:52:55 +0100 Subject: [PATCH 33/33] assert path ends with executable. On Windows the process executable contains the full path --- src/test/run-pass/issue-15149.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs index 5d3571e4d74..bf395b14eb4 100644 --- a/src/test/run-pass/issue-15149.rs +++ b/src/test/run-pass/issue-15149.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::slice::SliceExt; use std::io::{Command, fs, USER_RWX}; use std::os; use std::path::BytesContainer; @@ -17,8 +18,11 @@ fn main() { // If we're the child, make sure we were invoked correctly let args = os::args(); if args.len() > 1 && args[1].as_slice() == "child" { - return assert_eq!(args[0], - format!("mytest{}", os::consts::EXE_SUFFIX)); + // FIXME: This should check the whole `args[0]` instead of just + // checking that it ends_with the executable name. This + // is needed because of Windows, which has a different behavior. + // See #15149 for more info. + return assert!(args[0].ends_with(&format!("mytest{}", os::consts::EXE_SUFFIX)[])); } test();