From 3295afa6e2dd9d3b50bce0342199a7d448d480aa Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 21 Nov 2016 18:11:36 +1300 Subject: [PATCH 1/4] save-analysis: fix ICE on partially resolved path Occurs when we produce save-analysis before type checking is complete (due to errors). --- src/librustc/hir/def.rs | 11 ++++++++--- src/librustc_save_analysis/lib.rs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index feefc43f4013e..ce04a7c897a18 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -85,10 +85,15 @@ impl PathResolution { /// Get the definition, if fully resolved, otherwise panic. pub fn full_def(&self) -> Def { - if self.depth != 0 { - bug!("path not fully resolved: {:?}", self); + self.maybe_full_def().unwrap_or_else(|| bug!("path not fully resolved: {:?}", self)) + } + + pub fn maybe_full_def(&self) -> Option { + if self.depth == 0 { + Some(self.base_def) + } else { + None } - self.base_def } pub fn kind_name(&self) -> &'static str { diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 778f018414165..4c59f5e8a83cb 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -497,7 +497,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option { - let def = self.tcx.expect_def(id); + let def = option_try!(self.tcx.expect_resolution(id).maybe_full_def()); let sub_span = self.span_utils.span_for_last_ident(path.span); filter!(self.span_utils, sub_span, path.span, None); match def { From 9ea1544b804818c8df259de60c0e26a85ac52850 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 23 Nov 2016 13:35:07 +1300 Subject: [PATCH 2/4] Add a test --- src/test/run-make/save-analysis-fail/Makefile | 8 + .../run-make/save-analysis-fail/SameDir.rs | 15 + .../run-make/save-analysis-fail/SameDir3.rs | 13 + .../run-make/save-analysis-fail/SubDir/mod.rs | 37 ++ src/test/run-make/save-analysis-fail/foo.rs | 450 ++++++++++++++++++ .../run-make/save-analysis-fail/krate2.rs | 18 + 6 files changed, 541 insertions(+) create mode 100644 src/test/run-make/save-analysis-fail/Makefile create mode 100644 src/test/run-make/save-analysis-fail/SameDir.rs create mode 100644 src/test/run-make/save-analysis-fail/SameDir3.rs create mode 100644 src/test/run-make/save-analysis-fail/SubDir/mod.rs create mode 100644 src/test/run-make/save-analysis-fail/foo.rs create mode 100644 src/test/run-make/save-analysis-fail/krate2.rs diff --git a/src/test/run-make/save-analysis-fail/Makefile b/src/test/run-make/save-analysis-fail/Makefile new file mode 100644 index 0000000000000..3711b6ea8959b --- /dev/null +++ b/src/test/run-make/save-analysis-fail/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk +all: code +krate2: krate2.rs + $(RUSTC) $< +code: foo.rs krate2 + $(RUSTC) foo.rs -Zsave-analysis-csv + $(RUSTC) foo.rs -Zsave-analysis + $(RUSTC) foo.rs -Zsave-analysis-api diff --git a/src/test/run-make/save-analysis-fail/SameDir.rs b/src/test/run-make/save-analysis-fail/SameDir.rs new file mode 100644 index 0000000000000..fe70ac1edef6e --- /dev/null +++ b/src/test/run-make/save-analysis-fail/SameDir.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// sub-module in the same directory as the main crate file + +pub struct SameStruct { + pub name: String +} diff --git a/src/test/run-make/save-analysis-fail/SameDir3.rs b/src/test/run-make/save-analysis-fail/SameDir3.rs new file mode 100644 index 0000000000000..315f900868b45 --- /dev/null +++ b/src/test/run-make/save-analysis-fail/SameDir3.rs @@ -0,0 +1,13 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn hello(x: isize) { + println!("macro {} :-(", x); +} diff --git a/src/test/run-make/save-analysis-fail/SubDir/mod.rs b/src/test/run-make/save-analysis-fail/SubDir/mod.rs new file mode 100644 index 0000000000000..fe84db08da900 --- /dev/null +++ b/src/test/run-make/save-analysis-fail/SubDir/mod.rs @@ -0,0 +1,37 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// sub-module in a sub-directory + +use sub::sub2 as msalias; +use sub::sub2; + +static yy: usize = 25; + +mod sub { + pub mod sub2 { + pub mod sub3 { + pub fn hello() { + println!("hello from module 3"); + } + } + pub fn hello() { + println!("hello from a module"); + } + + pub struct nested_struct { + pub field2: u32, + } + } +} + +pub struct SubStruct { + pub name: String +} diff --git a/src/test/run-make/save-analysis-fail/foo.rs b/src/test/run-make/save-analysis-fail/foo.rs new file mode 100644 index 0000000000000..e331f65abb7b3 --- /dev/null +++ b/src/test/run-make/save-analysis-fail/foo.rs @@ -0,0 +1,450 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![ crate_name = "test" ] +#![feature(box_syntax)] +#![feature(rustc_private)] + +extern crate graphviz; +// A simple rust project + +extern crate krate2; +extern crate krate2 as krate3; +extern crate flate as myflate; + +use graphviz::RenderOption; +use std::collections::{HashMap,HashSet}; +use std::cell::RefCell; +use std::io::Write; + + +use sub::sub2 as msalias; +use sub::sub2; +use sub::sub2::nested_struct as sub_struct; + +use std::mem::size_of; + +use std::char::from_u32; + +static uni: &'static str = "Les Miséééééééérables"; +static yy: usize = 25; + +static bob: Option = None; + +// buglink test - see issue #1337. + +fn test_alias(i: Option<::Item>) { + let s = sub_struct{ field2: 45u32, }; + + // import tests + fn foo(x: &Write) {} + let _: Option<_> = from_u32(45); + + let x = 42usize; + + krate2::hello(); + krate3::hello(); + myflate::deflate_bytes(&[]); + + let x = (3isize, 4usize); + let y = x.1; +} + +// Issue #37700 +const LUT_BITS: usize = 3; +pub struct HuffmanTable { + ac_lut: Option<[(i16, u8); 1 << LUT_BITS]>, +} + +struct TupStruct(isize, isize, Box); + +fn test_tup_struct(x: TupStruct) -> isize { + x.1 +} + +fn println(s: &str) { + std::io::stdout().write_all(s.as_bytes()); +} + +mod sub { + pub mod sub2 { + use std::io::Write; + pub mod sub3 { + use std::io::Write; + pub fn hello() { + ::println("hello from module 3"); + } + } + pub fn hello() { + ::println("hello from a module"); + } + + pub struct nested_struct { + pub field2: u32, + } + + pub enum nested_enum { + Nest2 = 2, + Nest3 = 3 + } + } +} + +pub mod SameDir; +pub mod SubDir; + +#[path = "SameDir3.rs"] +pub mod SameDir2; + +struct nofields; + +#[derive(Clone)] +struct some_fields { + field1: u32, +} + +type SF = some_fields; + +trait SuperTrait { + fn qux(&self) { panic!(); } +} + +trait SomeTrait: SuperTrait { + fn Method(&self, x: u32) -> u32; + + fn prov(&self, x: u32) -> u32 { + println(&x.to_string()); + 42 + } + fn provided_method(&self) -> u32 { + 42 + } +} + +trait SubTrait: SomeTrait { + fn stat2(x: &Self) -> u32 { + 32 + } +} + +trait SizedTrait: Sized {} + +fn error(s: &SizedTrait) { + let foo = 42; + println!("Hello world! {}", foo); +} + +impl SomeTrait for some_fields { + fn Method(&self, x: u32) -> u32 { + println(&x.to_string()); + self.field1 + } +} + +impl SuperTrait for some_fields { +} + +impl SubTrait for some_fields {} + +impl some_fields { + fn stat(x: u32) -> u32 { + println(&x.to_string()); + 42 + } + fn stat2(x: &some_fields) -> u32 { + 42 + } + + fn align_to(&mut self) { + + } + + fn test(&mut self) { + self.align_to::(); + } +} + +impl SuperTrait for nofields { +} +impl SomeTrait for nofields { + fn Method(&self, x: u32) -> u32 { + self.Method(x); + 43 + } + + fn provided_method(&self) -> u32 { + 21 + } +} + +impl SubTrait for nofields {} + +impl SuperTrait for (Box, Box) {} + +fn f_with_params(x: &T) { + x.Method(41); +} + +type MyType = Box; + +enum SomeEnum<'a> { + Ints(isize, isize), + Floats(f64, f64), + Strings(&'a str, &'a str, &'a str), + MyTypes(MyType, MyType) +} + +#[derive(Copy, Clone)] +enum SomeOtherEnum { + SomeConst1, + SomeConst2, + SomeConst3 +} + +enum SomeStructEnum { + EnumStruct{a:isize, b:isize}, + EnumStruct2{f1:MyType, f2:MyType}, + EnumStruct3{f1:MyType, f2:MyType, f3:SomeEnum<'static>} +} + +fn matchSomeEnum(val: SomeEnum) { + match val { + SomeEnum::Ints(int1, int2) => { println(&(int1+int2).to_string()); } + SomeEnum::Floats(float1, float2) => { println(&(float2*float1).to_string()); } + SomeEnum::Strings(.., s3) => { println(s3); } + SomeEnum::MyTypes(mt1, mt2) => { println(&(mt1.field1 - mt2.field1).to_string()); } + } +} + +fn matchSomeStructEnum(se: SomeStructEnum) { + match se { + SomeStructEnum::EnumStruct{a:a, ..} => println(&a.to_string()), + SomeStructEnum::EnumStruct2{f1:f1, f2:f_2} => println(&f_2.field1.to_string()), + SomeStructEnum::EnumStruct3{f1, ..} => println(&f1.field1.to_string()), + } +} + + +fn matchSomeStructEnum2(se: SomeStructEnum) { + use SomeStructEnum::*; + match se { + EnumStruct{a: ref aaa, ..} => println(&aaa.to_string()), + EnumStruct2{f1, f2: f2} => println(&f1.field1.to_string()), + EnumStruct3{f1, f3: SomeEnum::Ints(..), f2} => println(&f1.field1.to_string()), + _ => {}, + } +} + +fn matchSomeOtherEnum(val: SomeOtherEnum) { + use SomeOtherEnum::{SomeConst2, SomeConst3}; + match val { + SomeOtherEnum::SomeConst1 => { println("I'm const1."); } + SomeConst2 | SomeConst3 => { println("I'm const2 or const3."); } + } +} + +fn hello((z, a) : (u32, String), ex: X) { + SameDir2::hello(43); + + println(&yy.to_string()); + let (x, y): (u32, u32) = (5, 3); + println(&x.to_string()); + println(&z.to_string()); + let x: u32 = x; + println(&x.to_string()); + let x = "hello"; + println(x); + + let x = 32.0f32; + let _ = (x + ((x * x) + 1.0).sqrt()).ln(); + + let s: Box = box some_fields {field1: 43}; + let s2: Box = box some_fields {field1: 43}; + let s3 = box nofields; + + s.Method(43); + s3.Method(43); + s2.Method(43); + + ex.prov(43); + + let y: u32 = 56; + // static method on struct + let r = some_fields::stat(y); + // trait static method, calls default + let r = SubTrait::stat2(&*s3); + + let s4 = s3 as Box; + s4.Method(43); + + s4.provided_method(); + s2.prov(45); + + let closure = |x: u32, s: &SomeTrait| { + s.Method(23); + return x + y; + }; + + let z = closure(10, &*s); +} + +pub struct blah { + used_link_args: RefCell<[&'static str; 0]>, +} + +#[macro_use] +mod macro_use_test { + macro_rules! test_rec { + (q, $src: expr) => {{ + print!("{}", $src); + test_rec!($src); + }}; + ($src: expr) => { + print!("{}", $src); + }; + } + + macro_rules! internal_vars { + ($src: ident) => {{ + let mut x = $src; + x += 100; + }}; + } +} + +fn main() { // foo + let s = box some_fields {field1: 43}; + hello((43, "a".to_string()), *s); + sub::sub2::hello(); + sub2::sub3::hello(); + + let h = sub2::sub3::hello; + h(); + + // utf8 chars + let ut = "Les Miséééééééérables"; + + // For some reason, this pattern of macro_rules foiled our generated code + // avoiding strategy. + macro_rules! variable_str(($name:expr) => ( + some_fields { + field1: $name, + } + )); + let vs = variable_str!(32); + + let mut candidates: RefCell> = RefCell::new(HashMap::new()); + let _ = blah { + used_link_args: RefCell::new([]), + }; + let s1 = nofields; + let s2 = SF { field1: 55}; + let s3: some_fields = some_fields{ field1: 55}; + let s4: msalias::nested_struct = sub::sub2::nested_struct{ field2: 55}; + let s4: msalias::nested_struct = sub2::nested_struct{ field2: 55}; + println(&s2.field1.to_string()); + let s5: MyType = box some_fields{ field1: 55}; + let s = SameDir::SameStruct{name: "Bob".to_string()}; + let s = SubDir::SubStruct{name:"Bob".to_string()}; + let s6: SomeEnum = SomeEnum::MyTypes(box s2.clone(), s5); + let s7: SomeEnum = SomeEnum::Strings("one", "two", "three"); + matchSomeEnum(s6); + matchSomeEnum(s7); + let s8: SomeOtherEnum = SomeOtherEnum::SomeConst2; + matchSomeOtherEnum(s8); + let s9: SomeStructEnum = SomeStructEnum::EnumStruct2{ f1: box some_fields{ field1:10 }, + f2: box s2 }; + matchSomeStructEnum(s9); + + for x in &vec![1, 2, 3] { + let _y = x; + } + + let s7: SomeEnum = SomeEnum::Strings("one", "two", "three"); + if let SomeEnum::Strings(..) = s7 { + println!("hello!"); + } + + for i in 0..5 { + foo_foo(i); + } + + if let Some(x) = None { + foo_foo(x); + } + + if false { + } else if let Some(y) = None { + foo_foo(y); + } + + while let Some(z) = None { + foo_foo(z); + } + + let mut x = 4; + test_rec!(q, "Hello"); + assert_eq!(x, 4); + internal_vars!(x); +} + +fn foo_foo(_: i32) {} + +impl Iterator for nofields { + type Item = (usize, usize); + + fn next(&mut self) -> Option<(usize, usize)> { + panic!() + } + + fn size_hint(&self) -> (usize, Option) { + panic!() + } +} + +trait Pattern<'a> { + type Searcher; +} + +struct CharEqPattern; + +impl<'a> Pattern<'a> for CharEqPattern { + type Searcher = CharEqPattern; +} + +struct CharSearcher<'a>(>::Searcher); + +pub trait Error { +} + +impl Error + 'static { + pub fn is(&self) -> bool { + panic!() + } +} + +impl Error + 'static + Send { + pub fn is(&self) -> bool { + ::is::(self) + } +} +extern crate serialize; +#[derive(Clone, Copy, Hash, Encodable, Decodable, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +struct AllDerives(i32); + +fn test_format_args() { + let x = 1; + let y = 2; + let name = "Joe Blogg"; + println!("Hello {}", name); + print!("Hello {0}", name); + print!("{0} + {} = {}", x, y); + print!("x is {}, y is {1}, name is {n}", x, y, n = name); +} diff --git a/src/test/run-make/save-analysis-fail/krate2.rs b/src/test/run-make/save-analysis-fail/krate2.rs new file mode 100644 index 0000000000000..2c6f517ff3882 --- /dev/null +++ b/src/test/run-make/save-analysis-fail/krate2.rs @@ -0,0 +1,18 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![ crate_name = "krate2" ] +#![ crate_type = "lib" ] + +use std::io::Write; + +pub fn hello() { + std::io::stdout().write_all(b"hello world!\n"); +} From 68312e3e20a881f2bcde20db0c7ee385c0aa27c1 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 23 Nov 2016 18:47:07 +1300 Subject: [PATCH 3/4] Fix a bunch of bugs shown by the test --- src/librustc_save_analysis/dump_visitor.rs | 31 ++++++++++++++++--- src/test/run-make/save-analysis-fail/Makefile | 4 +-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index e83c2359979c0..0476fc621d13f 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -275,7 +275,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { fn lookup_def_id(&self, ref_id: NodeId) -> Option { self.tcx.expect_def_or_none(ref_id).and_then(|def| { match def { - Def::PrimTy(..) | Def::SelfTy(..) => None, + Def::Label(..) | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => None, def => Some(def.def_id()), } }) @@ -357,7 +357,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { collector.visit_pat(&arg.pat); let span_utils = self.span.clone(); for &(id, ref p, ..) in &collector.collected_paths { - let typ = self.tcx.tables().node_types.get(&id).unwrap().to_string(); + let typ = match self.tcx.tables().node_types.get(&id) { + Some(s) => s.to_string(), + None => continue, + }; // get the span only for the name of the variable (I hope the path is only ever a // variable name, but who knows?) let sub_span = span_utils.span_for_last_ident(p.span); @@ -987,7 +990,13 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { match p.node { PatKind::Struct(ref path, ref fields, _) => { visit::walk_path(self, path); - let adt = self.tcx.tables().node_id_to_type(p.id).ty_adt_def().unwrap(); + let adt = match self.tcx.tables().node_id_to_type_opt(p.id) { + Some(ty) => ty.ty_adt_def().unwrap(), + None => { + visit::walk_pat(self, p); + return; + } + }; let variant = adt.variant_of_def(self.tcx.expect_def(p.id)); for &Spanned { node: ref field, span } in fields { @@ -1353,7 +1362,13 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D> } ast::ExprKind::Struct(ref path, ref fields, ref base) => { let hir_expr = self.save_ctxt.tcx.map.expect_expr(ex.id); - let adt = self.tcx.tables().expr_ty(&hir_expr).ty_adt_def().unwrap(); + let adt = match self.tcx.tables().expr_ty_opt(&hir_expr) { + Some(ty) => ty.ty_adt_def().unwrap(), + None => { + visit::walk_expr(self, ex); + return; + } + }; let def = self.tcx.expect_def(hir_expr.id); self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base) } @@ -1379,7 +1394,13 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D> return; } }; - let ty = &self.tcx.tables().expr_ty_adjusted(&hir_node).sty; + let ty = match self.tcx.tables().expr_ty_adjusted_opt(&hir_node) { + Some(ty) => &ty.sty, + None => { + visit::walk_expr(self, ex); + return; + } + }; match *ty { ty::TyAdt(def, _) => { let sub_span = self.span.sub_span_after_token(ex.span, token::Dot); diff --git a/src/test/run-make/save-analysis-fail/Makefile b/src/test/run-make/save-analysis-fail/Makefile index 3711b6ea8959b..f29f907cf387e 100644 --- a/src/test/run-make/save-analysis-fail/Makefile +++ b/src/test/run-make/save-analysis-fail/Makefile @@ -3,6 +3,4 @@ all: code krate2: krate2.rs $(RUSTC) $< code: foo.rs krate2 - $(RUSTC) foo.rs -Zsave-analysis-csv - $(RUSTC) foo.rs -Zsave-analysis - $(RUSTC) foo.rs -Zsave-analysis-api + $(RUSTC) foo.rs -Zsave-analysis || exit 0 From b1f86fb7c30c6296a9c2e07d3a58795bca8c4cf4 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 24 Nov 2016 07:50:22 +1300 Subject: [PATCH 4/4] Inspect def locally instead of using a method --- src/librustc/hir/def.rs | 11 +++-------- src/librustc_save_analysis/lib.rs | 7 ++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index ce04a7c897a18..feefc43f4013e 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -85,15 +85,10 @@ impl PathResolution { /// Get the definition, if fully resolved, otherwise panic. pub fn full_def(&self) -> Def { - self.maybe_full_def().unwrap_or_else(|| bug!("path not fully resolved: {:?}", self)) - } - - pub fn maybe_full_def(&self) -> Option { - if self.depth == 0 { - Some(self.base_def) - } else { - None + if self.depth != 0 { + bug!("path not fully resolved: {:?}", self); } + self.base_def } pub fn kind_name(&self) -> &'static str { diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 4c59f5e8a83cb..a82a51a2e1759 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -497,7 +497,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option { - let def = option_try!(self.tcx.expect_resolution(id).maybe_full_def()); + let resolution = self.tcx.expect_resolution(id); + if resolution.depth != 0 { + return None; + } + let def = resolution.base_def; + let sub_span = self.span_utils.span_for_last_ident(path.span); filter!(self.span_utils, sub_span, path.span, None); match def {