Commit Graph

6510 Commits

Author SHA1 Message Date
Jorge Aparicio
c1d48a8508 cleanup: &foo[0..a] -> &foo[..a] 2015-01-12 17:59:37 -05:00
Niko Matsakis
47424cda1e Normalize bounds that we extract from where clauses. Fixes #20765. 2015-01-12 09:23:50 -05:00
bors
b21a6da340 auto merge of #19870 : mdinger/rust/align_error, r=nick29581
#### Updated 1/12/2014

I updated the multi-line testcase to current but didn't modify the others. The spew code was broke by the `matches!` macro no longer working and I'm not interested in fixing the testcase.

I additionally added one testcase below.

Errors will in general look similar to below if the error is either `mismatched types` or a few other types. The rest are ignored.

---

#### Extra testcase:
```rust
pub trait Foo {
    type A;
    fn boo(&self) -> <Self as Foo>::A;
}

struct Bar;

impl Foo for i32 {
    type A = u32;
    fn boo(&self) -> u32 {
        42
    }
}

fn foo1<I: Foo<A=Bar>>(x: I) {
    let _: Bar = x.boo();
}

fn foo2<I: Foo>(x: I) {
    let _: Bar = x.boo();
}


pub fn baz(x: &Foo<A=Bar>) {
    let _: Bar = x.boo();
}


pub fn main() {
    let a = 42i32;
    foo1(a);
    baz(&a);
}
```

#### Multi-line output:
```cmd
$ ./rustc test3.rs
test3.rs:20:18: 20:25 error: mismatched types:
 expected `Bar`,
    found `<I as Foo>::A`
(expected struct `Bar`,
    found associated type)
test3.rs:20     let _: Bar = x.boo();
                             ^~~~~~~
test3.rs:31:5: 31:9 error: type mismatch resolving `<i32 as Foo>::A == Bar`:
 expected u32,
    found struct `Bar`
test3.rs:31     foo1(a);
                ^~~~
test3.rs:31:5: 31:9 note: required by `foo1`
test3.rs:31     foo1(a);
                ^~~~
test3.rs:32:9: 32:11 error: type mismatch resolving `<i32 as Foo>::A == Bar`:
 expected u32,
    found struct `Bar`
test3.rs:32     baz(&a);
                    ^~
test3.rs:32:9: 32:11 note: required for the cast to the object type `Foo`
test3.rs:32     baz(&a);
                    ^~
error: aborting due to 3 previous errors
```

---

This is a continuation of #19203 which I apparently broke by force pushing after it was closed. I'm attempting to add multi-line errors where they are largely beneficial - to help differentiate different types in compiler messages. As before, this is still a simple fix.

#### Testcase:
```rust
struct S;

fn test() -> Option<i32> {
    let s: S;

    s
}

fn test2() -> Option<i32> {
    Ok(7) // Should be Some(7)
}

impl Iterator for S {
    type Item = i32;
    fn next(&mut self) -> Result<i32, i32> { Ok(7) }
}

fn main(){ 
    test();
    test2();

}
```

---

#### Single-line playpen errors:
```cmd
<anon>:6:5: 6:6 error: mismatched types: expected `core::option::Option<int>`, found `S` (expected enum core::option::Option, found struct S)
<anon>:6     s
             ^
<anon>:10:5: 10:10 error: mismatched types: expected `core::option::Option<int>`, found `core::result::Result<_, _>` (expected enum core::option::Option, found enum core::result::Result)
<anon>:10     Ok(7) // Should be Some(7)
              ^~~~~
<anon>:14:5: 14:55 error: method `next` has an incompatible type for trait: expected enum core::option::Option, found enum core::result::Result [E0053]
<anon>:14     fn next(&mut self) -> Result<uint, uint> { Ok(7) }
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 3 previous errors
playpen: application terminated with error code 101
```

---

#### Multi-line errors:
```cmd
$ ./rustc test.rs
test.rs:6:5: 6:6 error: mismatched types:
 expected `core::option::Option<i32>`,
    found `S`
(expected enum `core::option::Option`,
    found struct `S`)
test.rs:6     s
              ^
test.rs:10:5: 10:10 error: mismatched types:
 expected `core::option::Option<i32>`,
    found `core::result::Result<_, _>`
(expected enum `core::option::Option`,
    found enum `core::result::Result`)
test.rs:10     Ok(7) // Should be Some(7)
               ^~~~~
test.rs:15:5: 15:53 error: method `next` has an incompatible type for trait: expected enum `core::option::Option`, found enum `core::result::Result` [E0053]
test.rs:15     fn next(&mut self) -> Result<i32, i32> { Ok(7) }
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 3 previous errors
```

---

#### Positive notes
* Vim worked fine with it: https://github.com/rust-lang/rust/pull/19203#issuecomment-66861668
* `make check` didn't find any errors
* Fixed *backtick* placement suggested by @p1start at https://github.com/rust-lang/rust/pull/19203#issuecomment-64062052

#### Negative notes
* Didn't check Emacs support but also wasn't provided a testcase...
* Needs to be tested with macro errors but I don't have a good testcase yet
* I would like to move the `E[0053]` earlier (see https://github.com/rust-lang/rust/issues/19464#issuecomment-65334301) but I don't know how
* It might be better to indent the types slightly like so (but I don't know how):
```cmd
test.rs:6:5: 6:6 error: mismatched types:
          expected `core::option::Option<int>`,
             found `S`
         (expected enum `core::option::Option`,
             found struct `S`)
test.rs:6     s
```
* Deep whitespace indentation may be a bad idea because early wrapping will cause misalignment between lines

#### Other
* I thought that compiler flags or something else (environment variables maybe) might be required because of comments against it but now that seems too much of a burden for users and for too little gain.
* There was concern that it will make large quantities of errors difficult to distinguish but I don't find that an issue. They both look awful and multi-line errors makes the types easier to understand.

---

#### Single lined spew:
```cmd
$ rustc test2.rs 
test2.rs:161:9: 170:10 error: method `next` has an incompatible type for trait: expected enum core::option::Option, found enum core::result::Result [E0053]
test2.rs:161         fn next(&mut self) -> Result<&'a str, int> {
test2.rs:162             self.curr = self.next;
test2.rs:163             
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
test2.rs:165                 self.next = if self.all.char_at(self.next) == '(' { close }
test2.rs:166                 else { open }
             ...
test2.rs:164:21: 164:31 error: mismatched types: expected `core::result::Result<uint, int>`, found `core::option::Option<_>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
                                 ^~~~~~~~~~
test2.rs:164:33: 164:44 error: mismatched types: expected `core::result::Result<uint, int>`, found `core::option::Option<_>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
                                             ^~~~~~~~~~~
test2.rs:169:40: 169:76 error: mismatched types: expected `core::result::Result<&'a str, int>`, found `core::option::Option<&str>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:169             if self.curr != self.len { Some(self.all[self.curr..self.next]) } else { None }
                                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.rs:169:86: 169:90 error: mismatched types: expected `core::result::Result<&'a str, int>`, found `core::option::Option<_>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:169             if self.curr != self.len { Some(self.all[self.curr..self.next]) } else { None }
                                                                                                  ^~~~
test2.rs:205:14: 205:18 error: mismatched types: expected `core::result::Result<uint, int>`, found `core::option::Option<uint>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:205             (open, close)
                          ^~~~
test2.rs:205:20: 205:25 error: mismatched types: expected `core::result::Result<uint, int>`, found `core::option::Option<uint>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:205             (open, close)
                                ^~~~~
test2.rs:210:21: 210:31 error: mismatched types: expected `core::result::Result<uint, int>`, found `core::option::Option<_>` (expected enum core::result::Result, found enum core::option::Option)
test2.rs:210             if let (Some(open), _) = Parens::find_parens(self.all, 0) {
                                 ^~~~~~~~~~
test2.rs:210:13: 212:28 error: mismatched types: expected `core::option::Option<&'a int>`, found `core::option::Option<&str>` (expected int, found str)
test2.rs:210             if let (Some(open), _) = Parens::find_parens(self.all, 0) {
test2.rs:211                 Some(self.all[0..open])
test2.rs:212             } else { None }
test2.rs:299:48: 299:58 error: mismatched types: expected `Box<translate::Entity>`, found `collections::vec::Vec<_>` (expected box, found struct collections::vec::Vec)
test2.rs:299         pub fn new() -> Entity { Entity::Group(Vec::new()) }
                                                            ^~~~~~~~~~
test2.rs:359:51: 359:58 error: type `&mut Box<translate::Entity>` does not implement any method in scope named `push`
test2.rs:359                 Entity::Group(ref mut vec) => vec.push(e),
                                                               ^~~~~~~
test2.rs:366:51: 366:85 error: type `&mut Box<translate::Entity>` does not implement any method in scope named `push`
test2.rs:366                 Entity::Group(ref mut vec) => vec.push(Entity::Inner(s.to_string())),
                                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 12 previous errors
```

---

#### Multi-line spew:

```cmd
$ ./rustc test2.rs 
test2.rs:161:9: 170:10 error: method `next` has an incompatible type for trait:
 expected enum `core::option::Option`,
    found enum `core::result::Result` [E0053]
test2.rs:161         fn next(&mut self) -> Result<&'a str, int> {
test2.rs:162             self.curr = self.next;
test2.rs:163             
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
test2.rs:165                 self.next = if self.all.char_at(self.next) == '(' { close }
test2.rs:166                 else { open }
             ...
test2.rs:164:21: 164:31 error: mismatched types:
 expected `core::result::Result<uint, int>`,
    found `core::option::Option<_>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
                                 ^~~~~~~~~~
test2.rs:164:33: 164:44 error: mismatched types:
 expected `core::result::Result<uint, int>`,
    found `core::option::Option<_>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:164             if let (Some(open), Some(close)) = Parens::find_parens(self.all, self.next) {
                                             ^~~~~~~~~~~
test2.rs:169:40: 169:76 error: mismatched types:
 expected `core::result::Result<&'a str, int>`,
    found `core::option::Option<&str>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:169             if self.curr != self.len { Some(self.all[self.curr..self.next]) } else { None }
                                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.rs:169:86: 169:90 error: mismatched types:
 expected `core::result::Result<&'a str, int>`,
    found `core::option::Option<_>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:169             if self.curr != self.len { Some(self.all[self.curr..self.next]) } else { None }
                                                                                                  ^~~~
test2.rs:205:14: 205:18 error: mismatched types:
 expected `core::result::Result<uint, int>`,
    found `core::option::Option<uint>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:205             (open, close)
                          ^~~~
test2.rs:205:20: 205:25 error: mismatched types:
 expected `core::result::Result<uint, int>`,
    found `core::option::Option<uint>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:205             (open, close)
                                ^~~~~
test2.rs:210:21: 210:31 error: mismatched types:
 expected `core::result::Result<uint, int>`,
    found `core::option::Option<_>`
(expected enum `core::result::Result`,
    found enum `core::option::Option`)
test2.rs:210             if let (Some(open), _) = Parens::find_parens(self.all, 0) {
                                 ^~~~~~~~~~
test2.rs:210:13: 212:28 error: mismatched types:
 expected `core::option::Option<&'a int>`,
    found `core::option::Option<&str>`
(expected int,
    found str)
test2.rs:210             if let (Some(open), _) = Parens::find_parens(self.all, 0) {
test2.rs:211                 Some(self.all[0..open])
test2.rs:212             } else { None }
test2.rs:229:57: 229:96 error: the trait `core::ops::Fn<(char,), bool>` is not implemented for the type `|char| -> bool`
test2.rs:229                                              .map(|s| s.trim_chars(|c: char| c.is_whitespace()))
                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.rs:238:46: 239:75 error: type `core::str::CharSplits<'_, |char| -> bool>` does not implement any method in scope named `filter_map`
test2.rs:238                                             .filter_map(|s| if !s.is_empty() { Some(s.trim_chars('\'')) }
test2.rs:239                                                             else { None })
test2.rs:237:46: 237:91 error: the trait `core::ops::Fn<(char,), bool>` is not implemented for the type `|char| -> bool`
test2.rs:237                 let vec: Vec<&str> = value[].split(|c: char| matches!(c, '(' | ')' | ','))
                                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.rs:238:65: 238:77 error: the type of this value must be known in this context
test2.rs:238                                             .filter_map(|s| if !s.is_empty() { Some(s.trim_chars('\'')) }
                                                                             ^~~~~~~~~~~~
test2.rs:299:48: 299:58 error: mismatched types:
 expected `Box<translate::Entity>`,
    found `collections::vec::Vec<_>`
(expected box,
    found struct `collections::vec::Vec`)
test2.rs:299         pub fn new() -> Entity { Entity::Group(Vec::new()) }
                                                            ^~~~~~~~~~
test2.rs:321:36: 322:65 error: type `core::str::CharSplits<'_, |char| -> bool>` does not implement any method in scope named `filter_map`
test2.rs:321                                   .filter_map(|s| if !s.is_empty() { Some(s.trim_chars('\'')) }
test2.rs:322                                                   else { None })
test2.rs:320:36: 320:81 error: the trait `core::ops::Fn<(char,), bool>` is not implemented for the type `|char| -> bool`
test2.rs:320             let vec: Vec<&str> = s.split(|c: char| matches!(c, '(' | ')' | ','))
                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.rs:321:55: 321:67 error: the type of this value must be known in this context
test2.rs:321                                   .filter_map(|s| if !s.is_empty() { Some(s.trim_chars('\'')) }
                                                                   ^~~~~~~~~~~~
test2.rs:359:51: 359:58 error: type `&mut Box<translate::Entity>` does not implement any method in scope named `push`
test2.rs:359                 Entity::Group(ref mut vec) => vec.push(e),
                                                               ^~~~~~~
test2.rs:366:51: 366:85 error: type `&mut Box<translate::Entity>` does not implement any method in scope named `push`
test2.rs:366                 Entity::Group(ref mut vec) => vec.push(Entity::Inner(s.to_string())),
                                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 24 previous errors
```

Closes #18946 #19464
cc @P1start @jakub- @tomjakubowski @kballard @chris-morgan
2015-01-12 08:55:22 +00:00
mdinger
5616b92e4d Implement multi-line errors 2015-01-12 01:34:12 -05:00
mdinger
24ace1665a Backtick nits 2015-01-12 01:34:12 -05:00
bors
0aec4db1c0 auto merge of #20889 : Manishearth/rust/trait-error, r=nikomatsakis
fixes #20783

r? @nikomatsakis
2015-01-12 04:45:18 +00:00
bors
4bed1e8c0a Merge pull request #20968 from estsauver/20762
Fix sentence fragment in librustc README

Reviewed-by: alexcrichton
2015-01-12 00:21:32 +00:00
bors
53ea263e37 Merge pull request #20934 from tomjakubowski/patch-1
Escape a leading # in a doc comment

Reviewed-by: eddyb
2015-01-12 00:21:30 +00:00
Earl St Sauver
a9b01f6cbc Fix sentence fragment in librustc README 2015-01-11 13:53:53 -08:00
Eduard Burtescu
e73fbc69cd rustc_typeck: unify expected return types with formal return types to propagate coercions through calls of generic functions. 2015-01-11 22:09:46 +02:00
Manish Goregaokar
ad7e33efee Feature gate #[rustc_on_unimplemented] 2015-01-12 00:00:53 +05:30
Manish Goregaokar
dd074ab4ee Rename #[on_unimplemented] -> #[rustc_on_unimplemented] 2015-01-11 20:52:43 +05:30
Manish Goregaokar
add20bbb6d Move error to typeck::check 2015-01-11 18:17:51 +05:30
Manish Goregaokar
e183277948 Make errors allow for cross-crate issues 2015-01-11 16:41:02 +05:30
Tom Jakubowski
6f45cb493e Escape a leading # in a doc comment
rustdoc was rendering this as a header
2015-01-11 02:34:55 -08:00
Manish Goregaokar
dc0de42035 Add lint and test for malformed but unused #[on_unimplemented] attributes 2015-01-11 09:51:09 +05:30
Manish Goregaokar
4d17fbaf37 Add ability to attach custom #[on_unimplemented] error messages for unimplemented traits (fixes #20783) 2015-01-11 09:49:02 +05:30
John Kåre Alsaker
9dea210730 Make the metadata loader use the appropriate Target structure
Fixes #19907
2015-01-09 04:51:47 +01:00
bors
00b112c45a auto merge of #20760 : alexcrichton/rust/rollup, r=alexcrichton 2015-01-08 18:40:04 +00:00
Alex Crichton
483fca9fa5 rollup merge of #20757: nikomatsakis/issue-20624-assoc-types-coherence 2015-01-08 09:32:06 -08:00
Alex Crichton
4281bd1932 rollup merge of #20754: nikomatsakis/int-feature
Conflicts:
	src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs
	src/test/compile-fail/issue-2590.rs
	src/test/compile-fail/lint-stability.rs
	src/test/compile-fail/slice-mut-2.rs
	src/test/compile-fail/std-uncopyable-atomics.rs
2015-01-08 09:24:08 -08:00
Alex Crichton
8ed88c11af rollup merge of #20751: nikomatsakis/issue-20232
Issue #20232. Fun.

r? @eddyb you prob know this system best
2015-01-08 09:22:10 -08:00
Alex Crichton
daee409b60 rollup merge of #20740: FlaPer87/remove-opt-out-copy
[breaking-change] code using this feature will break.
2015-01-08 09:22:06 -08:00
Niko Matsakis
0d9a11d6ad Normalize types bottom up. Fixes #20666. 2015-01-08 12:02:23 -05:00
Niko Matsakis
115a443cee Normalize types in supertraits when checking that impls are valid during wf.
Fixes #20559.
2015-01-08 11:16:07 -05:00
Niko Matsakis
bb0c8ef373 Normalize types in impls, add test for coherence failure.
Fixes #20624.
2015-01-08 11:16:06 -05:00
Huon Wilson
4f5a57e80e Remove warning from the libraries.
This adds the int_uint feature to *every* library, whether or not it
needs it.
2015-01-08 11:02:23 -05:00
Huon Wilson
d12514bc58 Add a warning feature gate for int/uint in types and i/u suffixes. 2015-01-08 11:02:23 -05:00
Huon Wilson
e95779554e Store deprecated status of i/u-suffixed literals. 2015-01-08 11:02:23 -05:00
Niko Matsakis
92425496e5 Fix the actual bug for #20232: when creating the cmt for the implicit
deref that is associated with an overloaded index, we should not
consult the method lookup table. This deref is *always* a deref of an
`&T` and hence is never overloaded (and is also not present in the
tables; it has no "id" or other associated key).
2015-01-08 09:19:27 -05:00
Niko Matsakis
2387651f7d Update the "English-language" to-string function of a cmt to use
more modern terminology and update tests accordingly.
2015-01-08 09:19:27 -05:00
Flavio Percoco
0d0869ad73 Remove the deprecated opt_out_copy feature 2015-01-08 13:39:14 +01:00
Brian Anderson
1f70acbf4c Improvements to feature staging
This gets rid of the 'experimental' level, removes the non-staged_api
case (i.e. stability levels for out-of-tree crates), and lets the
staged_api attributes use 'unstable' and 'deprecated' lints.

This makes the transition period to the full feature staging design
a bit nicer.
2015-01-08 03:07:23 -08:00
Manish Goregaokar
f4a2672600 Add test for -Z extra-plugins 2015-01-08 14:47:27 +05:30
Manish Goregaokar
0bd022c893 libsyntax: add COMMAND_LINE_SP and use it for spans generated from the command line 2015-01-08 13:40:50 +05:30
Manish Goregaokar
c41cafb10c librustc_driver: Add support for loading plugins via command line (fixes #15446) 2015-01-08 13:40:50 +05:30
Manish Goregaokar
9f5f706f96 librustc::plugin : make PluginLoader usable for loading argument-specified plugins 2015-01-08 13:40:48 +05:30
Manish Goregaokar
efaf613497 librustc::metadata : Allow passing a string to read_plugin_metadata 2015-01-08 13:39:27 +05:30
Manish Goregaokar
7e87ea9fc5 librustc::session : Make DebuggingOpts use the options! macro 2015-01-08 13:38:43 +05:30
Manish Goregaokar
3248bc5bd0 librustc::session : Make cgoptions macro more generic 2015-01-08 13:37:50 +05:30
Alex Crichton
0dc48b47a8 Test fixes and rebase conflicts 2015-01-07 19:27:27 -08:00
Alex Crichton
11e265c2e0 rollup merge of #20707: nikomatsakis/issue-20582 2015-01-07 17:44:24 -08:00
Alex Crichton
373cbab5b0 rollup merge of #20723: pnkfelix/feature-gate-box-syntax
Conflicts:
	src/compiletest/compiletest.rs
	src/libcollections/lib.rs
	src/libserialize/lib.rs
	src/libsyntax/feature_gate.rs
2015-01-07 17:42:47 -08:00
Alex Crichton
bcebec5084 rollup merge of #20706: nikomatsakis/assoc-types-projections-in-structs-issue-20470
Conflicts:
	src/librustc_trans/trans/expr.rs
2015-01-07 17:35:00 -08:00
Alex Crichton
51357e04be rollup merge of #20665: nikomatsakis/assoc-types-method-dispatch-projection
Conflicts:
	src/librustc/middle/ty.rs
2015-01-07 17:33:41 -08:00
Alex Crichton
773fdb3dbe rollup merge of #20631: huon/no-drop-and-copy 2015-01-07 17:28:38 -08:00
Alex Crichton
6e806bdefd rollup merge of #20721: japaric/snap
Conflicts:
	src/libcollections/vec.rs
	src/libcore/fmt/mod.rs
	src/librustc/lint/builtin.rs
	src/librustc/session/config.rs
	src/librustc_trans/trans/base.rs
	src/librustc_trans/trans/context.rs
	src/librustc_trans/trans/type_.rs
	src/librustc_typeck/check/_match.rs
	src/librustdoc/html/format.rs
	src/libsyntax/std_inject.rs
	src/libsyntax/util/interner.rs
	src/test/compile-fail/mut-pattern-mismatched.rs
2015-01-07 17:26:58 -08:00
Niko Matsakis
4dd368b90a Normalize associated types in with_field_tys 2015-01-07 20:26:20 -05:00
Niko Matsakis
9e4e8823c7 Use ty::type_is_sized() so that we handle projection types properly. 2015-01-07 20:26:19 -05:00
Alex Crichton
6301c7878e rollup merge of #20680: nick29581/target-word
Closes #20421

[breaking-change]

r? @brson
2015-01-07 17:17:23 -08:00