Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add foreign formatting directive detection. #37613

Merged
merged 1 commit into from
Nov 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions src/libsyntax_ext/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
use syntax::tokenstream;

use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;

#[derive(PartialEq)]
Expand Down Expand Up @@ -767,6 +767,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,

// Make sure that all arguments were used and all arguments have types.
let num_pos_args = cx.args.len() - cx.names.len();
let mut errs = vec![];
for (i, ty) in cx.arg_types.iter().enumerate() {
if ty.len() == 0 {
if cx.count_positions.contains_key(&i) {
Expand All @@ -779,9 +780,80 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
// positional argument
"argument never used"
};
cx.ecx.span_err(cx.args[i].span, msg);
errs.push((cx.args[i].span, msg));
}
}
if errs.len() > 0 {
let args_used = cx.arg_types.len() - errs.len();
let args_unused = errs.len();

let mut diag = {
if errs.len() == 1 {
let (sp, msg) = errs.into_iter().next().unwrap();
cx.ecx.struct_span_err(sp, msg)
} else {
let mut diag = cx.ecx.struct_span_err(cx.fmtsp,
"multiple unused formatting arguments");
for (sp, msg) in errs {
diag.span_note(sp, msg);
}
diag
}
};

// Decide if we want to look for foreign formatting directives.
if args_used < args_unused {
use super::format_foreign as foreign;
let fmt_str = &fmt.node.0[..];

// The set of foreign substitutions we've explained. This prevents spamming the user
// with `%d should be written as {}` over and over again.
let mut explained = HashSet::new();

// Used to ensure we only report translations for *one* kind of foreign format.
let mut found_foreign = false;

macro_rules! check_foreign {
($kind:ident) => {{
let mut show_doc_note = false;

for sub in foreign::$kind::iter_subs(fmt_str) {
let trn = match sub.translate() {
Some(trn) => trn,

// If it has no translation, don't call it out specifically.
None => continue,
};

let sub = String::from(sub.as_str());
if explained.contains(&sub) {
continue;
}
explained.insert(sub.clone());

if !found_foreign {
found_foreign = true;
show_doc_note = true;
}

diag.help(&format!("`{}` should be written as `{}`", sub, trn));
}

if show_doc_note {
diag.note(concat!(stringify!($kind), " formatting not supported; see \
the documentation for `std::fmt`"));
}
}};
}

check_foreign!(printf);
if !found_foreign {
check_foreign!(shell);
}
}

diag.emit();
}

cx.into_expr()
}
Loading