Auto merge of #27458 - mitaa:local_cpath, r=nikomatsakis
This changes the current behaviour for two cases (that I know of) ```rust mod foo { extern crate bar; } // `bar::` changes to `foo::bar::` ``` ```rust extern crate bar as quux; // `bar::` changes to `quux::` ``` For example: ```rust mod foo { extern crate core; } fn assert_clone<T>() where T : Clone { } fn main() { assert_clone::<foo::core::atomic::AtomicBool>(); // error: the trait `core::clone::Clone` is not implemented for the type `core::atomic::AtomicBool` [E0277] // changes to // error: the trait `foo::core::clone::Clone` is not implemented for the type `foo::core::atomic::AtomicBool` [E0277] } ``` Notably the following test case broke: ```rust #[bench] fn bar(x: isize) { } //~^ ERROR mismatched types //~| expected `fn(&mut test::Bencher)` // changed to //~| expected `fn(&mut __test::test::Bencher)` ``` If a crate is linked multiple times the path with the least segments is stored. Partially addresses #1920. (this doesn't solve the issue raised about re-exports) r? @nikomatsakis
This commit is contained in:
commit
0dc2910c9c
11 changed files with 260 additions and 145 deletions
|
@ -34,14 +34,22 @@ use syntax::codemap::{self, Span, mk_sp, Pos};
|
|||
use syntax::parse;
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax::visit;
|
||||
use syntax::util::small_vector::SmallVector;
|
||||
use ast_map;
|
||||
use log;
|
||||
|
||||
pub struct LocalCrateReader<'a, 'b:'a> {
|
||||
sess: &'a Session,
|
||||
creader: CrateReader<'a>,
|
||||
ast_map: &'a ast_map::Map<'b>,
|
||||
}
|
||||
|
||||
pub struct CrateReader<'a> {
|
||||
sess: &'a Session,
|
||||
next_crate_num: ast::CrateNum,
|
||||
}
|
||||
|
||||
impl<'a, 'v> visit::Visitor<'v> for CrateReader<'a> {
|
||||
impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> {
|
||||
fn visit_item(&mut self, a: &ast::Item) {
|
||||
self.process_item(a);
|
||||
visit::walk_item(self, a);
|
||||
|
@ -152,31 +160,6 @@ impl<'a> CrateReader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// Traverses an AST, reading all the information about use'd crates and
|
||||
// extern libraries necessary for later resolving, typechecking, linking,
|
||||
// etc.
|
||||
pub fn read_crates(&mut self, krate: &ast::Crate) {
|
||||
self.process_crate(krate);
|
||||
visit::walk_crate(self, krate);
|
||||
|
||||
if log_enabled!(log::DEBUG) {
|
||||
dump_crates(&self.sess.cstore);
|
||||
}
|
||||
|
||||
for &(ref name, kind) in &self.sess.opts.libs {
|
||||
register_native_lib(self.sess, None, name.clone(), kind);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_crate(&self, c: &ast::Crate) {
|
||||
for a in c.attrs.iter().filter(|m| m.name() == "link_args") {
|
||||
match a.value_str() {
|
||||
Some(ref linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
|
||||
None => { /* fallthrough */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_crate_info(&self, i: &ast::Item) -> Option<CrateInfo> {
|
||||
match i.node {
|
||||
ast::ItemExternCrate(ref path_opt) => {
|
||||
|
@ -201,103 +184,6 @@ impl<'a> CrateReader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_item(&mut self, i: &ast::Item) {
|
||||
match i.node {
|
||||
ast::ItemExternCrate(_) => {
|
||||
if !should_link(i) {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.extract_crate_info(i) {
|
||||
Some(info) => {
|
||||
let (cnum, _, _) = self.resolve_crate(&None,
|
||||
&info.ident,
|
||||
&info.name,
|
||||
None,
|
||||
i.span,
|
||||
PathKind::Crate);
|
||||
self.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
ast::ItemForeignMod(ref fm) => {
|
||||
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
|
||||
return;
|
||||
}
|
||||
|
||||
// First, add all of the custom link_args attributes
|
||||
let link_args = i.attrs.iter()
|
||||
.filter_map(|at| if at.name() == "link_args" {
|
||||
Some(at)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<Vec<&ast::Attribute>>();
|
||||
for m in &link_args {
|
||||
match m.value_str() {
|
||||
Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
|
||||
None => { /* fallthrough */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Next, process all of the #[link(..)]-style arguments
|
||||
let link_args = i.attrs.iter()
|
||||
.filter_map(|at| if at.name() == "link" {
|
||||
Some(at)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<Vec<&ast::Attribute>>();
|
||||
for m in &link_args {
|
||||
match m.meta_item_list() {
|
||||
Some(items) => {
|
||||
let kind = items.iter().find(|k| {
|
||||
k.name() == "kind"
|
||||
}).and_then(|a| a.value_str());
|
||||
let kind = match kind {
|
||||
Some(k) => {
|
||||
if k == "static" {
|
||||
cstore::NativeStatic
|
||||
} else if self.sess.target.target.options.is_like_osx
|
||||
&& k == "framework" {
|
||||
cstore::NativeFramework
|
||||
} else if k == "framework" {
|
||||
cstore::NativeFramework
|
||||
} else if k == "dylib" {
|
||||
cstore::NativeUnknown
|
||||
} else {
|
||||
self.sess.span_err(m.span,
|
||||
&format!("unknown kind: `{}`",
|
||||
k));
|
||||
cstore::NativeUnknown
|
||||
}
|
||||
}
|
||||
None => cstore::NativeUnknown
|
||||
};
|
||||
let n = items.iter().find(|n| {
|
||||
n.name() == "name"
|
||||
}).and_then(|a| a.value_str());
|
||||
let n = match n {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
self.sess.span_err(m.span,
|
||||
"#[link(...)] specified without \
|
||||
`name = \"foo\"`");
|
||||
InternedString::new("foo")
|
||||
}
|
||||
};
|
||||
register_native_lib(self.sess, Some(m.span),
|
||||
n.to_string(), kind);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
fn existing_match(&self, name: &str, hash: Option<&Svh>, kind: PathKind)
|
||||
-> Option<ast::CrateNum> {
|
||||
let mut ret = None;
|
||||
|
@ -378,6 +264,7 @@ impl<'a> CrateReader<'a> {
|
|||
|
||||
let cmeta = Rc::new( cstore::crate_metadata {
|
||||
name: name.to_string(),
|
||||
local_path: RefCell::new(SmallVector::zero()),
|
||||
data: metadata,
|
||||
cnum_map: cnum_map,
|
||||
cnum: cnum,
|
||||
|
@ -592,6 +479,140 @@ impl<'a> CrateReader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> LocalCrateReader<'a, 'b> {
|
||||
pub fn new(sess: &'a Session, map: &'a ast_map::Map<'b>) -> LocalCrateReader<'a, 'b> {
|
||||
LocalCrateReader {
|
||||
sess: sess,
|
||||
creader: CrateReader::new(sess),
|
||||
ast_map: map,
|
||||
}
|
||||
}
|
||||
|
||||
// Traverses an AST, reading all the information about use'd crates and
|
||||
// extern libraries necessary for later resolving, typechecking, linking,
|
||||
// etc.
|
||||
pub fn read_crates(&mut self, krate: &ast::Crate) {
|
||||
self.process_crate(krate);
|
||||
visit::walk_crate(self, krate);
|
||||
|
||||
if log_enabled!(log::DEBUG) {
|
||||
dump_crates(&self.sess.cstore);
|
||||
}
|
||||
|
||||
for &(ref name, kind) in &self.sess.opts.libs {
|
||||
register_native_lib(self.sess, None, name.clone(), kind);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_crate(&self, c: &ast::Crate) {
|
||||
for a in c.attrs.iter().filter(|m| m.name() == "link_args") {
|
||||
match a.value_str() {
|
||||
Some(ref linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
|
||||
None => { /* fallthrough */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_item(&mut self, i: &ast::Item) {
|
||||
match i.node {
|
||||
ast::ItemExternCrate(_) => {
|
||||
if !should_link(i) {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.creader.extract_crate_info(i) {
|
||||
Some(info) => {
|
||||
let (cnum, cmeta, _) = self.creader.resolve_crate(&None,
|
||||
&info.ident,
|
||||
&info.name,
|
||||
None,
|
||||
i.span,
|
||||
PathKind::Crate);
|
||||
self.ast_map.with_path(i.id, |path|
|
||||
cmeta.update_local_path(path));
|
||||
self.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
ast::ItemForeignMod(ref fm) => {
|
||||
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
|
||||
return;
|
||||
}
|
||||
|
||||
// First, add all of the custom link_args attributes
|
||||
let link_args = i.attrs.iter()
|
||||
.filter_map(|at| if at.name() == "link_args" {
|
||||
Some(at)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<Vec<&ast::Attribute>>();
|
||||
for m in &link_args {
|
||||
match m.value_str() {
|
||||
Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
|
||||
None => { /* fallthrough */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Next, process all of the #[link(..)]-style arguments
|
||||
let link_args = i.attrs.iter()
|
||||
.filter_map(|at| if at.name() == "link" {
|
||||
Some(at)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<Vec<&ast::Attribute>>();
|
||||
for m in &link_args {
|
||||
match m.meta_item_list() {
|
||||
Some(items) => {
|
||||
let kind = items.iter().find(|k| {
|
||||
k.name() == "kind"
|
||||
}).and_then(|a| a.value_str());
|
||||
let kind = match kind {
|
||||
Some(k) => {
|
||||
if k == "static" {
|
||||
cstore::NativeStatic
|
||||
} else if self.sess.target.target.options.is_like_osx
|
||||
&& k == "framework" {
|
||||
cstore::NativeFramework
|
||||
} else if k == "framework" {
|
||||
cstore::NativeFramework
|
||||
} else if k == "dylib" {
|
||||
cstore::NativeUnknown
|
||||
} else {
|
||||
self.sess.span_err(m.span,
|
||||
&format!("unknown kind: `{}`",
|
||||
k));
|
||||
cstore::NativeUnknown
|
||||
}
|
||||
}
|
||||
None => cstore::NativeUnknown
|
||||
};
|
||||
let n = items.iter().find(|n| {
|
||||
n.name() == "name"
|
||||
}).and_then(|a| a.value_str());
|
||||
let n = match n {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
self.sess.span_err(m.span,
|
||||
"#[link(...)] specified without \
|
||||
`name = \"foo\"`");
|
||||
InternedString::new("foo")
|
||||
}
|
||||
};
|
||||
register_native_lib(self.sess, Some(m.span),
|
||||
n.to_string(), kind);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports the codemap from an external crate into the codemap of the crate
|
||||
/// currently being compiled (the "local crate").
|
||||
///
|
||||
|
|
|
@ -24,7 +24,6 @@ use syntax::ast;
|
|||
use syntax::attr;
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
use syntax::diagnostic::expect;
|
||||
use syntax::parse::token;
|
||||
|
||||
use std::collections::hash_map::HashMap;
|
||||
|
||||
|
@ -89,11 +88,12 @@ pub fn get_item_path(tcx: &ty::ctxt, def: ast::DefId) -> Vec<ast_map::PathElem>
|
|||
let cdata = cstore.get_crate_data(def.krate);
|
||||
let path = decoder::get_item_path(&*cdata, def.node);
|
||||
|
||||
// FIXME #1920: This path is not always correct if the crate is not linked
|
||||
// into the root namespace.
|
||||
let mut r = vec![ast_map::PathMod(token::intern(&cdata.name))];
|
||||
r.push_all(&path);
|
||||
r
|
||||
cdata.with_local_path(|cpath| {
|
||||
let mut r = Vec::with_capacity(cpath.len() + path.len());
|
||||
r.push_all(cpath);
|
||||
r.push_all(&path);
|
||||
r
|
||||
})
|
||||
}
|
||||
|
||||
pub enum FoundAst<'ast> {
|
||||
|
|
|
@ -28,7 +28,10 @@ use std::path::PathBuf;
|
|||
use flate::Bytes;
|
||||
use syntax::ast;
|
||||
use syntax::codemap;
|
||||
use syntax::parse::token;
|
||||
use syntax::parse::token::IdentInterner;
|
||||
use syntax::util::small_vector::SmallVector;
|
||||
use ast_map;
|
||||
|
||||
// A map from external crate numbers (as decoded from some crate file) to
|
||||
// local crate numbers (as generated during this session). Each external
|
||||
|
@ -54,6 +57,7 @@ pub struct ImportedFileMap {
|
|||
|
||||
pub struct crate_metadata {
|
||||
pub name: String,
|
||||
pub local_path: RefCell<SmallVector<ast_map::PathElem>>,
|
||||
pub data: MetadataBlob,
|
||||
pub cnum_map: cnum_map,
|
||||
pub cnum: ast::CrateNum,
|
||||
|
@ -255,6 +259,30 @@ impl crate_metadata {
|
|||
filemaps
|
||||
}
|
||||
}
|
||||
pub fn with_local_path<T, F>(&self, f: F) -> T
|
||||
where F: Fn(&[ast_map::PathElem]) -> T {
|
||||
let cpath = self.local_path.borrow();
|
||||
if cpath.is_empty() {
|
||||
let name = ast_map::PathMod(token::intern(&self.name));
|
||||
f(&[name])
|
||||
} else {
|
||||
f(cpath.as_slice())
|
||||
}
|
||||
}
|
||||
pub fn update_local_path<'a, 'b>(&self, candidate: ast_map::PathElems<'a, 'b>) {
|
||||
let mut cpath = self.local_path.borrow_mut();
|
||||
let cap = cpath.len();
|
||||
match cap {
|
||||
0 => *cpath = candidate.collect(),
|
||||
1 => (),
|
||||
_ => {
|
||||
let candidate: SmallVector<_> = candidate.collect();
|
||||
if candidate.len() < cap {
|
||||
*cpath = candidate;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MetadataBlob {
|
||||
|
|
|
@ -14,7 +14,7 @@ use rustc::session::search_paths::PathKind;
|
|||
use rustc::ast_map;
|
||||
use rustc::lint;
|
||||
use rustc::metadata;
|
||||
use rustc::metadata::creader::CrateReader;
|
||||
use rustc::metadata::creader::LocalCrateReader;
|
||||
use rustc::middle::{stability, ty, reachable};
|
||||
use rustc::middle::dependency_format;
|
||||
use rustc::middle;
|
||||
|
@ -609,7 +609,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
|
|||
let krate = ast_map.krate();
|
||||
|
||||
time(time_passes, "external crate/lib resolution", (), |_|
|
||||
CrateReader::new(&sess).read_crates(krate));
|
||||
LocalCrateReader::new(&sess, &ast_map).read_crates(krate));
|
||||
|
||||
let lang_items = time(time_passes, "language item collection", (), |_|
|
||||
middle::lang_items::collect_language_items(krate, &sess));
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#[bench]
|
||||
fn bar(x: isize) { }
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected `fn(&mut test::Bencher)`
|
||||
//~| expected `fn(&mut __test::test::Bencher)`
|
||||
//~| found `fn(isize) {bar}`
|
||||
//~| expected &-ptr
|
||||
//~| found isize
|
||||
|
|
22
src/test/compile-fail/issue-1920-1.rs
Normal file
22
src/test/compile-fail/issue-1920-1.rs
Normal file
|
@ -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.
|
||||
|
||||
//! Test that absolute path names are correct when a crate is not linked into the root namespace
|
||||
|
||||
mod foo {
|
||||
extern crate core;
|
||||
}
|
||||
|
||||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<foo::core::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `foo::core::clone::Clone` is not implemented for the type `foo::core::
|
||||
}
|
20
src/test/compile-fail/issue-1920-2.rs
Normal file
20
src/test/compile-fail/issue-1920-2.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
|
||||
//! Test that when a crate is linked under another name that that name is used in global paths
|
||||
|
||||
extern crate core as bar;
|
||||
|
||||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<bar::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `bar::clone::Clone` is not implemented for the type `bar::atomic::
|
||||
}
|
24
src/test/compile-fail/issue-1920-3.rs
Normal file
24
src/test/compile-fail/issue-1920-3.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
// 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.
|
||||
|
||||
//! Test that when a crate is linked multiple times that the shortest absolute path name is used
|
||||
|
||||
mod foo {
|
||||
extern crate core;
|
||||
}
|
||||
|
||||
extern crate core;
|
||||
|
||||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<foo::core::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `core::clone::Clone` is not implemented for the type `core::atomic::
|
||||
}
|
|
@ -101,30 +101,30 @@ fn xcrate() {
|
|||
let c = other::C(2, 3); //~ ERROR: cannot invoke tuple struct constructor
|
||||
let d = other::D(4);
|
||||
|
||||
let other::A(()) = a; //~ ERROR: field #1 of struct `privacy_tuple_struct::A` is private
|
||||
let other::A(()) = a; //~ ERROR: field #1 of struct `other::A` is private
|
||||
let other::A(_) = a;
|
||||
match a { other::A(()) => {} }
|
||||
//~^ ERROR: field #1 of struct `privacy_tuple_struct::A` is private
|
||||
//~^ ERROR: field #1 of struct `other::A` is private
|
||||
match a { other::A(_) => {} }
|
||||
|
||||
let other::B(_) = b;
|
||||
let other::B(_b) = b; //~ ERROR: field #1 of struct `privacy_tuple_struct::B` is private
|
||||
let other::B(_b) = b; //~ ERROR: field #1 of struct `other::B` is private
|
||||
match b { other::B(_) => {} }
|
||||
match b { other::B(_b) => {} }
|
||||
//~^ ERROR: field #1 of struct `privacy_tuple_struct::B` is private
|
||||
//~^ ERROR: field #1 of struct `other::B` is private
|
||||
match b { other::B(1) => {} other::B(_) => {} }
|
||||
//~^ ERROR: field #1 of struct `privacy_tuple_struct::B` is private
|
||||
//~^ ERROR: field #1 of struct `other::B` is private
|
||||
|
||||
let other::C(_, _) = c;
|
||||
let other::C(_a, _) = c;
|
||||
let other::C(_, _b) = c; //~ ERROR: field #2 of struct `privacy_tuple_struct::C` is private
|
||||
let other::C(_a, _b) = c; //~ ERROR: field #2 of struct `privacy_tuple_struct::C` is private
|
||||
let other::C(_, _b) = c; //~ ERROR: field #2 of struct `other::C` is private
|
||||
let other::C(_a, _b) = c; //~ ERROR: field #2 of struct `other::C` is private
|
||||
match c { other::C(_, _) => {} }
|
||||
match c { other::C(_a, _) => {} }
|
||||
match c { other::C(_, _b) => {} }
|
||||
//~^ ERROR: field #2 of struct `privacy_tuple_struct::C` is private
|
||||
//~^ ERROR: field #2 of struct `other::C` is private
|
||||
match c { other::C(_a, _b) => {} }
|
||||
//~^ ERROR: field #2 of struct `privacy_tuple_struct::C` is private
|
||||
//~^ ERROR: field #2 of struct `other::C` is private
|
||||
|
||||
let other::D(_) = d;
|
||||
let other::D(_d) = d;
|
||||
|
|
|
@ -37,11 +37,11 @@ fn test(a: A, b: inner::A, c: inner::B, d: xc::A, e: xc::B) {
|
|||
c.a;
|
||||
c.b; //~ ERROR: field `b` of struct `inner::B` is private
|
||||
|
||||
d.a; //~ ERROR: field `a` of struct `struct_field_privacy::A` is private
|
||||
d.a; //~ ERROR: field `a` of struct `xc::A` is private
|
||||
d.b;
|
||||
|
||||
e.a;
|
||||
e.b; //~ ERROR: field `b` of struct `struct_field_privacy::B` is private
|
||||
e.b; //~ ERROR: field `b` of struct `xc::B` is private
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -22,9 +22,9 @@ struct A {
|
|||
fn main () {
|
||||
// external crate struct
|
||||
let k = B {
|
||||
aa: 20, //~ ERROR structure `struct_field_privacy::B` has no field named `aa`
|
||||
aa: 20, //~ ERROR structure `xc::B` has no field named `aa`
|
||||
//~^ HELP did you mean `a`?
|
||||
bb: 20, //~ ERROR structure `struct_field_privacy::B` has no field named `bb`
|
||||
bb: 20, //~ ERROR structure `xc::B` has no field named `bb`
|
||||
};
|
||||
// local crate struct
|
||||
let l = A {
|
||||
|
|
Loading…
Reference in a new issue