Skip to content

Commit ea16814

Browse files
committed
Auto merge of #46248 - zackmdavis:one_time_private_enum_variant_reexport_error, r=estebank
one-time diagnostics for private enum variants glob reëxport ![private_enum_reexport](https://user-images.githubusercontent.com/1076988/33224719-4e5805f0-d121-11e7-8bc0-a708a277a5db.png) r? @estebank
2 parents 2d4df95 + 4fb57e0 commit ea16814

File tree

5 files changed

+148
-24
lines changed

5 files changed

+148
-24
lines changed

src/librustc/lint/mod.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use errors::{DiagnosticBuilder, DiagnosticId};
3737
use hir::def_id::{CrateNum, LOCAL_CRATE};
3838
use hir::intravisit::{self, FnKind};
3939
use hir;
40-
use session::Session;
40+
use session::{Session, DiagnosticMessageId};
4141
use std::hash;
4242
use syntax::ast;
4343
use syntax::codemap::MultiSpan;
@@ -423,7 +423,7 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
423423
LintSource::Default => {
424424
sess.diag_note_once(
425425
&mut err,
426-
lint,
426+
DiagnosticMessageId::from(lint),
427427
&format!("#[{}({})] on by default", level.as_str(), name));
428428
}
429429
LintSource::CommandLine(lint_flag_val) => {
@@ -437,24 +437,25 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
437437
if lint_flag_val.as_str() == name {
438438
sess.diag_note_once(
439439
&mut err,
440-
lint,
440+
DiagnosticMessageId::from(lint),
441441
&format!("requested on the command line with `{} {}`",
442442
flag, hyphen_case_lint_name));
443443
} else {
444444
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
445445
sess.diag_note_once(
446446
&mut err,
447-
lint,
447+
DiagnosticMessageId::from(lint),
448448
&format!("`{} {}` implied by `{} {}`",
449449
flag, hyphen_case_lint_name, flag,
450450
hyphen_case_flag_val));
451451
}
452452
}
453453
LintSource::Node(lint_attr_name, src) => {
454-
sess.diag_span_note_once(&mut err, lint, src, "lint level defined here");
454+
sess.diag_span_note_once(&mut err, DiagnosticMessageId::from(lint),
455+
src, "lint level defined here");
455456
if lint_attr_name.as_str() != name {
456457
let level_str = level.as_str();
457-
sess.diag_note_once(&mut err, lint,
458+
sess.diag_note_once(&mut err, DiagnosticMessageId::from(lint),
458459
&format!("#[{}({})] implied by #[{}({})]",
459460
level_str, name, level_str, lint_attr_name));
460461
}

src/librustc/session/mod.rs

+32-8
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub struct PerfStats {
161161
enum DiagnosticBuilderMethod {
162162
Note,
163163
SpanNote,
164+
SpanSuggestion(String), // suggestion
164165
// add more variants as needed to support one-time diagnostics
165166
}
166167

@@ -173,6 +174,12 @@ pub enum DiagnosticMessageId {
173174
StabilityId(u32) // issue number
174175
}
175176

177+
impl From<&'static lint::Lint> for DiagnosticMessageId {
178+
fn from(lint: &'static lint::Lint) -> Self {
179+
DiagnosticMessageId::LintId(lint::LintId::of(lint))
180+
}
181+
}
182+
176183
impl Session {
177184
pub fn local_crate_disambiguator(&self) -> CrateDisambiguator {
178185
match *self.crate_disambiguator.borrow() {
@@ -358,33 +365,50 @@ impl Session {
358365
fn diag_once<'a, 'b>(&'a self,
359366
diag_builder: &'b mut DiagnosticBuilder<'a>,
360367
method: DiagnosticBuilderMethod,
361-
lint: &'static lint::Lint, message: &str, span: Option<Span>) {
368+
msg_id: DiagnosticMessageId,
369+
message: &str,
370+
span_maybe: Option<Span>) {
362371

363-
let lint_id = DiagnosticMessageId::LintId(lint::LintId::of(lint));
364-
let id_span_message = (lint_id, span, message.to_owned());
372+
let id_span_message = (msg_id, span_maybe, message.to_owned());
365373
let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message);
366374
if fresh {
367375
match method {
368376
DiagnosticBuilderMethod::Note => {
369377
diag_builder.note(message);
370378
},
371379
DiagnosticBuilderMethod::SpanNote => {
372-
diag_builder.span_note(span.expect("span_note expects a span"), message);
380+
let span = span_maybe.expect("span_note needs a span");
381+
diag_builder.span_note(span, message);
382+
},
383+
DiagnosticBuilderMethod::SpanSuggestion(suggestion) => {
384+
let span = span_maybe.expect("span_suggestion needs a span");
385+
diag_builder.span_suggestion(span, message, suggestion);
373386
}
374387
}
375388
}
376389
}
377390

378391
pub fn diag_span_note_once<'a, 'b>(&'a self,
379392
diag_builder: &'b mut DiagnosticBuilder<'a>,
380-
lint: &'static lint::Lint, span: Span, message: &str) {
381-
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanNote, lint, message, Some(span));
393+
msg_id: DiagnosticMessageId, span: Span, message: &str) {
394+
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanNote,
395+
msg_id, message, Some(span));
382396
}
383397

384398
pub fn diag_note_once<'a, 'b>(&'a self,
385399
diag_builder: &'b mut DiagnosticBuilder<'a>,
386-
lint: &'static lint::Lint, message: &str) {
387-
self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, lint, message, None);
400+
msg_id: DiagnosticMessageId, message: &str) {
401+
self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None);
402+
}
403+
404+
pub fn diag_span_suggestion_once<'a, 'b>(&'a self,
405+
diag_builder: &'b mut DiagnosticBuilder<'a>,
406+
msg_id: DiagnosticMessageId,
407+
span: Span,
408+
message: &str,
409+
suggestion: String) {
410+
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanSuggestion(suggestion),
411+
msg_id, message, Some(span));
388412
}
389413

390414
pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap {

src/librustc_resolve/resolve_imports.rs

+54-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc::ty;
2121
use rustc::lint::builtin::PUB_USE_OF_PRIVATE_EXTERN_CRATE;
2222
use rustc::hir::def_id::DefId;
2323
use rustc::hir::def::*;
24+
use rustc::session::DiagnosticMessageId;
2425
use rustc::util::nodemap::{FxHashMap, FxHashSet};
2526

2627
use syntax::ast::{Ident, Name, SpannedIdent, NodeId};
@@ -72,7 +73,7 @@ impl<'a> ImportDirective<'a> {
7273
}
7374
}
7475

75-
#[derive(Clone, Default)]
76+
#[derive(Clone, Default, Debug)]
7677
/// Records information about the resolution of a name in a namespace of a module.
7778
pub struct NameResolution<'a> {
7879
/// The single imports that define the name in the namespace.
@@ -867,12 +868,59 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
867868
}
868869

869870
match binding.kind {
870-
NameBindingKind::Import { binding: orig_binding, .. } => {
871+
NameBindingKind::Import { binding: orig_binding, directive, .. } => {
871872
if ns == TypeNS && orig_binding.is_variant() &&
872-
!orig_binding.vis.is_at_least(binding.vis, &*self) {
873-
let msg = format!("variant `{}` is private, and cannot be reexported, \
874-
consider declaring its enum as `pub`", ident);
875-
self.session.span_err(binding.span, &msg);
873+
!orig_binding.vis.is_at_least(binding.vis, &*self) {
874+
let msg = match directive.subclass {
875+
ImportDirectiveSubclass::SingleImport { .. } => {
876+
format!("variant `{}` is private and cannot be reexported",
877+
ident)
878+
},
879+
ImportDirectiveSubclass::GlobImport { .. } => {
880+
let msg = "enum is private and its variants \
881+
cannot be reexported".to_owned();
882+
let error_id = (DiagnosticMessageId::ErrorId(0), // no code?!
883+
Some(binding.span),
884+
msg.clone());
885+
let fresh = self.session.one_time_diagnostics
886+
.borrow_mut().insert(error_id);
887+
if !fresh {
888+
continue;
889+
}
890+
msg
891+
},
892+
ref s @ _ => bug!("unexpected import subclass {:?}", s)
893+
};
894+
let mut err = self.session.struct_span_err(binding.span, &msg);
895+
896+
let imported_module = directive.imported_module.get()
897+
.expect("module should exist");
898+
let resolutions = imported_module.parent.expect("parent should exist")
899+
.resolutions.borrow();
900+
let enum_path_segment_index = directive.module_path.len() - 1;
901+
let enum_ident = directive.module_path[enum_path_segment_index].node;
902+
903+
let enum_resolution = resolutions.get(&(enum_ident, TypeNS))
904+
.expect("resolution should exist");
905+
let enum_span = enum_resolution.borrow()
906+
.binding.expect("binding should exist")
907+
.span;
908+
let enum_def_span = self.session.codemap().def_span(enum_span);
909+
let enum_def_snippet = self.session.codemap()
910+
.span_to_snippet(enum_def_span).expect("snippet should exist");
911+
// potentially need to strip extant `crate`/`pub(path)` for suggestion
912+
let after_vis_index = enum_def_snippet.find("enum")
913+
.expect("`enum` keyword should exist in snippet");
914+
let suggestion = format!("pub {}",
915+
&enum_def_snippet[after_vis_index..]);
916+
917+
self.session
918+
.diag_span_suggestion_once(&mut err,
919+
DiagnosticMessageId::ErrorId(0),
920+
enum_def_span,
921+
"consider making the enum public",
922+
suggestion);
923+
err.emit();
876924
}
877925
}
878926
NameBindingKind::Ambiguity { b1, b2, .. }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(crate_visibility_modifier)]
12+
13+
mod rank {
14+
pub use self::Professor::*;
15+
//~^ ERROR enum is private and its variants cannot be reexported
16+
pub use self::Lieutenant::{JuniorGrade, Full};
17+
//~^ ERROR variant `JuniorGrade` is private and cannot be reexported
18+
//~| ERROR variant `Full` is private and cannot be reexported
19+
pub use self::PettyOfficer::*;
20+
//~^ ERROR enum is private and its variants cannot be reexported
21+
pub use self::Crewman::*;
22+
//~^ ERROR enum is private and its variants cannot be reexported
23+
24+
enum Professor {
25+
Adjunct,
26+
Assistant,
27+
Associate,
28+
Full
29+
}
30+
31+
enum Lieutenant {
32+
JuniorGrade,
33+
Full,
34+
}
35+
36+
pub(in rank) enum PettyOfficer {
37+
SecondClass,
38+
FirstClass,
39+
Chief,
40+
MasterChief
41+
}
42+
43+
crate enum Crewman {
44+
Recruit,
45+
Apprentice,
46+
Full
47+
}
48+
49+
}
50+
51+
fn main() {}

src/test/compile-fail/private-variant-reexport.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
// except according to those terms.
1010

1111
mod m1 {
12-
pub use ::E::V; //~ ERROR variant `V` is private, and cannot be reexported
12+
pub use ::E::V; //~ ERROR variant `V` is private and cannot be reexported
1313
}
1414

1515
mod m2 {
16-
pub use ::E::{V}; //~ ERROR variant `V` is private, and cannot be reexported
16+
pub use ::E::{V}; //~ ERROR variant `V` is private and cannot be reexported
1717
}
1818

1919
mod m3 {
20-
pub use ::E::V::{self}; //~ ERROR variant `V` is private, and cannot be reexported
20+
pub use ::E::V::{self}; //~ ERROR variant `V` is private and cannot be reexported
2121
}
2222

2323
mod m4 {
24-
pub use ::E::*; //~ ERROR variant `V` is private, and cannot be reexported
24+
pub use ::E::*; //~ ERROR enum is private and its variants cannot be reexported
2525
}
2626

2727
enum E { V }

0 commit comments

Comments
 (0)