SCRIPT: "./x.py dist --stage 2"
RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --host=aarch64-apple-darwin --target=aarch64-apple-darwin --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
- SELECT_XCODE: /Applications/Xcode_12_beta.app
+ SELECT_XCODE: /Applications/Xcode_12.2.app
USE_XCODE_CLANG: 1
MACOSX_DEPLOYMENT_TARGET: 11.0
MACOSX_STD_DEPLOYMENT_TARGET: 11.0
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
- let matches_prefix = |prefix| {
- // Check for a path that ends with 'prefix*/src/lib.rs'
+ let matches_prefix = |prefix, filename| {
+ // Check for a path that ends with 'prefix*/src/<filename>'
let mut iter = path.components().rev();
- iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs")
+ iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
&& iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
&& iter
.next()
.map_or(false, |p| p.starts_with(prefix))
};
- if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl"))
- || (macro_name == sym::arrays && matches_prefix("js-sys"))
+ if (macro_name == sym::impl_macros
+ && matches_prefix("time-macros-impl", "lib.rs"))
+ || (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
return Some((*ident, *is_raw));
}
}
+
+ if macro_name == sym::tuple_from_req
+ && (matches_prefix("actix-web", "extract.rs")
+ || matches_prefix("actori-web", "extract.rs"))
+ {
+ let snippet = source_map.span_to_snippet(orig_span);
+ if snippet.as_deref() == Ok("$T") {
+ return Some((*ident, *is_raw));
+ }
+ }
}
}
}
+++ /dev/null
-use crate::pp::Breaks::{Consistent, Inconsistent};
-use crate::pp::{self, Breaks};
-
-use rustc_ast::attr;
-use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
-use rustc_ast::tokenstream::{TokenStream, TokenTree};
-use rustc_ast::util::classify;
-use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
-use rustc_ast::util::parser::{self, AssocOp, Fixity};
-use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
-use rustc_ast::{GenericArg, MacArgs};
-use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier};
-use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
-use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
-use rustc_span::edition::Edition;
-use rustc_span::source_map::{SourceMap, Spanned};
-use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
-use rustc_span::{BytePos, FileName, Span};
-
-use std::borrow::Cow;
-
-#[cfg(test)]
-mod tests;
-
-pub enum MacHeader<'a> {
- Path(&'a ast::Path),
- Keyword(&'static str),
-}
-
-pub enum AnnNode<'a> {
- Ident(&'a Ident),
- Name(&'a Symbol),
- Block(&'a ast::Block),
- Item(&'a ast::Item),
- SubItem(ast::NodeId),
- Expr(&'a ast::Expr),
- Pat(&'a ast::Pat),
- Crate(&'a ast::Crate),
-}
-
-pub trait PpAnn {
- fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
- fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
-}
-
-#[derive(Copy, Clone)]
-pub struct NoAnn;
-
-impl PpAnn for NoAnn {}
-
-pub struct Comments<'a> {
- sm: &'a SourceMap,
- comments: Vec<Comment>,
- current: usize,
-}
-
-impl<'a> Comments<'a> {
- pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
- let comments = gather_comments(sm, filename, input);
- Comments { sm, comments, current: 0 }
- }
-
- pub fn next(&self) -> Option<Comment> {
- self.comments.get(self.current).cloned()
- }
-
- pub fn trailing_comment(
- &mut self,
- span: rustc_span::Span,
- next_pos: Option<BytePos>,
- ) -> Option<Comment> {
- if let Some(cmnt) = self.next() {
- if cmnt.style != CommentStyle::Trailing {
- return None;
- }
- let span_line = self.sm.lookup_char_pos(span.hi());
- let comment_line = self.sm.lookup_char_pos(cmnt.pos);
- let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
- if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
- return Some(cmnt);
- }
- }
-
- None
- }
-}
-
-pub struct State<'a> {
- pub s: pp::Printer,
- comments: Option<Comments<'a>>,
- ann: &'a (dyn PpAnn + 'a),
- is_expanded: bool,
-}
-
-crate const INDENT_UNIT: usize = 4;
-
-/// Requires you to pass an input filename and reader so that
-/// it can scan the input text for comments to copy forward.
-pub fn print_crate<'a>(
- sm: &'a SourceMap,
- krate: &ast::Crate,
- filename: FileName,
- input: String,
- ann: &'a dyn PpAnn,
- is_expanded: bool,
- edition: Edition,
- has_injected_crate: bool,
-) -> String {
- let mut s = State {
- s: pp::mk_printer(),
- comments: Some(Comments::new(sm, filename, input)),
- ann,
- is_expanded,
- };
-
- if is_expanded && has_injected_crate {
- // We need to print `#![no_std]` (and its feature gate) so that
- // compiling pretty-printed source won't inject libstd again.
- // However, we don't want these attributes in the AST because
- // of the feature gate, so we fake them up here.
-
- // `#![feature(prelude_import)]`
- let pi_nested = attr::mk_nested_word_item(Ident::with_dummy_span(sym::prelude_import));
- let list = attr::mk_list_item(Ident::with_dummy_span(sym::feature), vec![pi_nested]);
- let fake_attr = attr::mk_attr_inner(list);
- s.print_attribute(&fake_attr);
-
- // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
- // root, so this is not needed, and actually breaks things.
- if edition == Edition::Edition2015 {
- // `#![no_std]`
- let no_std_meta = attr::mk_word_item(Ident::with_dummy_span(sym::no_std));
- let fake_attr = attr::mk_attr_inner(no_std_meta);
- s.print_attribute(&fake_attr);
- }
- }
-
- s.print_mod(&krate.module, &krate.attrs);
- s.print_remaining_comments();
- s.ann.post(&mut s, AnnNode::Crate(krate));
- s.s.eof()
-}
-
-pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
- let mut printer =
- State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false };
- f(&mut printer);
- printer.s.eof()
-}
-
-// This makes printed token streams look slightly nicer,
-// and also addresses some specific regressions described in #63896 and #73345.
-fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
- if let TokenTree::Token(token) = prev {
- if let token::DocComment(comment_kind, ..) = token.kind {
- return comment_kind != CommentKind::Line;
- }
- }
- match tt {
- TokenTree::Token(token) => match token.kind {
- token::Comma => false,
- _ => true,
- },
- TokenTree::Delimited(_, DelimToken::Paren, _) => match prev {
- TokenTree::Token(token) => match token.kind {
- token::Ident(_, _) => false,
- _ => true,
- },
- _ => true,
- },
- TokenTree::Delimited(_, DelimToken::Bracket, _) => match prev {
- TokenTree::Token(token) => match token.kind {
- token::Pound => false,
- _ => true,
- },
- _ => true,
- },
- TokenTree::Delimited(..) => true,
- }
-}
-
-fn binop_to_string(op: BinOpToken) -> &'static str {
- match op {
- token::Plus => "+",
- token::Minus => "-",
- token::Star => "*",
- token::Slash => "/",
- token::Percent => "%",
- token::Caret => "^",
- token::And => "&",
- token::Or => "|",
- token::Shl => "<<",
- token::Shr => ">>",
- }
-}
-
-fn doc_comment_to_string(
- comment_kind: CommentKind,
- attr_style: ast::AttrStyle,
- data: Symbol,
-) -> String {
- match (comment_kind, attr_style) {
- (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
- (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
- (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
- (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
- }
-}
-
-pub fn literal_to_string(lit: token::Lit) -> String {
- let token::Lit { kind, symbol, suffix } = lit;
- let mut out = match kind {
- token::Byte => format!("b'{}'", symbol),
- token::Char => format!("'{}'", symbol),
- token::Str => format!("\"{}\"", symbol),
- token::StrRaw(n) => {
- format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
- }
- token::ByteStr => format!("b\"{}\"", symbol),
- token::ByteStrRaw(n) => {
- format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
- }
- token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
- };
-
- if let Some(suffix) = suffix {
- out.push_str(&suffix.as_str())
- }
-
- out
-}
-
-/// Print the token kind precisely, without converting `$crate` into its respective crate name.
-pub fn token_kind_to_string(tok: &TokenKind) -> String {
- token_kind_to_string_ext(tok, None)
-}
-
-fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
- match *tok {
- token::Eq => "=".to_string(),
- token::Lt => "<".to_string(),
- token::Le => "<=".to_string(),
- token::EqEq => "==".to_string(),
- token::Ne => "!=".to_string(),
- token::Ge => ">=".to_string(),
- token::Gt => ">".to_string(),
- token::Not => "!".to_string(),
- token::Tilde => "~".to_string(),
- token::OrOr => "||".to_string(),
- token::AndAnd => "&&".to_string(),
- token::BinOp(op) => binop_to_string(op).to_string(),
- token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
-
- /* Structural symbols */
- token::At => "@".to_string(),
- token::Dot => ".".to_string(),
- token::DotDot => "..".to_string(),
- token::DotDotDot => "...".to_string(),
- token::DotDotEq => "..=".to_string(),
- token::Comma => ",".to_string(),
- token::Semi => ";".to_string(),
- token::Colon => ":".to_string(),
- token::ModSep => "::".to_string(),
- token::RArrow => "->".to_string(),
- token::LArrow => "<-".to_string(),
- token::FatArrow => "=>".to_string(),
- token::OpenDelim(token::Paren) => "(".to_string(),
- token::CloseDelim(token::Paren) => ")".to_string(),
- token::OpenDelim(token::Bracket) => "[".to_string(),
- token::CloseDelim(token::Bracket) => "]".to_string(),
- token::OpenDelim(token::Brace) => "{".to_string(),
- token::CloseDelim(token::Brace) => "}".to_string(),
- token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(),
- token::Pound => "#".to_string(),
- token::Dollar => "$".to_string(),
- token::Question => "?".to_string(),
- token::SingleQuote => "'".to_string(),
-
- /* Literals */
- token::Literal(lit) => literal_to_string(lit),
-
- /* Name components */
- token::Ident(s, is_raw) => IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string(),
- token::Lifetime(s) => s.to_string(),
-
- /* Other */
- token::DocComment(comment_kind, attr_style, data) => {
- doc_comment_to_string(comment_kind, attr_style, data)
- }
- token::Eof => "<eof>".to_string(),
-
- token::Interpolated(ref nt) => nonterminal_to_string(nt),
- }
-}
-
-/// Print the token precisely, without converting `$crate` into its respective crate name.
-pub fn token_to_string(token: &Token) -> String {
- token_to_string_ext(token, false)
-}
-
-fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
- let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
- token_kind_to_string_ext(&token.kind, convert_dollar_crate)
-}
-
-pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
- match *nt {
- token::NtExpr(ref e) => expr_to_string(e),
- token::NtMeta(ref e) => attr_item_to_string(e),
- token::NtTy(ref e) => ty_to_string(e),
- token::NtPath(ref e) => path_to_string(e),
- token::NtItem(ref e) => item_to_string(e),
- token::NtBlock(ref e) => block_to_string(e),
- token::NtStmt(ref e) => stmt_to_string(e),
- token::NtPat(ref e) => pat_to_string(e),
- token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
- token::NtLifetime(e) => e.to_string(),
- token::NtLiteral(ref e) => expr_to_string(e),
- token::NtTT(ref tree) => tt_to_string(tree),
- token::NtVis(ref e) => vis_to_string(e),
- }
-}
-
-pub fn ty_to_string(ty: &ast::Ty) -> String {
- to_string(|s| s.print_type(ty))
-}
-
-pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
- to_string(|s| s.print_type_bounds("", bounds))
-}
-
-pub fn pat_to_string(pat: &ast::Pat) -> String {
- to_string(|s| s.print_pat(pat))
-}
-
-pub fn expr_to_string(e: &ast::Expr) -> String {
- to_string(|s| s.print_expr(e))
-}
-
-pub fn tt_to_string(tt: &TokenTree) -> String {
- to_string(|s| s.print_tt(tt, false))
-}
-
-pub fn tts_to_string(tokens: &TokenStream) -> String {
- to_string(|s| s.print_tts(tokens, false))
-}
-
-pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
- to_string(|s| s.print_stmt(stmt))
-}
-
-pub fn item_to_string(i: &ast::Item) -> String {
- to_string(|s| s.print_item(i))
-}
-
-pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
- to_string(|s| s.print_generic_params(generic_params))
-}
-
-pub fn path_to_string(p: &ast::Path) -> String {
- to_string(|s| s.print_path(p, false, 0))
-}
-
-pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
- to_string(|s| s.print_path_segment(p, false))
-}
-
-pub fn vis_to_string(v: &ast::Visibility) -> String {
- to_string(|s| s.print_visibility(v))
-}
-
-fn block_to_string(blk: &ast::Block) -> String {
- to_string(|s| {
- // Containing cbox, will be closed by `print_block` at `}`.
- s.cbox(INDENT_UNIT);
- // Head-ibox, will be closed by `print_block` after `{`.
- s.ibox(0);
- s.print_block(blk)
- })
-}
-
-pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
- to_string(|s| s.print_meta_list_item(li))
-}
-
-fn attr_item_to_string(ai: &ast::AttrItem) -> String {
- to_string(|s| s.print_attr_item(ai, ai.path.span))
-}
-
-pub fn attribute_to_string(attr: &ast::Attribute) -> String {
- to_string(|s| s.print_attribute(attr))
-}
-
-pub fn param_to_string(arg: &ast::Param) -> String {
- to_string(|s| s.print_param(arg, false))
-}
-
-fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
- format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
-}
-
-impl std::ops::Deref for State<'_> {
- type Target = pp::Printer;
- fn deref(&self) -> &Self::Target {
- &self.s
- }
-}
-
-impl std::ops::DerefMut for State<'_> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.s
- }
-}
-
-pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
- fn comments(&mut self) -> &mut Option<Comments<'a>>;
- fn print_ident(&mut self, ident: Ident);
- fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
-
- fn strsep<T, F>(
- &mut self,
- sep: &'static str,
- space_before: bool,
- b: Breaks,
- elts: &[T],
- mut op: F,
- ) where
- F: FnMut(&mut Self, &T),
- {
- self.rbox(0, b);
- if let Some((first, rest)) = elts.split_first() {
- op(self, first);
- for elt in rest {
- if space_before {
- self.space();
- }
- self.word_space(sep);
- op(self, elt);
- }
- }
- self.end();
- }
-
- fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
- where
- F: FnMut(&mut Self, &T),
- {
- self.strsep(",", false, b, elts, op)
- }
-
- fn maybe_print_comment(&mut self, pos: BytePos) {
- while let Some(ref cmnt) = self.next_comment() {
- if cmnt.pos < pos {
- self.print_comment(cmnt);
- } else {
- break;
- }
- }
- }
-
- fn print_comment(&mut self, cmnt: &Comment) {
- match cmnt.style {
- CommentStyle::Mixed => {
- if !self.is_beginning_of_line() {
- self.zerobreak();
- }
- if let Some((last, lines)) = cmnt.lines.split_last() {
- self.ibox(0);
-
- for line in lines {
- self.word(line.clone());
- self.hardbreak()
- }
-
- self.word(last.clone());
- self.space();
-
- self.end();
- }
- self.zerobreak()
- }
- CommentStyle::Isolated => {
- self.hardbreak_if_not_bol();
- for line in &cmnt.lines {
- // Don't print empty lines because they will end up as trailing
- // whitespace.
- if !line.is_empty() {
- self.word(line.clone());
- }
- self.hardbreak();
- }
- }
- CommentStyle::Trailing => {
- if !self.is_beginning_of_line() {
- self.word(" ");
- }
- if cmnt.lines.len() == 1 {
- self.word(cmnt.lines[0].clone());
- self.hardbreak()
- } else {
- self.ibox(0);
- for line in &cmnt.lines {
- if !line.is_empty() {
- self.word(line.clone());
- }
- self.hardbreak();
- }
- self.end();
- }
- }
- CommentStyle::BlankLine => {
- // We need to do at least one, possibly two hardbreaks.
- let twice = match self.last_token() {
- pp::Token::String(s) => ";" == s,
- pp::Token::Begin(_) => true,
- pp::Token::End => true,
- _ => false,
- };
- if twice {
- self.hardbreak();
- }
- self.hardbreak();
- }
- }
- if let Some(cmnts) = self.comments() {
- cmnts.current += 1;
- }
- }
-
- fn next_comment(&mut self) -> Option<Comment> {
- self.comments().as_mut().and_then(|c| c.next())
- }
-
- fn print_literal(&mut self, lit: &ast::Lit) {
- self.maybe_print_comment(lit.span.lo());
- self.word(lit.token.to_string())
- }
-
- fn print_string(&mut self, st: &str, style: ast::StrStyle) {
- let st = match style {
- ast::StrStyle::Cooked => (format!("\"{}\"", st.escape_debug())),
- ast::StrStyle::Raw(n) => {
- format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
- }
- };
- self.word(st)
- }
-
- fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
- self.print_string(&sym.as_str(), style);
- }
-
- fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) {
- self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
- }
-
- fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) {
- self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
- }
-
- fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) {
- self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
- }
-
- fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
- self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
- }
-
- fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
- self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
- }
-
- fn print_either_attributes(
- &mut self,
- attrs: &[ast::Attribute],
- kind: ast::AttrStyle,
- is_inline: bool,
- trailing_hardbreak: bool,
- ) {
- let mut count = 0;
- for attr in attrs {
- if attr.style == kind {
- self.print_attribute_inline(attr, is_inline);
- if is_inline {
- self.nbsp();
- }
- count += 1;
- }
- }
- if count > 0 && trailing_hardbreak && !is_inline {
- self.hardbreak_if_not_bol();
- }
- }
-
- fn print_attribute(&mut self, attr: &ast::Attribute) {
- self.print_attribute_inline(attr, false)
- }
-
- fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
- if !is_inline {
- self.hardbreak_if_not_bol();
- }
- self.maybe_print_comment(attr.span.lo());
- match attr.kind {
- ast::AttrKind::Normal(ref item) => {
- match attr.style {
- ast::AttrStyle::Inner => self.word("#!["),
- ast::AttrStyle::Outer => self.word("#["),
- }
- self.print_attr_item(&item, attr.span);
- self.word("]");
- }
- ast::AttrKind::DocComment(comment_kind, data) => {
- self.word(doc_comment_to_string(comment_kind, attr.style, data));
- self.hardbreak()
- }
- }
- }
-
- fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
- self.ibox(0);
- match &item.args {
- MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
- Some(MacHeader::Path(&item.path)),
- false,
- None,
- delim.to_token(),
- tokens,
- true,
- span,
- ),
- MacArgs::Empty | MacArgs::Eq(..) => {
- self.print_path(&item.path, false, 0);
- if let MacArgs::Eq(_, tokens) = &item.args {
- self.space();
- self.word_space("=");
- self.print_tts(tokens, true);
- }
- }
- }
- self.end();
- }
-
- fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
- match item {
- ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
- ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
- }
- }
-
- fn print_meta_item(&mut self, item: &ast::MetaItem) {
- self.ibox(INDENT_UNIT);
- match item.kind {
- ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
- ast::MetaItemKind::NameValue(ref value) => {
- self.print_path(&item.path, false, 0);
- self.space();
- self.word_space("=");
- self.print_literal(value);
- }
- ast::MetaItemKind::List(ref items) => {
- self.print_path(&item.path, false, 0);
- self.popen();
- self.commasep(Consistent, &items[..], |s, i| s.print_meta_list_item(i));
- self.pclose();
- }
- }
- self.end();
- }
-
- /// This doesn't deserve to be called "pretty" printing, but it should be
- /// meaning-preserving. A quick hack that might help would be to look at the
- /// spans embedded in the TTs to decide where to put spaces and newlines.
- /// But it'd be better to parse these according to the grammar of the
- /// appropriate macro, transcribe back into the grammar we just parsed from,
- /// and then pretty-print the resulting AST nodes (so, e.g., we print
- /// expression arguments as expressions). It can be done! I think.
- fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
- match tt {
- TokenTree::Token(token) => {
- self.word(token_to_string_ext(&token, convert_dollar_crate));
- if let token::DocComment(..) = token.kind {
- self.hardbreak()
- }
- }
- TokenTree::Delimited(dspan, delim, tts) => {
- self.print_mac_common(
- None,
- false,
- None,
- *delim,
- tts,
- convert_dollar_crate,
- dspan.entire(),
- );
- }
- }
- }
-
- fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
- let mut iter = tts.trees().peekable();
- while let Some(tt) = iter.next() {
- self.print_tt(&tt, convert_dollar_crate);
- if let Some(next) = iter.peek() {
- if tt_prepend_space(next, &tt) {
- self.space();
- }
- }
- }
- }
-
- fn print_mac_common(
- &mut self,
- header: Option<MacHeader<'_>>,
- has_bang: bool,
- ident: Option<Ident>,
- delim: DelimToken,
- tts: &TokenStream,
- convert_dollar_crate: bool,
- span: Span,
- ) {
- if delim == DelimToken::Brace {
- self.cbox(INDENT_UNIT);
- }
- match header {
- Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
- Some(MacHeader::Keyword(kw)) => self.word(kw),
- None => {}
- }
- if has_bang {
- self.word("!");
- }
- if let Some(ident) = ident {
- self.nbsp();
- self.print_ident(ident);
- }
- match delim {
- DelimToken::Brace => {
- if header.is_some() || has_bang || ident.is_some() {
- self.nbsp();
- }
- self.word("{");
- if !tts.is_empty() {
- self.space();
- }
- }
- _ => self.word(token_kind_to_string(&token::OpenDelim(delim))),
- }
- self.ibox(0);
- self.print_tts(tts, convert_dollar_crate);
- self.end();
- match delim {
- DelimToken::Brace => self.bclose(span),
- _ => self.word(token_kind_to_string(&token::CloseDelim(delim))),
- }
- }
-
- fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
- self.maybe_print_comment(path.span.lo());
-
- for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
- if i > 0 {
- self.word("::")
- }
- self.print_path_segment(segment, colons_before_params);
- }
- }
-
- fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
- if segment.ident.name != kw::PathRoot {
- self.print_ident(segment.ident);
- if let Some(ref args) = segment.args {
- self.print_generic_args(args, colons_before_params);
- }
- }
- }
-
- fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
- let w = w.into();
- // Outer-box is consistent.
- self.cbox(INDENT_UNIT);
- // Head-box is inconsistent.
- self.ibox(w.len() + 1);
- // Keyword that starts the head.
- if !w.is_empty() {
- self.word_nbsp(w);
- }
- }
-
- fn bopen(&mut self) {
- self.word("{");
- self.end(); // Close the head-box.
- }
-
- fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
- self.maybe_print_comment(span.hi());
- self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
- self.word("}");
- if close_box {
- self.end(); // Close the outer-box.
- }
- }
-
- fn bclose(&mut self, span: rustc_span::Span) {
- self.bclose_maybe_open(span, true)
- }
-
- fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
- if !self.is_beginning_of_line() {
- self.break_offset(n, off)
- } else {
- if off != 0 && self.last_token().is_hardbreak_tok() {
- // We do something pretty sketchy here: tuck the nonzero
- // offset-adjustment we were going to deposit along with the
- // break into the previous hardbreak.
- self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
- }
- }
- }
-}
-
-impl<'a> PrintState<'a> for State<'a> {
- fn comments(&mut self) -> &mut Option<Comments<'a>> {
- &mut self.comments
- }
-
- fn print_ident(&mut self, ident: Ident) {
- self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
- self.ann.post(self, AnnNode::Ident(&ident))
- }
-
- fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
- if colons_before_params {
- self.s.word("::")
- }
-
- match *args {
- ast::GenericArgs::AngleBracketed(ref data) => {
- self.s.word("<");
- self.commasep(Inconsistent, &data.args, |s, arg| match arg {
- ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
- ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c),
- });
- self.s.word(">")
- }
-
- ast::GenericArgs::Parenthesized(ref data) => {
- self.s.word("(");
- self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
- self.s.word(")");
- self.print_fn_ret_ty(&data.output);
- }
- }
- }
-}
-
-impl<'a> State<'a> {
- // Synthesizes a comment that was not textually present in the original source
- // file.
- pub fn synth_comment(&mut self, text: String) {
- self.s.word("/*");
- self.s.space();
- self.s.word(text);
- self.s.space();
- self.s.word("*/")
- }
-
- crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
- where
- F: FnMut(&mut State<'_>, &T),
- G: FnMut(&T) -> rustc_span::Span,
- {
- self.rbox(0, b);
- let len = elts.len();
- let mut i = 0;
- for elt in elts {
- self.maybe_print_comment(get_span(elt).hi());
- op(self, elt);
- i += 1;
- if i < len {
- self.s.word(",");
- self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
- self.space_if_not_bol();
- }
- }
- self.end();
- }
-
- crate fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
- self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
- }
-
- pub fn print_mod(&mut self, _mod: &ast::Mod, attrs: &[ast::Attribute]) {
- self.print_inner_attributes(attrs);
- for item in &_mod.items {
- self.print_item(item);
- }
- }
-
- crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) {
- self.print_inner_attributes(attrs);
- for item in &nmod.items {
- self.print_foreign_item(item);
- }
- }
-
- pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
- if let Some(lt) = *lifetime {
- self.print_lifetime(lt);
- self.nbsp();
- }
- }
-
- pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) {
- self.print_ident(constraint.ident);
- self.s.space();
- match &constraint.kind {
- ast::AssocTyConstraintKind::Equality { ty } => {
- self.word_space("=");
- self.print_type(ty);
- }
- ast::AssocTyConstraintKind::Bound { bounds } => {
- self.print_type_bounds(":", &*bounds);
- }
- }
- }
-
- pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
- match generic_arg {
- GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
- GenericArg::Type(ty) => self.print_type(ty),
- GenericArg::Const(ct) => self.print_expr(&ct.value),
- }
- }
-
- pub fn print_type(&mut self, ty: &ast::Ty) {
- self.maybe_print_comment(ty.span.lo());
- self.ibox(0);
- match ty.kind {
- ast::TyKind::Slice(ref ty) => {
- self.s.word("[");
- self.print_type(ty);
- self.s.word("]");
- }
- ast::TyKind::Ptr(ref mt) => {
- self.s.word("*");
- self.print_mt(mt, true);
- }
- ast::TyKind::Rptr(ref lifetime, ref mt) => {
- self.s.word("&");
- self.print_opt_lifetime(lifetime);
- self.print_mt(mt, false);
- }
- ast::TyKind::Never => {
- self.s.word("!");
- }
- ast::TyKind::Tup(ref elts) => {
- self.popen();
- self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(ty));
- if elts.len() == 1 {
- self.s.word(",");
- }
- self.pclose();
- }
- ast::TyKind::Paren(ref typ) => {
- self.popen();
- self.print_type(typ);
- self.pclose();
- }
- ast::TyKind::BareFn(ref f) => {
- self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
- }
- ast::TyKind::Path(None, ref path) => {
- self.print_path(path, false, 0);
- }
- ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
- ast::TyKind::TraitObject(ref bounds, syntax) => {
- let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn" } else { "" };
- self.print_type_bounds(prefix, &bounds[..]);
- }
- ast::TyKind::ImplTrait(_, ref bounds) => {
- self.print_type_bounds("impl", &bounds[..]);
- }
- ast::TyKind::Array(ref ty, ref length) => {
- self.s.word("[");
- self.print_type(ty);
- self.s.word("; ");
- self.print_expr(&length.value);
- self.s.word("]");
- }
- ast::TyKind::Typeof(ref e) => {
- self.s.word("typeof(");
- self.print_expr(&e.value);
- self.s.word(")");
- }
- ast::TyKind::Infer => {
- self.s.word("_");
- }
- ast::TyKind::Err => {
- self.popen();
- self.s.word("/*ERROR*/");
- self.pclose();
- }
- ast::TyKind::ImplicitSelf => {
- self.s.word("Self");
- }
- ast::TyKind::MacCall(ref m) => {
- self.print_mac(m);
- }
- ast::TyKind::CVarArgs => {
- self.s.word("...");
- }
- }
- self.end();
- }
-
- crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
- let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
- self.ann.pre(self, AnnNode::SubItem(id));
- self.hardbreak_if_not_bol();
- self.maybe_print_comment(span.lo());
- self.print_outer_attributes(attrs);
- match kind {
- ast::ForeignItemKind::Fn(def, sig, gen, body) => {
- self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs);
- }
- ast::ForeignItemKind::Static(ty, mutbl, body) => {
- let def = ast::Defaultness::Final;
- self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
- }
- ast::ForeignItemKind::TyAlias(def, generics, bounds, ty) => {
- self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def);
- }
- ast::ForeignItemKind::MacCall(m) => {
- self.print_mac(m);
- if m.args.need_semicolon() {
- self.s.word(";");
- }
- }
- }
- self.ann.post(self, AnnNode::SubItem(id))
- }
-
- fn print_item_const(
- &mut self,
- ident: Ident,
- mutbl: Option<ast::Mutability>,
- ty: &ast::Ty,
- body: Option<&ast::Expr>,
- vis: &ast::Visibility,
- defaultness: ast::Defaultness,
- ) {
- self.head("");
- self.print_visibility(vis);
- self.print_defaultness(defaultness);
- let leading = match mutbl {
- None => "const",
- Some(ast::Mutability::Not) => "static",
- Some(ast::Mutability::Mut) => "static mut",
- };
- self.word_space(leading);
- self.print_ident(ident);
- self.word_space(":");
- self.print_type(ty);
- self.s.space();
- self.end(); // end the head-ibox
- if let Some(body) = body {
- self.word_space("=");
- self.print_expr(body);
- }
- self.s.word(";");
- self.end(); // end the outer cbox
- }
-
- fn print_associated_type(
- &mut self,
- ident: Ident,
- generics: &ast::Generics,
- bounds: &ast::GenericBounds,
- ty: Option<&ast::Ty>,
- vis: &ast::Visibility,
- defaultness: ast::Defaultness,
- ) {
- self.head("");
- self.print_visibility(vis);
- self.print_defaultness(defaultness);
- self.word_space("type");
- self.print_ident(ident);
- self.print_generic_params(&generics.params);
- self.print_type_bounds(":", bounds);
- self.print_where_clause(&generics.where_clause);
- if let Some(ty) = ty {
- self.s.space();
- self.word_space("=");
- self.print_type(ty);
- }
- self.s.word(";");
- self.end(); // end inner head-block
- self.end(); // end outer head-block
- }
-
- /// Pretty-prints an item.
- crate fn print_item(&mut self, item: &ast::Item) {
- self.hardbreak_if_not_bol();
- self.maybe_print_comment(item.span.lo());
- self.print_outer_attributes(&item.attrs);
- self.ann.pre(self, AnnNode::Item(item));
- match item.kind {
- ast::ItemKind::ExternCrate(orig_name) => {
- self.head(visibility_qualified(&item.vis, "extern crate"));
- if let Some(orig_name) = orig_name {
- self.print_name(orig_name);
- self.s.space();
- self.s.word("as");
- self.s.space();
- }
- self.print_ident(item.ident);
- self.s.word(";");
- self.end(); // end inner head-block
- self.end(); // end outer head-block
- }
- ast::ItemKind::Use(ref tree) => {
- self.head(visibility_qualified(&item.vis, "use"));
- self.print_use_tree(tree);
- self.s.word(";");
- self.end(); // end inner head-block
- self.end(); // end outer head-block
- }
- ast::ItemKind::Static(ref ty, mutbl, ref body) => {
- let def = ast::Defaultness::Final;
- self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def);
- }
- ast::ItemKind::Const(def, ref ty, ref body) => {
- self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def);
- }
- ast::ItemKind::Fn(def, ref sig, ref gen, ref body) => {
- let body = body.as_deref();
- self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
- }
- ast::ItemKind::Mod(ref _mod) => {
- self.head(to_string(|s| {
- s.print_visibility(&item.vis);
- s.print_unsafety(_mod.unsafety);
- s.word("mod");
- }));
- self.print_ident(item.ident);
-
- if _mod.inline || self.is_expanded {
- self.nbsp();
- self.bopen();
- self.print_mod(_mod, &item.attrs);
- self.bclose(item.span);
- } else {
- self.s.word(";");
- self.end(); // end inner head-block
- self.end(); // end outer head-block
- }
- }
- ast::ItemKind::ForeignMod(ref nmod) => {
- self.head(to_string(|s| {
- s.print_unsafety(nmod.unsafety);
- s.word("extern");
- }));
- if let Some(abi) = nmod.abi {
- self.print_literal(&abi.as_lit());
- self.nbsp();
- }
- self.bopen();
- self.print_foreign_mod(nmod, &item.attrs);
- self.bclose(item.span);
- }
- ast::ItemKind::GlobalAsm(ref ga) => {
- self.head(visibility_qualified(&item.vis, "global_asm!"));
- self.s.word(ga.asm.to_string());
- self.end();
- }
- ast::ItemKind::TyAlias(def, ref generics, ref bounds, ref ty) => {
- let ty = ty.as_deref();
- self.print_associated_type(item.ident, generics, bounds, ty, &item.vis, def);
- }
- ast::ItemKind::Enum(ref enum_definition, ref params) => {
- self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
- }
- ast::ItemKind::Struct(ref struct_def, ref generics) => {
- self.head(visibility_qualified(&item.vis, "struct"));
- self.print_struct(struct_def, generics, item.ident, item.span, true);
- }
- ast::ItemKind::Union(ref struct_def, ref generics) => {
- self.head(visibility_qualified(&item.vis, "union"));
- self.print_struct(struct_def, generics, item.ident, item.span, true);
- }
- ast::ItemKind::Impl {
- unsafety,
- polarity,
- defaultness,
- constness,
- ref generics,
- ref of_trait,
- ref self_ty,
- ref items,
- } => {
- self.head("");
- self.print_visibility(&item.vis);
- self.print_defaultness(defaultness);
- self.print_unsafety(unsafety);
- self.word_nbsp("impl");
- self.print_constness(constness);
-
- if !generics.params.is_empty() {
- self.print_generic_params(&generics.params);
- self.s.space();
- }
-
- if let ast::ImplPolarity::Negative(_) = polarity {
- self.s.word("!");
- }
-
- if let Some(ref t) = *of_trait {
- self.print_trait_ref(t);
- self.s.space();
- self.word_space("for");
- }
-
- self.print_type(self_ty);
- self.print_where_clause(&generics.where_clause);
-
- self.s.space();
- self.bopen();
- self.print_inner_attributes(&item.attrs);
- for impl_item in items {
- self.print_assoc_item(impl_item);
- }
- self.bclose(item.span);
- }
- ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => {
- self.head("");
- self.print_visibility(&item.vis);
- self.print_unsafety(unsafety);
- self.print_is_auto(is_auto);
- self.word_nbsp("trait");
- self.print_ident(item.ident);
- self.print_generic_params(&generics.params);
- let mut real_bounds = Vec::with_capacity(bounds.len());
- for b in bounds.iter() {
- if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
- self.s.space();
- self.word_space("for ?");
- self.print_trait_ref(&ptr.trait_ref);
- } else {
- real_bounds.push(b.clone());
- }
- }
- self.print_type_bounds(":", &real_bounds[..]);
- self.print_where_clause(&generics.where_clause);
- self.s.word(" ");
- self.bopen();
- self.print_inner_attributes(&item.attrs);
- for trait_item in trait_items {
- self.print_assoc_item(trait_item);
- }
- self.bclose(item.span);
- }
- ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
- self.head("");
- self.print_visibility(&item.vis);
- self.word_nbsp("trait");
- self.print_ident(item.ident);
- self.print_generic_params(&generics.params);
- let mut real_bounds = Vec::with_capacity(bounds.len());
- // FIXME(durka) this seems to be some quite outdated syntax
- for b in bounds.iter() {
- if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
- self.s.space();
- self.word_space("for ?");
- self.print_trait_ref(&ptr.trait_ref);
- } else {
- real_bounds.push(b.clone());
- }
- }
- self.nbsp();
- self.print_type_bounds("=", &real_bounds[..]);
- self.print_where_clause(&generics.where_clause);
- self.s.word(";");
- }
- ast::ItemKind::MacCall(ref mac) => {
- self.print_mac(mac);
- if mac.args.need_semicolon() {
- self.s.word(";");
- }
- }
- ast::ItemKind::MacroDef(ref macro_def) => {
- let (kw, has_bang) = if macro_def.macro_rules {
- ("macro_rules", true)
- } else {
- self.print_visibility(&item.vis);
- ("macro", false)
- };
- self.print_mac_common(
- Some(MacHeader::Keyword(kw)),
- has_bang,
- Some(item.ident),
- macro_def.body.delim(),
- ¯o_def.body.inner_tokens(),
- true,
- item.span,
- );
- }
- }
- self.ann.post(self, AnnNode::Item(item))
- }
-
- fn print_trait_ref(&mut self, t: &ast::TraitRef) {
- self.print_path(&t.path, false, 0)
- }
-
- fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
- if !generic_params.is_empty() {
- self.s.word("for");
- self.print_generic_params(generic_params);
- self.nbsp();
- }
- }
-
- fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
- self.print_formal_generic_params(&t.bound_generic_params);
- self.print_trait_ref(&t.trait_ref)
- }
-
- crate fn print_enum_def(
- &mut self,
- enum_definition: &ast::EnumDef,
- generics: &ast::Generics,
- ident: Ident,
- span: rustc_span::Span,
- visibility: &ast::Visibility,
- ) {
- self.head(visibility_qualified(visibility, "enum"));
- self.print_ident(ident);
- self.print_generic_params(&generics.params);
- self.print_where_clause(&generics.where_clause);
- self.s.space();
- self.print_variants(&enum_definition.variants, span)
- }
-
- crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
- self.bopen();
- for v in variants {
- self.space_if_not_bol();
- self.maybe_print_comment(v.span.lo());
- self.print_outer_attributes(&v.attrs);
- self.ibox(INDENT_UNIT);
- self.print_variant(v);
- self.s.word(",");
- self.end();
- self.maybe_print_trailing_comment(v.span, None);
- }
- self.bclose(span)
- }
-
- crate fn print_visibility(&mut self, vis: &ast::Visibility) {
- match vis.kind {
- ast::VisibilityKind::Public => self.word_nbsp("pub"),
- ast::VisibilityKind::Crate(sugar) => match sugar {
- ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
- ast::CrateSugar::JustCrate => self.word_nbsp("crate"),
- },
- ast::VisibilityKind::Restricted { ref path, .. } => {
- let path = to_string(|s| s.print_path(path, false, 0));
- if path == "self" || path == "super" {
- self.word_nbsp(format!("pub({})", path))
- } else {
- self.word_nbsp(format!("pub(in {})", path))
- }
- }
- ast::VisibilityKind::Inherited => {}
- }
- }
-
- crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
- if let ast::Defaultness::Default(_) = defaultness {
- self.word_nbsp("default");
- }
- }
-
- crate fn print_struct(
- &mut self,
- struct_def: &ast::VariantData,
- generics: &ast::Generics,
- ident: Ident,
- span: rustc_span::Span,
- print_finalizer: bool,
- ) {
- self.print_ident(ident);
- self.print_generic_params(&generics.params);
- match struct_def {
- ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
- if let ast::VariantData::Tuple(..) = struct_def {
- self.popen();
- self.commasep(Inconsistent, struct_def.fields(), |s, field| {
- s.maybe_print_comment(field.span.lo());
- s.print_outer_attributes(&field.attrs);
- s.print_visibility(&field.vis);
- s.print_type(&field.ty)
- });
- self.pclose();
- }
- self.print_where_clause(&generics.where_clause);
- if print_finalizer {
- self.s.word(";");
- }
- self.end();
- self.end(); // Close the outer-box.
- }
- ast::VariantData::Struct(..) => {
- self.print_where_clause(&generics.where_clause);
- self.nbsp();
- self.bopen();
- self.hardbreak_if_not_bol();
-
- for field in struct_def.fields() {
- self.hardbreak_if_not_bol();
- self.maybe_print_comment(field.span.lo());
- self.print_outer_attributes(&field.attrs);
- self.print_visibility(&field.vis);
- self.print_ident(field.ident.unwrap());
- self.word_nbsp(":");
- self.print_type(&field.ty);
- self.s.word(",");
- }
-
- self.bclose(span)
- }
- }
- }
-
- crate fn print_variant(&mut self, v: &ast::Variant) {
- self.head("");
- self.print_visibility(&v.vis);
- let generics = ast::Generics::default();
- self.print_struct(&v.data, &generics, v.ident, v.span, false);
- if let Some(ref d) = v.disr_expr {
- self.s.space();
- self.word_space("=");
- self.print_expr(&d.value)
- }
- }
-
- crate fn print_assoc_item(&mut self, item: &ast::AssocItem) {
- let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
- self.ann.pre(self, AnnNode::SubItem(id));
- self.hardbreak_if_not_bol();
- self.maybe_print_comment(span.lo());
- self.print_outer_attributes(attrs);
- match kind {
- ast::AssocItemKind::Fn(def, sig, gen, body) => {
- self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs);
- }
- ast::AssocItemKind::Const(def, ty, body) => {
- self.print_item_const(ident, None, ty, body.as_deref(), vis, *def);
- }
- ast::AssocItemKind::TyAlias(def, generics, bounds, ty) => {
- self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def);
- }
- ast::AssocItemKind::MacCall(m) => {
- self.print_mac(m);
- if m.args.need_semicolon() {
- self.s.word(";");
- }
- }
- }
- self.ann.post(self, AnnNode::SubItem(id))
- }
-
- crate fn print_stmt(&mut self, st: &ast::Stmt) {
- self.maybe_print_comment(st.span.lo());
- match st.kind {
- ast::StmtKind::Local(ref loc) => {
- self.print_outer_attributes(&loc.attrs);
- self.space_if_not_bol();
- self.ibox(INDENT_UNIT);
- self.word_nbsp("let");
-
- self.ibox(INDENT_UNIT);
- self.print_local_decl(loc);
- self.end();
- if let Some(ref init) = loc.init {
- self.nbsp();
- self.word_space("=");
- self.print_expr(init);
- }
- self.s.word(";");
- self.end();
- }
- ast::StmtKind::Item(ref item) => self.print_item(item),
- ast::StmtKind::Expr(ref expr) => {
- self.space_if_not_bol();
- self.print_expr_outer_attr_style(expr, false);
- if classify::expr_requires_semi_to_be_stmt(expr) {
- self.s.word(";");
- }
- }
- ast::StmtKind::Semi(ref expr) => {
- self.space_if_not_bol();
- self.print_expr_outer_attr_style(expr, false);
- self.s.word(";");
- }
- ast::StmtKind::Empty => {
- self.space_if_not_bol();
- self.s.word(";");
- }
- ast::StmtKind::MacCall(ref mac) => {
- self.space_if_not_bol();
- self.print_outer_attributes(&mac.attrs);
- self.print_mac(&mac.mac);
- if mac.style == ast::MacStmtStyle::Semicolon {
- self.s.word(";");
- }
- }
- }
- self.maybe_print_trailing_comment(st.span, None)
- }
-
- crate fn print_block(&mut self, blk: &ast::Block) {
- self.print_block_with_attrs(blk, &[])
- }
-
- crate fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
- self.print_block_maybe_unclosed(blk, &[], false)
- }
-
- crate fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
- self.print_block_maybe_unclosed(blk, attrs, true)
- }
-
- crate fn print_block_maybe_unclosed(
- &mut self,
- blk: &ast::Block,
- attrs: &[ast::Attribute],
- close_box: bool,
- ) {
- match blk.rules {
- BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
- BlockCheckMode::Default => (),
- }
- self.maybe_print_comment(blk.span.lo());
- self.ann.pre(self, AnnNode::Block(blk));
- self.bopen();
-
- self.print_inner_attributes(attrs);
-
- for (i, st) in blk.stmts.iter().enumerate() {
- match st.kind {
- ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
- self.maybe_print_comment(st.span.lo());
- self.space_if_not_bol();
- self.print_expr_outer_attr_style(expr, false);
- self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
- }
- _ => self.print_stmt(st),
- }
- }
-
- self.bclose_maybe_open(blk.span, close_box);
- self.ann.post(self, AnnNode::Block(blk))
- }
-
- /// Print a `let pat = scrutinee` expression.
- crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
- self.s.word("let ");
-
- self.print_pat(pat);
- self.s.space();
-
- self.word_space("=");
- self.print_expr_cond_paren(
- scrutinee,
- Self::cond_needs_par(scrutinee)
- || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()),
- )
- }
-
- fn print_else(&mut self, els: Option<&ast::Expr>) {
- if let Some(_else) = els {
- match _else.kind {
- // Another `else if` block.
- ast::ExprKind::If(ref i, ref then, ref e) => {
- self.cbox(INDENT_UNIT - 1);
- self.ibox(0);
- self.s.word(" else if ");
- self.print_expr_as_cond(i);
- self.s.space();
- self.print_block(then);
- self.print_else(e.as_deref())
- }
- // Final `else` block.
- ast::ExprKind::Block(ref b, _) => {
- self.cbox(INDENT_UNIT - 1);
- self.ibox(0);
- self.s.word(" else ");
- self.print_block(b)
- }
- // Constraints would be great here!
- _ => {
- panic!("print_if saw if with weird alternative");
- }
- }
- }
- }
-
- crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
- self.head("if");
-
- self.print_expr_as_cond(test);
- self.s.space();
-
- self.print_block(blk);
- self.print_else(elseopt)
- }
-
- crate fn print_mac(&mut self, m: &ast::MacCall) {
- self.print_mac_common(
- Some(MacHeader::Path(&m.path)),
- true,
- None,
- m.args.delim(),
- &m.args.inner_tokens(),
- true,
- m.span(),
- );
- }
-
- fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
- self.popen();
- self.commasep_exprs(Inconsistent, args);
- self.pclose()
- }
-
- crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
- self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
- }
-
- /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
- /// `if cond { ... }`.
- crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
- self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
- }
-
- /// Does `expr` need parenthesis when printed in a condition position?
- fn cond_needs_par(expr: &ast::Expr) -> bool {
- match expr.kind {
- // These cases need parens due to the parse error observed in #26461: `if return {}`
- // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
- ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Break(..) => true,
-
- _ => parser::contains_exterior_struct_lit(expr),
- }
- }
-
- /// Prints `expr` or `(expr)` when `needs_par` holds.
- fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
- if needs_par {
- self.popen();
- }
- self.print_expr(expr);
- if needs_par {
- self.pclose();
- }
- }
-
- fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>], attrs: &[ast::Attribute]) {
- self.ibox(INDENT_UNIT);
- self.s.word("[");
- self.print_inner_attributes_inline(attrs);
- self.commasep_exprs(Inconsistent, &exprs[..]);
- self.s.word("]");
- self.end();
- }
-
- fn print_expr_repeat(
- &mut self,
- element: &ast::Expr,
- count: &ast::AnonConst,
- attrs: &[ast::Attribute],
- ) {
- self.ibox(INDENT_UNIT);
- self.s.word("[");
- self.print_inner_attributes_inline(attrs);
- self.print_expr(element);
- self.word_space(";");
- self.print_expr(&count.value);
- self.s.word("]");
- self.end();
- }
-
- fn print_expr_struct(
- &mut self,
- path: &ast::Path,
- fields: &[ast::Field],
- wth: &Option<P<ast::Expr>>,
- attrs: &[ast::Attribute],
- ) {
- self.print_path(path, true, 0);
- self.s.word("{");
- self.print_inner_attributes_inline(attrs);
- self.commasep_cmnt(
- Consistent,
- &fields[..],
- |s, field| {
- s.print_outer_attributes(&field.attrs);
- s.ibox(INDENT_UNIT);
- if !field.is_shorthand {
- s.print_ident(field.ident);
- s.word_space(":");
- }
- s.print_expr(&field.expr);
- s.end();
- },
- |f| f.span,
- );
- match *wth {
- Some(ref expr) => {
- self.ibox(INDENT_UNIT);
- if !fields.is_empty() {
- self.s.word(",");
- self.s.space();
- }
- self.s.word("..");
- self.print_expr(expr);
- self.end();
- }
- _ => {
- if !fields.is_empty() {
- self.s.word(",")
- }
- }
- }
- self.s.word("}");
- }
-
- fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>], attrs: &[ast::Attribute]) {
- self.popen();
- self.print_inner_attributes_inline(attrs);
- self.commasep_exprs(Inconsistent, &exprs[..]);
- if exprs.len() == 1 {
- self.s.word(",");
- }
- self.pclose()
- }
-
- fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
- let prec = match func.kind {
- ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
- _ => parser::PREC_POSTFIX,
- };
-
- self.print_expr_maybe_paren(func, prec);
- self.print_call_post(args)
- }
-
- fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P<ast::Expr>]) {
- let base_args = &args[1..];
- self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
- self.s.word(".");
- self.print_ident(segment.ident);
- if let Some(ref args) = segment.args {
- self.print_generic_args(args, true);
- }
- self.print_call_post(base_args)
- }
-
- fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
- let assoc_op = AssocOp::from_ast_binop(op.node);
- let prec = assoc_op.precedence() as i8;
- let fixity = assoc_op.fixity();
-
- let (left_prec, right_prec) = match fixity {
- Fixity::Left => (prec, prec + 1),
- Fixity::Right => (prec + 1, prec),
- Fixity::None => (prec + 1, prec + 1),
- };
-
- let left_prec = match (&lhs.kind, op.node) {
- // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
- // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
- // of `(x as i32) < ...`. We need to convince it _not_ to do that.
- (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
- parser::PREC_FORCE_PAREN
- }
- // We are given `(let _ = a) OP b`.
- //
- // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
- // as the parser will interpret this as `(let _ = a) OP b`.
- //
- // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
- // parens are required since the parser would interpret `let a = b < c` as
- // `let a = (b < c)`. To achieve this, we force parens.
- (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
- parser::PREC_FORCE_PAREN
- }
- _ => left_prec,
- };
-
- self.print_expr_maybe_paren(lhs, left_prec);
- self.s.space();
- self.word_space(op.node.to_string());
- self.print_expr_maybe_paren(rhs, right_prec)
- }
-
- fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
- self.s.word(ast::UnOp::to_string(op));
- self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
- }
-
- fn print_expr_addr_of(
- &mut self,
- kind: ast::BorrowKind,
- mutability: ast::Mutability,
- expr: &ast::Expr,
- ) {
- self.s.word("&");
- match kind {
- ast::BorrowKind::Ref => self.print_mutability(mutability, false),
- ast::BorrowKind::Raw => {
- self.word_nbsp("raw");
- self.print_mutability(mutability, true);
- }
- }
- self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
- }
-
- pub fn print_expr(&mut self, expr: &ast::Expr) {
- self.print_expr_outer_attr_style(expr, true)
- }
-
- fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
- self.maybe_print_comment(expr.span.lo());
-
- let attrs = &expr.attrs;
- if is_inline {
- self.print_outer_attributes_inline(attrs);
- } else {
- self.print_outer_attributes(attrs);
- }
-
- self.ibox(INDENT_UNIT);
- self.ann.pre(self, AnnNode::Expr(expr));
- match expr.kind {
- ast::ExprKind::Box(ref expr) => {
- self.word_space("box");
- self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
- }
- ast::ExprKind::Array(ref exprs) => {
- self.print_expr_vec(&exprs[..], attrs);
- }
- ast::ExprKind::Repeat(ref element, ref count) => {
- self.print_expr_repeat(element, count, attrs);
- }
- ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
- self.print_expr_struct(path, &fields[..], wth, attrs);
- }
- ast::ExprKind::Tup(ref exprs) => {
- self.print_expr_tup(&exprs[..], attrs);
- }
- ast::ExprKind::Call(ref func, ref args) => {
- self.print_expr_call(func, &args[..]);
- }
- ast::ExprKind::MethodCall(ref segment, ref args, _) => {
- self.print_expr_method_call(segment, &args[..]);
- }
- ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
- self.print_expr_binary(op, lhs, rhs);
- }
- ast::ExprKind::Unary(op, ref expr) => {
- self.print_expr_unary(op, expr);
- }
- ast::ExprKind::AddrOf(k, m, ref expr) => {
- self.print_expr_addr_of(k, m, expr);
- }
- ast::ExprKind::Lit(ref lit) => {
- self.print_literal(lit);
- }
- ast::ExprKind::Cast(ref expr, ref ty) => {
- let prec = AssocOp::As.precedence() as i8;
- self.print_expr_maybe_paren(expr, prec);
- self.s.space();
- self.word_space("as");
- self.print_type(ty);
- }
- ast::ExprKind::Type(ref expr, ref ty) => {
- let prec = AssocOp::Colon.precedence() as i8;
- self.print_expr_maybe_paren(expr, prec);
- self.word_space(":");
- self.print_type(ty);
- }
- ast::ExprKind::Let(ref pat, ref scrutinee) => {
- self.print_let(pat, scrutinee);
- }
- ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
- self.print_if(test, blk, elseopt.as_deref())
- }
- ast::ExprKind::While(ref test, ref blk, opt_label) => {
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.word_space(":");
- }
- self.head("while");
- self.print_expr_as_cond(test);
- self.s.space();
- self.print_block_with_attrs(blk, attrs);
- }
- ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.word_space(":");
- }
- self.head("for");
- self.print_pat(pat);
- self.s.space();
- self.word_space("in");
- self.print_expr_as_cond(iter);
- self.s.space();
- self.print_block_with_attrs(blk, attrs);
- }
- ast::ExprKind::Loop(ref blk, opt_label) => {
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.word_space(":");
- }
- self.head("loop");
- self.s.space();
- self.print_block_with_attrs(blk, attrs);
- }
- ast::ExprKind::Match(ref expr, ref arms) => {
- self.cbox(INDENT_UNIT);
- self.ibox(INDENT_UNIT);
- self.word_nbsp("match");
- self.print_expr_as_cond(expr);
- self.s.space();
- self.bopen();
- self.print_inner_attributes_no_trailing_hardbreak(attrs);
- for arm in arms {
- self.print_arm(arm);
- }
- self.bclose(expr.span);
- }
- ast::ExprKind::Closure(
- capture_clause,
- asyncness,
- movability,
- ref decl,
- ref body,
- _,
- ) => {
- self.print_movability(movability);
- self.print_asyncness(asyncness);
- self.print_capture_clause(capture_clause);
-
- self.print_fn_params_and_ret(decl, true);
- self.s.space();
- self.print_expr(body);
- self.end(); // need to close a box
-
- // a box will be closed by print_expr, but we didn't want an overall
- // wrapper so we closed the corresponding opening. so create an
- // empty box to satisfy the close.
- self.ibox(0);
- }
- ast::ExprKind::Block(ref blk, opt_label) => {
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.word_space(":");
- }
- // containing cbox, will be closed by print-block at }
- self.cbox(INDENT_UNIT);
- // head-box, will be closed by print-block after {
- self.ibox(0);
- self.print_block_with_attrs(blk, attrs);
- }
- ast::ExprKind::Async(capture_clause, _, ref blk) => {
- self.word_nbsp("async");
- self.print_capture_clause(capture_clause);
- self.s.space();
- // cbox/ibox in analogy to the `ExprKind::Block` arm above
- self.cbox(INDENT_UNIT);
- self.ibox(0);
- self.print_block_with_attrs(blk, attrs);
- }
- ast::ExprKind::Await(ref expr) => {
- self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
- self.s.word(".await");
- }
- ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
- let prec = AssocOp::Assign.precedence() as i8;
- self.print_expr_maybe_paren(lhs, prec + 1);
- self.s.space();
- self.word_space("=");
- self.print_expr_maybe_paren(rhs, prec);
- }
- ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
- let prec = AssocOp::Assign.precedence() as i8;
- self.print_expr_maybe_paren(lhs, prec + 1);
- self.s.space();
- self.s.word(op.node.to_string());
- self.word_space("=");
- self.print_expr_maybe_paren(rhs, prec);
- }
- ast::ExprKind::Field(ref expr, ident) => {
- self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
- self.s.word(".");
- self.print_ident(ident);
- }
- ast::ExprKind::Index(ref expr, ref index) => {
- self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
- self.s.word("[");
- self.print_expr(index);
- self.s.word("]");
- }
- ast::ExprKind::Range(ref start, ref end, limits) => {
- // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
- // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
- // Here we use a fake precedence value so that any child with lower precedence than
- // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
- let fake_prec = AssocOp::LOr.precedence() as i8;
- if let Some(ref e) = *start {
- self.print_expr_maybe_paren(e, fake_prec);
- }
- if limits == ast::RangeLimits::HalfOpen {
- self.s.word("..");
- } else {
- self.s.word("..=");
- }
- if let Some(ref e) = *end {
- self.print_expr_maybe_paren(e, fake_prec);
- }
- }
- ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
- ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
- ast::ExprKind::Break(opt_label, ref opt_expr) => {
- self.s.word("break");
- self.s.space();
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.s.space();
- }
- if let Some(ref expr) = *opt_expr {
- self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
- self.s.space();
- }
- }
- ast::ExprKind::Continue(opt_label) => {
- self.s.word("continue");
- self.s.space();
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.s.space()
- }
- }
- ast::ExprKind::Ret(ref result) => {
- self.s.word("return");
- if let Some(ref expr) = *result {
- self.s.word(" ");
- self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
- }
- }
- ast::ExprKind::InlineAsm(ref a) => {
- enum AsmArg<'a> {
- Template(String),
- Operand(&'a InlineAsmOperand),
- Options(InlineAsmOptions),
- }
-
- let mut args = vec![];
- args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&a.template)));
- args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
- if !a.options.is_empty() {
- args.push(AsmArg::Options(a.options));
- }
-
- self.word("asm!");
- self.popen();
- self.commasep(Consistent, &args, |s, arg| match arg {
- AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
- AsmArg::Operand(op) => {
- let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r
- {
- InlineAsmRegOrRegClass::Reg(r) => {
- s.print_symbol(*r, ast::StrStyle::Cooked)
- }
- InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
- };
- match op {
- InlineAsmOperand::In { reg, expr } => {
- s.word("in");
- s.popen();
- print_reg_or_class(s, reg);
- s.pclose();
- s.space();
- s.print_expr(expr);
- }
- InlineAsmOperand::Out { reg, late, expr } => {
- s.word(if *late { "lateout" } else { "out" });
- s.popen();
- print_reg_or_class(s, reg);
- s.pclose();
- s.space();
- match expr {
- Some(expr) => s.print_expr(expr),
- None => s.word("_"),
- }
- }
- InlineAsmOperand::InOut { reg, late, expr } => {
- s.word(if *late { "inlateout" } else { "inout" });
- s.popen();
- print_reg_or_class(s, reg);
- s.pclose();
- s.space();
- s.print_expr(expr);
- }
- InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
- s.word(if *late { "inlateout" } else { "inout" });
- s.popen();
- print_reg_or_class(s, reg);
- s.pclose();
- s.space();
- s.print_expr(in_expr);
- s.space();
- s.word_space("=>");
- match out_expr {
- Some(out_expr) => s.print_expr(out_expr),
- None => s.word("_"),
- }
- }
- InlineAsmOperand::Const { expr } => {
- s.word("const");
- s.space();
- s.print_expr(expr);
- }
- InlineAsmOperand::Sym { expr } => {
- s.word("sym");
- s.space();
- s.print_expr(expr);
- }
- }
- }
- AsmArg::Options(opts) => {
- s.word("options");
- s.popen();
- let mut options = vec![];
- if opts.contains(InlineAsmOptions::PURE) {
- options.push("pure");
- }
- if opts.contains(InlineAsmOptions::NOMEM) {
- options.push("nomem");
- }
- if opts.contains(InlineAsmOptions::READONLY) {
- options.push("readonly");
- }
- if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
- options.push("preserves_flags");
- }
- if opts.contains(InlineAsmOptions::NORETURN) {
- options.push("noreturn");
- }
- if opts.contains(InlineAsmOptions::NOSTACK) {
- options.push("nostack");
- }
- if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
- options.push("att_syntax");
- }
- s.commasep(Inconsistent, &options, |s, &opt| {
- s.word(opt);
- });
- s.pclose();
- }
- });
- self.pclose();
- }
- ast::ExprKind::LlvmInlineAsm(ref a) => {
- self.s.word("llvm_asm!");
- self.popen();
- self.print_symbol(a.asm, a.asm_str_style);
- self.word_space(":");
-
- self.commasep(Inconsistent, &a.outputs, |s, out| {
- let constraint = out.constraint.as_str();
- let mut ch = constraint.chars();
- match ch.next() {
- Some('=') if out.is_rw => {
- s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked)
- }
- _ => s.print_string(&constraint, ast::StrStyle::Cooked),
- }
- s.popen();
- s.print_expr(&out.expr);
- s.pclose();
- });
- self.s.space();
- self.word_space(":");
-
- self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
- s.print_symbol(co, ast::StrStyle::Cooked);
- s.popen();
- s.print_expr(o);
- s.pclose();
- });
- self.s.space();
- self.word_space(":");
-
- self.commasep(Inconsistent, &a.clobbers, |s, &co| {
- s.print_symbol(co, ast::StrStyle::Cooked);
- });
-
- let mut options = vec![];
- if a.volatile {
- options.push("volatile");
- }
- if a.alignstack {
- options.push("alignstack");
- }
- if a.dialect == ast::LlvmAsmDialect::Intel {
- options.push("intel");
- }
-
- if !options.is_empty() {
- self.s.space();
- self.word_space(":");
- self.commasep(Inconsistent, &options, |s, &co| {
- s.print_string(co, ast::StrStyle::Cooked);
- });
- }
-
- self.pclose();
- }
- ast::ExprKind::MacCall(ref m) => self.print_mac(m),
- ast::ExprKind::Paren(ref e) => {
- self.popen();
- self.print_inner_attributes_inline(attrs);
- self.print_expr(e);
- self.pclose();
- }
- ast::ExprKind::Yield(ref e) => {
- self.s.word("yield");
-
- if let Some(ref expr) = *e {
- self.s.space();
- self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
- }
- }
- ast::ExprKind::Try(ref e) => {
- self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
- self.s.word("?")
- }
- ast::ExprKind::TryBlock(ref blk) => {
- self.head("try");
- self.s.space();
- self.print_block_with_attrs(blk, attrs)
- }
- ast::ExprKind::Err => {
- self.popen();
- self.s.word("/*ERROR*/");
- self.pclose()
- }
- }
- self.ann.post(self, AnnNode::Expr(expr));
- self.end();
- }
-
- crate fn print_local_decl(&mut self, loc: &ast::Local) {
- self.print_pat(&loc.pat);
- if let Some(ref ty) = loc.ty {
- self.word_space(":");
- self.print_type(ty);
- }
- }
-
- pub fn print_usize(&mut self, i: usize) {
- self.s.word(i.to_string())
- }
-
- crate fn print_name(&mut self, name: Symbol) {
- self.s.word(name.to_string());
- self.ann.post(self, AnnNode::Name(&name))
- }
-
- fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
- self.s.word("<");
- self.print_type(&qself.ty);
- if qself.position > 0 {
- self.s.space();
- self.word_space("as");
- let depth = path.segments.len() - qself.position;
- self.print_path(path, false, depth);
- }
- self.s.word(">");
- self.s.word("::");
- let item_segment = path.segments.last().unwrap();
- self.print_ident(item_segment.ident);
- if let Some(ref args) = item_segment.args {
- self.print_generic_args(args, colons_before_params)
- }
- }
-
- crate fn print_pat(&mut self, pat: &ast::Pat) {
- self.maybe_print_comment(pat.span.lo());
- self.ann.pre(self, AnnNode::Pat(pat));
- /* Pat isn't normalized, but the beauty of it
- is that it doesn't matter */
- match pat.kind {
- PatKind::Wild => self.s.word("_"),
- PatKind::Ident(binding_mode, ident, ref sub) => {
- match binding_mode {
- ast::BindingMode::ByRef(mutbl) => {
- self.word_nbsp("ref");
- self.print_mutability(mutbl, false);
- }
- ast::BindingMode::ByValue(ast::Mutability::Not) => {}
- ast::BindingMode::ByValue(ast::Mutability::Mut) => {
- self.word_nbsp("mut");
- }
- }
- self.print_ident(ident);
- if let Some(ref p) = *sub {
- self.s.space();
- self.s.word_space("@");
- self.print_pat(p);
- }
- }
- PatKind::TupleStruct(ref path, ref elts) => {
- self.print_path(path, true, 0);
- self.popen();
- self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
- self.pclose();
- }
- PatKind::Or(ref pats) => {
- self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
- }
- PatKind::Path(None, ref path) => {
- self.print_path(path, true, 0);
- }
- PatKind::Path(Some(ref qself), ref path) => {
- self.print_qpath(path, qself, false);
- }
- PatKind::Struct(ref path, ref fields, etc) => {
- self.print_path(path, true, 0);
- self.nbsp();
- self.word_space("{");
- self.commasep_cmnt(
- Consistent,
- &fields[..],
- |s, f| {
- s.cbox(INDENT_UNIT);
- if !f.is_shorthand {
- s.print_ident(f.ident);
- s.word_nbsp(":");
- }
- s.print_pat(&f.pat);
- s.end();
- },
- |f| f.pat.span,
- );
- if etc {
- if !fields.is_empty() {
- self.word_space(",");
- }
- self.s.word("..");
- }
- self.s.space();
- self.s.word("}");
- }
- PatKind::Tuple(ref elts) => {
- self.popen();
- self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
- if elts.len() == 1 {
- self.s.word(",");
- }
- self.pclose();
- }
- PatKind::Box(ref inner) => {
- self.s.word("box ");
- self.print_pat(inner);
- }
- PatKind::Ref(ref inner, mutbl) => {
- self.s.word("&");
- if mutbl == ast::Mutability::Mut {
- self.s.word("mut ");
- }
- self.print_pat(inner);
- }
- PatKind::Lit(ref e) => self.print_expr(&**e),
- PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
- if let Some(e) = begin {
- self.print_expr(e);
- self.s.space();
- }
- match *end_kind {
- RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
- RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
- RangeEnd::Excluded => self.s.word(".."),
- }
- if let Some(e) = end {
- self.print_expr(e);
- }
- }
- PatKind::Slice(ref elts) => {
- self.s.word("[");
- self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
- self.s.word("]");
- }
- PatKind::Rest => self.s.word(".."),
- PatKind::Paren(ref inner) => {
- self.popen();
- self.print_pat(inner);
- self.pclose();
- }
- PatKind::MacCall(ref m) => self.print_mac(m),
- }
- self.ann.post(self, AnnNode::Pat(pat))
- }
-
- fn print_arm(&mut self, arm: &ast::Arm) {
- // Note, I have no idea why this check is necessary, but here it is.
- if arm.attrs.is_empty() {
- self.s.space();
- }
- self.cbox(INDENT_UNIT);
- self.ibox(0);
- self.maybe_print_comment(arm.pat.span.lo());
- self.print_outer_attributes(&arm.attrs);
- self.print_pat(&arm.pat);
- self.s.space();
- if let Some(ref e) = arm.guard {
- self.word_space("if");
- self.print_expr(e);
- self.s.space();
- }
- self.word_space("=>");
-
- match arm.body.kind {
- ast::ExprKind::Block(ref blk, opt_label) => {
- if let Some(label) = opt_label {
- self.print_ident(label.ident);
- self.word_space(":");
- }
-
- // The block will close the pattern's ibox.
- self.print_block_unclosed_indent(blk);
-
- // If it is a user-provided unsafe block, print a comma after it.
- if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
- self.s.word(",");
- }
- }
- _ => {
- self.end(); // Close the ibox for the pattern.
- self.print_expr(&arm.body);
- self.s.word(",");
- }
- }
- self.end(); // Close enclosing cbox.
- }
-
- fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
- match explicit_self.node {
- SelfKind::Value(m) => {
- self.print_mutability(m, false);
- self.s.word("self")
- }
- SelfKind::Region(ref lt, m) => {
- self.s.word("&");
- self.print_opt_lifetime(lt);
- self.print_mutability(m, false);
- self.s.word("self")
- }
- SelfKind::Explicit(ref typ, m) => {
- self.print_mutability(m, false);
- self.s.word("self");
- self.word_space(":");
- self.print_type(typ)
- }
- }
- }
-
- fn print_fn_full(
- &mut self,
- sig: &ast::FnSig,
- name: Ident,
- generics: &ast::Generics,
- vis: &ast::Visibility,
- defaultness: ast::Defaultness,
- body: Option<&ast::Block>,
- attrs: &[ast::Attribute],
- ) {
- if body.is_some() {
- self.head("");
- }
- self.print_visibility(vis);
- self.print_defaultness(defaultness);
- self.print_fn(&sig.decl, sig.header, Some(name), generics);
- if let Some(body) = body {
- self.nbsp();
- self.print_block_with_attrs(body, attrs);
- } else {
- self.s.word(";");
- }
- }
-
- crate fn print_fn(
- &mut self,
- decl: &ast::FnDecl,
- header: ast::FnHeader,
- name: Option<Ident>,
- generics: &ast::Generics,
- ) {
- self.print_fn_header_info(header);
- if let Some(name) = name {
- self.nbsp();
- self.print_ident(name);
- }
- self.print_generic_params(&generics.params);
- self.print_fn_params_and_ret(decl, false);
- self.print_where_clause(&generics.where_clause)
- }
-
- crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
- let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") };
- self.word(open);
- self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure));
- self.word(close);
- self.print_fn_ret_ty(&decl.output)
- }
-
- crate fn print_movability(&mut self, movability: ast::Movability) {
- match movability {
- ast::Movability::Static => self.word_space("static"),
- ast::Movability::Movable => {}
- }
- }
-
- crate fn print_asyncness(&mut self, asyncness: ast::Async) {
- if asyncness.is_async() {
- self.word_nbsp("async");
- }
- }
-
- crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
- match capture_clause {
- ast::CaptureBy::Value => self.word_space("move"),
- ast::CaptureBy::Ref => {}
- }
- }
-
- pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) {
- if !bounds.is_empty() {
- self.s.word(prefix);
- let mut first = true;
- for bound in bounds {
- if !(first && prefix.is_empty()) {
- self.nbsp();
- }
- if first {
- first = false;
- } else {
- self.word_space("+");
- }
-
- match bound {
- GenericBound::Trait(tref, modifier) => {
- if modifier == &TraitBoundModifier::Maybe {
- self.s.word("?");
- }
- self.print_poly_trait_ref(tref);
- }
- GenericBound::Outlives(lt) => self.print_lifetime(*lt),
- }
- }
- }
- }
-
- crate fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
- self.print_name(lifetime.ident.name)
- }
-
- crate fn print_lifetime_bounds(
- &mut self,
- lifetime: ast::Lifetime,
- bounds: &ast::GenericBounds,
- ) {
- self.print_lifetime(lifetime);
- if !bounds.is_empty() {
- self.s.word(": ");
- for (i, bound) in bounds.iter().enumerate() {
- if i != 0 {
- self.s.word(" + ");
- }
- match bound {
- ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
- _ => panic!(),
- }
- }
- }
- }
-
- crate fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
- if generic_params.is_empty() {
- return;
- }
-
- self.s.word("<");
-
- self.commasep(Inconsistent, &generic_params, |s, param| {
- s.print_outer_attributes_inline(¶m.attrs);
-
- match param.kind {
- ast::GenericParamKind::Lifetime => {
- let lt = ast::Lifetime { id: param.id, ident: param.ident };
- s.print_lifetime_bounds(lt, ¶m.bounds)
- }
- ast::GenericParamKind::Type { ref default } => {
- s.print_ident(param.ident);
- s.print_type_bounds(":", ¶m.bounds);
- if let Some(ref default) = default {
- s.s.space();
- s.word_space("=");
- s.print_type(default)
- }
- }
- ast::GenericParamKind::Const { ref ty, kw_span: _ } => {
- s.word_space("const");
- s.print_ident(param.ident);
- s.s.space();
- s.word_space(":");
- s.print_type(ty);
- s.print_type_bounds(":", ¶m.bounds)
- }
- }
- });
-
- self.s.word(">");
- }
-
- crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
- if where_clause.predicates.is_empty() && !where_clause.has_where_token {
- return;
- }
-
- self.s.space();
- self.word_space("where");
-
- for (i, predicate) in where_clause.predicates.iter().enumerate() {
- if i != 0 {
- self.word_space(",");
- }
-
- match *predicate {
- ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
- ref bound_generic_params,
- ref bounded_ty,
- ref bounds,
- ..
- }) => {
- self.print_formal_generic_params(bound_generic_params);
- self.print_type(bounded_ty);
- self.print_type_bounds(":", bounds);
- }
- ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
- ref lifetime,
- ref bounds,
- ..
- }) => {
- self.print_lifetime_bounds(*lifetime, bounds);
- }
- ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
- ref lhs_ty,
- ref rhs_ty,
- ..
- }) => {
- self.print_type(lhs_ty);
- self.s.space();
- self.word_space("=");
- self.print_type(rhs_ty);
- }
- }
- }
- }
-
- crate fn print_use_tree(&mut self, tree: &ast::UseTree) {
- match tree.kind {
- ast::UseTreeKind::Simple(rename, ..) => {
- self.print_path(&tree.prefix, false, 0);
- if let Some(rename) = rename {
- self.s.space();
- self.word_space("as");
- self.print_ident(rename);
- }
- }
- ast::UseTreeKind::Glob => {
- if !tree.prefix.segments.is_empty() {
- self.print_path(&tree.prefix, false, 0);
- self.s.word("::");
- }
- self.s.word("*");
- }
- ast::UseTreeKind::Nested(ref items) => {
- if tree.prefix.segments.is_empty() {
- self.s.word("{");
- } else {
- self.print_path(&tree.prefix, false, 0);
- self.s.word("::{");
- }
- self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| {
- this.print_use_tree(tree)
- });
- self.s.word("}");
- }
- }
- }
-
- pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
- match mutbl {
- ast::Mutability::Mut => self.word_nbsp("mut"),
- ast::Mutability::Not => {
- if print_const {
- self.word_nbsp("const");
- }
- }
- }
- }
-
- crate fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
- self.print_mutability(mt.mutbl, print_const);
- self.print_type(&mt.ty)
- }
-
- crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
- self.ibox(INDENT_UNIT);
-
- self.print_outer_attributes_inline(&input.attrs);
-
- match input.ty.kind {
- ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
- _ => {
- if let Some(eself) = input.to_self() {
- self.print_explicit_self(&eself);
- } else {
- let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
- ident.name == kw::Invalid
- } else {
- false
- };
- if !invalid {
- self.print_pat(&input.pat);
- self.s.word(":");
- self.s.space();
- }
- self.print_type(&input.ty);
- }
- }
- }
- self.end();
- }
-
- crate fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
- if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
- self.space_if_not_bol();
- self.ibox(INDENT_UNIT);
- self.word_space("->");
- self.print_type(ty);
- self.end();
- self.maybe_print_comment(ty.span.lo());
- }
- }
-
- crate fn print_ty_fn(
- &mut self,
- ext: ast::Extern,
- unsafety: ast::Unsafe,
- decl: &ast::FnDecl,
- name: Option<Ident>,
- generic_params: &[ast::GenericParam],
- ) {
- self.ibox(INDENT_UNIT);
- if !generic_params.is_empty() {
- self.s.word("for");
- self.print_generic_params(generic_params);
- }
- let generics = ast::Generics {
- params: Vec::new(),
- where_clause: ast::WhereClause {
- has_where_token: false,
- predicates: Vec::new(),
- span: rustc_span::DUMMY_SP,
- },
- span: rustc_span::DUMMY_SP,
- };
- let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
- self.print_fn(decl, header, name, &generics);
- self.end();
- }
-
- crate fn maybe_print_trailing_comment(
- &mut self,
- span: rustc_span::Span,
- next_pos: Option<BytePos>,
- ) {
- if let Some(cmnts) = self.comments() {
- if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
- self.print_comment(&cmnt);
- }
- }
- }
-
- crate fn print_remaining_comments(&mut self) {
- // If there aren't any remaining comments, then we need to manually
- // make sure there is a line break at the end.
- if self.next_comment().is_none() {
- self.s.hardbreak();
- }
- while let Some(ref cmnt) = self.next_comment() {
- self.print_comment(cmnt);
- }
- }
-
- crate fn print_fn_header_info(&mut self, header: ast::FnHeader) {
- self.print_constness(header.constness);
- self.print_asyncness(header.asyncness);
- self.print_unsafety(header.unsafety);
-
- match header.ext {
- ast::Extern::None => {}
- ast::Extern::Implicit => {
- self.word_nbsp("extern");
- }
- ast::Extern::Explicit(abi) => {
- self.word_nbsp("extern");
- self.print_literal(&abi.as_lit());
- self.nbsp();
- }
- }
-
- self.s.word("fn")
- }
-
- crate fn print_unsafety(&mut self, s: ast::Unsafe) {
- match s {
- ast::Unsafe::No => {}
- ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"),
- }
- }
-
- crate fn print_constness(&mut self, s: ast::Const) {
- match s {
- ast::Const::No => {}
- ast::Const::Yes(_) => self.word_nbsp("const"),
- }
- }
-
- crate fn print_is_auto(&mut self, s: ast::IsAuto) {
- match s {
- ast::IsAuto::Yes => self.word_nbsp("auto"),
- ast::IsAuto::No => {}
- }
- }
-}
--- /dev/null
+#[cfg(test)]
+mod tests;
+
+pub mod state;
+pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State};
+
+use rustc_ast as ast;
+use rustc_ast::token::{Nonterminal, Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+
+pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String {
+ let state = State::without_insert_extra_parens();
+ state.nonterminal_to_string(nt)
+}
+
+pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
+ State::new().nonterminal_to_string(nt)
+}
+
+/// Print the token kind precisely, without converting `$crate` into its respective crate name.
+pub fn token_kind_to_string(tok: &TokenKind) -> String {
+ State::new().token_kind_to_string(tok)
+}
+
+/// Print the token precisely, without converting `$crate` into its respective crate name.
+pub fn token_to_string(token: &Token) -> String {
+ State::new().token_to_string(token)
+}
+
+pub fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
+ State::new().token_to_string_ext(token, convert_dollar_crate)
+}
+
+pub fn ty_to_string(ty: &ast::Ty) -> String {
+ State::new().ty_to_string(ty)
+}
+
+pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
+ State::new().bounds_to_string(bounds)
+}
+
+pub fn pat_to_string(pat: &ast::Pat) -> String {
+ State::new().pat_to_string(pat)
+}
+
+pub fn expr_to_string(e: &ast::Expr) -> String {
+ State::new().expr_to_string(e)
+}
+
+pub fn tt_to_string(tt: &TokenTree) -> String {
+ State::new().tt_to_string(tt)
+}
+
+pub fn tts_to_string(tokens: &TokenStream) -> String {
+ State::new().tts_to_string(tokens)
+}
+
+pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
+ State::new().stmt_to_string(stmt)
+}
+
+pub fn item_to_string(i: &ast::Item) -> String {
+ State::new().item_to_string(i)
+}
+
+pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
+ State::new().generic_params_to_string(generic_params)
+}
+
+pub fn path_to_string(p: &ast::Path) -> String {
+ State::new().path_to_string(p)
+}
+
+pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
+ State::new().path_segment_to_string(p)
+}
+
+pub fn vis_to_string(v: &ast::Visibility) -> String {
+ State::new().vis_to_string(v)
+}
+
+pub fn block_to_string(blk: &ast::Block) -> String {
+ State::new().block_to_string(blk)
+}
+
+pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
+ State::new().meta_list_item_to_string(li)
+}
+
+pub fn attr_item_to_string(ai: &ast::AttrItem) -> String {
+ State::new().attr_item_to_string(ai)
+}
+
+pub fn attribute_to_string(attr: &ast::Attribute) -> String {
+ State::new().attribute_to_string(attr)
+}
+
+pub fn param_to_string(arg: &ast::Param) -> String {
+ State::new().param_to_string(arg)
+}
+
+pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
+ State::new().to_string(f)
+}
--- /dev/null
+use crate::pp::Breaks::{Consistent, Inconsistent};
+use crate::pp::{self, Breaks};
+
+use rustc_ast::attr;
+use rustc_ast::ptr::P;
+use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::util::classify;
+use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
+use rustc_ast::util::parser::{self, AssocOp, Fixity};
+use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
+use rustc_ast::{GenericArg, MacArgs};
+use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier};
+use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
+use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
+use rustc_span::{BytePos, FileName, Span};
+
+use std::borrow::Cow;
+
+pub enum MacHeader<'a> {
+ Path(&'a ast::Path),
+ Keyword(&'static str),
+}
+
+pub enum AnnNode<'a> {
+ Ident(&'a Ident),
+ Name(&'a Symbol),
+ Block(&'a ast::Block),
+ Item(&'a ast::Item),
+ SubItem(ast::NodeId),
+ Expr(&'a ast::Expr),
+ Pat(&'a ast::Pat),
+ Crate(&'a ast::Crate),
+}
+
+pub trait PpAnn {
+ fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+ fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+}
+
+#[derive(Copy, Clone)]
+pub struct NoAnn;
+
+impl PpAnn for NoAnn {}
+
+pub struct Comments<'a> {
+ sm: &'a SourceMap,
+ comments: Vec<Comment>,
+ current: usize,
+}
+
+impl<'a> Comments<'a> {
+ pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
+ let comments = gather_comments(sm, filename, input);
+ Comments { sm, comments, current: 0 }
+ }
+
+ pub fn next(&self) -> Option<Comment> {
+ self.comments.get(self.current).cloned()
+ }
+
+ pub fn trailing_comment(
+ &mut self,
+ span: rustc_span::Span,
+ next_pos: Option<BytePos>,
+ ) -> Option<Comment> {
+ if let Some(cmnt) = self.next() {
+ if cmnt.style != CommentStyle::Trailing {
+ return None;
+ }
+ let span_line = self.sm.lookup_char_pos(span.hi());
+ let comment_line = self.sm.lookup_char_pos(cmnt.pos);
+ let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
+ if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
+ return Some(cmnt);
+ }
+ }
+
+ None
+ }
+}
+
+pub struct State<'a> {
+ pub s: pp::Printer,
+ comments: Option<Comments<'a>>,
+ ann: &'a (dyn PpAnn + 'a),
+ is_expanded: bool,
+ // If `true`, additional parenthesis (separate from `ExprKind::Paren`)
+ // are inserted to ensure that proper precedence is preserved
+ // in the pretty-printed output.
+ //
+ // This is usually `true`, except when performing the pretty-print/reparse
+ // check in `nt_to_tokenstream`
+ insert_extra_parens: bool,
+}
+
+crate const INDENT_UNIT: usize = 4;
+
+/// Requires you to pass an input filename and reader so that
+/// it can scan the input text for comments to copy forward.
+pub fn print_crate<'a>(
+ sm: &'a SourceMap,
+ krate: &ast::Crate,
+ filename: FileName,
+ input: String,
+ ann: &'a dyn PpAnn,
+ is_expanded: bool,
+ edition: Edition,
+ has_injected_crate: bool,
+) -> String {
+ let mut s = State {
+ s: pp::mk_printer(),
+ comments: Some(Comments::new(sm, filename, input)),
+ ann,
+ is_expanded,
+ insert_extra_parens: true,
+ };
+
+ if is_expanded && has_injected_crate {
+ // We need to print `#![no_std]` (and its feature gate) so that
+ // compiling pretty-printed source won't inject libstd again.
+ // However, we don't want these attributes in the AST because
+ // of the feature gate, so we fake them up here.
+
+ // `#![feature(prelude_import)]`
+ let pi_nested = attr::mk_nested_word_item(Ident::with_dummy_span(sym::prelude_import));
+ let list = attr::mk_list_item(Ident::with_dummy_span(sym::feature), vec![pi_nested]);
+ let fake_attr = attr::mk_attr_inner(list);
+ s.print_attribute(&fake_attr);
+
+ // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
+ // root, so this is not needed, and actually breaks things.
+ if edition == Edition::Edition2015 {
+ // `#![no_std]`
+ let no_std_meta = attr::mk_word_item(Ident::with_dummy_span(sym::no_std));
+ let fake_attr = attr::mk_attr_inner(no_std_meta);
+ s.print_attribute(&fake_attr);
+ }
+ }
+
+ s.print_mod(&krate.module, &krate.attrs);
+ s.print_remaining_comments();
+ s.ann.post(&mut s, AnnNode::Crate(krate));
+ s.s.eof()
+}
+
+// This makes printed token streams look slightly nicer,
+// and also addresses some specific regressions described in #63896 and #73345.
+fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
+ if let TokenTree::Token(token) = prev {
+ if let token::DocComment(comment_kind, ..) = token.kind {
+ return comment_kind != CommentKind::Line;
+ }
+ }
+ match tt {
+ TokenTree::Token(token) => match token.kind {
+ token::Comma => false,
+ _ => true,
+ },
+ TokenTree::Delimited(_, DelimToken::Paren, _) => match prev {
+ TokenTree::Token(token) => match token.kind {
+ token::Ident(_, _) => false,
+ _ => true,
+ },
+ _ => true,
+ },
+ TokenTree::Delimited(_, DelimToken::Bracket, _) => match prev {
+ TokenTree::Token(token) => match token.kind {
+ token::Pound => false,
+ _ => true,
+ },
+ _ => true,
+ },
+ TokenTree::Delimited(..) => true,
+ }
+}
+
+fn binop_to_string(op: BinOpToken) -> &'static str {
+ match op {
+ token::Plus => "+",
+ token::Minus => "-",
+ token::Star => "*",
+ token::Slash => "/",
+ token::Percent => "%",
+ token::Caret => "^",
+ token::And => "&",
+ token::Or => "|",
+ token::Shl => "<<",
+ token::Shr => ">>",
+ }
+}
+
+fn doc_comment_to_string(
+ comment_kind: CommentKind,
+ attr_style: ast::AttrStyle,
+ data: Symbol,
+) -> String {
+ match (comment_kind, attr_style) {
+ (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
+ (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
+ (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
+ (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
+ }
+}
+
+pub fn literal_to_string(lit: token::Lit) -> String {
+ let token::Lit { kind, symbol, suffix } = lit;
+ let mut out = match kind {
+ token::Byte => format!("b'{}'", symbol),
+ token::Char => format!("'{}'", symbol),
+ token::Str => format!("\"{}\"", symbol),
+ token::StrRaw(n) => {
+ format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+ }
+ token::ByteStr => format!("b\"{}\"", symbol),
+ token::ByteStrRaw(n) => {
+ format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+ }
+ token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
+ };
+
+ if let Some(suffix) = suffix {
+ out.push_str(&suffix.as_str())
+ }
+
+ out
+}
+
+fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
+ format!("{}{}", State::new().to_string(|s| s.print_visibility(vis)), s)
+}
+
+impl std::ops::Deref for State<'_> {
+ type Target = pp::Printer;
+ fn deref(&self) -> &Self::Target {
+ &self.s
+ }
+}
+
+impl std::ops::DerefMut for State<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.s
+ }
+}
+
+pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
+ fn insert_extra_parens(&self) -> bool;
+ fn comments(&mut self) -> &mut Option<Comments<'a>>;
+ fn print_ident(&mut self, ident: Ident);
+ fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
+
+ fn strsep<T, F>(
+ &mut self,
+ sep: &'static str,
+ space_before: bool,
+ b: Breaks,
+ elts: &[T],
+ mut op: F,
+ ) where
+ F: FnMut(&mut Self, &T),
+ {
+ self.rbox(0, b);
+ if let Some((first, rest)) = elts.split_first() {
+ op(self, first);
+ for elt in rest {
+ if space_before {
+ self.space();
+ }
+ self.word_space(sep);
+ op(self, elt);
+ }
+ }
+ self.end();
+ }
+
+ fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
+ where
+ F: FnMut(&mut Self, &T),
+ {
+ self.strsep(",", false, b, elts, op)
+ }
+
+ fn maybe_print_comment(&mut self, pos: BytePos) {
+ while let Some(ref cmnt) = self.next_comment() {
+ if cmnt.pos < pos {
+ self.print_comment(cmnt);
+ } else {
+ break;
+ }
+ }
+ }
+
+ fn print_comment(&mut self, cmnt: &Comment) {
+ match cmnt.style {
+ CommentStyle::Mixed => {
+ if !self.is_beginning_of_line() {
+ self.zerobreak();
+ }
+ if let Some((last, lines)) = cmnt.lines.split_last() {
+ self.ibox(0);
+
+ for line in lines {
+ self.word(line.clone());
+ self.hardbreak()
+ }
+
+ self.word(last.clone());
+ self.space();
+
+ self.end();
+ }
+ self.zerobreak()
+ }
+ CommentStyle::Isolated => {
+ self.hardbreak_if_not_bol();
+ for line in &cmnt.lines {
+ // Don't print empty lines because they will end up as trailing
+ // whitespace.
+ if !line.is_empty() {
+ self.word(line.clone());
+ }
+ self.hardbreak();
+ }
+ }
+ CommentStyle::Trailing => {
+ if !self.is_beginning_of_line() {
+ self.word(" ");
+ }
+ if cmnt.lines.len() == 1 {
+ self.word(cmnt.lines[0].clone());
+ self.hardbreak()
+ } else {
+ self.ibox(0);
+ for line in &cmnt.lines {
+ if !line.is_empty() {
+ self.word(line.clone());
+ }
+ self.hardbreak();
+ }
+ self.end();
+ }
+ }
+ CommentStyle::BlankLine => {
+ // We need to do at least one, possibly two hardbreaks.
+ let twice = match self.last_token() {
+ pp::Token::String(s) => ";" == s,
+ pp::Token::Begin(_) => true,
+ pp::Token::End => true,
+ _ => false,
+ };
+ if twice {
+ self.hardbreak();
+ }
+ self.hardbreak();
+ }
+ }
+ if let Some(cmnts) = self.comments() {
+ cmnts.current += 1;
+ }
+ }
+
+ fn next_comment(&mut self) -> Option<Comment> {
+ self.comments().as_mut().and_then(|c| c.next())
+ }
+
+ fn print_literal(&mut self, lit: &ast::Lit) {
+ self.maybe_print_comment(lit.span.lo());
+ self.word(lit.token.to_string())
+ }
+
+ fn print_string(&mut self, st: &str, style: ast::StrStyle) {
+ let st = match style {
+ ast::StrStyle::Cooked => (format!("\"{}\"", st.escape_debug())),
+ ast::StrStyle::Raw(n) => {
+ format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
+ }
+ };
+ self.word(st)
+ }
+
+ fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
+ self.print_string(&sym.as_str(), style);
+ }
+
+ fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
+ }
+
+ fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
+ }
+
+ fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) {
+ self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
+ }
+
+ fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
+ }
+
+ fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
+ self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
+ }
+
+ fn print_either_attributes(
+ &mut self,
+ attrs: &[ast::Attribute],
+ kind: ast::AttrStyle,
+ is_inline: bool,
+ trailing_hardbreak: bool,
+ ) {
+ let mut count = 0;
+ for attr in attrs {
+ if attr.style == kind {
+ self.print_attribute_inline(attr, is_inline);
+ if is_inline {
+ self.nbsp();
+ }
+ count += 1;
+ }
+ }
+ if count > 0 && trailing_hardbreak && !is_inline {
+ self.hardbreak_if_not_bol();
+ }
+ }
+
+ fn print_attribute(&mut self, attr: &ast::Attribute) {
+ self.print_attribute_inline(attr, false)
+ }
+
+ fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
+ if !is_inline {
+ self.hardbreak_if_not_bol();
+ }
+ self.maybe_print_comment(attr.span.lo());
+ match attr.kind {
+ ast::AttrKind::Normal(ref item) => {
+ match attr.style {
+ ast::AttrStyle::Inner => self.word("#!["),
+ ast::AttrStyle::Outer => self.word("#["),
+ }
+ self.print_attr_item(&item, attr.span);
+ self.word("]");
+ }
+ ast::AttrKind::DocComment(comment_kind, data) => {
+ self.word(doc_comment_to_string(comment_kind, attr.style, data));
+ self.hardbreak()
+ }
+ }
+ }
+
+ fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
+ self.ibox(0);
+ match &item.args {
+ MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+ Some(MacHeader::Path(&item.path)),
+ false,
+ None,
+ delim.to_token(),
+ tokens,
+ true,
+ span,
+ ),
+ MacArgs::Empty | MacArgs::Eq(..) => {
+ self.print_path(&item.path, false, 0);
+ if let MacArgs::Eq(_, tokens) = &item.args {
+ self.space();
+ self.word_space("=");
+ self.print_tts(tokens, true);
+ }
+ }
+ }
+ self.end();
+ }
+
+ fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
+ match item {
+ ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
+ ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
+ }
+ }
+
+ fn print_meta_item(&mut self, item: &ast::MetaItem) {
+ self.ibox(INDENT_UNIT);
+ match item.kind {
+ ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
+ ast::MetaItemKind::NameValue(ref value) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ self.print_literal(value);
+ }
+ ast::MetaItemKind::List(ref items) => {
+ self.print_path(&item.path, false, 0);
+ self.popen();
+ self.commasep(Consistent, &items[..], |s, i| s.print_meta_list_item(i));
+ self.pclose();
+ }
+ }
+ self.end();
+ }
+
+ /// This doesn't deserve to be called "pretty" printing, but it should be
+ /// meaning-preserving. A quick hack that might help would be to look at the
+ /// spans embedded in the TTs to decide where to put spaces and newlines.
+ /// But it'd be better to parse these according to the grammar of the
+ /// appropriate macro, transcribe back into the grammar we just parsed from,
+ /// and then pretty-print the resulting AST nodes (so, e.g., we print
+ /// expression arguments as expressions). It can be done! I think.
+ fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
+ match tt {
+ TokenTree::Token(token) => {
+ let token_str = self.token_to_string_ext(&token, convert_dollar_crate);
+ self.word(token_str);
+ if let token::DocComment(..) = token.kind {
+ self.hardbreak()
+ }
+ }
+ TokenTree::Delimited(dspan, delim, tts) => {
+ self.print_mac_common(
+ None,
+ false,
+ None,
+ *delim,
+ tts,
+ convert_dollar_crate,
+ dspan.entire(),
+ );
+ }
+ }
+ }
+
+ fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
+ let mut iter = tts.trees().peekable();
+ while let Some(tt) = iter.next() {
+ self.print_tt(&tt, convert_dollar_crate);
+ if let Some(next) = iter.peek() {
+ if tt_prepend_space(next, &tt) {
+ self.space();
+ }
+ }
+ }
+ }
+
+ fn print_mac_common(
+ &mut self,
+ header: Option<MacHeader<'_>>,
+ has_bang: bool,
+ ident: Option<Ident>,
+ delim: DelimToken,
+ tts: &TokenStream,
+ convert_dollar_crate: bool,
+ span: Span,
+ ) {
+ if delim == DelimToken::Brace {
+ self.cbox(INDENT_UNIT);
+ }
+ match header {
+ Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
+ Some(MacHeader::Keyword(kw)) => self.word(kw),
+ None => {}
+ }
+ if has_bang {
+ self.word("!");
+ }
+ if let Some(ident) = ident {
+ self.nbsp();
+ self.print_ident(ident);
+ }
+ match delim {
+ DelimToken::Brace => {
+ if header.is_some() || has_bang || ident.is_some() {
+ self.nbsp();
+ }
+ self.word("{");
+ if !tts.is_empty() {
+ self.space();
+ }
+ }
+ _ => {
+ let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
+ self.word(token_str)
+ }
+ }
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
+ match delim {
+ DelimToken::Brace => self.bclose(span),
+ _ => {
+ let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
+ self.word(token_str)
+ }
+ }
+ }
+
+ fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
+ self.maybe_print_comment(path.span.lo());
+
+ for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
+ if i > 0 {
+ self.word("::")
+ }
+ self.print_path_segment(segment, colons_before_params);
+ }
+ }
+
+ fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
+ if segment.ident.name != kw::PathRoot {
+ self.print_ident(segment.ident);
+ if let Some(ref args) = segment.args {
+ self.print_generic_args(args, colons_before_params);
+ }
+ }
+ }
+
+ fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+ let w = w.into();
+ // Outer-box is consistent.
+ self.cbox(INDENT_UNIT);
+ // Head-box is inconsistent.
+ self.ibox(w.len() + 1);
+ // Keyword that starts the head.
+ if !w.is_empty() {
+ self.word_nbsp(w);
+ }
+ }
+
+ fn bopen(&mut self) {
+ self.word("{");
+ self.end(); // Close the head-box.
+ }
+
+ fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
+ self.maybe_print_comment(span.hi());
+ self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+ self.word("}");
+ if close_box {
+ self.end(); // Close the outer-box.
+ }
+ }
+
+ fn bclose(&mut self, span: rustc_span::Span) {
+ self.bclose_maybe_open(span, true)
+ }
+
+ fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
+ if !self.is_beginning_of_line() {
+ self.break_offset(n, off)
+ } else {
+ if off != 0 && self.last_token().is_hardbreak_tok() {
+ // We do something pretty sketchy here: tuck the nonzero
+ // offset-adjustment we were going to deposit along with the
+ // break into the previous hardbreak.
+ self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
+ }
+ }
+ }
+
+ fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
+ match *nt {
+ token::NtExpr(ref e) => self.expr_to_string(e),
+ token::NtMeta(ref e) => self.attr_item_to_string(e),
+ token::NtTy(ref e) => self.ty_to_string(e),
+ token::NtPath(ref e) => self.path_to_string(e),
+ token::NtItem(ref e) => self.item_to_string(e),
+ token::NtBlock(ref e) => self.block_to_string(e),
+ token::NtStmt(ref e) => self.stmt_to_string(e),
+ token::NtPat(ref e) => self.pat_to_string(e),
+ token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
+ token::NtLifetime(e) => e.to_string(),
+ token::NtLiteral(ref e) => self.expr_to_string(e),
+ token::NtTT(ref tree) => self.tt_to_string(tree),
+ token::NtVis(ref e) => self.vis_to_string(e),
+ }
+ }
+
+ /// Print the token kind precisely, without converting `$crate` into its respective crate name.
+ fn token_kind_to_string(&self, tok: &TokenKind) -> String {
+ self.token_kind_to_string_ext(tok, None)
+ }
+
+ fn token_kind_to_string_ext(
+ &self,
+ tok: &TokenKind,
+ convert_dollar_crate: Option<Span>,
+ ) -> String {
+ match *tok {
+ token::Eq => "=".to_string(),
+ token::Lt => "<".to_string(),
+ token::Le => "<=".to_string(),
+ token::EqEq => "==".to_string(),
+ token::Ne => "!=".to_string(),
+ token::Ge => ">=".to_string(),
+ token::Gt => ">".to_string(),
+ token::Not => "!".to_string(),
+ token::Tilde => "~".to_string(),
+ token::OrOr => "||".to_string(),
+ token::AndAnd => "&&".to_string(),
+ token::BinOp(op) => binop_to_string(op).to_string(),
+ token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
+
+ /* Structural symbols */
+ token::At => "@".to_string(),
+ token::Dot => ".".to_string(),
+ token::DotDot => "..".to_string(),
+ token::DotDotDot => "...".to_string(),
+ token::DotDotEq => "..=".to_string(),
+ token::Comma => ",".to_string(),
+ token::Semi => ";".to_string(),
+ token::Colon => ":".to_string(),
+ token::ModSep => "::".to_string(),
+ token::RArrow => "->".to_string(),
+ token::LArrow => "<-".to_string(),
+ token::FatArrow => "=>".to_string(),
+ token::OpenDelim(token::Paren) => "(".to_string(),
+ token::CloseDelim(token::Paren) => ")".to_string(),
+ token::OpenDelim(token::Bracket) => "[".to_string(),
+ token::CloseDelim(token::Bracket) => "]".to_string(),
+ token::OpenDelim(token::Brace) => "{".to_string(),
+ token::CloseDelim(token::Brace) => "}".to_string(),
+ token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(),
+ token::Pound => "#".to_string(),
+ token::Dollar => "$".to_string(),
+ token::Question => "?".to_string(),
+ token::SingleQuote => "'".to_string(),
+
+ /* Literals */
+ token::Literal(lit) => literal_to_string(lit),
+
+ /* Name components */
+ token::Ident(s, is_raw) => {
+ IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string()
+ }
+ token::Lifetime(s) => s.to_string(),
+
+ /* Other */
+ token::DocComment(comment_kind, attr_style, data) => {
+ doc_comment_to_string(comment_kind, attr_style, data)
+ }
+ token::Eof => "<eof>".to_string(),
+
+ token::Interpolated(ref nt) => self.nonterminal_to_string(nt),
+ }
+ }
+
+ /// Print the token precisely, without converting `$crate` into its respective crate name.
+ fn token_to_string(&self, token: &Token) -> String {
+ self.token_to_string_ext(token, false)
+ }
+
+ fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> String {
+ let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
+ self.token_kind_to_string_ext(&token.kind, convert_dollar_crate)
+ }
+
+ fn ty_to_string(&self, ty: &ast::Ty) -> String {
+ self.to_string(|s| s.print_type(ty))
+ }
+
+ fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
+ self.to_string(|s| s.print_type_bounds("", bounds))
+ }
+
+ fn pat_to_string(&self, pat: &ast::Pat) -> String {
+ self.to_string(|s| s.print_pat(pat))
+ }
+
+ fn expr_to_string(&self, e: &ast::Expr) -> String {
+ self.to_string(|s| s.print_expr(e))
+ }
+
+ fn tt_to_string(&self, tt: &TokenTree) -> String {
+ self.to_string(|s| s.print_tt(tt, false))
+ }
+
+ fn tts_to_string(&self, tokens: &TokenStream) -> String {
+ self.to_string(|s| s.print_tts(tokens, false))
+ }
+
+ fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
+ self.to_string(|s| s.print_stmt(stmt))
+ }
+
+ fn item_to_string(&self, i: &ast::Item) -> String {
+ self.to_string(|s| s.print_item(i))
+ }
+
+ fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
+ self.to_string(|s| s.print_generic_params(generic_params))
+ }
+
+ fn path_to_string(&self, p: &ast::Path) -> String {
+ self.to_string(|s| s.print_path(p, false, 0))
+ }
+
+ fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
+ self.to_string(|s| s.print_path_segment(p, false))
+ }
+
+ fn vis_to_string(&self, v: &ast::Visibility) -> String {
+ self.to_string(|s| s.print_visibility(v))
+ }
+
+ fn block_to_string(&self, blk: &ast::Block) -> String {
+ self.to_string(|s| {
+ // Containing cbox, will be closed by `print_block` at `}`.
+ s.cbox(INDENT_UNIT);
+ // Head-ibox, will be closed by `print_block` after `{`.
+ s.ibox(0);
+ s.print_block(blk)
+ })
+ }
+
+ fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
+ self.to_string(|s| s.print_meta_list_item(li))
+ }
+
+ fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
+ self.to_string(|s| s.print_attr_item(ai, ai.path.span))
+ }
+
+ fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
+ self.to_string(|s| s.print_attribute(attr))
+ }
+
+ fn param_to_string(&self, arg: &ast::Param) -> String {
+ self.to_string(|s| s.print_param(arg, false))
+ }
+
+ fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String {
+ let mut printer = State::new();
+ printer.insert_extra_parens = self.insert_extra_parens();
+ f(&mut printer);
+ printer.s.eof()
+ }
+}
+
+impl<'a> PrintState<'a> for State<'a> {
+ fn insert_extra_parens(&self) -> bool {
+ self.insert_extra_parens
+ }
+ fn comments(&mut self) -> &mut Option<Comments<'a>> {
+ &mut self.comments
+ }
+
+ fn print_ident(&mut self, ident: Ident) {
+ self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
+ self.ann.post(self, AnnNode::Ident(&ident))
+ }
+
+ fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
+ if colons_before_params {
+ self.s.word("::")
+ }
+
+ match *args {
+ ast::GenericArgs::AngleBracketed(ref data) => {
+ self.s.word("<");
+ self.commasep(Inconsistent, &data.args, |s, arg| match arg {
+ ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
+ ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c),
+ });
+ self.s.word(">")
+ }
+
+ ast::GenericArgs::Parenthesized(ref data) => {
+ self.s.word("(");
+ self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
+ self.s.word(")");
+ self.print_fn_ret_ty(&data.output);
+ }
+ }
+ }
+}
+
+impl<'a> State<'a> {
+ pub fn new() -> State<'a> {
+ State {
+ s: pp::mk_printer(),
+ comments: None,
+ ann: &NoAnn,
+ is_expanded: false,
+ insert_extra_parens: true,
+ }
+ }
+
+ pub(super) fn without_insert_extra_parens() -> State<'a> {
+ State { insert_extra_parens: false, ..State::new() }
+ }
+
+ // Synthesizes a comment that was not textually present in the original source
+ // file.
+ pub fn synth_comment(&mut self, text: String) {
+ self.s.word("/*");
+ self.s.space();
+ self.s.word(text);
+ self.s.space();
+ self.s.word("*/")
+ }
+
+ crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
+ where
+ F: FnMut(&mut State<'_>, &T),
+ G: FnMut(&T) -> rustc_span::Span,
+ {
+ self.rbox(0, b);
+ let len = elts.len();
+ let mut i = 0;
+ for elt in elts {
+ self.maybe_print_comment(get_span(elt).hi());
+ op(self, elt);
+ i += 1;
+ if i < len {
+ self.s.word(",");
+ self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
+ self.space_if_not_bol();
+ }
+ }
+ self.end();
+ }
+
+ crate fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
+ self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
+ }
+
+ pub fn print_mod(&mut self, _mod: &ast::Mod, attrs: &[ast::Attribute]) {
+ self.print_inner_attributes(attrs);
+ for item in &_mod.items {
+ self.print_item(item);
+ }
+ }
+
+ crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) {
+ self.print_inner_attributes(attrs);
+ for item in &nmod.items {
+ self.print_foreign_item(item);
+ }
+ }
+
+ pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
+ if let Some(lt) = *lifetime {
+ self.print_lifetime(lt);
+ self.nbsp();
+ }
+ }
+
+ pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) {
+ self.print_ident(constraint.ident);
+ self.s.space();
+ match &constraint.kind {
+ ast::AssocTyConstraintKind::Equality { ty } => {
+ self.word_space("=");
+ self.print_type(ty);
+ }
+ ast::AssocTyConstraintKind::Bound { bounds } => {
+ self.print_type_bounds(":", &*bounds);
+ }
+ }
+ }
+
+ pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
+ match generic_arg {
+ GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
+ GenericArg::Type(ty) => self.print_type(ty),
+ GenericArg::Const(ct) => self.print_expr(&ct.value),
+ }
+ }
+
+ pub fn print_type(&mut self, ty: &ast::Ty) {
+ self.maybe_print_comment(ty.span.lo());
+ self.ibox(0);
+ match ty.kind {
+ ast::TyKind::Slice(ref ty) => {
+ self.s.word("[");
+ self.print_type(ty);
+ self.s.word("]");
+ }
+ ast::TyKind::Ptr(ref mt) => {
+ self.s.word("*");
+ self.print_mt(mt, true);
+ }
+ ast::TyKind::Rptr(ref lifetime, ref mt) => {
+ self.s.word("&");
+ self.print_opt_lifetime(lifetime);
+ self.print_mt(mt, false);
+ }
+ ast::TyKind::Never => {
+ self.s.word("!");
+ }
+ ast::TyKind::Tup(ref elts) => {
+ self.popen();
+ self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(ty));
+ if elts.len() == 1 {
+ self.s.word(",");
+ }
+ self.pclose();
+ }
+ ast::TyKind::Paren(ref typ) => {
+ self.popen();
+ self.print_type(typ);
+ self.pclose();
+ }
+ ast::TyKind::BareFn(ref f) => {
+ self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
+ }
+ ast::TyKind::Path(None, ref path) => {
+ self.print_path(path, false, 0);
+ }
+ ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
+ ast::TyKind::TraitObject(ref bounds, syntax) => {
+ let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn" } else { "" };
+ self.print_type_bounds(prefix, &bounds[..]);
+ }
+ ast::TyKind::ImplTrait(_, ref bounds) => {
+ self.print_type_bounds("impl", &bounds[..]);
+ }
+ ast::TyKind::Array(ref ty, ref length) => {
+ self.s.word("[");
+ self.print_type(ty);
+ self.s.word("; ");
+ self.print_expr(&length.value);
+ self.s.word("]");
+ }
+ ast::TyKind::Typeof(ref e) => {
+ self.s.word("typeof(");
+ self.print_expr(&e.value);
+ self.s.word(")");
+ }
+ ast::TyKind::Infer => {
+ self.s.word("_");
+ }
+ ast::TyKind::Err => {
+ self.popen();
+ self.s.word("/*ERROR*/");
+ self.pclose();
+ }
+ ast::TyKind::ImplicitSelf => {
+ self.s.word("Self");
+ }
+ ast::TyKind::MacCall(ref m) => {
+ self.print_mac(m);
+ }
+ ast::TyKind::CVarArgs => {
+ self.s.word("...");
+ }
+ }
+ self.end();
+ }
+
+ crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
+ let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+ self.ann.pre(self, AnnNode::SubItem(id));
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(span.lo());
+ self.print_outer_attributes(attrs);
+ match kind {
+ ast::ForeignItemKind::Fn(def, sig, gen, body) => {
+ self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs);
+ }
+ ast::ForeignItemKind::Static(ty, mutbl, body) => {
+ let def = ast::Defaultness::Final;
+ self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
+ }
+ ast::ForeignItemKind::TyAlias(def, generics, bounds, ty) => {
+ self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def);
+ }
+ ast::ForeignItemKind::MacCall(m) => {
+ self.print_mac(m);
+ if m.args.need_semicolon() {
+ self.s.word(";");
+ }
+ }
+ }
+ self.ann.post(self, AnnNode::SubItem(id))
+ }
+
+ fn print_item_const(
+ &mut self,
+ ident: Ident,
+ mutbl: Option<ast::Mutability>,
+ ty: &ast::Ty,
+ body: Option<&ast::Expr>,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ ) {
+ self.head("");
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ let leading = match mutbl {
+ None => "const",
+ Some(ast::Mutability::Not) => "static",
+ Some(ast::Mutability::Mut) => "static mut",
+ };
+ self.word_space(leading);
+ self.print_ident(ident);
+ self.word_space(":");
+ self.print_type(ty);
+ self.s.space();
+ self.end(); // end the head-ibox
+ if let Some(body) = body {
+ self.word_space("=");
+ self.print_expr(body);
+ }
+ self.s.word(";");
+ self.end(); // end the outer cbox
+ }
+
+ fn print_associated_type(
+ &mut self,
+ ident: Ident,
+ generics: &ast::Generics,
+ bounds: &ast::GenericBounds,
+ ty: Option<&ast::Ty>,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ ) {
+ self.head("");
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ self.word_space("type");
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ self.print_type_bounds(":", bounds);
+ self.print_where_clause(&generics.where_clause);
+ if let Some(ty) = ty {
+ self.s.space();
+ self.word_space("=");
+ self.print_type(ty);
+ }
+ self.s.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+
+ /// Pretty-prints an item.
+ crate fn print_item(&mut self, item: &ast::Item) {
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(item.span.lo());
+ self.print_outer_attributes(&item.attrs);
+ self.ann.pre(self, AnnNode::Item(item));
+ match item.kind {
+ ast::ItemKind::ExternCrate(orig_name) => {
+ self.head(visibility_qualified(&item.vis, "extern crate"));
+ if let Some(orig_name) = orig_name {
+ self.print_name(orig_name);
+ self.s.space();
+ self.s.word("as");
+ self.s.space();
+ }
+ self.print_ident(item.ident);
+ self.s.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ ast::ItemKind::Use(ref tree) => {
+ self.head(visibility_qualified(&item.vis, "use"));
+ self.print_use_tree(tree);
+ self.s.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ ast::ItemKind::Static(ref ty, mutbl, ref body) => {
+ let def = ast::Defaultness::Final;
+ self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def);
+ }
+ ast::ItemKind::Const(def, ref ty, ref body) => {
+ self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def);
+ }
+ ast::ItemKind::Fn(def, ref sig, ref gen, ref body) => {
+ let body = body.as_deref();
+ self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
+ }
+ ast::ItemKind::Mod(ref _mod) => {
+ self.head(self.to_string(|s| {
+ s.print_visibility(&item.vis);
+ s.print_unsafety(_mod.unsafety);
+ s.word("mod");
+ }));
+ self.print_ident(item.ident);
+
+ if _mod.inline || self.is_expanded {
+ self.nbsp();
+ self.bopen();
+ self.print_mod(_mod, &item.attrs);
+ self.bclose(item.span);
+ } else {
+ self.s.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ }
+ ast::ItemKind::ForeignMod(ref nmod) => {
+ self.head(self.to_string(|s| {
+ s.print_unsafety(nmod.unsafety);
+ s.word("extern");
+ }));
+ if let Some(abi) = nmod.abi {
+ self.print_literal(&abi.as_lit());
+ self.nbsp();
+ }
+ self.bopen();
+ self.print_foreign_mod(nmod, &item.attrs);
+ self.bclose(item.span);
+ }
+ ast::ItemKind::GlobalAsm(ref ga) => {
+ self.head(visibility_qualified(&item.vis, "global_asm!"));
+ self.s.word(ga.asm.to_string());
+ self.end();
+ }
+ ast::ItemKind::TyAlias(def, ref generics, ref bounds, ref ty) => {
+ let ty = ty.as_deref();
+ self.print_associated_type(item.ident, generics, bounds, ty, &item.vis, def);
+ }
+ ast::ItemKind::Enum(ref enum_definition, ref params) => {
+ self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
+ }
+ ast::ItemKind::Struct(ref struct_def, ref generics) => {
+ self.head(visibility_qualified(&item.vis, "struct"));
+ self.print_struct(struct_def, generics, item.ident, item.span, true);
+ }
+ ast::ItemKind::Union(ref struct_def, ref generics) => {
+ self.head(visibility_qualified(&item.vis, "union"));
+ self.print_struct(struct_def, generics, item.ident, item.span, true);
+ }
+ ast::ItemKind::Impl {
+ unsafety,
+ polarity,
+ defaultness,
+ constness,
+ ref generics,
+ ref of_trait,
+ ref self_ty,
+ ref items,
+ } => {
+ self.head("");
+ self.print_visibility(&item.vis);
+ self.print_defaultness(defaultness);
+ self.print_unsafety(unsafety);
+ self.word_nbsp("impl");
+ self.print_constness(constness);
+
+ if !generics.params.is_empty() {
+ self.print_generic_params(&generics.params);
+ self.s.space();
+ }
+
+ if let ast::ImplPolarity::Negative(_) = polarity {
+ self.s.word("!");
+ }
+
+ if let Some(ref t) = *of_trait {
+ self.print_trait_ref(t);
+ self.s.space();
+ self.word_space("for");
+ }
+
+ self.print_type(self_ty);
+ self.print_where_clause(&generics.where_clause);
+
+ self.s.space();
+ self.bopen();
+ self.print_inner_attributes(&item.attrs);
+ for impl_item in items {
+ self.print_assoc_item(impl_item);
+ }
+ self.bclose(item.span);
+ }
+ ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => {
+ self.head("");
+ self.print_visibility(&item.vis);
+ self.print_unsafety(unsafety);
+ self.print_is_auto(is_auto);
+ self.word_nbsp("trait");
+ self.print_ident(item.ident);
+ self.print_generic_params(&generics.params);
+ let mut real_bounds = Vec::with_capacity(bounds.len());
+ for b in bounds.iter() {
+ if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+ self.s.space();
+ self.word_space("for ?");
+ self.print_trait_ref(&ptr.trait_ref);
+ } else {
+ real_bounds.push(b.clone());
+ }
+ }
+ self.print_type_bounds(":", &real_bounds[..]);
+ self.print_where_clause(&generics.where_clause);
+ self.s.word(" ");
+ self.bopen();
+ self.print_inner_attributes(&item.attrs);
+ for trait_item in trait_items {
+ self.print_assoc_item(trait_item);
+ }
+ self.bclose(item.span);
+ }
+ ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
+ self.head("");
+ self.print_visibility(&item.vis);
+ self.word_nbsp("trait");
+ self.print_ident(item.ident);
+ self.print_generic_params(&generics.params);
+ let mut real_bounds = Vec::with_capacity(bounds.len());
+ // FIXME(durka) this seems to be some quite outdated syntax
+ for b in bounds.iter() {
+ if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+ self.s.space();
+ self.word_space("for ?");
+ self.print_trait_ref(&ptr.trait_ref);
+ } else {
+ real_bounds.push(b.clone());
+ }
+ }
+ self.nbsp();
+ self.print_type_bounds("=", &real_bounds[..]);
+ self.print_where_clause(&generics.where_clause);
+ self.s.word(";");
+ }
+ ast::ItemKind::MacCall(ref mac) => {
+ self.print_mac(mac);
+ if mac.args.need_semicolon() {
+ self.s.word(";");
+ }
+ }
+ ast::ItemKind::MacroDef(ref macro_def) => {
+ let (kw, has_bang) = if macro_def.macro_rules {
+ ("macro_rules", true)
+ } else {
+ self.print_visibility(&item.vis);
+ ("macro", false)
+ };
+ self.print_mac_common(
+ Some(MacHeader::Keyword(kw)),
+ has_bang,
+ Some(item.ident),
+ macro_def.body.delim(),
+ ¯o_def.body.inner_tokens(),
+ true,
+ item.span,
+ );
+ }
+ }
+ self.ann.post(self, AnnNode::Item(item))
+ }
+
+ fn print_trait_ref(&mut self, t: &ast::TraitRef) {
+ self.print_path(&t.path, false, 0)
+ }
+
+ fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+ if !generic_params.is_empty() {
+ self.s.word("for");
+ self.print_generic_params(generic_params);
+ self.nbsp();
+ }
+ }
+
+ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
+ self.print_formal_generic_params(&t.bound_generic_params);
+ self.print_trait_ref(&t.trait_ref)
+ }
+
+ crate fn print_enum_def(
+ &mut self,
+ enum_definition: &ast::EnumDef,
+ generics: &ast::Generics,
+ ident: Ident,
+ span: rustc_span::Span,
+ visibility: &ast::Visibility,
+ ) {
+ self.head(visibility_qualified(visibility, "enum"));
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ self.print_where_clause(&generics.where_clause);
+ self.s.space();
+ self.print_variants(&enum_definition.variants, span)
+ }
+
+ crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
+ self.bopen();
+ for v in variants {
+ self.space_if_not_bol();
+ self.maybe_print_comment(v.span.lo());
+ self.print_outer_attributes(&v.attrs);
+ self.ibox(INDENT_UNIT);
+ self.print_variant(v);
+ self.s.word(",");
+ self.end();
+ self.maybe_print_trailing_comment(v.span, None);
+ }
+ self.bclose(span)
+ }
+
+ crate fn print_visibility(&mut self, vis: &ast::Visibility) {
+ match vis.kind {
+ ast::VisibilityKind::Public => self.word_nbsp("pub"),
+ ast::VisibilityKind::Crate(sugar) => match sugar {
+ ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
+ ast::CrateSugar::JustCrate => self.word_nbsp("crate"),
+ },
+ ast::VisibilityKind::Restricted { ref path, .. } => {
+ let path = self.to_string(|s| s.print_path(path, false, 0));
+ if path == "self" || path == "super" {
+ self.word_nbsp(format!("pub({})", path))
+ } else {
+ self.word_nbsp(format!("pub(in {})", path))
+ }
+ }
+ ast::VisibilityKind::Inherited => {}
+ }
+ }
+
+ crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
+ if let ast::Defaultness::Default(_) = defaultness {
+ self.word_nbsp("default");
+ }
+ }
+
+ crate fn print_struct(
+ &mut self,
+ struct_def: &ast::VariantData,
+ generics: &ast::Generics,
+ ident: Ident,
+ span: rustc_span::Span,
+ print_finalizer: bool,
+ ) {
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ match struct_def {
+ ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
+ if let ast::VariantData::Tuple(..) = struct_def {
+ self.popen();
+ self.commasep(Inconsistent, struct_def.fields(), |s, field| {
+ s.maybe_print_comment(field.span.lo());
+ s.print_outer_attributes(&field.attrs);
+ s.print_visibility(&field.vis);
+ s.print_type(&field.ty)
+ });
+ self.pclose();
+ }
+ self.print_where_clause(&generics.where_clause);
+ if print_finalizer {
+ self.s.word(";");
+ }
+ self.end();
+ self.end(); // Close the outer-box.
+ }
+ ast::VariantData::Struct(..) => {
+ self.print_where_clause(&generics.where_clause);
+ self.nbsp();
+ self.bopen();
+ self.hardbreak_if_not_bol();
+
+ for field in struct_def.fields() {
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(field.span.lo());
+ self.print_outer_attributes(&field.attrs);
+ self.print_visibility(&field.vis);
+ self.print_ident(field.ident.unwrap());
+ self.word_nbsp(":");
+ self.print_type(&field.ty);
+ self.s.word(",");
+ }
+
+ self.bclose(span)
+ }
+ }
+ }
+
+ crate fn print_variant(&mut self, v: &ast::Variant) {
+ self.head("");
+ self.print_visibility(&v.vis);
+ let generics = ast::Generics::default();
+ self.print_struct(&v.data, &generics, v.ident, v.span, false);
+ if let Some(ref d) = v.disr_expr {
+ self.s.space();
+ self.word_space("=");
+ self.print_expr(&d.value)
+ }
+ }
+
+ crate fn print_assoc_item(&mut self, item: &ast::AssocItem) {
+ let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+ self.ann.pre(self, AnnNode::SubItem(id));
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(span.lo());
+ self.print_outer_attributes(attrs);
+ match kind {
+ ast::AssocItemKind::Fn(def, sig, gen, body) => {
+ self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs);
+ }
+ ast::AssocItemKind::Const(def, ty, body) => {
+ self.print_item_const(ident, None, ty, body.as_deref(), vis, *def);
+ }
+ ast::AssocItemKind::TyAlias(def, generics, bounds, ty) => {
+ self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def);
+ }
+ ast::AssocItemKind::MacCall(m) => {
+ self.print_mac(m);
+ if m.args.need_semicolon() {
+ self.s.word(";");
+ }
+ }
+ }
+ self.ann.post(self, AnnNode::SubItem(id))
+ }
+
+ crate fn print_stmt(&mut self, st: &ast::Stmt) {
+ self.maybe_print_comment(st.span.lo());
+ match st.kind {
+ ast::StmtKind::Local(ref loc) => {
+ self.print_outer_attributes(&loc.attrs);
+ self.space_if_not_bol();
+ self.ibox(INDENT_UNIT);
+ self.word_nbsp("let");
+
+ self.ibox(INDENT_UNIT);
+ self.print_local_decl(loc);
+ self.end();
+ if let Some(ref init) = loc.init {
+ self.nbsp();
+ self.word_space("=");
+ self.print_expr(init);
+ }
+ self.s.word(";");
+ self.end();
+ }
+ ast::StmtKind::Item(ref item) => self.print_item(item),
+ ast::StmtKind::Expr(ref expr) => {
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ if classify::expr_requires_semi_to_be_stmt(expr) {
+ self.s.word(";");
+ }
+ }
+ ast::StmtKind::Semi(ref expr) => {
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ self.s.word(";");
+ }
+ ast::StmtKind::Empty => {
+ self.space_if_not_bol();
+ self.s.word(";");
+ }
+ ast::StmtKind::MacCall(ref mac) => {
+ self.space_if_not_bol();
+ self.print_outer_attributes(&mac.attrs);
+ self.print_mac(&mac.mac);
+ if mac.style == ast::MacStmtStyle::Semicolon {
+ self.s.word(";");
+ }
+ }
+ }
+ self.maybe_print_trailing_comment(st.span, None)
+ }
+
+ crate fn print_block(&mut self, blk: &ast::Block) {
+ self.print_block_with_attrs(blk, &[])
+ }
+
+ crate fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
+ self.print_block_maybe_unclosed(blk, &[], false)
+ }
+
+ crate fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
+ self.print_block_maybe_unclosed(blk, attrs, true)
+ }
+
+ crate fn print_block_maybe_unclosed(
+ &mut self,
+ blk: &ast::Block,
+ attrs: &[ast::Attribute],
+ close_box: bool,
+ ) {
+ match blk.rules {
+ BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
+ BlockCheckMode::Default => (),
+ }
+ self.maybe_print_comment(blk.span.lo());
+ self.ann.pre(self, AnnNode::Block(blk));
+ self.bopen();
+
+ self.print_inner_attributes(attrs);
+
+ for (i, st) in blk.stmts.iter().enumerate() {
+ match st.kind {
+ ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+ self.maybe_print_comment(st.span.lo());
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
+ }
+ _ => self.print_stmt(st),
+ }
+ }
+
+ self.bclose_maybe_open(blk.span, close_box);
+ self.ann.post(self, AnnNode::Block(blk))
+ }
+
+ /// Print a `let pat = scrutinee` expression.
+ crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
+ self.s.word("let ");
+
+ self.print_pat(pat);
+ self.s.space();
+
+ self.word_space("=");
+ self.print_expr_cond_paren(
+ scrutinee,
+ Self::cond_needs_par(scrutinee)
+ || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()),
+ )
+ }
+
+ fn print_else(&mut self, els: Option<&ast::Expr>) {
+ if let Some(_else) = els {
+ match _else.kind {
+ // Another `else if` block.
+ ast::ExprKind::If(ref i, ref then, ref e) => {
+ self.cbox(INDENT_UNIT - 1);
+ self.ibox(0);
+ self.s.word(" else if ");
+ self.print_expr_as_cond(i);
+ self.s.space();
+ self.print_block(then);
+ self.print_else(e.as_deref())
+ }
+ // Final `else` block.
+ ast::ExprKind::Block(ref b, _) => {
+ self.cbox(INDENT_UNIT - 1);
+ self.ibox(0);
+ self.s.word(" else ");
+ self.print_block(b)
+ }
+ // Constraints would be great here!
+ _ => {
+ panic!("print_if saw if with weird alternative");
+ }
+ }
+ }
+ }
+
+ crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
+ self.head("if");
+
+ self.print_expr_as_cond(test);
+ self.s.space();
+
+ self.print_block(blk);
+ self.print_else(elseopt)
+ }
+
+ crate fn print_mac(&mut self, m: &ast::MacCall) {
+ self.print_mac_common(
+ Some(MacHeader::Path(&m.path)),
+ true,
+ None,
+ m.args.delim(),
+ &m.args.inner_tokens(),
+ true,
+ m.span(),
+ );
+ }
+
+ fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
+ self.popen();
+ self.commasep_exprs(Inconsistent, args);
+ self.pclose()
+ }
+
+ crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
+ self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
+ }
+
+ /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
+ /// `if cond { ... }`.
+ crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
+ self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
+ }
+
+ /// Does `expr` need parenthesis when printed in a condition position?
+ fn cond_needs_par(expr: &ast::Expr) -> bool {
+ match expr.kind {
+ // These cases need parens due to the parse error observed in #26461: `if return {}`
+ // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
+ ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Break(..) => true,
+
+ _ => parser::contains_exterior_struct_lit(expr),
+ }
+ }
+
+ /// Prints `expr` or `(expr)` when `needs_par` holds.
+ fn print_expr_cond_paren(&mut self, expr: &ast::Expr, mut needs_par: bool) {
+ needs_par &= self.insert_extra_parens;
+ if needs_par {
+ self.popen();
+ }
+ self.print_expr(expr);
+ if needs_par {
+ self.pclose();
+ }
+ }
+
+ fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>], attrs: &[ast::Attribute]) {
+ self.ibox(INDENT_UNIT);
+ self.s.word("[");
+ self.print_inner_attributes_inline(attrs);
+ self.commasep_exprs(Inconsistent, &exprs[..]);
+ self.s.word("]");
+ self.end();
+ }
+
+ fn print_expr_repeat(
+ &mut self,
+ element: &ast::Expr,
+ count: &ast::AnonConst,
+ attrs: &[ast::Attribute],
+ ) {
+ self.ibox(INDENT_UNIT);
+ self.s.word("[");
+ self.print_inner_attributes_inline(attrs);
+ self.print_expr(element);
+ self.word_space(";");
+ self.print_expr(&count.value);
+ self.s.word("]");
+ self.end();
+ }
+
+ fn print_expr_struct(
+ &mut self,
+ path: &ast::Path,
+ fields: &[ast::Field],
+ wth: &Option<P<ast::Expr>>,
+ attrs: &[ast::Attribute],
+ ) {
+ self.print_path(path, true, 0);
+ self.s.word("{");
+ self.print_inner_attributes_inline(attrs);
+ self.commasep_cmnt(
+ Consistent,
+ &fields[..],
+ |s, field| {
+ s.print_outer_attributes(&field.attrs);
+ s.ibox(INDENT_UNIT);
+ if !field.is_shorthand {
+ s.print_ident(field.ident);
+ s.word_space(":");
+ }
+ s.print_expr(&field.expr);
+ s.end();
+ },
+ |f| f.span,
+ );
+ match *wth {
+ Some(ref expr) => {
+ self.ibox(INDENT_UNIT);
+ if !fields.is_empty() {
+ self.s.word(",");
+ self.s.space();
+ }
+ self.s.word("..");
+ self.print_expr(expr);
+ self.end();
+ }
+ _ => {
+ if !fields.is_empty() {
+ self.s.word(",")
+ }
+ }
+ }
+ self.s.word("}");
+ }
+
+ fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>], attrs: &[ast::Attribute]) {
+ self.popen();
+ self.print_inner_attributes_inline(attrs);
+ self.commasep_exprs(Inconsistent, &exprs[..]);
+ if exprs.len() == 1 {
+ self.s.word(",");
+ }
+ self.pclose()
+ }
+
+ fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
+ let prec = match func.kind {
+ ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
+ _ => parser::PREC_POSTFIX,
+ };
+
+ self.print_expr_maybe_paren(func, prec);
+ self.print_call_post(args)
+ }
+
+ fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P<ast::Expr>]) {
+ let base_args = &args[1..];
+ self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
+ self.s.word(".");
+ self.print_ident(segment.ident);
+ if let Some(ref args) = segment.args {
+ self.print_generic_args(args, true);
+ }
+ self.print_call_post(base_args)
+ }
+
+ fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
+ let assoc_op = AssocOp::from_ast_binop(op.node);
+ let prec = assoc_op.precedence() as i8;
+ let fixity = assoc_op.fixity();
+
+ let (left_prec, right_prec) = match fixity {
+ Fixity::Left => (prec, prec + 1),
+ Fixity::Right => (prec + 1, prec),
+ Fixity::None => (prec + 1, prec + 1),
+ };
+
+ let left_prec = match (&lhs.kind, op.node) {
+ // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
+ // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
+ // of `(x as i32) < ...`. We need to convince it _not_ to do that.
+ (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
+ parser::PREC_FORCE_PAREN
+ }
+ // We are given `(let _ = a) OP b`.
+ //
+ // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
+ // as the parser will interpret this as `(let _ = a) OP b`.
+ //
+ // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
+ // parens are required since the parser would interpret `let a = b < c` as
+ // `let a = (b < c)`. To achieve this, we force parens.
+ (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
+ parser::PREC_FORCE_PAREN
+ }
+ _ => left_prec,
+ };
+
+ self.print_expr_maybe_paren(lhs, left_prec);
+ self.s.space();
+ self.word_space(op.node.to_string());
+ self.print_expr_maybe_paren(rhs, right_prec)
+ }
+
+ fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
+ self.s.word(ast::UnOp::to_string(op));
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+ }
+
+ fn print_expr_addr_of(
+ &mut self,
+ kind: ast::BorrowKind,
+ mutability: ast::Mutability,
+ expr: &ast::Expr,
+ ) {
+ self.s.word("&");
+ match kind {
+ ast::BorrowKind::Ref => self.print_mutability(mutability, false),
+ ast::BorrowKind::Raw => {
+ self.word_nbsp("raw");
+ self.print_mutability(mutability, true);
+ }
+ }
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+ }
+
+ pub fn print_expr(&mut self, expr: &ast::Expr) {
+ self.print_expr_outer_attr_style(expr, true)
+ }
+
+ fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
+ self.maybe_print_comment(expr.span.lo());
+
+ let attrs = &expr.attrs;
+ if is_inline {
+ self.print_outer_attributes_inline(attrs);
+ } else {
+ self.print_outer_attributes(attrs);
+ }
+
+ self.ibox(INDENT_UNIT);
+ self.ann.pre(self, AnnNode::Expr(expr));
+ match expr.kind {
+ ast::ExprKind::Box(ref expr) => {
+ self.word_space("box");
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
+ }
+ ast::ExprKind::Array(ref exprs) => {
+ self.print_expr_vec(&exprs[..], attrs);
+ }
+ ast::ExprKind::Repeat(ref element, ref count) => {
+ self.print_expr_repeat(element, count, attrs);
+ }
+ ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
+ self.print_expr_struct(path, &fields[..], wth, attrs);
+ }
+ ast::ExprKind::Tup(ref exprs) => {
+ self.print_expr_tup(&exprs[..], attrs);
+ }
+ ast::ExprKind::Call(ref func, ref args) => {
+ self.print_expr_call(func, &args[..]);
+ }
+ ast::ExprKind::MethodCall(ref segment, ref args, _) => {
+ self.print_expr_method_call(segment, &args[..]);
+ }
+ ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
+ self.print_expr_binary(op, lhs, rhs);
+ }
+ ast::ExprKind::Unary(op, ref expr) => {
+ self.print_expr_unary(op, expr);
+ }
+ ast::ExprKind::AddrOf(k, m, ref expr) => {
+ self.print_expr_addr_of(k, m, expr);
+ }
+ ast::ExprKind::Lit(ref lit) => {
+ self.print_literal(lit);
+ }
+ ast::ExprKind::Cast(ref expr, ref ty) => {
+ let prec = AssocOp::As.precedence() as i8;
+ self.print_expr_maybe_paren(expr, prec);
+ self.s.space();
+ self.word_space("as");
+ self.print_type(ty);
+ }
+ ast::ExprKind::Type(ref expr, ref ty) => {
+ let prec = AssocOp::Colon.precedence() as i8;
+ self.print_expr_maybe_paren(expr, prec);
+ self.word_space(":");
+ self.print_type(ty);
+ }
+ ast::ExprKind::Let(ref pat, ref scrutinee) => {
+ self.print_let(pat, scrutinee);
+ }
+ ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
+ self.print_if(test, blk, elseopt.as_deref())
+ }
+ ast::ExprKind::While(ref test, ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.head("while");
+ self.print_expr_as_cond(test);
+ self.s.space();
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.head("for");
+ self.print_pat(pat);
+ self.s.space();
+ self.word_space("in");
+ self.print_expr_as_cond(iter);
+ self.s.space();
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Loop(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.head("loop");
+ self.s.space();
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Match(ref expr, ref arms) => {
+ self.cbox(INDENT_UNIT);
+ self.ibox(INDENT_UNIT);
+ self.word_nbsp("match");
+ self.print_expr_as_cond(expr);
+ self.s.space();
+ self.bopen();
+ self.print_inner_attributes_no_trailing_hardbreak(attrs);
+ for arm in arms {
+ self.print_arm(arm);
+ }
+ self.bclose(expr.span);
+ }
+ ast::ExprKind::Closure(
+ capture_clause,
+ asyncness,
+ movability,
+ ref decl,
+ ref body,
+ _,
+ ) => {
+ self.print_movability(movability);
+ self.print_asyncness(asyncness);
+ self.print_capture_clause(capture_clause);
+
+ self.print_fn_params_and_ret(decl, true);
+ self.s.space();
+ self.print_expr(body);
+ self.end(); // need to close a box
+
+ // a box will be closed by print_expr, but we didn't want an overall
+ // wrapper so we closed the corresponding opening. so create an
+ // empty box to satisfy the close.
+ self.ibox(0);
+ }
+ ast::ExprKind::Block(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ // containing cbox, will be closed by print-block at }
+ self.cbox(INDENT_UNIT);
+ // head-box, will be closed by print-block after {
+ self.ibox(0);
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Async(capture_clause, _, ref blk) => {
+ self.word_nbsp("async");
+ self.print_capture_clause(capture_clause);
+ self.s.space();
+ // cbox/ibox in analogy to the `ExprKind::Block` arm above
+ self.cbox(INDENT_UNIT);
+ self.ibox(0);
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Await(ref expr) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.s.word(".await");
+ }
+ ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
+ let prec = AssocOp::Assign.precedence() as i8;
+ self.print_expr_maybe_paren(lhs, prec + 1);
+ self.s.space();
+ self.word_space("=");
+ self.print_expr_maybe_paren(rhs, prec);
+ }
+ ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+ let prec = AssocOp::Assign.precedence() as i8;
+ self.print_expr_maybe_paren(lhs, prec + 1);
+ self.s.space();
+ self.s.word(op.node.to_string());
+ self.word_space("=");
+ self.print_expr_maybe_paren(rhs, prec);
+ }
+ ast::ExprKind::Field(ref expr, ident) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.s.word(".");
+ self.print_ident(ident);
+ }
+ ast::ExprKind::Index(ref expr, ref index) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.s.word("[");
+ self.print_expr(index);
+ self.s.word("]");
+ }
+ ast::ExprKind::Range(ref start, ref end, limits) => {
+ // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
+ // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
+ // Here we use a fake precedence value so that any child with lower precedence than
+ // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
+ let fake_prec = AssocOp::LOr.precedence() as i8;
+ if let Some(ref e) = *start {
+ self.print_expr_maybe_paren(e, fake_prec);
+ }
+ if limits == ast::RangeLimits::HalfOpen {
+ self.s.word("..");
+ } else {
+ self.s.word("..=");
+ }
+ if let Some(ref e) = *end {
+ self.print_expr_maybe_paren(e, fake_prec);
+ }
+ }
+ ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
+ ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
+ ast::ExprKind::Break(opt_label, ref opt_expr) => {
+ self.s.word("break");
+ self.s.space();
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.s.space();
+ }
+ if let Some(ref expr) = *opt_expr {
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ self.s.space();
+ }
+ }
+ ast::ExprKind::Continue(opt_label) => {
+ self.s.word("continue");
+ self.s.space();
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.s.space()
+ }
+ }
+ ast::ExprKind::Ret(ref result) => {
+ self.s.word("return");
+ if let Some(ref expr) = *result {
+ self.s.word(" ");
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::InlineAsm(ref a) => {
+ enum AsmArg<'a> {
+ Template(String),
+ Operand(&'a InlineAsmOperand),
+ Options(InlineAsmOptions),
+ }
+
+ let mut args = vec![];
+ args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&a.template)));
+ args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
+ if !a.options.is_empty() {
+ args.push(AsmArg::Options(a.options));
+ }
+
+ self.word("asm!");
+ self.popen();
+ self.commasep(Consistent, &args, |s, arg| match arg {
+ AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
+ AsmArg::Operand(op) => {
+ let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r
+ {
+ InlineAsmRegOrRegClass::Reg(r) => {
+ s.print_symbol(*r, ast::StrStyle::Cooked)
+ }
+ InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
+ };
+ match op {
+ InlineAsmOperand::In { reg, expr } => {
+ s.word("in");
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(expr);
+ }
+ InlineAsmOperand::Out { reg, late, expr } => {
+ s.word(if *late { "lateout" } else { "out" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ match expr {
+ Some(expr) => s.print_expr(expr),
+ None => s.word("_"),
+ }
+ }
+ InlineAsmOperand::InOut { reg, late, expr } => {
+ s.word(if *late { "inlateout" } else { "inout" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(expr);
+ }
+ InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
+ s.word(if *late { "inlateout" } else { "inout" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(in_expr);
+ s.space();
+ s.word_space("=>");
+ match out_expr {
+ Some(out_expr) => s.print_expr(out_expr),
+ None => s.word("_"),
+ }
+ }
+ InlineAsmOperand::Const { expr } => {
+ s.word("const");
+ s.space();
+ s.print_expr(expr);
+ }
+ InlineAsmOperand::Sym { expr } => {
+ s.word("sym");
+ s.space();
+ s.print_expr(expr);
+ }
+ }
+ }
+ AsmArg::Options(opts) => {
+ s.word("options");
+ s.popen();
+ let mut options = vec![];
+ if opts.contains(InlineAsmOptions::PURE) {
+ options.push("pure");
+ }
+ if opts.contains(InlineAsmOptions::NOMEM) {
+ options.push("nomem");
+ }
+ if opts.contains(InlineAsmOptions::READONLY) {
+ options.push("readonly");
+ }
+ if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
+ options.push("preserves_flags");
+ }
+ if opts.contains(InlineAsmOptions::NORETURN) {
+ options.push("noreturn");
+ }
+ if opts.contains(InlineAsmOptions::NOSTACK) {
+ options.push("nostack");
+ }
+ if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
+ options.push("att_syntax");
+ }
+ s.commasep(Inconsistent, &options, |s, &opt| {
+ s.word(opt);
+ });
+ s.pclose();
+ }
+ });
+ self.pclose();
+ }
+ ast::ExprKind::LlvmInlineAsm(ref a) => {
+ self.s.word("llvm_asm!");
+ self.popen();
+ self.print_symbol(a.asm, a.asm_str_style);
+ self.word_space(":");
+
+ self.commasep(Inconsistent, &a.outputs, |s, out| {
+ let constraint = out.constraint.as_str();
+ let mut ch = constraint.chars();
+ match ch.next() {
+ Some('=') if out.is_rw => {
+ s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked)
+ }
+ _ => s.print_string(&constraint, ast::StrStyle::Cooked),
+ }
+ s.popen();
+ s.print_expr(&out.expr);
+ s.pclose();
+ });
+ self.s.space();
+ self.word_space(":");
+
+ self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
+ s.print_symbol(co, ast::StrStyle::Cooked);
+ s.popen();
+ s.print_expr(o);
+ s.pclose();
+ });
+ self.s.space();
+ self.word_space(":");
+
+ self.commasep(Inconsistent, &a.clobbers, |s, &co| {
+ s.print_symbol(co, ast::StrStyle::Cooked);
+ });
+
+ let mut options = vec![];
+ if a.volatile {
+ options.push("volatile");
+ }
+ if a.alignstack {
+ options.push("alignstack");
+ }
+ if a.dialect == ast::LlvmAsmDialect::Intel {
+ options.push("intel");
+ }
+
+ if !options.is_empty() {
+ self.s.space();
+ self.word_space(":");
+ self.commasep(Inconsistent, &options, |s, &co| {
+ s.print_string(co, ast::StrStyle::Cooked);
+ });
+ }
+
+ self.pclose();
+ }
+ ast::ExprKind::MacCall(ref m) => self.print_mac(m),
+ ast::ExprKind::Paren(ref e) => {
+ self.popen();
+ self.print_inner_attributes_inline(attrs);
+ self.print_expr(e);
+ self.pclose();
+ }
+ ast::ExprKind::Yield(ref e) => {
+ self.s.word("yield");
+
+ if let Some(ref expr) = *e {
+ self.s.space();
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::Try(ref e) => {
+ self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
+ self.s.word("?")
+ }
+ ast::ExprKind::TryBlock(ref blk) => {
+ self.head("try");
+ self.s.space();
+ self.print_block_with_attrs(blk, attrs)
+ }
+ ast::ExprKind::Err => {
+ self.popen();
+ self.s.word("/*ERROR*/");
+ self.pclose()
+ }
+ }
+ self.ann.post(self, AnnNode::Expr(expr));
+ self.end();
+ }
+
+ crate fn print_local_decl(&mut self, loc: &ast::Local) {
+ self.print_pat(&loc.pat);
+ if let Some(ref ty) = loc.ty {
+ self.word_space(":");
+ self.print_type(ty);
+ }
+ }
+
+ pub fn print_usize(&mut self, i: usize) {
+ self.s.word(i.to_string())
+ }
+
+ crate fn print_name(&mut self, name: Symbol) {
+ self.s.word(name.to_string());
+ self.ann.post(self, AnnNode::Name(&name))
+ }
+
+ fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
+ self.s.word("<");
+ self.print_type(&qself.ty);
+ if qself.position > 0 {
+ self.s.space();
+ self.word_space("as");
+ let depth = path.segments.len() - qself.position;
+ self.print_path(path, false, depth);
+ }
+ self.s.word(">");
+ self.s.word("::");
+ let item_segment = path.segments.last().unwrap();
+ self.print_ident(item_segment.ident);
+ if let Some(ref args) = item_segment.args {
+ self.print_generic_args(args, colons_before_params)
+ }
+ }
+
+ crate fn print_pat(&mut self, pat: &ast::Pat) {
+ self.maybe_print_comment(pat.span.lo());
+ self.ann.pre(self, AnnNode::Pat(pat));
+ /* Pat isn't normalized, but the beauty of it
+ is that it doesn't matter */
+ match pat.kind {
+ PatKind::Wild => self.s.word("_"),
+ PatKind::Ident(binding_mode, ident, ref sub) => {
+ match binding_mode {
+ ast::BindingMode::ByRef(mutbl) => {
+ self.word_nbsp("ref");
+ self.print_mutability(mutbl, false);
+ }
+ ast::BindingMode::ByValue(ast::Mutability::Not) => {}
+ ast::BindingMode::ByValue(ast::Mutability::Mut) => {
+ self.word_nbsp("mut");
+ }
+ }
+ self.print_ident(ident);
+ if let Some(ref p) = *sub {
+ self.s.space();
+ self.s.word_space("@");
+ self.print_pat(p);
+ }
+ }
+ PatKind::TupleStruct(ref path, ref elts) => {
+ self.print_path(path, true, 0);
+ self.popen();
+ self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+ self.pclose();
+ }
+ PatKind::Or(ref pats) => {
+ self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
+ }
+ PatKind::Path(None, ref path) => {
+ self.print_path(path, true, 0);
+ }
+ PatKind::Path(Some(ref qself), ref path) => {
+ self.print_qpath(path, qself, false);
+ }
+ PatKind::Struct(ref path, ref fields, etc) => {
+ self.print_path(path, true, 0);
+ self.nbsp();
+ self.word_space("{");
+ self.commasep_cmnt(
+ Consistent,
+ &fields[..],
+ |s, f| {
+ s.cbox(INDENT_UNIT);
+ if !f.is_shorthand {
+ s.print_ident(f.ident);
+ s.word_nbsp(":");
+ }
+ s.print_pat(&f.pat);
+ s.end();
+ },
+ |f| f.pat.span,
+ );
+ if etc {
+ if !fields.is_empty() {
+ self.word_space(",");
+ }
+ self.s.word("..");
+ }
+ self.s.space();
+ self.s.word("}");
+ }
+ PatKind::Tuple(ref elts) => {
+ self.popen();
+ self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+ if elts.len() == 1 {
+ self.s.word(",");
+ }
+ self.pclose();
+ }
+ PatKind::Box(ref inner) => {
+ self.s.word("box ");
+ self.print_pat(inner);
+ }
+ PatKind::Ref(ref inner, mutbl) => {
+ self.s.word("&");
+ if mutbl == ast::Mutability::Mut {
+ self.s.word("mut ");
+ }
+ self.print_pat(inner);
+ }
+ PatKind::Lit(ref e) => self.print_expr(&**e),
+ PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
+ if let Some(e) = begin {
+ self.print_expr(e);
+ self.s.space();
+ }
+ match *end_kind {
+ RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
+ RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
+ RangeEnd::Excluded => self.s.word(".."),
+ }
+ if let Some(e) = end {
+ self.print_expr(e);
+ }
+ }
+ PatKind::Slice(ref elts) => {
+ self.s.word("[");
+ self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+ self.s.word("]");
+ }
+ PatKind::Rest => self.s.word(".."),
+ PatKind::Paren(ref inner) => {
+ self.popen();
+ self.print_pat(inner);
+ self.pclose();
+ }
+ PatKind::MacCall(ref m) => self.print_mac(m),
+ }
+ self.ann.post(self, AnnNode::Pat(pat))
+ }
+
+ fn print_arm(&mut self, arm: &ast::Arm) {
+ // Note, I have no idea why this check is necessary, but here it is.
+ if arm.attrs.is_empty() {
+ self.s.space();
+ }
+ self.cbox(INDENT_UNIT);
+ self.ibox(0);
+ self.maybe_print_comment(arm.pat.span.lo());
+ self.print_outer_attributes(&arm.attrs);
+ self.print_pat(&arm.pat);
+ self.s.space();
+ if let Some(ref e) = arm.guard {
+ self.word_space("if");
+ self.print_expr(e);
+ self.s.space();
+ }
+ self.word_space("=>");
+
+ match arm.body.kind {
+ ast::ExprKind::Block(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+
+ // The block will close the pattern's ibox.
+ self.print_block_unclosed_indent(blk);
+
+ // If it is a user-provided unsafe block, print a comma after it.
+ if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
+ self.s.word(",");
+ }
+ }
+ _ => {
+ self.end(); // Close the ibox for the pattern.
+ self.print_expr(&arm.body);
+ self.s.word(",");
+ }
+ }
+ self.end(); // Close enclosing cbox.
+ }
+
+ fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
+ match explicit_self.node {
+ SelfKind::Value(m) => {
+ self.print_mutability(m, false);
+ self.s.word("self")
+ }
+ SelfKind::Region(ref lt, m) => {
+ self.s.word("&");
+ self.print_opt_lifetime(lt);
+ self.print_mutability(m, false);
+ self.s.word("self")
+ }
+ SelfKind::Explicit(ref typ, m) => {
+ self.print_mutability(m, false);
+ self.s.word("self");
+ self.word_space(":");
+ self.print_type(typ)
+ }
+ }
+ }
+
+ fn print_fn_full(
+ &mut self,
+ sig: &ast::FnSig,
+ name: Ident,
+ generics: &ast::Generics,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ body: Option<&ast::Block>,
+ attrs: &[ast::Attribute],
+ ) {
+ if body.is_some() {
+ self.head("");
+ }
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ self.print_fn(&sig.decl, sig.header, Some(name), generics);
+ if let Some(body) = body {
+ self.nbsp();
+ self.print_block_with_attrs(body, attrs);
+ } else {
+ self.s.word(";");
+ }
+ }
+
+ crate fn print_fn(
+ &mut self,
+ decl: &ast::FnDecl,
+ header: ast::FnHeader,
+ name: Option<Ident>,
+ generics: &ast::Generics,
+ ) {
+ self.print_fn_header_info(header);
+ if let Some(name) = name {
+ self.nbsp();
+ self.print_ident(name);
+ }
+ self.print_generic_params(&generics.params);
+ self.print_fn_params_and_ret(decl, false);
+ self.print_where_clause(&generics.where_clause)
+ }
+
+ crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
+ let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") };
+ self.word(open);
+ self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure));
+ self.word(close);
+ self.print_fn_ret_ty(&decl.output)
+ }
+
+ crate fn print_movability(&mut self, movability: ast::Movability) {
+ match movability {
+ ast::Movability::Static => self.word_space("static"),
+ ast::Movability::Movable => {}
+ }
+ }
+
+ crate fn print_asyncness(&mut self, asyncness: ast::Async) {
+ if asyncness.is_async() {
+ self.word_nbsp("async");
+ }
+ }
+
+ crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
+ match capture_clause {
+ ast::CaptureBy::Value => self.word_space("move"),
+ ast::CaptureBy::Ref => {}
+ }
+ }
+
+ pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) {
+ if !bounds.is_empty() {
+ self.s.word(prefix);
+ let mut first = true;
+ for bound in bounds {
+ if !(first && prefix.is_empty()) {
+ self.nbsp();
+ }
+ if first {
+ first = false;
+ } else {
+ self.word_space("+");
+ }
+
+ match bound {
+ GenericBound::Trait(tref, modifier) => {
+ if modifier == &TraitBoundModifier::Maybe {
+ self.s.word("?");
+ }
+ self.print_poly_trait_ref(tref);
+ }
+ GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+ }
+ }
+ }
+ }
+
+ crate fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
+ self.print_name(lifetime.ident.name)
+ }
+
+ crate fn print_lifetime_bounds(
+ &mut self,
+ lifetime: ast::Lifetime,
+ bounds: &ast::GenericBounds,
+ ) {
+ self.print_lifetime(lifetime);
+ if !bounds.is_empty() {
+ self.s.word(": ");
+ for (i, bound) in bounds.iter().enumerate() {
+ if i != 0 {
+ self.s.word(" + ");
+ }
+ match bound {
+ ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+ _ => panic!(),
+ }
+ }
+ }
+ }
+
+ crate fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+ if generic_params.is_empty() {
+ return;
+ }
+
+ self.s.word("<");
+
+ self.commasep(Inconsistent, &generic_params, |s, param| {
+ s.print_outer_attributes_inline(¶m.attrs);
+
+ match param.kind {
+ ast::GenericParamKind::Lifetime => {
+ let lt = ast::Lifetime { id: param.id, ident: param.ident };
+ s.print_lifetime_bounds(lt, ¶m.bounds)
+ }
+ ast::GenericParamKind::Type { ref default } => {
+ s.print_ident(param.ident);
+ s.print_type_bounds(":", ¶m.bounds);
+ if let Some(ref default) = default {
+ s.s.space();
+ s.word_space("=");
+ s.print_type(default)
+ }
+ }
+ ast::GenericParamKind::Const { ref ty, kw_span: _ } => {
+ s.word_space("const");
+ s.print_ident(param.ident);
+ s.s.space();
+ s.word_space(":");
+ s.print_type(ty);
+ s.print_type_bounds(":", ¶m.bounds)
+ }
+ }
+ });
+
+ self.s.word(">");
+ }
+
+ crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
+ if where_clause.predicates.is_empty() && !where_clause.has_where_token {
+ return;
+ }
+
+ self.s.space();
+ self.word_space("where");
+
+ for (i, predicate) in where_clause.predicates.iter().enumerate() {
+ if i != 0 {
+ self.word_space(",");
+ }
+
+ match *predicate {
+ ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+ ref bound_generic_params,
+ ref bounded_ty,
+ ref bounds,
+ ..
+ }) => {
+ self.print_formal_generic_params(bound_generic_params);
+ self.print_type(bounded_ty);
+ self.print_type_bounds(":", bounds);
+ }
+ ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+ ref lifetime,
+ ref bounds,
+ ..
+ }) => {
+ self.print_lifetime_bounds(*lifetime, bounds);
+ }
+ ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
+ ref lhs_ty,
+ ref rhs_ty,
+ ..
+ }) => {
+ self.print_type(lhs_ty);
+ self.s.space();
+ self.word_space("=");
+ self.print_type(rhs_ty);
+ }
+ }
+ }
+ }
+
+ crate fn print_use_tree(&mut self, tree: &ast::UseTree) {
+ match tree.kind {
+ ast::UseTreeKind::Simple(rename, ..) => {
+ self.print_path(&tree.prefix, false, 0);
+ if let Some(rename) = rename {
+ self.s.space();
+ self.word_space("as");
+ self.print_ident(rename);
+ }
+ }
+ ast::UseTreeKind::Glob => {
+ if !tree.prefix.segments.is_empty() {
+ self.print_path(&tree.prefix, false, 0);
+ self.s.word("::");
+ }
+ self.s.word("*");
+ }
+ ast::UseTreeKind::Nested(ref items) => {
+ if tree.prefix.segments.is_empty() {
+ self.s.word("{");
+ } else {
+ self.print_path(&tree.prefix, false, 0);
+ self.s.word("::{");
+ }
+ self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| {
+ this.print_use_tree(tree)
+ });
+ self.s.word("}");
+ }
+ }
+ }
+
+ pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
+ match mutbl {
+ ast::Mutability::Mut => self.word_nbsp("mut"),
+ ast::Mutability::Not => {
+ if print_const {
+ self.word_nbsp("const");
+ }
+ }
+ }
+ }
+
+ crate fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
+ self.print_mutability(mt.mutbl, print_const);
+ self.print_type(&mt.ty)
+ }
+
+ crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
+ self.ibox(INDENT_UNIT);
+
+ self.print_outer_attributes_inline(&input.attrs);
+
+ match input.ty.kind {
+ ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
+ _ => {
+ if let Some(eself) = input.to_self() {
+ self.print_explicit_self(&eself);
+ } else {
+ let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
+ ident.name == kw::Invalid
+ } else {
+ false
+ };
+ if !invalid {
+ self.print_pat(&input.pat);
+ self.s.word(":");
+ self.s.space();
+ }
+ self.print_type(&input.ty);
+ }
+ }
+ }
+ self.end();
+ }
+
+ crate fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
+ if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
+ self.space_if_not_bol();
+ self.ibox(INDENT_UNIT);
+ self.word_space("->");
+ self.print_type(ty);
+ self.end();
+ self.maybe_print_comment(ty.span.lo());
+ }
+ }
+
+ crate fn print_ty_fn(
+ &mut self,
+ ext: ast::Extern,
+ unsafety: ast::Unsafe,
+ decl: &ast::FnDecl,
+ name: Option<Ident>,
+ generic_params: &[ast::GenericParam],
+ ) {
+ self.ibox(INDENT_UNIT);
+ if !generic_params.is_empty() {
+ self.s.word("for");
+ self.print_generic_params(generic_params);
+ }
+ let generics = ast::Generics {
+ params: Vec::new(),
+ where_clause: ast::WhereClause {
+ has_where_token: false,
+ predicates: Vec::new(),
+ span: rustc_span::DUMMY_SP,
+ },
+ span: rustc_span::DUMMY_SP,
+ };
+ let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
+ self.print_fn(decl, header, name, &generics);
+ self.end();
+ }
+
+ crate fn maybe_print_trailing_comment(
+ &mut self,
+ span: rustc_span::Span,
+ next_pos: Option<BytePos>,
+ ) {
+ if let Some(cmnts) = self.comments() {
+ if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
+ self.print_comment(&cmnt);
+ }
+ }
+ }
+
+ crate fn print_remaining_comments(&mut self) {
+ // If there aren't any remaining comments, then we need to manually
+ // make sure there is a line break at the end.
+ if self.next_comment().is_none() {
+ self.s.hardbreak();
+ }
+ while let Some(ref cmnt) = self.next_comment() {
+ self.print_comment(cmnt);
+ }
+ }
+
+ crate fn print_fn_header_info(&mut self, header: ast::FnHeader) {
+ self.print_constness(header.constness);
+ self.print_asyncness(header.asyncness);
+ self.print_unsafety(header.unsafety);
+
+ match header.ext {
+ ast::Extern::None => {}
+ ast::Extern::Implicit => {
+ self.word_nbsp("extern");
+ }
+ ast::Extern::Explicit(abi) => {
+ self.word_nbsp("extern");
+ self.print_literal(&abi.as_lit());
+ self.nbsp();
+ }
+ }
+
+ self.s.word("fn")
+ }
+
+ crate fn print_unsafety(&mut self, s: ast::Unsafe) {
+ match s {
+ ast::Unsafe::No => {}
+ ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"),
+ }
+ }
+
+ crate fn print_constness(&mut self, s: ast::Const) {
+ match s {
+ ast::Const::No => {}
+ ast::Const::Yes(_) => self.word_nbsp("const"),
+ }
+ }
+
+ crate fn print_is_auto(&mut self, s: ast::IsAuto) {
+ match s {
+ ast::IsAuto::Yes => self.word_nbsp("auto"),
+ ast::IsAuto::No => {}
+ }
+ }
+}
}
/// The available stability levels.
-#[derive(Encodable, Decodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)]
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub enum StabilityLevel {
// Reason for the current stability level and the relevant rust-lang issue
use rustc_data_structures::const_cstr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::query::Providers;
}
}
-pub fn provide(providers: &mut Providers) {
- use rustc_codegen_ssa::target_features::{all_known_features, supported_target_features};
- providers.supported_target_features = |tcx, cnum| {
- assert_eq!(cnum, LOCAL_CRATE);
- if tcx.sess.opts.actually_rustdoc {
- // rustdoc needs to be able to document functions that use all the features, so
- // provide them all.
- all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
- } else {
- supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
- }
- };
-
- provide_extern(providers);
-}
-
-pub fn provide_extern(providers: &mut Providers) {
+pub fn provide_both(providers: &mut Providers) {
providers.wasm_import_module_map = |tcx, cnum| {
// Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about
// for macOS to understand. For more info see #11352
// This can be overridden using --llvm-opts -dwarf-version,N.
// Android has the same issue (#22398)
- if cx.sess().target.target.options.is_like_osx
- || cx.sess().target.target.options.is_like_android
- {
- llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), 2)
+ if let Some(version) = cx.sess().target.target.options.dwarf_version {
+ llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), version)
}
// Indicate that we want CodeView debug information on MSVC
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::ModuleCodegen;
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{ErrorReported, FatalError, Handler};
-use rustc_middle::dep_graph::{DepGraph, WorkProduct};
+use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
use rustc_middle::ty::{self, TyCtxt};
-use rustc_serialize::json;
-use rustc_session::config::{self, OptLevel, OutputFilenames, PrintRequest};
+use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use std::any::Any;
use std::ffi::CStr;
-use std::fs;
use std::sync::Arc;
mod back {
}
fn provide(&self, providers: &mut ty::query::Providers) {
- attributes::provide(providers);
+ attributes::provide_both(providers);
}
fn provide_extern(&self, providers: &mut ty::query::Providers) {
- attributes::provide_extern(providers);
+ attributes::provide_both(providers);
}
fn codegen_crate<'tcx>(
&self,
ongoing_codegen: Box<dyn Any>,
sess: &Session,
- dep_graph: &DepGraph,
- ) -> Result<Box<dyn Any>, ErrorReported> {
+ ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
let (codegen_results, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess);
- if sess.opts.debugging_opts.incremental_info {
- rustc_codegen_ssa::back::write::dump_incremental_data(&codegen_results);
- }
- sess.time("serialize_work_products", move || {
- rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)
+ sess.time("llvm_dump_timing_file", || {
+ if sess.opts.debugging_opts.llvm_time_trace {
+ llvm_util::time_trace_profiler_finish("llvm_timings.json");
+ }
});
- sess.compile_status()?;
-
- Ok(Box::new(codegen_results))
+ Ok((codegen_results, work_products))
}
fn link(
&self,
sess: &Session,
- codegen_results: Box<dyn Any>,
+ codegen_results: CodegenResults,
outputs: &OutputFilenames,
) -> Result<(), ErrorReported> {
- let codegen_results = codegen_results
- .downcast::<CodegenResults>()
- .expect("Expected CodegenResults, found Box<Any>");
-
- if sess.opts.debugging_opts.no_link {
- // FIXME: use a binary format to encode the `.rlink` file
- let rlink_data = json::encode(&codegen_results).map_err(|err| {
- sess.fatal(&format!("failed to encode rlink: {}", err));
- })?;
- let rlink_file = outputs.with_extension(config::RLINK_EXT);
- fs::write(&rlink_file, rlink_data).map_err(|err| {
- sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
- })?;
- return Ok(());
- }
-
// Run the linker on any artifacts that resulted from the LLVM run.
// This should produce either a finished executable or library.
sess.time("link_crate", || {
);
});
- // Now that we won't touch anything in the incremental compilation directory
- // any more, we can finalize it (which involves renaming it)
- rustc_incremental::finalize_session_directory(sess, codegen_results.crate_hash);
-
- sess.time("llvm_dump_timing_file", || {
- if sess.opts.debugging_opts.llvm_time_trace {
- llvm_util::time_trace_profiler_finish("llvm_timings.json");
- }
- });
-
Ok(())
}
}
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::profiling::TimingGuard;
use rustc_data_structures::profiling::VerboseTimingGuard;
-use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter;
use rustc_errors::{DiagnosticId, FatalError, Handler, Level};
let sess = tcx.sess;
let crate_name = tcx.crate_name(LOCAL_CRATE);
- let crate_hash = tcx.crate_hash(LOCAL_CRATE);
let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
let is_compiler_builtins =
tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
OngoingCodegen {
backend,
crate_name,
- crate_hash,
metadata,
windows_subsystem,
linker_info,
// These are used in linking steps and will be cleaned up afterward.
}
-pub fn dump_incremental_data(_codegen_results: &CodegenResults) {
- // FIXME(mw): This does not work at the moment because the situation has
- // become more complicated due to incremental LTO. Now a CGU
- // can have more than two caching states.
- // println!("[incremental] Re-using {} out of {} modules",
- // codegen_results.modules.iter().filter(|m| m.pre_existing).count(),
- // codegen_results.modules.len());
-}
-
pub enum WorkItem<B: WriteBackendMethods> {
/// Optimize a newly codegened, totally unoptimized module.
Optimize(ModuleCodegen<B::Module>),
pub struct OngoingCodegen<B: ExtraBackendMethods> {
pub backend: B,
pub crate_name: Symbol,
- pub crate_hash: Svh,
pub metadata: EncodedMetadata,
pub windows_subsystem: Option<String>,
pub linker_info: LinkerInfo,
(
CodegenResults {
crate_name: self.crate_name,
- crate_hash: self.crate_hash,
metadata: self.metadata,
windows_subsystem: self.windows_subsystem,
linker_info: self.linker_info,
extern crate rustc_middle;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::Lrc;
use rustc_hir::def_id::CrateNum;
use rustc_hir::LangItem;
pub modules: Vec<CompiledModule>,
pub allocator_module: Option<CompiledModule>,
pub metadata_module: Option<CompiledModule>,
- pub crate_hash: Svh,
pub metadata: rustc_middle::middle::cstore::EncodedMetadata,
pub windows_subsystem: Option<String>,
pub linker_info: back::linker::LinkerInfo,
pub fn provide(providers: &mut Providers) {
crate::back::symbol_export::provide(providers);
crate::base::provide_both(providers);
+ crate::target_features::provide(providers);
}
pub fn provide_extern(providers: &mut Providers) {
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::ty::query::Providers;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::symbol::Symbol;
_ => &[],
}
}
+
+pub(crate) fn provide(providers: &mut Providers) {
+ providers.supported_target_features = |tcx, cnum| {
+ assert_eq!(cnum, LOCAL_CRATE);
+ if tcx.sess.opts.actually_rustdoc {
+ // rustdoc needs to be able to document functions that use all the features, so
+ // whitelist them all
+ all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
+ } else {
+ supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
+ }
+ };
+}
use super::write::WriteBackendMethods;
use super::CodegenObject;
-use crate::ModuleCodegen;
+use crate::{CodegenResults, ModuleCodegen};
use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorReported;
-use rustc_middle::dep_graph::DepGraph;
+use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
use rustc_middle::ty::query::Providers;
&self,
ongoing_codegen: Box<dyn Any>,
sess: &Session,
- dep_graph: &DepGraph,
- ) -> Result<Box<dyn Any>, ErrorReported>;
+ ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported>;
/// This is called on the returned `Box<dyn Any>` from `join_codegen`
///
fn link(
&self,
sess: &Session,
- codegen_results: Box<dyn Any>,
+ codegen_results: CodegenResults,
outputs: &OutputFilenames,
) -> Result<(), ErrorReported>;
}
let codegen_results: CodegenResults = json::decode(&rlink_data).unwrap_or_else(|err| {
sess.fatal(&format!("failed to decode rlink: {}", err));
});
- compiler.codegen_backend().link(&sess, Box::new(codegen_results), &outputs)
+ compiler.codegen_backend().link(&sess, codegen_results, &outputs)
} else {
sess.fatal("rlink must be a file")
}
}
impl<'a> PrintState<'a> for State<'a> {
+ fn insert_extra_parens(&self) -> bool {
+ true
+ }
fn comments(&mut self) -> &mut Option<Comments<'a>> {
&mut self.comments
}
use rustc_ast as ast;
use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
use rustc_errors::ErrorReported;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::ty::steal::Steal;
use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
-use rustc_session::config::{OutputFilenames, OutputType};
+use rustc_serialize::json;
+use rustc_session::config::{self, OutputFilenames, OutputType};
use rustc_session::{output::find_crate_name, Session};
use rustc_span::symbol::sym;
use std::any::Any;
pub fn linker(&'tcx self) -> Result<Linker> {
let dep_graph = self.dep_graph()?;
let prepare_outputs = self.prepare_outputs()?;
+ let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
let ongoing_codegen = self.ongoing_codegen()?;
let sess = self.session().clone();
sess,
dep_graph: dep_graph.peek().clone(),
prepare_outputs: prepare_outputs.take(),
+ crate_hash,
ongoing_codegen: ongoing_codegen.take(),
codegen_backend,
})
sess: Lrc<Session>,
dep_graph: DepGraph,
prepare_outputs: OutputFilenames,
+ crate_hash: Svh,
ongoing_codegen: Box<dyn Any>,
codegen_backend: Lrc<Box<dyn CodegenBackend>>,
}
impl Linker {
pub fn link(self) -> Result<()> {
- let codegen_results =
- self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?;
- let prof = self.sess.prof.clone();
+ let (codegen_results, work_products) =
+ self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess)?;
+
+ self.sess.compile_status()?;
+
+ let sess = &self.sess;
let dep_graph = self.dep_graph;
+ sess.time("serialize_work_products", || {
+ rustc_incremental::save_work_product_index(&sess, &dep_graph, work_products)
+ });
+
+ let prof = self.sess.prof.clone();
prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph));
+ // Now that we won't touch anything in the incremental compilation directory
+ // any more, we can finalize it (which involves renaming it)
+ rustc_incremental::finalize_session_directory(&self.sess, self.crate_hash);
+
if !self
.sess
.opts
{
return Ok(());
}
+
+ if sess.opts.debugging_opts.no_link {
+ // FIXME: use a binary format to encode the `.rlink` file
+ let rlink_data = json::encode(&codegen_results).map_err(|err| {
+ sess.fatal(&format!("failed to encode rlink: {}", err));
+ })?;
+ let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
+ std::fs::write(&rlink_file, rlink_data).map_err(|err| {
+ sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
+ })?;
+ return Ok(());
+ }
+
self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs)
}
}
#![feature(or_patterns)]
use rustc_ast as ast;
-use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{self, TokenStream, TokenTree};
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
};
// FIXME(#43081): Avoid this pretty-print + reparse hack
- let source = pprust::nonterminal_to_string(nt);
+ // Pretty-print the AST struct without inserting any parenthesis
+ // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`).
+ // The resulting stream may have incorrect precedence, but it's only
+ // ever used for a comparison against the capture tokenstream.
+ let source = pprust::nonterminal_to_string_no_extra_parens(nt);
let filename = FileName::macro_expansion_source_code(&source);
let reparsed_tokens = parse_stream_from_source_str(filename, source, sess, Some(span));
// modifications, including adding/removing typically non-semantic
// tokens such as extra braces and commas, don't happen.
if let Some(tokens) = tokens {
- if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess) {
+ // Compare with a non-relaxed delim match to start.
+ if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) {
return tokens;
}
+
+ // The check failed. This time, we pretty-print the AST struct with parenthesis
+ // inserted to preserve precedence. This may cause `None`-delimiters in the captured
+ // token stream to match up with inserted parenthesis in the reparsed stream.
+ let source_with_parens = pprust::nonterminal_to_string(nt);
+ let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens);
+ let reparsed_tokens_with_parens = parse_stream_from_source_str(
+ filename_with_parens,
+ source_with_parens,
+ sess,
+ Some(span),
+ );
+
+ // Compare with a relaxed delim match - we want inserted parenthesis in the
+ // reparsed stream to match `None`-delimiters in the original stream.
+ if tokenstream_probably_equal_for_proc_macro(
+ &tokens,
+ &reparsed_tokens_with_parens,
+ sess,
+ true,
+ ) {
+ return tokens;
+ }
+
info!(
"cached tokens found, but they're not \"probably equal\", \
going with stringified version"
);
- info!("cached tokens: {:?}", tokens);
- info!("reparsed tokens: {:?}", reparsed_tokens);
+ info!("cached tokens: {}", pprust::tts_to_string(&tokens));
+ info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens));
+
+ info!("cached tokens debug: {:?}", tokens);
+ info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens);
}
reparsed_tokens
}
tokens: &TokenStream,
reparsed_tokens: &TokenStream,
sess: &ParseSess,
+ relaxed_delim_match: bool,
) -> bool {
// When checking for `probably_eq`, we ignore certain tokens that aren't
// preserved in the AST. Because they are not preserved, the pretty
let tokens = tokens.trees().flat_map(|t| expand_token(t, sess));
let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess));
- tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess))
+ tokens.eq_by(reparsed_tokens, |t, rt| {
+ tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match)
+ })
}
// See comments in `Nonterminal::to_tokenstream` for why we care about
token: &TokenTree,
reparsed_token: &TokenTree,
sess: &ParseSess,
+ relaxed_delim_match: bool,
) -> bool {
match (token, reparsed_token) {
(TokenTree::Token(token), TokenTree::Token(reparsed_token)) => {
(
TokenTree::Delimited(_, delim, tokens),
TokenTree::Delimited(_, reparsed_delim, reparsed_tokens),
- ) => {
- delim == reparsed_delim
- && tokenstream_probably_equal_for_proc_macro(tokens, reparsed_tokens, sess)
+ ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro(
+ tokens,
+ reparsed_tokens,
+ sess,
+ relaxed_delim_match,
+ ),
+ (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => {
+ if relaxed_delim_match {
+ if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token
+ {
+ if tokenstream_probably_equal_for_proc_macro(
+ tokens,
+ reparsed_tokens,
+ sess,
+ relaxed_delim_match,
+ ) {
+ return true;
+ }
+ }
+ }
+ tokens.len() == 1
+ && tokentree_probably_equal_for_proc_macro(
+ &tokens.trees().next().unwrap(),
+ reparsed_token,
+ sess,
+ relaxed_delim_match,
+ )
}
_ => false,
}
self.doc_alias_str_error(meta);
return false;
}
- if let Some(c) =
- doc_alias.chars().find(|&c| c == '"' || c == '\'' || c.is_whitespace())
+ if let Some(c) = doc_alias
+ .chars()
+ .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
{
self.tcx
.sess
.emit();
return false;
}
+ if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
+ self.tcx
+ .sess
+ .struct_span_err(
+ meta.span(),
+ "`#[doc(alias = \"...\")]` cannot start or end with ' '",
+ )
+ .emit();
+ return false;
+ }
if let Some(err) = match target {
Target::Impl => Some("implementation block"),
Target::ForeignMod => Some("extern block"),
try_trait,
tt,
tuple,
+ tuple_from_req,
tuple_indexing,
two_phase,
ty,
.unwrap()
.push("-Wl,--allow-multiple-definition".to_string());
base.is_like_android = true;
+ base.dwarf_version = Some(2);
base.position_independent_executables = true;
base.has_elf_tls = false;
base.requires_uwtable = true;
executables: true,
target_family: Some("unix".to_string()),
is_like_osx: true,
+ dwarf_version: Some(2),
has_rpath: true,
dll_prefix: "lib".to_string(),
dll_suffix: ".dylib".to_string(),
pre_link_args: args,
position_independent_executables: true,
relro_level: RelroLevel::Full,
+ dwarf_version: Some(2),
..Default::default()
}
}
eliminate_frame_pointer: false, // FIXME 43575
relro_level: RelroLevel::Full,
abi_return_struct_as_int: true,
+ dwarf_version: Some(2),
..Default::default()
}
}
pub is_like_emscripten: bool,
/// Whether the target toolchain is like Fuchsia's.
pub is_like_fuchsia: bool,
+ /// Version of DWARF to use if not using the default.
+ /// Useful because some platforms (osx, bsd) only want up to DWARF2.
+ pub dwarf_version: Option<u32>,
/// Whether the linker support GNU-like arguments such as -O. Defaults to false.
pub linker_is_gnu: bool,
/// The MinGW toolchain has a known issue that prevents it from correctly
is_like_emscripten: false,
is_like_msvc: false,
is_like_fuchsia: false,
+ dwarf_version: None,
linker_is_gnu: false,
allows_weak_linkage: true,
has_rpath: false,
base.options.$key_name = s;
}
} );
+ ($key_name:ident, Option<u32>) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
+ if s < 1 || s > 5 {
+ return Err("Not a valid DWARF version number".to_string());
+ }
+ base.options.$key_name = Some(s as u32);
+ }
+ } );
($key_name:ident, Option<u64>) => ( {
let name = (stringify!($key_name)).replace("_", "-");
if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
key!(is_like_emscripten, bool);
key!(is_like_android, bool);
key!(is_like_fuchsia, bool);
+ key!(dwarf_version, Option<u32>);
key!(linker_is_gnu, bool);
key!(allows_weak_linkage, bool);
key!(has_rpath, bool);
target_option_val!(is_like_emscripten);
target_option_val!(is_like_android);
target_option_val!(is_like_fuchsia);
+ target_option_val!(dwarf_version);
target_option_val!(linker_is_gnu);
target_option_val!(allows_weak_linkage);
target_option_val!(has_rpath);
position_independent_executables: true,
relro_level: RelroLevel::Full,
use_ctors_section: true,
+ dwarf_version: Some(2),
..Default::default()
}
}
position_independent_executables: true,
eliminate_frame_pointer: false, // FIXME 43575
relro_level: RelroLevel::Full,
+ dwarf_version: Some(2),
..Default::default()
}
}
.iter()
.map(|&(region_bound, span)| {
let outlives = ty::OutlivesPredicate(param_ty, region_bound);
- (ty::Binder::dummy(outlives).to_predicate(tcx), span)
+ (ty::Binder::bind(outlives).to_predicate(tcx), span)
})
.chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| {
let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx);
+++ /dev/null
-// ignore-tidy-filelength
-// FIXME: This file seems to have too much functionality wrapped into it,
-// leading to it being too long.
-// Splitting this file may involve abstracting functionality into other files.
-
-use super::callee::{self, DeferredCallResolution};
-use super::coercion::{CoerceMany, DynamicCoerceMany};
-use super::method::{self, MethodCallee, SelfSource};
-use super::Expectation::*;
-use super::TupleArgumentsFlag::*;
-use super::{
- potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, EnclosingBreakables,
- Expectation, FallbackMode, Inherited, LocalTy, Needs, TupleArgumentsFlag, UnsafetyState,
-};
-use crate::astconv::{
- AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
-};
-
-use rustc_ast as ast;
-use rustc_ast::util::parser::ExprPrecedence;
-use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::ErrorReported;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath};
-use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
-use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_infer::infer::{self, InferOk, InferResult};
-use rustc_middle::hir::map::blocks::FnLikeNode;
-use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
-};
-use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::subst::{
- self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
-};
-use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind, ToPolyTraitRef,
- ToPredicate, Ty, TyCtxt, UserType,
-};
-use rustc_session::{lint, Session};
-use rustc_span::hygiene::DesugaringKind;
-use rustc_span::source_map::{original_sp, DUMMY_SP};
-use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{self, BytePos, MultiSpan, Span};
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::opaque_types::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
-};
-
-use std::cell::{Cell, RefCell};
-use std::collections::hash_map::Entry;
-use std::iter;
-use std::mem::replace;
-use std::ops::Deref;
-use std::slice;
-
-pub struct FnCtxt<'a, 'tcx> {
- pub(super) body_id: hir::HirId,
-
- /// The parameter environment used for proving trait obligations
- /// in this function. This can change when we descend into
- /// closures (as they bring new things into scope), hence it is
- /// not part of `Inherited` (as of the time of this writing,
- /// closures do not yet change the environment, but they will
- /// eventually).
- pub(super) param_env: ty::ParamEnv<'tcx>,
-
- /// Number of errors that had been reported when we started
- /// checking this function. On exit, if we find that *more* errors
- /// have been reported, we will skip regionck and other work that
- /// expects the types within the function to be consistent.
- // FIXME(matthewjasper) This should not exist, and it's not correct
- // if type checking is run in parallel.
- err_count_on_creation: usize,
-
- /// If `Some`, this stores coercion information for returned
- /// expressions. If `None`, this is in a context where return is
- /// inappropriate, such as a const expression.
- ///
- /// This is a `RefCell<DynamicCoerceMany>`, which means that we
- /// can track all the return expressions and then use them to
- /// compute a useful coercion from the set, similar to a match
- /// expression or other branching context. You can use methods
- /// like `expected_ty` to access the declared return type (if
- /// any).
- pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
-
- pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,
-
- pub(super) ret_type_span: Option<Span>,
-
- /// Used exclusively to reduce cost of advanced evaluation used for
- /// more helpful diagnostics.
- pub(super) in_tail_expr: bool,
-
- /// First span of a return site that we find. Used in error messages.
- pub(super) ret_coercion_span: RefCell<Option<Span>>,
-
- pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
-
- pub(super) ps: RefCell<UnsafetyState>,
-
- /// Whether the last checked node generates a divergence (e.g.,
- /// `return` will set this to `Always`). In general, when entering
- /// an expression or other node in the tree, the initial value
- /// indicates whether prior parts of the containing expression may
- /// have diverged. It is then typically set to `Maybe` (and the
- /// old value remembered) for processing the subparts of the
- /// current expression. As each subpart is processed, they may set
- /// the flag to `Always`, etc. Finally, at the end, we take the
- /// result and "union" it with the original value, so that when we
- /// return the flag indicates if any subpart of the parent
- /// expression (up to and including this part) has diverged. So,
- /// if you read it after evaluating a subexpression `X`, the value
- /// you get indicates whether any subexpression that was
- /// evaluating up to and including `X` diverged.
- ///
- /// We currently use this flag only for diagnostic purposes:
- ///
- /// - To warn about unreachable code: if, after processing a
- /// sub-expression but before we have applied the effects of the
- /// current node, we see that the flag is set to `Always`, we
- /// can issue a warning. This corresponds to something like
- /// `foo(return)`; we warn on the `foo()` expression. (We then
- /// update the flag to `WarnedAlways` to suppress duplicate
- /// reports.) Similarly, if we traverse to a fresh statement (or
- /// tail expression) from a `Always` setting, we will issue a
- /// warning. This corresponds to something like `{return;
- /// foo();}` or `{return; 22}`, where we would warn on the
- /// `foo()` or `22`.
- ///
- /// An expression represents dead code if, after checking it,
- /// the diverges flag is set to something other than `Maybe`.
- pub(super) diverges: Cell<Diverges>,
-
- /// Whether any child nodes have any type errors.
- pub(super) has_errors: Cell<bool>,
-
- pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
-
- pub(super) inh: &'a Inherited<'a, 'tcx>,
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn new(
- inh: &'a Inherited<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ) -> FnCtxt<'a, 'tcx> {
- FnCtxt {
- body_id,
- param_env,
- err_count_on_creation: inh.tcx.sess.err_count(),
- ret_coercion: None,
- ret_coercion_impl_trait: None,
- ret_type_span: None,
- in_tail_expr: false,
- ret_coercion_span: RefCell::new(None),
- resume_yield_tys: None,
- ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
- diverges: Cell::new(Diverges::Maybe),
- has_errors: Cell::new(false),
- enclosing_breakables: RefCell::new(EnclosingBreakables {
- stack: Vec::new(),
- by_id: Default::default(),
- }),
- inh,
- }
- }
-
- pub fn sess(&self) -> &Session {
- &self.tcx.sess
- }
-
- pub fn errors_reported_since_creation(&self) -> bool {
- self.tcx.sess.err_count() > self.err_count_on_creation
- }
-
- /// Produces warning on the given node, if the current point in the
- /// function is unreachable, and there hasn't been another warning.
- pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
- // FIXME: Combine these two 'if' expressions into one once
- // let chains are implemented
- if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
- // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
- // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
- // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
- if !span.is_desugaring(DesugaringKind::CondTemporary)
- && !span.is_desugaring(DesugaringKind::Async)
- && !orig_span.is_desugaring(DesugaringKind::Await)
- {
- self.diverges.set(Diverges::WarnedAlways);
-
- debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
-
- self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
- let msg = format!("unreachable {}", kind);
- lint.build(&msg)
- .span_label(span, &msg)
- .span_label(
- orig_span,
- custom_note
- .unwrap_or("any code following this expression is unreachable"),
- )
- .emit();
- })
- }
- }
- }
-
- pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
- ObligationCause::new(span, self.body_id, code)
- }
-
- pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
- self.cause(span, ObligationCauseCode::MiscObligation)
- }
-
- /// Resolves type and const variables in `ty` if possible. Unlike the infcx
- /// version (resolve_vars_if_possible), this version will
- /// also select obligations if it seems useful, in an effort
- /// to get more type information.
- pub(super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
- debug!("resolve_vars_with_obligations(ty={:?})", ty);
-
- // No Infer()? Nothing needs doing.
- if !ty.has_infer_types_or_consts() {
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
- return ty;
- }
-
- // If `ty` is a type variable, see whether we already know what it is.
- ty = self.resolve_vars_if_possible(&ty);
- if !ty.has_infer_types_or_consts() {
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
- return ty;
- }
-
- // If not, try resolving pending obligations as much as
- // possible. This can help substantially when there are
- // indirect dependencies that don't seem worth tracking
- // precisely.
- self.select_obligations_where_possible(false, |_| {});
- ty = self.resolve_vars_if_possible(&ty);
-
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
- ty
- }
-
- pub(super) fn record_deferred_call_resolution(
- &self,
- closure_def_id: DefId,
- r: DeferredCallResolution<'tcx>,
- ) {
- let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
- deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
- }
-
- pub(super) fn remove_deferred_call_resolutions(
- &self,
- closure_def_id: DefId,
- ) -> Vec<DeferredCallResolution<'tcx>> {
- let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
- deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
- }
-
- pub fn tag(&self) -> String {
- format!("{:p}", self)
- }
-
- pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
- self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
- span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
- })
- }
-
- #[inline]
- pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
- debug!(
- "write_ty({:?}, {:?}) in fcx {}",
- id,
- self.resolve_vars_if_possible(&ty),
- self.tag()
- );
- self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
-
- if ty.references_error() {
- self.has_errors.set(true);
- self.set_tainted_by_errors();
- }
- }
-
- pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
- self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
- }
-
- fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) {
- self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
- }
-
- pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
- debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
- self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
- self.write_substs(hir_id, method.substs);
-
- // When the method is confirmed, the `method.substs` includes
- // parameters from not just the method, but also the impl of
- // the method -- in particular, the `Self` type will be fully
- // resolved. However, those are not something that the "user
- // specified" -- i.e., those types come from the inferred type
- // of the receiver, not something the user wrote. So when we
- // create the user-substs, we want to replace those earlier
- // types with just the types that the user actually wrote --
- // that is, those that appear on the *method itself*.
- //
- // As an example, if the user wrote something like
- // `foo.bar::<u32>(...)` -- the `Self` type here will be the
- // type of `foo` (possibly adjusted), but we don't want to
- // include that. We want just the `[_, u32]` part.
- if !method.substs.is_noop() {
- let method_generics = self.tcx.generics_of(method.def_id);
- if !method_generics.params.is_empty() {
- let user_type_annotation = self.infcx.probe(|_| {
- let user_substs = UserSubsts {
- substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
- let i = param.index as usize;
- if i < method_generics.parent_count {
- self.infcx.var_for_def(DUMMY_SP, param)
- } else {
- method.substs[i]
- }
- }),
- user_self_ty: None, // not relevant here
- };
-
- self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
- method.def_id,
- user_substs,
- ))
- });
-
- debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
- self.write_user_type_annotation(hir_id, user_type_annotation);
- }
- }
- }
-
- pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
- if !substs.is_noop() {
- debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
-
- self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
- }
- }
-
- /// Given the substs that we just converted from the HIR, try to
- /// canonicalize them and store them as user-given substitutions
- /// (i.e., substitutions that must be respected by the NLL check).
- ///
- /// This should be invoked **before any unifications have
- /// occurred**, so that annotations like `Vec<_>` are preserved
- /// properly.
- pub fn write_user_type_annotation_from_substs(
- &self,
- hir_id: hir::HirId,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- user_self_ty: Option<UserSelfTy<'tcx>>,
- ) {
- debug!(
- "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
- user_self_ty={:?} in fcx {}",
- hir_id,
- def_id,
- substs,
- user_self_ty,
- self.tag(),
- );
-
- if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
- let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
- def_id,
- UserSubsts { substs, user_self_ty },
- ));
- debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
- self.write_user_type_annotation(hir_id, canonicalized);
- }
- }
-
- pub fn write_user_type_annotation(
- &self,
- hir_id: hir::HirId,
- canonical_user_type_annotation: CanonicalUserType<'tcx>,
- ) {
- debug!(
- "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
- hir_id,
- canonical_user_type_annotation,
- self.tag(),
- );
-
- if !canonical_user_type_annotation.is_identity() {
- self.typeck_results
- .borrow_mut()
- .user_provided_types_mut()
- .insert(hir_id, canonical_user_type_annotation);
- } else {
- debug!("write_user_type_annotation: skipping identity substs");
- }
- }
-
- pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
- debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
-
- if adj.is_empty() {
- return;
- }
-
- let autoborrow_mut = adj.iter().any(|adj| {
- matches!(adj, &Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
- ..
- })
- });
-
- match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
- Entry::Vacant(entry) => {
- entry.insert(adj);
- }
- Entry::Occupied(mut entry) => {
- debug!(" - composing on top of {:?}", entry.get());
- match (&entry.get()[..], &adj[..]) {
- // Applying any adjustment on top of a NeverToAny
- // is a valid NeverToAny adjustment, because it can't
- // be reached.
- (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
- (&[
- Adjustment { kind: Adjust::Deref(_), .. },
- Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
- ], &[
- Adjustment { kind: Adjust::Deref(_), .. },
- .. // Any following adjustments are allowed.
- ]) => {
- // A reborrow has no effect before a dereference.
- }
- // FIXME: currently we never try to compose autoderefs
- // and ReifyFnPointer/UnsafeFnPointer, but we could.
- _ =>
- bug!("while adjusting {:?}, can't compose {:?} and {:?}",
- expr, entry.get(), adj)
- };
- *entry.get_mut() = adj;
- }
- }
-
- // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
- // In this case implicit use of `Deref` and `Index` within `<expr>` should
- // instead be `DerefMut` and `IndexMut`, so fix those up.
- if autoborrow_mut {
- self.convert_place_derefs_to_mutable(expr);
- }
- }
-
- /// Basically whenever we are converting from a type scheme into
- /// the fn body space, we always want to normalize associated
- /// types as well. This function combines the two.
- fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- let value = value.subst(self.tcx, substs);
- let result = self.normalize_associated_types_in(span, &value);
- debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
- result
- }
-
- /// As `instantiate_type_scheme`, but for the bounds found in a
- /// generic type scheme.
- fn instantiate_bounds(
- &self,
- span: Span,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
- let bounds = self.tcx.predicates_of(def_id);
- let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
- let result = bounds.instantiate(self.tcx, substs);
- let result = self.normalize_associated_types_in(span, &result);
- debug!(
- "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
- bounds, substs, result, spans,
- );
- (result, spans)
- }
-
- /// Replaces the opaque types from the given value with type variables,
- /// and records the `OpaqueTypeMap` for later use during writeback. See
- /// `InferCtxt::instantiate_opaque_types` for more details.
- pub(super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
- &self,
- parent_id: hir::HirId,
- value: &T,
- value_span: Span,
- ) -> T {
- let parent_def_id = self.tcx.hir().local_def_id(parent_id);
- debug!(
- "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
- parent_def_id, value
- );
-
- let (value, opaque_type_map) =
- self.register_infer_ok_obligations(self.instantiate_opaque_types(
- parent_def_id,
- self.body_id,
- self.param_env,
- value,
- value_span,
- ));
-
- let mut opaque_types = self.opaque_types.borrow_mut();
- let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
- for (ty, decl) in opaque_type_map {
- let _ = opaque_types.insert(ty, decl);
- let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
- }
-
- value
- }
-
- pub(super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
- }
-
- pub(super) fn normalize_associated_types_in_as_infer_ok<T>(
- &self,
- span: Span,
- value: &T,
- ) -> InferOk<'tcx, T>
- where
- T: TypeFoldable<'tcx>,
- {
- self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
- }
-
- pub fn require_type_meets(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- def_id: DefId,
- ) {
- self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
- }
-
- pub fn require_type_is_sized(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- if !ty.references_error() {
- let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
- self.require_type_meets(ty, span, code, lang_item);
- }
- }
-
- pub fn require_type_is_sized_deferred(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- if !ty.references_error() {
- self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
- }
- }
-
- pub fn register_bound(
- &self,
- ty: Ty<'tcx>,
- def_id: DefId,
- cause: traits::ObligationCause<'tcx>,
- ) {
- if !ty.references_error() {
- self.fulfillment_cx.borrow_mut().register_bound(
- self,
- self.param_env,
- ty,
- def_id,
- cause,
- );
- }
- }
-
- pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
- let t = AstConv::ast_ty_to_ty(self, ast_t);
- self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
- t
- }
-
- pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- let ty = self.to_ty(ast_ty);
- debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
-
- if Self::can_contain_user_lifetime_bounds(ty) {
- let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
- debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
- self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
- }
-
- ty
- }
-
- pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
- let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
- let c = ty::Const::from_anon_const(self.tcx, const_def_id);
- self.register_wf_obligation(
- c.into(),
- self.tcx.hir().span(ast_c.hir_id),
- ObligationCauseCode::MiscObligation,
- );
- c
- }
-
- pub fn const_arg_to_const(
- &self,
- ast_c: &hir::AnonConst,
- param_def_id: DefId,
- ) -> &'tcx ty::Const<'tcx> {
- let const_def = ty::WithOptConstParam {
- did: self.tcx.hir().local_def_id(ast_c.hir_id),
- const_param_did: Some(param_def_id),
- };
- let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
- self.register_wf_obligation(
- c.into(),
- self.tcx.hir().span(ast_c.hir_id),
- ObligationCauseCode::MiscObligation,
- );
- c
- }
-
- // If the type given by the user has free regions, save it for later, since
- // NLL would like to enforce those. Also pass in types that involve
- // projections, since those can resolve to `'static` bounds (modulo #54940,
- // which hopefully will be fixed by the time you see this comment, dear
- // reader, although I have my doubts). Also pass in types with inference
- // types, because they may be repeated. Other sorts of things are already
- // sufficiently enforced with erased regions. =)
- fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
- where
- T: TypeFoldable<'tcx>,
- {
- t.has_free_regions() || t.has_projections() || t.has_infer_types()
- }
-
- pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
- match self.typeck_results.borrow().node_types().get(id) {
- Some(&t) => t,
- None if self.is_tainted_by_errors() => self.tcx.ty_error(),
- None => {
- bug!(
- "no type for node {}: {} in fcx {}",
- id,
- self.tcx.hir().node_to_string(id),
- self.tag()
- );
- }
- }
- }
-
- /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
- pub fn register_wf_obligation(
- &self,
- arg: subst::GenericArg<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- // WF obligations never themselves fail, so no real need to give a detailed cause:
- let cause = traits::ObligationCause::new(span, self.body_id, code);
- self.register_predicate(traits::Obligation::new(
- cause,
- self.param_env,
- ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
- ));
- }
-
- /// Registers obligations that all `substs` are well-formed.
- pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
- for arg in substs.iter().filter(|arg| {
- matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
- }) {
- self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
- }
- }
-
- /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
- /// type/region parameter was instantiated (`substs`), creates and registers suitable
- /// trait/region obligations.
- ///
- /// For example, if there is a function:
- ///
- /// ```
- /// fn foo<'a,T:'a>(...)
- /// ```
- ///
- /// and a reference:
- ///
- /// ```
- /// let f = foo;
- /// ```
- ///
- /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
- /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
- pub fn add_obligations_for_parameters(
- &self,
- cause: traits::ObligationCause<'tcx>,
- predicates: ty::InstantiatedPredicates<'tcx>,
- ) {
- assert!(!predicates.has_escaping_bound_vars());
-
- debug!("add_obligations_for_parameters(predicates={:?})", predicates);
-
- for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
- self.register_predicate(obligation);
- }
- }
-
- // FIXME(arielb1): use this instead of field.ty everywhere
- // Only for fields! Returns <none> for methods>
- // Indifferent to privacy flags
- pub fn field_ty(
- &self,
- span: Span,
- field: &'tcx ty::FieldDef,
- substs: SubstsRef<'tcx>,
- ) -> Ty<'tcx> {
- self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
- }
-
- pub(super) fn check_casts(&self) {
- let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
- for cast in deferred_cast_checks.drain(..) {
- cast.check(self);
- }
- }
-
- pub(super) fn resolve_generator_interiors(&self, def_id: DefId) {
- let mut generators = self.deferred_generator_interiors.borrow_mut();
- for (body_id, interior, kind) in generators.drain(..) {
- self.select_obligations_where_possible(false, |_| {});
- super::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
- }
- }
-
- // Tries to apply a fallback to `ty` if it is an unsolved variable.
- //
- // - Unconstrained ints are replaced with `i32`.
- //
- // - Unconstrained floats are replaced with with `f64`.
- //
- // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
- // is enabled. Otherwise, they are replaced with `()`.
- //
- // Fallback becomes very dubious if we have encountered type-checking errors.
- // In that case, fallback to Error.
- // The return value indicates whether fallback has occurred.
- pub(super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
- use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
- use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
-
- assert!(ty.is_ty_infer());
- let fallback = match self.type_is_unconstrained_numeric(ty) {
- _ if self.is_tainted_by_errors() => self.tcx().ty_error(),
- UnconstrainedInt => self.tcx.types.i32,
- UnconstrainedFloat => self.tcx.types.f64,
- Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
- Neither => {
- // This type variable was created from the instantiation of an opaque
- // type. The fact that we're attempting to perform fallback for it
- // means that the function neither constrained it to a concrete
- // type, nor to the opaque type itself.
- //
- // For example, in this code:
- //
- //```
- // type MyType = impl Copy;
- // fn defining_use() -> MyType { true }
- // fn other_use() -> MyType { defining_use() }
- // ```
- //
- // `defining_use` will constrain the instantiated inference
- // variable to `bool`, while `other_use` will constrain
- // the instantiated inference variable to `MyType`.
- //
- // When we process opaque types during writeback, we
- // will handle cases like `other_use`, and not count
- // them as defining usages
- //
- // However, we also need to handle cases like this:
- //
- // ```rust
- // pub type Foo = impl Copy;
- // fn produce() -> Option<Foo> {
- // None
- // }
- // ```
- //
- // In the above snippet, the inference variable created by
- // instantiating `Option<Foo>` will be completely unconstrained.
- // We treat this as a non-defining use by making the inference
- // variable fall back to the opaque type itself.
- if let FallbackMode::All = mode {
- if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
- debug!(
- "fallback_if_possible: falling back opaque type var {:?} to {:?}",
- ty, opaque_ty
- );
- *opaque_ty
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
- };
- debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
- self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
- true
- }
-
- pub(super) fn select_all_obligations_or_error(&self) {
- debug!("select_all_obligations_or_error");
- if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
- self.report_fulfillment_errors(&errors, self.inh.body_id, false);
- }
- }
-
- /// Select as many obligations as we can at present.
- pub(super) fn select_obligations_where_possible(
- &self,
- fallback_has_occurred: bool,
- mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
- ) {
- let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
- if let Err(mut errors) = result {
- mutate_fullfillment_errors(&mut errors);
- self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
- }
- }
-
- /// For the overloaded place expressions (`*x`, `x[3]`), the trait
- /// returns a type of `&T`, but the actual type we assign to the
- /// *expression* is `T`. So this function just peels off the return
- /// type by one layer to yield `T`.
- pub(super) fn make_overloaded_place_return_type(
- &self,
- method: MethodCallee<'tcx>,
- ) -> ty::TypeAndMut<'tcx> {
- // extract method return type, which will be &T;
- let ret_ty = method.sig.output();
-
- // method returns &T, but the type as visible to user is T, so deref
- ret_ty.builtin_deref(true).unwrap()
- }
-
- pub(super) fn check_method_argument_types(
- &self,
- sp: Span,
- expr: &'tcx hir::Expr<'tcx>,
- method: Result<MethodCallee<'tcx>, ()>,
- args_no_rcvr: &'tcx [hir::Expr<'tcx>],
- tuple_arguments: TupleArgumentsFlag,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let has_error = match method {
- Ok(method) => method.substs.references_error() || method.sig.references_error(),
- Err(_) => true,
- };
- if has_error {
- let err_inputs = self.err_args(args_no_rcvr.len());
-
- let err_inputs = match tuple_arguments {
- DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
- };
-
- self.check_argument_types(
- sp,
- expr,
- &err_inputs[..],
- &[],
- args_no_rcvr,
- false,
- tuple_arguments,
- None,
- );
- return self.tcx.ty_error();
- }
-
- let method = method.unwrap();
- // HACK(eddyb) ignore self in the definition (see above).
- let expected_arg_tys = self.expected_inputs_for_expected_output(
- sp,
- expected,
- method.sig.output(),
- &method.sig.inputs()[1..],
- );
- self.check_argument_types(
- sp,
- expr,
- &method.sig.inputs()[1..],
- &expected_arg_tys[..],
- args_no_rcvr,
- method.sig.c_variadic,
- tuple_arguments,
- self.tcx.hir().span_if_local(method.def_id),
- );
- method.sig.output()
- }
-
- fn self_type_matches_expected_vid(
- &self,
- trait_ref: ty::PolyTraitRef<'tcx>,
- expected_vid: ty::TyVid,
- ) -> bool {
- let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
- debug!(
- "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
- trait_ref, self_ty, expected_vid
- );
- match *self_ty.kind() {
- ty::Infer(ty::TyVar(found_vid)) => {
- // FIXME: consider using `sub_root_var` here so we
- // can see through subtyping.
- let found_vid = self.root_var(found_vid);
- debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
- expected_vid == found_vid
- }
- _ => false,
- }
- }
-
- pub(super) fn obligations_for_self_ty<'b>(
- &'b self,
- self_ty: ty::TyVid,
- ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
- + Captures<'tcx>
- + 'b {
- // FIXME: consider using `sub_root_var` here so we
- // can see through subtyping.
- let ty_var_root = self.root_var(self_ty);
- debug!(
- "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
- self_ty,
- ty_var_root,
- self.fulfillment_cx.borrow().pending_obligations()
- );
-
- self.fulfillment_cx
- .borrow()
- .pending_obligations()
- .into_iter()
- .filter_map(move |obligation| {
- match obligation.predicate.skip_binders() {
- ty::PredicateAtom::Projection(data) => {
- Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
- }
- ty::PredicateAtom::Trait(data, _) => {
- Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
- }
- ty::PredicateAtom::Subtype(..) => None,
- ty::PredicateAtom::RegionOutlives(..) => None,
- ty::PredicateAtom::TypeOutlives(..) => None,
- ty::PredicateAtom::WellFormed(..) => None,
- ty::PredicateAtom::ObjectSafe(..) => None,
- ty::PredicateAtom::ConstEvaluatable(..) => None,
- ty::PredicateAtom::ConstEquate(..) => None,
- // N.B., this predicate is created by breaking down a
- // `ClosureType: FnFoo()` predicate, where
- // `ClosureType` represents some `Closure`. It can't
- // possibly be referring to the current closure,
- // because we haven't produced the `Closure` for
- // this closure yet; this is exactly why the other
- // code is looking for a self type of a unresolved
- // inference variable.
- ty::PredicateAtom::ClosureKind(..) => None,
- ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
- }
- })
- .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
- }
-
- pub(super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
- self.obligations_for_self_ty(self_ty)
- .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
- }
-
- /// Generic function that factors out common logic from function calls,
- /// method calls and overloaded operators.
- pub(super) fn check_argument_types(
- &self,
- sp: Span,
- expr: &'tcx hir::Expr<'tcx>,
- fn_inputs: &[Ty<'tcx>],
- expected_arg_tys: &[Ty<'tcx>],
- args: &'tcx [hir::Expr<'tcx>],
- c_variadic: bool,
- tuple_arguments: TupleArgumentsFlag,
- def_span: Option<Span>,
- ) {
- let tcx = self.tcx;
- // Grab the argument types, supplying fresh type variables
- // if the wrong number of arguments were supplied
- let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
-
- // All the input types from the fn signature must outlive the call
- // so as to validate implied bounds.
- for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
- self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
- }
-
- let expected_arg_count = fn_inputs.len();
-
- let param_count_error = |expected_count: usize,
- arg_count: usize,
- error_code: &str,
- c_variadic: bool,
- sugg_unit: bool| {
- let (span, start_span, args) = match &expr.kind {
- hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
- hir::ExprKind::MethodCall(path_segment, span, args, _) => (
- *span,
- // `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
- path_segment
- .args
- .and_then(|args| args.args.iter().last())
- // Account for `foo.bar::<T>()`.
- .map(|arg| {
- // Skip the closing `>`.
- tcx.sess
- .source_map()
- .next_point(tcx.sess.source_map().next_point(arg.span()))
- })
- .unwrap_or(*span),
- &args[1..], // Skip the receiver.
- ),
- k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
- };
- let arg_spans = if args.is_empty() {
- // foo()
- // ^^^-- supplied 0 arguments
- // |
- // expected 2 arguments
- vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
- } else {
- // foo(1, 2, 3)
- // ^^^ - - - supplied 3 arguments
- // |
- // expected 2 arguments
- args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
- };
-
- let mut err = tcx.sess.struct_span_err_with_code(
- span,
- &format!(
- "this function takes {}{} but {} {} supplied",
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(expected_count, "argument"),
- potentially_plural_count(arg_count, "argument"),
- if arg_count == 1 { "was" } else { "were" }
- ),
- DiagnosticId::Error(error_code.to_owned()),
- );
- let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
- for (i, span) in arg_spans.into_iter().enumerate() {
- err.span_label(
- span,
- if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
- );
- }
-
- if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
- err.span_label(def_s, "defined here");
- }
- if sugg_unit {
- let sugg_span = tcx.sess.source_map().end_point(expr.span);
- // remove closing `)` from the span
- let sugg_span = sugg_span.shrink_to_lo();
- err.span_suggestion(
- sugg_span,
- "expected the unit value `()`; create it with empty parentheses",
- String::from("()"),
- Applicability::MachineApplicable,
- );
- } else {
- err.span_label(
- span,
- format!(
- "expected {}{}",
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(expected_count, "argument")
- ),
- );
- }
- err.emit();
- };
-
- let mut expected_arg_tys = expected_arg_tys.to_vec();
-
- let formal_tys = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
- match tuple_type.kind() {
- ty::Tuple(arg_types) if arg_types.len() != args.len() => {
- param_count_error(arg_types.len(), args.len(), "E0057", false, false);
- expected_arg_tys = vec![];
- self.err_args(args.len())
- }
- ty::Tuple(arg_types) => {
- expected_arg_tys = match expected_arg_tys.get(0) {
- Some(&ty) => match ty.kind() {
- ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
- _ => vec![],
- },
- None => vec![],
- };
- arg_types.iter().map(|k| k.expect_ty()).collect()
- }
- _ => {
- struct_span_err!(
- tcx.sess,
- sp,
- E0059,
- "cannot use call notation; the first type parameter \
- for the function trait is neither a tuple nor unit"
- )
- .emit();
- expected_arg_tys = vec![];
- self.err_args(args.len())
- }
- }
- } else if expected_arg_count == supplied_arg_count {
- fn_inputs.to_vec()
- } else if c_variadic {
- if supplied_arg_count >= expected_arg_count {
- fn_inputs.to_vec()
- } else {
- param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
- expected_arg_tys = vec![];
- self.err_args(supplied_arg_count)
- }
- } else {
- // is the missing argument of type `()`?
- let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
- self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
- } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
- self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
- } else {
- false
- };
- param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
-
- expected_arg_tys = vec![];
- self.err_args(supplied_arg_count)
- };
-
- debug!(
- "check_argument_types: formal_tys={:?}",
- formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
- );
-
- // If there is no expectation, expect formal_tys.
- let expected_arg_tys =
- if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
-
- let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
-
- // Check the arguments.
- // We do this in a pretty awful way: first we type-check any arguments
- // that are not closures, then we type-check the closures. This is so
- // that we have more information about the types of arguments when we
- // type-check the functions. This isn't really the right way to do this.
- for &check_closures in &[false, true] {
- debug!("check_closures={}", check_closures);
-
- // More awful hacks: before we check argument types, try to do
- // an "opportunistic" trait resolution of any trait bounds on
- // the call. This helps coercions.
- if check_closures {
- self.select_obligations_where_possible(false, |errors| {
- self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
- self.point_at_arg_instead_of_call_if_possible(
- errors,
- &final_arg_types[..],
- sp,
- &args,
- );
- })
- }
-
- // For C-variadic functions, we don't have a declared type for all of
- // the arguments hence we only do our usual type checking with
- // the arguments who's types we do know.
- let t = if c_variadic {
- expected_arg_count
- } else if tuple_arguments == TupleArguments {
- args.len()
- } else {
- supplied_arg_count
- };
- for (i, arg) in args.iter().take(t).enumerate() {
- // Warn only for the first loop (the "no closures" one).
- // Closure arguments themselves can't be diverging, but
- // a previous argument can, e.g., `foo(panic!(), || {})`.
- if !check_closures {
- self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
- }
-
- let is_closure = match arg.kind {
- ExprKind::Closure(..) => true,
- _ => false,
- };
-
- if is_closure != check_closures {
- continue;
- }
-
- debug!("checking the argument");
- let formal_ty = formal_tys[i];
-
- // The special-cased logic below has three functions:
- // 1. Provide as good of an expected type as possible.
- let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
-
- let checked_ty = self.check_expr_with_expectation(&arg, expected);
-
- // 2. Coerce to the most detailed type that could be coerced
- // to, which is `expected_ty` if `rvalue_hint` returns an
- // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
- let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
- // We're processing function arguments so we definitely want to use
- // two-phase borrows.
- self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
- final_arg_types.push((i, checked_ty, coerce_ty));
-
- // 3. Relate the expected type and the formal one,
- // if the expected type was used for the coercion.
- self.demand_suptype(arg.span, formal_ty, coerce_ty);
- }
- }
-
- // We also need to make sure we at least write the ty of the other
- // arguments which we skipped above.
- if c_variadic {
- fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
- use crate::structured_errors::{StructuredDiagnostic, VariadicError};
- VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
- }
-
- for arg in args.iter().skip(expected_arg_count) {
- let arg_ty = self.check_expr(&arg);
-
- // There are a few types which get autopromoted when passed via varargs
- // in C but we just error out instead and require explicit casts.
- let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
- match arg_ty.kind() {
- ty::Float(ast::FloatTy::F32) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
- }
- ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
- }
- ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
- }
- ty::FnDef(..) => {
- let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
- let ptr_ty = self.resolve_vars_if_possible(&ptr_ty);
- variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
- }
- _ => {}
- }
- }
- }
- }
-
- pub(super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
- vec![self.tcx.ty_error(); len]
- }
-
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
- /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
- /// reference a type argument. The reason to walk also the checked type is that the coerced type
- /// can be not easily comparable with predicate type (because of coercion). If the types match
- /// for either checked or coerced type, and there's only *one* argument that does, we point at
- /// the corresponding argument's expression span instead of the `fn` call path span.
- fn point_at_arg_instead_of_call_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
- call_sp: Span,
- args: &'tcx [hir::Expr<'tcx>],
- ) {
- // We *do not* do this for desugared call spans to keep good diagnostics when involving
- // the `?` operator.
- if call_sp.desugaring_kind().is_some() {
- return;
- }
-
- for error in errors {
- // Only if the cause is somewhere inside the expression we want try to point at arg.
- // Otherwise, it means that the cause is somewhere else and we should not change
- // anything because we can break the correct span.
- if !call_sp.contains(error.obligation.cause.span) {
- continue;
- }
-
- if let ty::PredicateAtom::Trait(predicate, _) =
- error.obligation.predicate.skip_binders()
- {
- // Collect the argument position for all arguments that could have caused this
- // `FulfillmentError`.
- let mut referenced_in = final_arg_types
- .iter()
- .map(|&(i, checked_ty, _)| (i, checked_ty))
- .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
- .flat_map(|(i, ty)| {
- let ty = self.resolve_vars_if_possible(&ty);
- // We walk the argument type because the argument's type could have
- // been `Option<T>`, but the `FulfillmentError` references `T`.
- if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
- Some(i)
- } else {
- None
- }
- })
- .collect::<Vec<usize>>();
-
- // Both checked and coerced types could have matched, thus we need to remove
- // duplicates.
-
- // We sort primitive type usize here and can use unstable sort
- referenced_in.sort_unstable();
- referenced_in.dedup();
-
- if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
- // We make sure that only *one* argument matches the obligation failure
- // and we assign the obligation's span to its expression's.
- error.obligation.cause.make_mut().span = args[ref_in].span;
- error.points_at_arg_span = true;
- }
- }
- }
- }
-
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
- /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
- /// were caused by them. If they were, we point at the corresponding type argument's span
- /// instead of the `fn` call path span.
- fn point_at_type_arg_instead_of_call_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- call_expr: &'tcx hir::Expr<'tcx>,
- ) {
- if let hir::ExprKind::Call(path, _) = &call_expr.kind {
- if let hir::ExprKind::Path(qpath) = &path.kind {
- if let hir::QPath::Resolved(_, path) = &qpath {
- for error in errors {
- if let ty::PredicateAtom::Trait(predicate, _) =
- error.obligation.predicate.skip_binders()
- {
- // If any of the type arguments in this path segment caused the
- // `FullfillmentError`, point at its span (#61860).
- for arg in path
- .segments
- .iter()
- .filter_map(|seg| seg.args.as_ref())
- .flat_map(|a| a.args.iter())
- {
- if let hir::GenericArg::Type(hir_ty) = &arg {
- if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
- &hir_ty.kind
- {
- // Avoid ICE with associated types. As this is best
- // effort only, it's ok to ignore the case. It
- // would trigger in `is_send::<T::AssocType>();`
- // from `typeck-default-trait-impl-assoc-type.rs`.
- } else {
- let ty = AstConv::ast_ty_to_ty(self, hir_ty);
- let ty = self.resolve_vars_if_possible(&ty);
- if ty == predicate.self_ty() {
- error.obligation.cause.make_mut().span = hir_ty.span;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- // AST fragment checking
- pub(super) fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> {
- let tcx = self.tcx;
-
- match lit.node {
- ast::LitKind::Str(..) => tcx.mk_static_str(),
- ast::LitKind::ByteStr(ref v) => {
- tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
- }
- ast::LitKind::Byte(_) => tcx.types.u8,
- ast::LitKind::Char(_) => tcx.types.char,
- ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t),
- ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Int(_) | ty::Uint(_) => Some(ty),
- ty::Char => Some(tcx.types.u8),
- ty::RawPtr(..) => Some(tcx.types.usize),
- ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_int_var())
- }
- ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
- ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Float(_) => Some(ty),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_float_var())
- }
- ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::Err(_) => tcx.ty_error(),
- }
- }
-
- /// Unifies the output type with the expected type early, for more coercions
- /// and forward type information on the input expressions.
- pub(super) fn expected_inputs_for_expected_output(
- &self,
- call_span: Span,
- expected_ret: Expectation<'tcx>,
- formal_ret: Ty<'tcx>,
- formal_args: &[Ty<'tcx>],
- ) -> Vec<Ty<'tcx>> {
- let formal_ret = self.resolve_vars_with_obligations(formal_ret);
- let ret_ty = match expected_ret.only_has_type(self) {
- Some(ret) => ret,
- None => return Vec::new(),
- };
- let expect_args = self
- .fudge_inference_if_ok(|| {
- // Attempt to apply a subtyping relationship between the formal
- // return type (likely containing type variables if the function
- // is polymorphic) and the expected return type.
- // No argument expectations are produced if unification fails.
- let origin = self.misc(call_span);
- let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
-
- // FIXME(#27336) can't use ? here, Try::from_error doesn't default
- // to identity so the resulting type is not constrained.
- match ures {
- Ok(ok) => {
- // Process any obligations locally as much as
- // we can. We don't care if some things turn
- // out unconstrained or ambiguous, as we're
- // just trying to get hints here.
- self.save_and_restore_in_snapshot_flag(|_| {
- let mut fulfill = TraitEngine::new(self.tcx);
- for obligation in ok.obligations {
- fulfill.register_predicate_obligation(self, obligation);
- }
- fulfill.select_where_possible(self)
- })
- .map_err(|_| ())?;
- }
- Err(_) => return Err(()),
- }
-
- // Record all the argument types, with the substitutions
- // produced from the above subtyping unification.
- Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
- })
- .unwrap_or_default();
- debug!(
- "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
- formal_args, formal_ret, expect_args, expected_ret
- );
- expect_args
- }
-
- pub fn check_struct_path(
- &self,
- qpath: &QPath<'_>,
- hir_id: hir::HirId,
- ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
- let path_span = qpath.qself_span();
- let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
- let variant = match def {
- Res::Err => {
- self.set_tainted_by_errors();
- return None;
- }
- Res::Def(DefKind::Variant, _) => match ty.kind() {
- ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
- _ => bug!("unexpected type: {:?}", ty),
- },
- Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
- | Res::SelfTy(..) => match ty.kind() {
- ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did, substs))
- }
- _ => None,
- },
- _ => bug!("unexpected definition: {:?}", def),
- };
-
- if let Some((variant, did, substs)) = variant {
- debug!("check_struct_path: did={:?} substs={:?}", did, substs);
- self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
-
- // Check bounds on type arguments used in the path.
- let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
- let cause =
- traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
- self.add_obligations_for_parameters(cause, bounds);
-
- Some((variant, ty))
- } else {
- struct_span_err!(
- self.tcx.sess,
- path_span,
- E0071,
- "expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
- )
- .span_label(path_span, "not a struct")
- .emit();
- None
- }
- }
-
- // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
- // The newly resolved definition is written into `type_dependent_defs`.
- fn finish_resolving_struct_path(
- &self,
- qpath: &QPath<'_>,
- path_span: Span,
- hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
- match *qpath {
- QPath::Resolved(ref maybe_qself, ref path) => {
- let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
- let ty = AstConv::res_to_ty(self, self_ty, path, true);
- (path.res, ty)
- }
- QPath::TypeRelative(ref qself, ref segment) => {
- let ty = self.to_ty(qself);
-
- let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
- path.res
- } else {
- Res::Err
- };
- let result =
- AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
- let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
- let result = result.map(|(_, kind, def_id)| (kind, def_id));
-
- // Write back the new resolution.
- self.write_resolution(hir_id, result);
-
- (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
- }
- QPath::LangItem(lang_item, span) => {
- self.resolve_lang_item_path(lang_item, span, hir_id)
- }
- }
- }
-
- pub(super) fn resolve_lang_item_path(
- &self,
- lang_item: hir::LangItem,
- span: Span,
- hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
- let def_id = self.tcx.require_lang_item(lang_item, Some(span));
- let def_kind = self.tcx.def_kind(def_id);
-
- let item_ty = if let DefKind::Variant = def_kind {
- self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
- } else {
- self.tcx.type_of(def_id)
- };
- let substs = self.infcx.fresh_substs_for_item(span, def_id);
- let ty = item_ty.subst(self.tcx, substs);
-
- self.write_resolution(hir_id, Ok((def_kind, def_id)));
- self.add_required_obligations(span, def_id, &substs);
- (Res::Def(def_kind, def_id), ty)
- }
-
- /// Resolves an associated value path into a base type and associated constant, or method
- /// resolution. The newly resolved definition is written into `type_dependent_defs`.
- pub fn resolve_ty_and_res_ufcs<'b>(
- &self,
- qpath: &'b QPath<'b>,
- hir_id: hir::HirId,
- span: Span,
- ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
- debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
- let (ty, qself, item_segment) = match *qpath {
- QPath::Resolved(ref opt_qself, ref path) => {
- return (
- path.res,
- opt_qself.as_ref().map(|qself| self.to_ty(qself)),
- &path.segments[..],
- );
- }
- QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
- QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
- };
- if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
- {
- // Return directly on cache hit. This is useful to avoid doubly reporting
- // errors with default match binding modes. See #44614.
- let def =
- cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
- return (def, Some(ty), slice::from_ref(&**item_segment));
- }
- let item_name = item_segment.ident;
- let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
- let result = match error {
- method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
- _ => Err(ErrorReported),
- };
- if item_name.name != kw::Invalid {
- if let Some(mut e) = self.report_method_error(
- span,
- ty,
- item_name,
- SelfSource::QPath(qself),
- error,
- None,
- ) {
- e.emit();
- }
- }
- result
- });
-
- // Write back the new resolution.
- self.write_resolution(hir_id, result);
- (
- result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err),
- Some(ty),
- slice::from_ref(&**item_segment),
- )
- }
-
- pub fn check_decl_initializer(
- &self,
- local: &'tcx hir::Local<'tcx>,
- init: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
- // for #42640 (default match binding modes).
- //
- // See #44848.
- let ref_bindings = local.pat.contains_explicit_ref_binding();
-
- let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
- if let Some(m) = ref_bindings {
- // Somewhat subtle: if we have a `ref` binding in the pattern,
- // we want to avoid introducing coercions for the RHS. This is
- // both because it helps preserve sanity and, in the case of
- // ref mut, for soundness (issue #23116). In particular, in
- // the latter case, we need to be clear that the type of the
- // referent for the reference that results is *equal to* the
- // type of the place it is referencing, and not some
- // supertype thereof.
- let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
- self.demand_eqtype(init.span, local_ty, init_ty);
- init_ty
- } else {
- self.check_expr_coercable_to_type(init, local_ty, None)
- }
- }
-
- /// Type check a `let` statement.
- pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
- // Determine and write the type which we'll check the pattern against.
- let ty = self.local_ty(local.span, local.hir_id).decl_ty;
- self.write_ty(local.hir_id, ty);
-
- // Type check the initializer.
- if let Some(ref init) = local.init {
- let init_ty = self.check_decl_initializer(local, &init);
- self.overwrite_local_ty_if_err(local, ty, init_ty);
- }
-
- // Does the expected pattern type originate from an expression and what is the span?
- let (origin_expr, ty_span) = match (local.ty, local.init) {
- (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
- (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
- _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
- };
-
- // Type check the pattern. Override if necessary to avoid knock-on errors.
- self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
- let pat_ty = self.node_ty(local.pat.hir_id);
- self.overwrite_local_ty_if_err(local, ty, pat_ty);
- }
-
- fn overwrite_local_ty_if_err(
- &self,
- local: &'tcx hir::Local<'tcx>,
- decl_ty: Ty<'tcx>,
- ty: Ty<'tcx>,
- ) {
- if ty.references_error() {
- // Override the types everywhere with `err()` to avoid knock on errors.
- self.write_ty(local.hir_id, ty);
- self.write_ty(local.pat.hir_id, ty);
- let local_ty = LocalTy { decl_ty, revealed_ty: ty };
- self.locals.borrow_mut().insert(local.hir_id, local_ty);
- self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
- }
- }
-
- pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
- err.span_suggestion_short(
- span.shrink_to_hi(),
- "consider using a semicolon here",
- ";".to_string(),
- Applicability::MachineApplicable,
- );
- }
-
- pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
- // Don't do all the complex logic below for `DeclItem`.
- match stmt.kind {
- hir::StmtKind::Item(..) => return,
- hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
- }
-
- self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
-
- // Hide the outer diverging and `has_errors` flags.
- let old_diverges = self.diverges.replace(Diverges::Maybe);
- let old_has_errors = self.has_errors.replace(false);
-
- match stmt.kind {
- hir::StmtKind::Local(ref l) => {
- self.check_decl_local(&l);
- }
- // Ignore for now.
- hir::StmtKind::Item(_) => {}
- hir::StmtKind::Expr(ref expr) => {
- // Check with expected type of `()`.
- self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
- self.suggest_semicolon_at_end(expr.span, err);
- });
- }
- hir::StmtKind::Semi(ref expr) => {
- self.check_expr(&expr);
- }
- }
-
- // Combine the diverging and `has_error` flags.
- self.diverges.set(self.diverges.get() | old_diverges);
- self.has_errors.set(self.has_errors.get() | old_has_errors);
- }
-
- pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
- let unit = self.tcx.mk_unit();
- let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-
- // if the block produces a `!` value, that can always be
- // (effectively) coerced to unit.
- if !ty.is_never() {
- self.demand_suptype(blk.span, unit, ty);
- }
- }
-
- /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
- /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
- /// when given code like the following:
- /// ```text
- /// if false { return 0i32; } else { 1u32 }
- /// // ^^^^ point at this instead of the whole `if` expression
- /// ```
- fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
- if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
- let arm_spans: Vec<Span> = arms
- .iter()
- .filter_map(|arm| {
- self.in_progress_typeck_results
- .and_then(|typeck_results| {
- typeck_results.borrow().node_type_opt(arm.body.hir_id)
- })
- .and_then(|arm_ty| {
- if arm_ty.is_never() {
- None
- } else {
- Some(match &arm.body.kind {
- // Point at the tail expression when possible.
- hir::ExprKind::Block(block, _) => {
- block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
- }
- _ => arm.body.span,
- })
- }
- })
- })
- .collect();
- if arm_spans.len() == 1 {
- return arm_spans[0];
- }
- }
- expr.span
- }
-
- pub(super) fn check_block_with_expected(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let prev = {
- let mut fcx_ps = self.ps.borrow_mut();
- let unsafety_state = fcx_ps.recurse(blk);
- replace(&mut *fcx_ps, unsafety_state)
- };
-
- // In some cases, blocks have just one exit, but other blocks
- // can be targeted by multiple breaks. This can happen both
- // with labeled blocks as well as when we desugar
- // a `try { ... }` expression.
- //
- // Example 1:
- //
- // 'a: { if true { break 'a Err(()); } Ok(()) }
- //
- // Here we would wind up with two coercions, one from
- // `Err(())` and the other from the tail expression
- // `Ok(())`. If the tail expression is omitted, that's a
- // "forced unit" -- unless the block diverges, in which
- // case we can ignore the tail expression (e.g., `'a: {
- // break 'a 22; }` would not force the type of the block
- // to be `()`).
- let tail_expr = blk.expr.as_ref();
- let coerce_to_ty = expected.coercion_target_type(self, blk.span);
- let coerce = if blk.targeted_by_break {
- CoerceMany::new(coerce_to_ty)
- } else {
- let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
- Some(e) => slice::from_ref(e),
- None => &[],
- };
- CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
- };
-
- let prev_diverges = self.diverges.get();
- let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
-
- let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
- for s in blk.stmts {
- self.check_stmt(s);
- }
-
- // check the tail expression **without** holding the
- // `enclosing_breakables` lock below.
- let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
-
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
- let coerce = ctxt.coerce.as_mut().unwrap();
- if let Some(tail_expr_ty) = tail_expr_ty {
- let tail_expr = tail_expr.unwrap();
- let span = self.get_expr_coercion_span(tail_expr);
- let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
- coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
- } else {
- // Subtle: if there is no explicit tail expression,
- // that is typically equivalent to a tail expression
- // of `()` -- except if the block diverges. In that
- // case, there is no value supplied from the tail
- // expression (assuming there are no other breaks,
- // this implies that the type of the block will be
- // `!`).
- //
- // #41425 -- label the implicit `()` as being the
- // "found type" here, rather than the "expected type".
- if !self.diverges.get().is_always() {
- // #50009 -- Do not point at the entire fn block span, point at the return type
- // span, as it is the cause of the requirement, and
- // `consider_hint_about_removing_semicolon` will point at the last expression
- // if it were a relevant part of the error. This improves usability in editors
- // that highlight errors inline.
- let mut sp = blk.span;
- let mut fn_span = None;
- if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
- let ret_sp = decl.output.span();
- if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
- // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
- // output would otherwise be incorrect and even misleading. Make sure
- // the span we're aiming at correspond to a `fn` body.
- if block_sp == blk.span {
- sp = ret_sp;
- fn_span = Some(ident.span);
- }
- }
- }
- coerce.coerce_forced_unit(
- self,
- &self.misc(sp),
- &mut |err| {
- if let Some(expected_ty) = expected.only_has_type(self) {
- self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
- }
- if let Some(fn_span) = fn_span {
- err.span_label(
- fn_span,
- "implicitly returns `()` as its body has no tail or `return` \
- expression",
- );
- }
- },
- false,
- );
- }
- }
- });
-
- if ctxt.may_break {
- // If we can break from the block, then the block's exit is always reachable
- // (... as long as the entry is reachable) - regardless of the tail of the block.
- self.diverges.set(prev_diverges);
- }
-
- let mut ty = ctxt.coerce.unwrap().complete(self);
-
- if self.has_errors.get() || ty.references_error() {
- ty = self.tcx.ty_error()
- }
-
- self.write_ty(blk.hir_id, ty);
-
- *self.ps.borrow_mut() = prev;
- ty
- }
-
- fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
- let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
- match node {
- Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
- | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
- let body = self.tcx.hir().body(body_id);
- if let ExprKind::Block(block, _) = &body.value.kind {
- return Some(block.span);
- }
- }
- _ => {}
- }
- None
- }
-
- /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
- fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
- let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
- self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
- }
-
- /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
- pub(super) fn get_node_fn_decl(
- &self,
- node: Node<'tcx>,
- ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
- match node {
- Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
- // This is less than ideal, it will not suggest a return type span on any
- // method called `main`, regardless of whether it is actually the entry point,
- // but it will still present it as the reason for the expected type.
- Some((&sig.decl, ident, ident.name != sym::main))
- }
- Node::TraitItem(&hir::TraitItem {
- ident,
- kind: hir::TraitItemKind::Fn(ref sig, ..),
- ..
- }) => Some((&sig.decl, ident, true)),
- Node::ImplItem(&hir::ImplItem {
- ident,
- kind: hir::ImplItemKind::Fn(ref sig, ..),
- ..
- }) => Some((&sig.decl, ident, false)),
- _ => None,
- }
- }
-
- /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
- /// suggestion can be made, `None` otherwise.
- pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
- // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
- // `while` before reaching it, as block tail returns are not available in them.
- self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
- let parent = self.tcx.hir().get(blk_id);
- self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
- })
- }
-
- /// On implicit return expressions with mismatched types, provides the following suggestions:
- ///
- /// - Points out the method's return type as the reason for the expected type.
- /// - Possible missing semicolon.
- /// - Possible missing return type if the return type is the default, and not `fn main()`.
- pub fn suggest_mismatched_types_on_tail(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- cause_span: Span,
- blk_id: hir::HirId,
- ) -> bool {
- let expr = expr.peel_drop_temps();
- self.suggest_missing_semicolon(err, expr, expected, cause_span);
- let mut pointing_at_return_type = false;
- if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
- pointing_at_return_type =
- self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
- }
- pointing_at_return_type
- }
-
- /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
- /// the ctor would successfully solve the type mismatch and if so, suggest it:
- /// ```
- /// fn foo(x: usize) -> usize { x }
- /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
- /// ```
- fn suggest_fn_call(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- let hir = self.tcx.hir();
- let (def_id, sig) = match *found.kind() {
- ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
- ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
- _ => return false,
- };
-
- let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
- let sig = self.normalize_associated_types_in(expr.span, &sig);
- if self.can_coerce(sig.output(), expected) {
- let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
- (String::new(), Applicability::MachineApplicable)
- } else {
- ("...".to_string(), Applicability::HasPlaceholders)
- };
- let mut msg = "call this function";
- match hir.get_if_local(def_id) {
- Some(
- Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
- | Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(_, body_id), ..
- })
- | Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
- ..
- }),
- ) => {
- let body = hir.body(*body_id);
- sugg_call = body
- .params
- .iter()
- .map(|param| match ¶m.pat.kind {
- hir::PatKind::Binding(_, _, ident, None)
- if ident.name != kw::SelfLower =>
- {
- ident.to_string()
- }
- _ => "_".to_string(),
- })
- .collect::<Vec<_>>()
- .join(", ");
- }
- Some(Node::Expr(hir::Expr {
- kind: ExprKind::Closure(_, _, body_id, _, _),
- span: full_closure_span,
- ..
- })) => {
- if *full_closure_span == expr.span {
- return false;
- }
- msg = "call this closure";
- let body = hir.body(*body_id);
- sugg_call = body
- .params
- .iter()
- .map(|param| match ¶m.pat.kind {
- hir::PatKind::Binding(_, _, ident, None)
- if ident.name != kw::SelfLower =>
- {
- ident.to_string()
- }
- _ => "_".to_string(),
- })
- .collect::<Vec<_>>()
- .join(", ");
- }
- Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
- sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
- match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
- Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
- msg = "instantiate this tuple variant";
- }
- Some(DefKind::Ctor(CtorOf::Struct, _)) => {
- msg = "instantiate this tuple struct";
- }
- _ => {}
- }
- }
- Some(Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Fn(_, idents, _),
- ..
- })) => {
- sugg_call = idents
- .iter()
- .map(|ident| {
- if ident.name != kw::SelfLower {
- ident.to_string()
- } else {
- "_".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", ")
- }
- Some(Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
- ..
- })) => {
- sugg_call = idents
- .iter()
- .map(|ident| {
- if ident.name != kw::SelfLower {
- ident.to_string()
- } else {
- "_".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", ")
- }
- _ => {}
- }
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- &format!("use parentheses to {}", msg),
- format!("({})", sugg_call),
- applicability,
- );
- return true;
- }
- false
- }
-
- pub fn suggest_deref_ref_or_into(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) {
- if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
- err.span_suggestion(sp, msg, suggestion, applicability);
- } else if let (ty::FnDef(def_id, ..), true) =
- (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
- {
- if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
- let sp = self.sess().source_map().guess_head_span(sp);
- err.span_label(sp, &format!("{} defined here", found));
- }
- } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
- let is_struct_pat_shorthand_field =
- self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
- let mut suggestions = iter::repeat(&expr_text)
- .zip(methods.iter())
- .filter_map(|(receiver, method)| {
- let method_call = format!(".{}()", method.ident);
- if receiver.ends_with(&method_call) {
- None // do not suggest code that is already there (#53348)
- } else {
- let method_call_list = [".to_vec()", ".to_string()"];
- let sugg = if receiver.ends_with(".clone()")
- && method_call_list.contains(&method_call.as_str())
- {
- let max_len = receiver.rfind('.').unwrap();
- format!("{}{}", &receiver[..max_len], method_call)
- } else {
- if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
- format!("({}){}", receiver, method_call)
- } else {
- format!("{}{}", receiver, method_call)
- }
- };
- Some(if is_struct_pat_shorthand_field {
- format!("{}: {}", receiver, sugg)
- } else {
- sugg
- })
- }
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.span_suggestions(
- expr.span,
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
-
- /// When encountering the expected boxed value allocated in the stack, suggest allocating it
- /// in the heap by calling `Box::new()`.
- pub(super) fn suggest_boxing_when_appropriate(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return;
- }
- if !expected.is_box() || found.is_box() {
- return;
- }
- let boxed_found = self.tcx.mk_box(found);
- if let (true, Ok(snippet)) = (
- self.can_coerce(boxed_found, expected),
- self.sess().source_map().span_to_snippet(expr.span),
- ) {
- err.span_suggestion(
- expr.span,
- "store this in the heap by calling `Box::new`",
- format!("Box::new({})", snippet),
- Applicability::MachineApplicable,
- );
- err.note(
- "for more on the distinction between the stack and the heap, read \
- https://doc.rust-lang.org/book/ch15-01-box.html, \
- https://doc.rust-lang.org/rust-by-example/std/box.html, and \
- https://doc.rust-lang.org/std/boxed/index.html",
- );
- }
- }
-
- pub(super) fn note_internal_mutation_in_method(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if found != self.tcx.types.unit {
- return;
- }
- if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
- if self
- .typeck_results
- .borrow()
- .expr_ty_adjusted_opt(rcvr)
- .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
- {
- return;
- }
- let mut sp = MultiSpan::from_span(path_segment.ident.span);
- sp.push_span_label(
- path_segment.ident.span,
- format!(
- "this call modifies {} in-place",
- match rcvr.kind {
- ExprKind::Path(QPath::Resolved(
- None,
- hir::Path { segments: [segment], .. },
- )) => format!("`{}`", segment.ident),
- _ => "its receiver".to_string(),
- }
- ),
- );
- sp.push_span_label(
- rcvr.span,
- "you probably want to use this value after calling the method...".to_string(),
- );
- err.span_note(
- sp,
- &format!("method `{}` modifies its receiver in-place", path_segment.ident),
- );
- err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
- }
- }
-
- /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
- pub(super) fn suggest_calling_boxed_future_when_appropriate(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- // Handle #68197.
-
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return false;
- }
- let pin_did = self.tcx.lang_items().pin_type();
- match expected.kind() {
- ty::Adt(def, _) if Some(def.did) != pin_did => return false,
- // This guards the `unwrap` and `mk_box` below.
- _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
- _ => {}
- }
- let boxed_found = self.tcx.mk_box(found);
- let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
- if let (true, Ok(snippet)) = (
- self.can_coerce(new_found, expected),
- self.sess().source_map().span_to_snippet(expr.span),
- ) {
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- }
- _ => {
- err.span_suggestion(
- expr.span,
- "you need to pin and box this expression",
- format!("Box::pin({})", snippet),
- Applicability::MachineApplicable,
- );
- }
- }
- true
- } else {
- false
- }
- }
-
- /// A common error is to forget to add a semicolon at the end of a block, e.g.,
- ///
- /// ```
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return expression in a block would make sense on its own as a
- /// statement and the return type has been left as default or has been specified as `()`. If so,
- /// it suggests adding a semicolon.
- fn suggest_missing_semicolon(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expression: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- cause_span: Span,
- ) {
- if expected.is_unit() {
- // `BlockTailExpression` only relevant if the tail expr would be
- // useful on its own.
- match expression.kind {
- ExprKind::Call(..)
- | ExprKind::MethodCall(..)
- | ExprKind::Loop(..)
- | ExprKind::Match(..)
- | ExprKind::Block(..) => {
- err.span_suggestion(
- cause_span.shrink_to_hi(),
- "try adding a semicolon",
- ";".to_string(),
- Applicability::MachineApplicable,
- );
- }
- _ => (),
- }
- }
- }
-
- /// A possible error is to forget to add a return type that is needed:
- ///
- /// ```
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return type is left as default, the method is not part of an
- /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
- /// type.
- pub(super) fn suggest_missing_return_type(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- can_suggest: bool,
- ) -> bool {
- // Only suggest changing the return type for methods that
- // haven't set a return type at all (and aren't `fn main()` or an impl).
- match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
- (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.span_suggestion(
- span,
- "try adding a return type",
- format!("-> {} ", self.resolve_vars_with_obligations(found)),
- Applicability::MachineApplicable,
- );
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
- err.span_label(span, "possibly return type missing here?");
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
- // `fn main()` must return `()`, do not suggest changing return type
- err.span_label(span, "expected `()` because of default return type");
- true
- }
- // expectation was caused by something else, not the default return
- (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
- (&hir::FnRetTy::Return(ref ty), _, _, _) => {
- // Only point to return type if the expected type is the return type, as if they
- // are not, the expectation must have been caused by something else.
- debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let sp = ty.span;
- let ty = AstConv::ast_ty_to_ty(self, ty);
- debug!("suggest_missing_return_type: return type {:?}", ty);
- debug!("suggest_missing_return_type: expected type {:?}", ty);
- if ty.kind() == expected.kind() {
- err.span_label(sp, format!("expected `{}` because of return type", expected));
- return true;
- }
- false
- }
- }
- }
-
- /// A possible error is to forget to add `.await` when using futures:
- ///
- /// ```
- /// async fn make_u32() -> u32 {
- /// 22
- /// }
- ///
- /// fn take_u32(x: u32) {}
- ///
- /// async fn foo() {
- /// let x = make_u32();
- /// take_u32(x);
- /// }
- /// ```
- ///
- /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
- /// expected type. If this is the case, and we are inside of an async body, it suggests adding
- /// `.await` to the tail of the expression.
- pub(super) fn suggest_missing_await(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
- // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
- // body isn't `async`.
- let item_id = self.tcx().hir().get_parent_node(self.body_id);
- if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
- let body = self.tcx().hir().body(body_id);
- if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
- let sp = expr.span;
- // Check for `Future` implementations by constructing a predicate to
- // prove: `<T as Future>::Output == U`
- let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
- let item_def_id = self
- .tcx
- .associated_items(future_trait)
- .in_definition_order()
- .next()
- .unwrap()
- .def_id;
- // `<T as Future>::Output`
- let projection_ty = ty::ProjectionTy {
- // `T`
- substs: self
- .tcx
- .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
- // `Future::Output`
- item_def_id,
- };
-
- let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
- projection_ty,
- ty: expected,
- })
- .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
- let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
-
- debug!("suggest_missing_await: trying obligation {:?}", obligation);
-
- if self.infcx.predicate_may_hold(&obligation) {
- debug!("suggest_missing_await: obligation held: {:?}", obligation);
- if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
- err.span_suggestion(
- sp,
- "consider using `.await` here",
- format!("{}.await", code),
- Applicability::MaybeIncorrect,
- );
- } else {
- debug!("suggest_missing_await: no snippet for {:?}", sp);
- }
- } else {
- debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
- }
- }
- }
- }
-
- pub(super) fn suggest_missing_parentheses(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- ) {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
- if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
- }
- }
-
- pub(super) fn note_need_for_fn_pointer(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
- (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
- if sig1 != sig2 {
- return;
- }
- err.note(
- "different `fn` items always have unique types, even if their signatures are \
- the same",
- );
- (sig1, *did1, substs1)
- }
- (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
- if sig1 != *sig2 {
- return;
- }
- (sig1, *did, substs)
- }
- _ => return,
- };
- err.help(&format!("change the expected type to be function pointer `{}`", sig));
- err.help(&format!(
- "if the expected type is due to type inference, cast the expected `fn` to a function \
- pointer: `{} as {}`",
- self.tcx.def_path_str_with_substs(did, substs),
- sig
- ));
- }
-
- /// A common error is to add an extra semicolon:
- ///
- /// ```
- /// fn foo() -> usize {
- /// 22;
- /// }
- /// ```
- ///
- /// This routine checks if the final statement in a block is an
- /// expression with an explicit semicolon whose type is compatible
- /// with `expected_ty`. If so, it suggests removing the semicolon.
- fn consider_hint_about_removing_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut DiagnosticBuilder<'_>,
- ) {
- if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
- err.span_suggestion(
- span_semi,
- "consider removing this semicolon",
- String::new(),
- Applicability::MachineApplicable,
- );
- }
- }
-
- pub(super) fn could_remove_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- ) -> Option<Span> {
- // Be helpful when the user wrote `{... expr;}` and
- // taking the `;` off is enough to fix the error.
- let last_stmt = blk.stmts.last()?;
- let last_expr = match last_stmt.kind {
- hir::StmtKind::Semi(ref e) => e,
- _ => return None,
- };
- let last_expr_ty = self.node_ty(last_expr.hir_id);
- if matches!(last_expr_ty.kind(), ty::Error(_))
- || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
- {
- return None;
- }
- let original_span = original_sp(last_stmt.span, blk.span);
- Some(original_span.with_lo(original_span.hi() - BytePos(1)))
- }
-
- // Instantiates the given path, which must refer to an item with the given
- // number of type parameters and type.
- pub fn instantiate_value_path(
- &self,
- segments: &[hir::PathSegment<'_>],
- self_ty: Option<Ty<'tcx>>,
- res: Res,
- span: Span,
- hir_id: hir::HirId,
- ) -> (Ty<'tcx>, Res) {
- debug!(
- "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
- segments, self_ty, res, hir_id,
- );
-
- let tcx = self.tcx;
-
- let path_segs = match res {
- Res::Local(_) | Res::SelfCtor(_) => vec![],
- Res::Def(kind, def_id) => {
- AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
- }
- _ => bug!("instantiate_value_path on {:?}", res),
- };
-
- let mut user_self_ty = None;
- let mut is_alias_variant_ctor = false;
- match res {
- Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
- if let Some(self_ty) = self_ty {
- let adt_def = self_ty.ty_adt_def().unwrap();
- user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
- is_alias_variant_ctor = true;
- }
- }
- Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
- let container = tcx.associated_item(def_id).container;
- debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
- match container {
- ty::TraitContainer(trait_did) => {
- callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
- }
- ty::ImplContainer(impl_def_id) => {
- if segments.len() == 1 {
- // `<T>::assoc` will end up here, and so
- // can `T::assoc`. It this came from an
- // inherent impl, we need to record the
- // `T` for posterity (see `UserSelfTy` for
- // details).
- let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
- user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
- }
- }
- }
- }
- _ => {}
- }
-
- // Now that we have categorized what space the parameters for each
- // segment belong to, let's sort out the parameters that the user
- // provided (if any) into their appropriate spaces. We'll also report
- // errors if type parameters are provided in an inappropriate place.
-
- let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
- let generics_has_err = AstConv::prohibit_generics(
- self,
- segments.iter().enumerate().filter_map(|(index, seg)| {
- if !generic_segs.contains(&index) || is_alias_variant_ctor {
- Some(seg)
- } else {
- None
- }
- }),
- );
-
- if let Res::Local(hid) = res {
- let ty = self.local_ty(span, hid).decl_ty;
- let ty = self.normalize_associated_types_in(span, &ty);
- self.write_ty(hir_id, ty);
- return (ty, res);
- }
-
- if generics_has_err {
- // Don't try to infer type parameters when prohibited generic arguments were given.
- user_self_ty = None;
- }
-
- // Now we have to compare the types that the user *actually*
- // provided against the types that were *expected*. If the user
- // did not provide any types, then we want to substitute inference
- // variables. If the user provided some types, we may still need
- // to add defaults. If the user provided *too many* types, that's
- // a problem.
-
- let mut infer_args_for_err = FxHashSet::default();
- for &PathSeg(def_id, index) in &path_segs {
- let seg = &segments[index];
- let generics = tcx.generics_of(def_id);
- // Argument-position `impl Trait` is treated as a normal generic
- // parameter internally, but we don't allow users to specify the
- // parameter's value explicitly, so we have to do some error-
- // checking here.
- if let GenericArgCountResult {
- correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
- ..
- } = AstConv::check_generic_arg_count_for_call(
- tcx, span, &generics, &seg, false, // `is_method_call`
- ) {
- infer_args_for_err.insert(index);
- self.set_tainted_by_errors(); // See issue #53251.
- }
- }
-
- let has_self = path_segs
- .last()
- .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
- .unwrap_or(false);
-
- let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
- let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
- match *ty.kind() {
- ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
- let variant = adt_def.non_enum_variant();
- let ctor_def_id = variant.ctor_def_id.unwrap();
- (
- Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
- Some(substs),
- )
- }
- _ => {
- let mut err = tcx.sess.struct_span_err(
- span,
- "the `Self` constructor can only be used with tuple or unit structs",
- );
- if let Some(adt_def) = ty.ty_adt_def() {
- match adt_def.adt_kind() {
- AdtKind::Enum => {
- err.help("did you mean to use one of the enum's variants?");
- }
- AdtKind::Struct | AdtKind::Union => {
- err.span_suggestion(
- span,
- "use curly brackets",
- String::from("Self { /* fields */ }"),
- Applicability::HasPlaceholders,
- );
- }
- }
- }
- err.emit();
-
- return (tcx.ty_error(), res);
- }
- }
- } else {
- (res, None)
- };
- let def_id = res.def_id();
-
- // The things we are substituting into the type should not contain
- // escaping late-bound regions, and nor should the base type scheme.
- let ty = tcx.type_of(def_id);
-
- let arg_count = GenericArgCountResult {
- explicit_late_bound: ExplicitLateBound::No,
- correct: if infer_args_for_err.is_empty() {
- Ok(())
- } else {
- Err(GenericArgCountMismatch::default())
- },
- };
-
- let substs = self_ctor_substs.unwrap_or_else(|| {
- AstConv::create_substs_for_generic_args(
- tcx,
- def_id,
- &[][..],
- has_self,
- self_ty,
- arg_count,
- // Provide the generic args, and whether types should be inferred.
- |def_id| {
- if let Some(&PathSeg(_, index)) =
- path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
- {
- // If we've encountered an `impl Trait`-related error, we're just
- // going to infer the arguments for better error messages.
- if !infer_args_for_err.contains(&index) {
- // Check whether the user has provided generic arguments.
- if let Some(ref data) = segments[index].args {
- return (Some(data), segments[index].infer_args);
- }
- }
- return (None, segments[index].infer_args);
- }
-
- (None, true)
- },
- // Provide substitutions for parameters for which (valid) arguments have been provided.
- |param, arg| match (¶m.kind, arg) {
- (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- AstConv::ast_region_to_region(self, lt, Some(param)).into()
- }
- (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
- self.to_ty(ty).into()
- }
- (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
- self.const_arg_to_const(&ct.value, param.def_id).into()
- }
- _ => unreachable!(),
- },
- // Provide substitutions for parameters for which arguments are inferred.
- |substs, param, infer_args| {
- match param.kind {
- GenericParamDefKind::Lifetime => {
- self.re_infer(Some(param), span).unwrap().into()
- }
- GenericParamDefKind::Type { has_default, .. } => {
- if !infer_args && has_default {
- // If we have a default, then we it doesn't matter that we're not
- // inferring the type arguments: we provide the default where any
- // is missing.
- let default = tcx.type_of(param.def_id);
- self.normalize_ty(
- span,
- default.subst_spanned(tcx, substs.unwrap(), Some(span)),
- )
- .into()
- } else {
- // If no type arguments were provided, we have to infer them.
- // This case also occurs as a result of some malformed input, e.g.
- // a lifetime argument being given instead of a type parameter.
- // Using inference instead of `Error` gives better error messages.
- self.var_for_def(span, param)
- }
- }
- GenericParamDefKind::Const => {
- // FIXME(const_generics:defaults)
- // No const parameters were provided, we have to infer them.
- self.var_for_def(span, param)
- }
- }
- },
- )
- });
- assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.has_escaping_bound_vars());
-
- // First, store the "user substs" for later.
- self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
-
- self.add_required_obligations(span, def_id, &substs);
-
- // Substitute the values for the type parameters into the type of
- // the referenced item.
- let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
-
- if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
- // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
- // is inherent, there is no `Self` parameter; instead, the impl needs
- // type parameters, which we can infer by unifying the provided `Self`
- // with the substituted impl type.
- // This also occurs for an enum variant on a type alias.
- let ty = tcx.type_of(impl_def_id);
-
- let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
- match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
- Ok(ok) => self.register_infer_ok_obligations(ok),
- Err(_) => {
- self.tcx.sess.delay_span_bug(
- span,
- &format!(
- "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
- self_ty,
- impl_ty,
- ),
- );
- }
- }
- }
-
- self.check_rustc_args_require_const(def_id, hir_id, span);
-
- debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
- self.write_substs(hir_id, substs);
-
- (ty_substituted, res)
- }
-
- /// Add all the obligations that are required, substituting and normalized appropriately.
- fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
- let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
-
- for (i, mut obligation) in traits::predicates_for_generics(
- traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
- self.param_env,
- bounds,
- )
- .enumerate()
- {
- // This makes the error point at the bound, but we want to point at the argument
- if let Some(span) = spans.get(i) {
- obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
- }
- self.register_predicate(obligation);
- }
- }
-
- fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) {
- // We're only interested in functions tagged with
- // #[rustc_args_required_const], so ignore anything that's not.
- if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
- return;
- }
-
- // If our calling expression is indeed the function itself, we're good!
- // If not, generate an error that this can only be called directly.
- if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
- if let ExprKind::Call(ref callee, ..) = expr.kind {
- if callee.hir_id == hir_id {
- return;
- }
- }
- }
-
- self.tcx.sess.span_err(
- span,
- "this function can only be invoked directly, not through a function pointer",
- );
- }
-
- /// Resolves `typ` by a single level if `typ` is a type variable.
- /// If no resolution is possible, then an error is reported.
- /// Numeric inference variables may be left unresolved.
- pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- let ty = self.resolve_vars_with_obligations(ty);
- if !ty.is_ty_var() {
- ty
- } else {
- if !self.is_tainted_by_errors() {
- self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282)
- .note("type must be known at this point")
- .emit();
- }
- let err = self.tcx.ty_error();
- self.demand_suptype(sp, err, ty);
- err
- }
- }
-
- pub(super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
- &self,
- id: hir::HirId,
- ctxt: BreakableCtxt<'tcx>,
- f: F,
- ) -> (BreakableCtxt<'tcx>, R) {
- let index;
- {
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- index = enclosing_breakables.stack.len();
- enclosing_breakables.by_id.insert(id, index);
- enclosing_breakables.stack.push(ctxt);
- }
- let result = f();
- let ctxt = {
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- debug_assert!(enclosing_breakables.stack.len() == index + 1);
- enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
- enclosing_breakables.stack.pop().expect("missing breakable context")
- };
- (ctxt, result)
- }
-
- /// Instantiate a QueryResponse in a probe context, without a
- /// good ObligationCause.
- pub(super) fn probe_instantiate_query_response(
- &self,
- span: Span,
- original_values: &OriginalQueryValues<'tcx>,
- query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
- ) -> InferResult<'tcx, Ty<'tcx>> {
- self.instantiate_query_response_and_region_obligations(
- &traits::ObligationCause::misc(span, self.body_id),
- self.param_env,
- original_values,
- query_result,
- )
- }
-
- /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
- pub(super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
- let mut contained_in_place = false;
-
- while let hir::Node::Expr(parent_expr) =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
- {
- match &parent_expr.kind {
- hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
- if lhs.hir_id == expr_id {
- contained_in_place = true;
- break;
- }
- }
- _ => (),
- }
- expr_id = parent_expr.hir_id;
- }
-
- contained_in_place
- }
-}
-impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
- type Target = Inherited<'a, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.inh
- }
-}
-
-impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
- fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn item_def_id(&self) -> Option<DefId> {
- None
- }
-
- fn default_constness_for_trait_bounds(&self) -> hir::Constness {
- // FIXME: refactor this into a method
- let node = self.tcx.hir().get(self.body_id);
- if let Some(fn_like) = FnLikeNode::from_node(node) {
- fn_like.constness()
- } else {
- hir::Constness::NotConst
- }
- }
-
- fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
- let tcx = self.tcx;
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- let item_id = tcx.hir().ty_param_owner(hir_id);
- let item_def_id = tcx.hir().local_def_id(item_id);
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
- ty::GenericPredicates {
- parent: None,
- predicates: tcx.arena.alloc_from_iter(
- self.param_env.caller_bounds().iter().filter_map(|predicate| {
- match predicate.skip_binders() {
- ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
- // HACK(eddyb) should get the original `Span`.
- let span = tcx.def_span(def_id);
- Some((predicate, span))
- }
- _ => None,
- }
- }),
- ),
- }
- }
-
- fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
- let v = match def {
- Some(def) => infer::EarlyBoundRegion(span, def.name),
- None => infer::MiscVariable(span),
- };
- Some(self.next_region_var(v))
- }
-
- fn allow_ty_infer(&self) -> bool {
- true
- }
-
- fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
- return ty;
- }
- unreachable!()
- } else {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span,
- })
- }
- }
-
- fn ct_infer(
- &self,
- ty: Ty<'tcx>,
- param: Option<&ty::GenericParamDef>,
- span: Span,
- ) -> &'tcx Const<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
- return ct;
- }
- unreachable!()
- } else {
- self.next_const_var(
- ty,
- ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
- )
- }
- }
-
- fn projected_ty_from_poly_trait_ref(
- &self,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Ty<'tcx> {
- let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
- span,
- infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
- &poly_trait_ref,
- );
-
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
- self.tcx,
- span,
- item_def_id,
- item_segment,
- trait_ref.substs,
- );
-
- self.tcx().mk_projection(item_def_id, item_substs)
- }
-
- fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- if ty.has_escaping_bound_vars() {
- ty // FIXME: normalization and escaping regions
- } else {
- self.normalize_associated_types_in(span, &ty)
- }
- }
-
- fn set_tainted_by_errors(&self) {
- self.infcx.set_tainted_by_errors()
- }
-
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
- self.write_ty(hir_id, ty)
- }
-}
--- /dev/null
+use crate::astconv::{
+ AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
+};
+use crate::check::callee::{self, DeferredCallResolution};
+use crate::check::method::{self, MethodCallee, SelfSource};
+use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy};
+
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ExprKind, GenericArg, Node, QPath};
+use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
+use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
+use rustc_infer::infer::{InferOk, InferResult};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::{
+ self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
+};
+use rustc_middle::ty::{
+ self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
+ Ty, UserType,
+};
+use rustc_session::lint;
+use rustc_span::hygiene::DesugaringKind;
+use rustc_span::source_map::{original_sp, DUMMY_SP};
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::{self, BytePos, MultiSpan, Span};
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::opaque_types::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCauseCode, TraitEngine, TraitEngineExt};
+
+use std::collections::hash_map::Entry;
+use std::slice;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ /// Produces warning on the given node, if the current point in the
+ /// function is unreachable, and there hasn't been another warning.
+ pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
+ // FIXME: Combine these two 'if' expressions into one once
+ // let chains are implemented
+ if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
+ // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
+ // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
+ // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
+ if !span.is_desugaring(DesugaringKind::CondTemporary)
+ && !span.is_desugaring(DesugaringKind::Async)
+ && !orig_span.is_desugaring(DesugaringKind::Await)
+ {
+ self.diverges.set(Diverges::WarnedAlways);
+
+ debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
+
+ self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
+ let msg = format!("unreachable {}", kind);
+ lint.build(&msg)
+ .span_label(span, &msg)
+ .span_label(
+ orig_span,
+ custom_note
+ .unwrap_or("any code following this expression is unreachable"),
+ )
+ .emit();
+ })
+ }
+ }
+ }
+
+ /// Resolves type and const variables in `ty` if possible. Unlike the infcx
+ /// version (resolve_vars_if_possible), this version will
+ /// also select obligations if it seems useful, in an effort
+ /// to get more type information.
+ pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+ debug!("resolve_vars_with_obligations(ty={:?})", ty);
+
+ // No Infer()? Nothing needs doing.
+ if !ty.has_infer_types_or_consts() {
+ debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ return ty;
+ }
+
+ // If `ty` is a type variable, see whether we already know what it is.
+ ty = self.resolve_vars_if_possible(&ty);
+ if !ty.has_infer_types_or_consts() {
+ debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ return ty;
+ }
+
+ // If not, try resolving pending obligations as much as
+ // possible. This can help substantially when there are
+ // indirect dependencies that don't seem worth tracking
+ // precisely.
+ self.select_obligations_where_possible(false, |_| {});
+ ty = self.resolve_vars_if_possible(&ty);
+
+ debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ ty
+ }
+
+ pub(in super::super) fn record_deferred_call_resolution(
+ &self,
+ closure_def_id: DefId,
+ r: DeferredCallResolution<'tcx>,
+ ) {
+ let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+ deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
+ }
+
+ pub(in super::super) fn remove_deferred_call_resolutions(
+ &self,
+ closure_def_id: DefId,
+ ) -> Vec<DeferredCallResolution<'tcx>> {
+ let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+ deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
+ }
+
+ pub fn tag(&self) -> String {
+ format!("{:p}", self)
+ }
+
+ pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
+ self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
+ span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
+ })
+ }
+
+ #[inline]
+ pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
+ debug!(
+ "write_ty({:?}, {:?}) in fcx {}",
+ id,
+ self.resolve_vars_if_possible(&ty),
+ self.tag()
+ );
+ self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
+
+ if ty.references_error() {
+ self.has_errors.set(true);
+ self.set_tainted_by_errors();
+ }
+ }
+
+ pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
+ self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
+ }
+
+ pub(in super::super) fn write_resolution(
+ &self,
+ hir_id: hir::HirId,
+ r: Result<(DefKind, DefId), ErrorReported>,
+ ) {
+ self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
+ }
+
+ pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
+ debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
+ self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
+ self.write_substs(hir_id, method.substs);
+
+ // When the method is confirmed, the `method.substs` includes
+ // parameters from not just the method, but also the impl of
+ // the method -- in particular, the `Self` type will be fully
+ // resolved. However, those are not something that the "user
+ // specified" -- i.e., those types come from the inferred type
+ // of the receiver, not something the user wrote. So when we
+ // create the user-substs, we want to replace those earlier
+ // types with just the types that the user actually wrote --
+ // that is, those that appear on the *method itself*.
+ //
+ // As an example, if the user wrote something like
+ // `foo.bar::<u32>(...)` -- the `Self` type here will be the
+ // type of `foo` (possibly adjusted), but we don't want to
+ // include that. We want just the `[_, u32]` part.
+ if !method.substs.is_noop() {
+ let method_generics = self.tcx.generics_of(method.def_id);
+ if !method_generics.params.is_empty() {
+ let user_type_annotation = self.infcx.probe(|_| {
+ let user_substs = UserSubsts {
+ substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
+ let i = param.index as usize;
+ if i < method_generics.parent_count {
+ self.infcx.var_for_def(DUMMY_SP, param)
+ } else {
+ method.substs[i]
+ }
+ }),
+ user_self_ty: None, // not relevant here
+ };
+
+ self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
+ method.def_id,
+ user_substs,
+ ))
+ });
+
+ debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
+ self.write_user_type_annotation(hir_id, user_type_annotation);
+ }
+ }
+ }
+
+ pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
+ if !substs.is_noop() {
+ debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
+
+ self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
+ }
+ }
+
+ /// Given the substs that we just converted from the HIR, try to
+ /// canonicalize them and store them as user-given substitutions
+ /// (i.e., substitutions that must be respected by the NLL check).
+ ///
+ /// This should be invoked **before any unifications have
+ /// occurred**, so that annotations like `Vec<_>` are preserved
+ /// properly.
+ pub fn write_user_type_annotation_from_substs(
+ &self,
+ hir_id: hir::HirId,
+ def_id: DefId,
+ substs: SubstsRef<'tcx>,
+ user_self_ty: Option<UserSelfTy<'tcx>>,
+ ) {
+ debug!(
+ "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
+ user_self_ty={:?} in fcx {}",
+ hir_id,
+ def_id,
+ substs,
+ user_self_ty,
+ self.tag(),
+ );
+
+ if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
+ let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
+ def_id,
+ UserSubsts { substs, user_self_ty },
+ ));
+ debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
+ self.write_user_type_annotation(hir_id, canonicalized);
+ }
+ }
+
+ pub fn write_user_type_annotation(
+ &self,
+ hir_id: hir::HirId,
+ canonical_user_type_annotation: CanonicalUserType<'tcx>,
+ ) {
+ debug!(
+ "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
+ hir_id,
+ canonical_user_type_annotation,
+ self.tag(),
+ );
+
+ if !canonical_user_type_annotation.is_identity() {
+ self.typeck_results
+ .borrow_mut()
+ .user_provided_types_mut()
+ .insert(hir_id, canonical_user_type_annotation);
+ } else {
+ debug!("write_user_type_annotation: skipping identity substs");
+ }
+ }
+
+ pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
+ debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
+
+ if adj.is_empty() {
+ return;
+ }
+
+ let autoborrow_mut = adj.iter().any(|adj| {
+ matches!(adj, &Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
+ ..
+ })
+ });
+
+ match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
+ Entry::Vacant(entry) => {
+ entry.insert(adj);
+ }
+ Entry::Occupied(mut entry) => {
+ debug!(" - composing on top of {:?}", entry.get());
+ match (&entry.get()[..], &adj[..]) {
+ // Applying any adjustment on top of a NeverToAny
+ // is a valid NeverToAny adjustment, because it can't
+ // be reached.
+ (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
+ (&[
+ Adjustment { kind: Adjust::Deref(_), .. },
+ Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
+ ], &[
+ Adjustment { kind: Adjust::Deref(_), .. },
+ .. // Any following adjustments are allowed.
+ ]) => {
+ // A reborrow has no effect before a dereference.
+ }
+ // FIXME: currently we never try to compose autoderefs
+ // and ReifyFnPointer/UnsafeFnPointer, but we could.
+ _ =>
+ bug!("while adjusting {:?}, can't compose {:?} and {:?}",
+ expr, entry.get(), adj)
+ };
+ *entry.get_mut() = adj;
+ }
+ }
+
+ // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
+ // In this case implicit use of `Deref` and `Index` within `<expr>` should
+ // instead be `DerefMut` and `IndexMut`, so fix those up.
+ if autoborrow_mut {
+ self.convert_place_derefs_to_mutable(expr);
+ }
+ }
+
+ /// Basically whenever we are converting from a type scheme into
+ /// the fn body space, we always want to normalize associated
+ /// types as well. This function combines the two.
+ fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ let value = value.subst(self.tcx, substs);
+ let result = self.normalize_associated_types_in(span, &value);
+ debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
+ result
+ }
+
+ /// As `instantiate_type_scheme`, but for the bounds found in a
+ /// generic type scheme.
+ pub(in super::super) fn instantiate_bounds(
+ &self,
+ span: Span,
+ def_id: DefId,
+ substs: SubstsRef<'tcx>,
+ ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
+ let bounds = self.tcx.predicates_of(def_id);
+ let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
+ let result = bounds.instantiate(self.tcx, substs);
+ let result = self.normalize_associated_types_in(span, &result);
+ debug!(
+ "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
+ bounds, substs, result, spans,
+ );
+ (result, spans)
+ }
+
+ /// Replaces the opaque types from the given value with type variables,
+ /// and records the `OpaqueTypeMap` for later use during writeback. See
+ /// `InferCtxt::instantiate_opaque_types` for more details.
+ pub(in super::super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
+ &self,
+ parent_id: hir::HirId,
+ value: &T,
+ value_span: Span,
+ ) -> T {
+ let parent_def_id = self.tcx.hir().local_def_id(parent_id);
+ debug!(
+ "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
+ parent_def_id, value
+ );
+
+ let (value, opaque_type_map) =
+ self.register_infer_ok_obligations(self.instantiate_opaque_types(
+ parent_def_id,
+ self.body_id,
+ self.param_env,
+ value,
+ value_span,
+ ));
+
+ let mut opaque_types = self.opaque_types.borrow_mut();
+ let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
+ for (ty, decl) in opaque_type_map {
+ let _ = opaque_types.insert(ty, decl);
+ let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
+ }
+
+ value
+ }
+
+ pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
+ }
+
+ pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>(
+ &self,
+ span: Span,
+ value: &T,
+ ) -> InferOk<'tcx, T>
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
+ }
+
+ pub fn require_type_meets(
+ &self,
+ ty: Ty<'tcx>,
+ span: Span,
+ code: traits::ObligationCauseCode<'tcx>,
+ def_id: DefId,
+ ) {
+ self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
+ }
+
+ pub fn require_type_is_sized(
+ &self,
+ ty: Ty<'tcx>,
+ span: Span,
+ code: traits::ObligationCauseCode<'tcx>,
+ ) {
+ if !ty.references_error() {
+ let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
+ self.require_type_meets(ty, span, code, lang_item);
+ }
+ }
+
+ pub fn require_type_is_sized_deferred(
+ &self,
+ ty: Ty<'tcx>,
+ span: Span,
+ code: traits::ObligationCauseCode<'tcx>,
+ ) {
+ if !ty.references_error() {
+ self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
+ }
+ }
+
+ pub fn register_bound(
+ &self,
+ ty: Ty<'tcx>,
+ def_id: DefId,
+ cause: traits::ObligationCause<'tcx>,
+ ) {
+ if !ty.references_error() {
+ self.fulfillment_cx.borrow_mut().register_bound(
+ self,
+ self.param_env,
+ ty,
+ def_id,
+ cause,
+ );
+ }
+ }
+
+ pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
+ let t = AstConv::ast_ty_to_ty(self, ast_t);
+ self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
+ t
+ }
+
+ pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+ let ty = self.to_ty(ast_ty);
+ debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
+
+ if Self::can_contain_user_lifetime_bounds(ty) {
+ let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
+ debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
+ self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
+ }
+
+ ty
+ }
+
+ pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
+ let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
+ let c = ty::Const::from_anon_const(self.tcx, const_def_id);
+ self.register_wf_obligation(
+ c.into(),
+ self.tcx.hir().span(ast_c.hir_id),
+ ObligationCauseCode::MiscObligation,
+ );
+ c
+ }
+
+ pub fn const_arg_to_const(
+ &self,
+ ast_c: &hir::AnonConst,
+ param_def_id: DefId,
+ ) -> &'tcx ty::Const<'tcx> {
+ let const_def = ty::WithOptConstParam {
+ did: self.tcx.hir().local_def_id(ast_c.hir_id),
+ const_param_did: Some(param_def_id),
+ };
+ let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
+ self.register_wf_obligation(
+ c.into(),
+ self.tcx.hir().span(ast_c.hir_id),
+ ObligationCauseCode::MiscObligation,
+ );
+ c
+ }
+
+ // If the type given by the user has free regions, save it for later, since
+ // NLL would like to enforce those. Also pass in types that involve
+ // projections, since those can resolve to `'static` bounds (modulo #54940,
+ // which hopefully will be fixed by the time you see this comment, dear
+ // reader, although I have my doubts). Also pass in types with inference
+ // types, because they may be repeated. Other sorts of things are already
+ // sufficiently enforced with erased regions. =)
+ fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ t.has_free_regions() || t.has_projections() || t.has_infer_types()
+ }
+
+ pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
+ match self.typeck_results.borrow().node_types().get(id) {
+ Some(&t) => t,
+ None if self.is_tainted_by_errors() => self.tcx.ty_error(),
+ None => {
+ bug!(
+ "no type for node {}: {} in fcx {}",
+ id,
+ self.tcx.hir().node_to_string(id),
+ self.tag()
+ );
+ }
+ }
+ }
+
+ /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
+ pub fn register_wf_obligation(
+ &self,
+ arg: subst::GenericArg<'tcx>,
+ span: Span,
+ code: traits::ObligationCauseCode<'tcx>,
+ ) {
+ // WF obligations never themselves fail, so no real need to give a detailed cause:
+ let cause = traits::ObligationCause::new(span, self.body_id, code);
+ self.register_predicate(traits::Obligation::new(
+ cause,
+ self.param_env,
+ ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
+ ));
+ }
+
+ /// Registers obligations that all `substs` are well-formed.
+ pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
+ for arg in substs.iter().filter(|arg| {
+ matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
+ }) {
+ self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
+ }
+ }
+
+ /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
+ /// type/region parameter was instantiated (`substs`), creates and registers suitable
+ /// trait/region obligations.
+ ///
+ /// For example, if there is a function:
+ ///
+ /// ```
+ /// fn foo<'a,T:'a>(...)
+ /// ```
+ ///
+ /// and a reference:
+ ///
+ /// ```
+ /// let f = foo;
+ /// ```
+ ///
+ /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
+ /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
+ pub fn add_obligations_for_parameters(
+ &self,
+ cause: traits::ObligationCause<'tcx>,
+ predicates: ty::InstantiatedPredicates<'tcx>,
+ ) {
+ assert!(!predicates.has_escaping_bound_vars());
+
+ debug!("add_obligations_for_parameters(predicates={:?})", predicates);
+
+ for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
+ self.register_predicate(obligation);
+ }
+ }
+
+ // FIXME(arielb1): use this instead of field.ty everywhere
+ // Only for fields! Returns <none> for methods>
+ // Indifferent to privacy flags
+ pub fn field_ty(
+ &self,
+ span: Span,
+ field: &'tcx ty::FieldDef,
+ substs: SubstsRef<'tcx>,
+ ) -> Ty<'tcx> {
+ self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
+ }
+
+ pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
+ let mut generators = self.deferred_generator_interiors.borrow_mut();
+ for (body_id, interior, kind) in generators.drain(..) {
+ self.select_obligations_where_possible(false, |_| {});
+ crate::check::generator_interior::resolve_interior(
+ self, def_id, body_id, interior, kind,
+ );
+ }
+ }
+
+ // Tries to apply a fallback to `ty` if it is an unsolved variable.
+ //
+ // - Unconstrained ints are replaced with `i32`.
+ //
+ // - Unconstrained floats are replaced with with `f64`.
+ //
+ // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
+ // is enabled. Otherwise, they are replaced with `()`.
+ //
+ // Fallback becomes very dubious if we have encountered type-checking errors.
+ // In that case, fallback to Error.
+ // The return value indicates whether fallback has occurred.
+ pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
+ use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
+ use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
+
+ assert!(ty.is_ty_infer());
+ let fallback = match self.type_is_unconstrained_numeric(ty) {
+ _ if self.is_tainted_by_errors() => self.tcx().ty_error(),
+ UnconstrainedInt => self.tcx.types.i32,
+ UnconstrainedFloat => self.tcx.types.f64,
+ Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
+ Neither => {
+ // This type variable was created from the instantiation of an opaque
+ // type. The fact that we're attempting to perform fallback for it
+ // means that the function neither constrained it to a concrete
+ // type, nor to the opaque type itself.
+ //
+ // For example, in this code:
+ //
+ //```
+ // type MyType = impl Copy;
+ // fn defining_use() -> MyType { true }
+ // fn other_use() -> MyType { defining_use() }
+ // ```
+ //
+ // `defining_use` will constrain the instantiated inference
+ // variable to `bool`, while `other_use` will constrain
+ // the instantiated inference variable to `MyType`.
+ //
+ // When we process opaque types during writeback, we
+ // will handle cases like `other_use`, and not count
+ // them as defining usages
+ //
+ // However, we also need to handle cases like this:
+ //
+ // ```rust
+ // pub type Foo = impl Copy;
+ // fn produce() -> Option<Foo> {
+ // None
+ // }
+ // ```
+ //
+ // In the above snippet, the inference variable created by
+ // instantiating `Option<Foo>` will be completely unconstrained.
+ // We treat this as a non-defining use by making the inference
+ // variable fall back to the opaque type itself.
+ if let FallbackMode::All = mode {
+ if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
+ debug!(
+ "fallback_if_possible: falling back opaque type var {:?} to {:?}",
+ ty, opaque_ty
+ );
+ *opaque_ty
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ };
+ debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
+ self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
+ true
+ }
+
+ pub(in super::super) fn select_all_obligations_or_error(&self) {
+ debug!("select_all_obligations_or_error");
+ if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
+ self.report_fulfillment_errors(&errors, self.inh.body_id, false);
+ }
+ }
+
+ /// Select as many obligations as we can at present.
+ pub(in super::super) fn select_obligations_where_possible(
+ &self,
+ fallback_has_occurred: bool,
+ mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
+ ) {
+ let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
+ if let Err(mut errors) = result {
+ mutate_fullfillment_errors(&mut errors);
+ self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
+ }
+ }
+
+ /// For the overloaded place expressions (`*x`, `x[3]`), the trait
+ /// returns a type of `&T`, but the actual type we assign to the
+ /// *expression* is `T`. So this function just peels off the return
+ /// type by one layer to yield `T`.
+ pub(in super::super) fn make_overloaded_place_return_type(
+ &self,
+ method: MethodCallee<'tcx>,
+ ) -> ty::TypeAndMut<'tcx> {
+ // extract method return type, which will be &T;
+ let ret_ty = method.sig.output();
+
+ // method returns &T, but the type as visible to user is T, so deref
+ ret_ty.builtin_deref(true).unwrap()
+ }
+
+ fn self_type_matches_expected_vid(
+ &self,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ expected_vid: ty::TyVid,
+ ) -> bool {
+ let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
+ debug!(
+ "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
+ trait_ref, self_ty, expected_vid
+ );
+ match *self_ty.kind() {
+ ty::Infer(ty::TyVar(found_vid)) => {
+ // FIXME: consider using `sub_root_var` here so we
+ // can see through subtyping.
+ let found_vid = self.root_var(found_vid);
+ debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
+ expected_vid == found_vid
+ }
+ _ => false,
+ }
+ }
+
+ pub(in super::super) fn obligations_for_self_ty<'b>(
+ &'b self,
+ self_ty: ty::TyVid,
+ ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+ + Captures<'tcx>
+ + 'b {
+ // FIXME: consider using `sub_root_var` here so we
+ // can see through subtyping.
+ let ty_var_root = self.root_var(self_ty);
+ debug!(
+ "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
+ self_ty,
+ ty_var_root,
+ self.fulfillment_cx.borrow().pending_obligations()
+ );
+
+ self.fulfillment_cx
+ .borrow()
+ .pending_obligations()
+ .into_iter()
+ .filter_map(move |obligation| {
+ match obligation.predicate.skip_binders() {
+ ty::PredicateAtom::Projection(data) => {
+ Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
+ }
+ ty::PredicateAtom::Trait(data, _) => {
+ Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
+ }
+ ty::PredicateAtom::Subtype(..) => None,
+ ty::PredicateAtom::RegionOutlives(..) => None,
+ ty::PredicateAtom::TypeOutlives(..) => None,
+ ty::PredicateAtom::WellFormed(..) => None,
+ ty::PredicateAtom::ObjectSafe(..) => None,
+ ty::PredicateAtom::ConstEvaluatable(..) => None,
+ ty::PredicateAtom::ConstEquate(..) => None,
+ // N.B., this predicate is created by breaking down a
+ // `ClosureType: FnFoo()` predicate, where
+ // `ClosureType` represents some `Closure`. It can't
+ // possibly be referring to the current closure,
+ // because we haven't produced the `Closure` for
+ // this closure yet; this is exactly why the other
+ // code is looking for a self type of a unresolved
+ // inference variable.
+ ty::PredicateAtom::ClosureKind(..) => None,
+ ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
+ }
+ })
+ .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
+ }
+
+ pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
+ self.obligations_for_self_ty(self_ty)
+ .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
+ }
+
+ pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
+ vec![self.tcx.ty_error(); len]
+ }
+
+ /// Unifies the output type with the expected type early, for more coercions
+ /// and forward type information on the input expressions.
+ pub(in super::super) fn expected_inputs_for_expected_output(
+ &self,
+ call_span: Span,
+ expected_ret: Expectation<'tcx>,
+ formal_ret: Ty<'tcx>,
+ formal_args: &[Ty<'tcx>],
+ ) -> Vec<Ty<'tcx>> {
+ let formal_ret = self.resolve_vars_with_obligations(formal_ret);
+ let ret_ty = match expected_ret.only_has_type(self) {
+ Some(ret) => ret,
+ None => return Vec::new(),
+ };
+ let expect_args = self
+ .fudge_inference_if_ok(|| {
+ // Attempt to apply a subtyping relationship between the formal
+ // return type (likely containing type variables if the function
+ // is polymorphic) and the expected return type.
+ // No argument expectations are produced if unification fails.
+ let origin = self.misc(call_span);
+ let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
+
+ // FIXME(#27336) can't use ? here, Try::from_error doesn't default
+ // to identity so the resulting type is not constrained.
+ match ures {
+ Ok(ok) => {
+ // Process any obligations locally as much as
+ // we can. We don't care if some things turn
+ // out unconstrained or ambiguous, as we're
+ // just trying to get hints here.
+ self.save_and_restore_in_snapshot_flag(|_| {
+ let mut fulfill = TraitEngine::new(self.tcx);
+ for obligation in ok.obligations {
+ fulfill.register_predicate_obligation(self, obligation);
+ }
+ fulfill.select_where_possible(self)
+ })
+ .map_err(|_| ())?;
+ }
+ Err(_) => return Err(()),
+ }
+
+ // Record all the argument types, with the substitutions
+ // produced from the above subtyping unification.
+ Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
+ })
+ .unwrap_or_default();
+ debug!(
+ "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
+ formal_args, formal_ret, expect_args, expected_ret
+ );
+ expect_args
+ }
+
+ pub(in super::super) fn resolve_lang_item_path(
+ &self,
+ lang_item: hir::LangItem,
+ span: Span,
+ hir_id: hir::HirId,
+ ) -> (Res, Ty<'tcx>) {
+ let def_id = self.tcx.require_lang_item(lang_item, Some(span));
+ let def_kind = self.tcx.def_kind(def_id);
+
+ let item_ty = if let DefKind::Variant = def_kind {
+ self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
+ } else {
+ self.tcx.type_of(def_id)
+ };
+ let substs = self.infcx.fresh_substs_for_item(span, def_id);
+ let ty = item_ty.subst(self.tcx, substs);
+
+ self.write_resolution(hir_id, Ok((def_kind, def_id)));
+ self.add_required_obligations(span, def_id, &substs);
+ (Res::Def(def_kind, def_id), ty)
+ }
+
+ /// Resolves an associated value path into a base type and associated constant, or method
+ /// resolution. The newly resolved definition is written into `type_dependent_defs`.
+ pub fn resolve_ty_and_res_ufcs<'b>(
+ &self,
+ qpath: &'b QPath<'b>,
+ hir_id: hir::HirId,
+ span: Span,
+ ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
+ debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
+ let (ty, qself, item_segment) = match *qpath {
+ QPath::Resolved(ref opt_qself, ref path) => {
+ return (
+ path.res,
+ opt_qself.as_ref().map(|qself| self.to_ty(qself)),
+ &path.segments[..],
+ );
+ }
+ QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
+ QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
+ };
+ if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
+ {
+ // Return directly on cache hit. This is useful to avoid doubly reporting
+ // errors with default match binding modes. See #44614.
+ let def =
+ cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
+ return (def, Some(ty), slice::from_ref(&**item_segment));
+ }
+ let item_name = item_segment.ident;
+ let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
+ let result = match error {
+ method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
+ _ => Err(ErrorReported),
+ };
+ if item_name.name != kw::Invalid {
+ if let Some(mut e) = self.report_method_error(
+ span,
+ ty,
+ item_name,
+ SelfSource::QPath(qself),
+ error,
+ None,
+ ) {
+ e.emit();
+ }
+ }
+ result
+ });
+
+ // Write back the new resolution.
+ self.write_resolution(hir_id, result);
+ (
+ result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err),
+ Some(ty),
+ slice::from_ref(&**item_segment),
+ )
+ }
+
+ /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
+ pub(in super::super) fn get_node_fn_decl(
+ &self,
+ node: Node<'tcx>,
+ ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
+ match node {
+ Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
+ // This is less than ideal, it will not suggest a return type span on any
+ // method called `main`, regardless of whether it is actually the entry point,
+ // but it will still present it as the reason for the expected type.
+ Some((&sig.decl, ident, ident.name != sym::main))
+ }
+ Node::TraitItem(&hir::TraitItem {
+ ident,
+ kind: hir::TraitItemKind::Fn(ref sig, ..),
+ ..
+ }) => Some((&sig.decl, ident, true)),
+ Node::ImplItem(&hir::ImplItem {
+ ident,
+ kind: hir::ImplItemKind::Fn(ref sig, ..),
+ ..
+ }) => Some((&sig.decl, ident, false)),
+ _ => None,
+ }
+ }
+
+ /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
+ /// suggestion can be made, `None` otherwise.
+ pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
+ // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
+ // `while` before reaching it, as block tail returns are not available in them.
+ self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
+ let parent = self.tcx.hir().get(blk_id);
+ self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
+ })
+ }
+
+ pub(in super::super) fn note_internal_mutation_in_method(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ if found != self.tcx.types.unit {
+ return;
+ }
+ if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
+ if self
+ .typeck_results
+ .borrow()
+ .expr_ty_adjusted_opt(rcvr)
+ .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
+ {
+ return;
+ }
+ let mut sp = MultiSpan::from_span(path_segment.ident.span);
+ sp.push_span_label(
+ path_segment.ident.span,
+ format!(
+ "this call modifies {} in-place",
+ match rcvr.kind {
+ ExprKind::Path(QPath::Resolved(
+ None,
+ hir::Path { segments: [segment], .. },
+ )) => format!("`{}`", segment.ident),
+ _ => "its receiver".to_string(),
+ }
+ ),
+ );
+ sp.push_span_label(
+ rcvr.span,
+ "you probably want to use this value after calling the method...".to_string(),
+ );
+ err.span_note(
+ sp,
+ &format!("method `{}` modifies its receiver in-place", path_segment.ident),
+ );
+ err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
+ }
+ }
+
+ pub(in super::super) fn note_need_for_fn_pointer(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
+ (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ if sig1 != sig2 {
+ return;
+ }
+ err.note(
+ "different `fn` items always have unique types, even if their signatures are \
+ the same",
+ );
+ (sig1, *did1, substs1)
+ }
+ (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
+ let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
+ if sig1 != *sig2 {
+ return;
+ }
+ (sig1, *did, substs)
+ }
+ _ => return,
+ };
+ err.help(&format!("change the expected type to be function pointer `{}`", sig));
+ err.help(&format!(
+ "if the expected type is due to type inference, cast the expected `fn` to a function \
+ pointer: `{} as {}`",
+ self.tcx.def_path_str_with_substs(did, substs),
+ sig
+ ));
+ }
+
+ pub(in super::super) fn could_remove_semicolon(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> Option<Span> {
+ // Be helpful when the user wrote `{... expr;}` and
+ // taking the `;` off is enough to fix the error.
+ let last_stmt = blk.stmts.last()?;
+ let last_expr = match last_stmt.kind {
+ hir::StmtKind::Semi(ref e) => e,
+ _ => return None,
+ };
+ let last_expr_ty = self.node_ty(last_expr.hir_id);
+ if matches!(last_expr_ty.kind(), ty::Error(_))
+ || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
+ {
+ return None;
+ }
+ let original_span = original_sp(last_stmt.span, blk.span);
+ Some(original_span.with_lo(original_span.hi() - BytePos(1)))
+ }
+
+ // Instantiates the given path, which must refer to an item with the given
+ // number of type parameters and type.
+ pub fn instantiate_value_path(
+ &self,
+ segments: &[hir::PathSegment<'_>],
+ self_ty: Option<Ty<'tcx>>,
+ res: Res,
+ span: Span,
+ hir_id: hir::HirId,
+ ) -> (Ty<'tcx>, Res) {
+ debug!(
+ "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
+ segments, self_ty, res, hir_id,
+ );
+
+ let tcx = self.tcx;
+
+ let path_segs = match res {
+ Res::Local(_) | Res::SelfCtor(_) => vec![],
+ Res::Def(kind, def_id) => {
+ AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
+ }
+ _ => bug!("instantiate_value_path on {:?}", res),
+ };
+
+ let mut user_self_ty = None;
+ let mut is_alias_variant_ctor = false;
+ match res {
+ Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
+ if let Some(self_ty) = self_ty {
+ let adt_def = self_ty.ty_adt_def().unwrap();
+ user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
+ is_alias_variant_ctor = true;
+ }
+ }
+ Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
+ let container = tcx.associated_item(def_id).container;
+ debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
+ match container {
+ ty::TraitContainer(trait_did) => {
+ callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
+ }
+ ty::ImplContainer(impl_def_id) => {
+ if segments.len() == 1 {
+ // `<T>::assoc` will end up here, and so
+ // can `T::assoc`. It this came from an
+ // inherent impl, we need to record the
+ // `T` for posterity (see `UserSelfTy` for
+ // details).
+ let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
+ user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+
+ // Now that we have categorized what space the parameters for each
+ // segment belong to, let's sort out the parameters that the user
+ // provided (if any) into their appropriate spaces. We'll also report
+ // errors if type parameters are provided in an inappropriate place.
+
+ let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
+ let generics_has_err = AstConv::prohibit_generics(
+ self,
+ segments.iter().enumerate().filter_map(|(index, seg)| {
+ if !generic_segs.contains(&index) || is_alias_variant_ctor {
+ Some(seg)
+ } else {
+ None
+ }
+ }),
+ );
+
+ if let Res::Local(hid) = res {
+ let ty = self.local_ty(span, hid).decl_ty;
+ let ty = self.normalize_associated_types_in(span, &ty);
+ self.write_ty(hir_id, ty);
+ return (ty, res);
+ }
+
+ if generics_has_err {
+ // Don't try to infer type parameters when prohibited generic arguments were given.
+ user_self_ty = None;
+ }
+
+ // Now we have to compare the types that the user *actually*
+ // provided against the types that were *expected*. If the user
+ // did not provide any types, then we want to substitute inference
+ // variables. If the user provided some types, we may still need
+ // to add defaults. If the user provided *too many* types, that's
+ // a problem.
+
+ let mut infer_args_for_err = FxHashSet::default();
+ for &PathSeg(def_id, index) in &path_segs {
+ let seg = &segments[index];
+ let generics = tcx.generics_of(def_id);
+ // Argument-position `impl Trait` is treated as a normal generic
+ // parameter internally, but we don't allow users to specify the
+ // parameter's value explicitly, so we have to do some error-
+ // checking here.
+ if let GenericArgCountResult {
+ correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
+ ..
+ } = AstConv::check_generic_arg_count_for_call(
+ tcx, span, &generics, &seg, false, // `is_method_call`
+ ) {
+ infer_args_for_err.insert(index);
+ self.set_tainted_by_errors(); // See issue #53251.
+ }
+ }
+
+ let has_self = path_segs
+ .last()
+ .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
+ .unwrap_or(false);
+
+ let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
+ let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
+ match *ty.kind() {
+ ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
+ let variant = adt_def.non_enum_variant();
+ let ctor_def_id = variant.ctor_def_id.unwrap();
+ (
+ Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
+ Some(substs),
+ )
+ }
+ _ => {
+ let mut err = tcx.sess.struct_span_err(
+ span,
+ "the `Self` constructor can only be used with tuple or unit structs",
+ );
+ if let Some(adt_def) = ty.ty_adt_def() {
+ match adt_def.adt_kind() {
+ AdtKind::Enum => {
+ err.help("did you mean to use one of the enum's variants?");
+ }
+ AdtKind::Struct | AdtKind::Union => {
+ err.span_suggestion(
+ span,
+ "use curly brackets",
+ String::from("Self { /* fields */ }"),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+ err.emit();
+
+ return (tcx.ty_error(), res);
+ }
+ }
+ } else {
+ (res, None)
+ };
+ let def_id = res.def_id();
+
+ // The things we are substituting into the type should not contain
+ // escaping late-bound regions, and nor should the base type scheme.
+ let ty = tcx.type_of(def_id);
+
+ let arg_count = GenericArgCountResult {
+ explicit_late_bound: ExplicitLateBound::No,
+ correct: if infer_args_for_err.is_empty() {
+ Ok(())
+ } else {
+ Err(GenericArgCountMismatch::default())
+ },
+ };
+
+ let substs = self_ctor_substs.unwrap_or_else(|| {
+ AstConv::create_substs_for_generic_args(
+ tcx,
+ def_id,
+ &[][..],
+ has_self,
+ self_ty,
+ arg_count,
+ // Provide the generic args, and whether types should be inferred.
+ |def_id| {
+ if let Some(&PathSeg(_, index)) =
+ path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
+ {
+ // If we've encountered an `impl Trait`-related error, we're just
+ // going to infer the arguments for better error messages.
+ if !infer_args_for_err.contains(&index) {
+ // Check whether the user has provided generic arguments.
+ if let Some(ref data) = segments[index].args {
+ return (Some(data), segments[index].infer_args);
+ }
+ }
+ return (None, segments[index].infer_args);
+ }
+
+ (None, true)
+ },
+ // Provide substitutions for parameters for which (valid) arguments have been provided.
+ |param, arg| match (¶m.kind, arg) {
+ (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+ AstConv::ast_region_to_region(self, lt, Some(param)).into()
+ }
+ (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
+ self.to_ty(ty).into()
+ }
+ (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
+ self.const_arg_to_const(&ct.value, param.def_id).into()
+ }
+ _ => unreachable!(),
+ },
+ // Provide substitutions for parameters for which arguments are inferred.
+ |substs, param, infer_args| {
+ match param.kind {
+ GenericParamDefKind::Lifetime => {
+ self.re_infer(Some(param), span).unwrap().into()
+ }
+ GenericParamDefKind::Type { has_default, .. } => {
+ if !infer_args && has_default {
+ // If we have a default, then we it doesn't matter that we're not
+ // inferring the type arguments: we provide the default where any
+ // is missing.
+ let default = tcx.type_of(param.def_id);
+ self.normalize_ty(
+ span,
+ default.subst_spanned(tcx, substs.unwrap(), Some(span)),
+ )
+ .into()
+ } else {
+ // If no type arguments were provided, we have to infer them.
+ // This case also occurs as a result of some malformed input, e.g.
+ // a lifetime argument being given instead of a type parameter.
+ // Using inference instead of `Error` gives better error messages.
+ self.var_for_def(span, param)
+ }
+ }
+ GenericParamDefKind::Const => {
+ // FIXME(const_generics:defaults)
+ // No const parameters were provided, we have to infer them.
+ self.var_for_def(span, param)
+ }
+ }
+ },
+ )
+ });
+ assert!(!substs.has_escaping_bound_vars());
+ assert!(!ty.has_escaping_bound_vars());
+
+ // First, store the "user substs" for later.
+ self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
+
+ self.add_required_obligations(span, def_id, &substs);
+
+ // Substitute the values for the type parameters into the type of
+ // the referenced item.
+ let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
+
+ if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+ // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
+ // is inherent, there is no `Self` parameter; instead, the impl needs
+ // type parameters, which we can infer by unifying the provided `Self`
+ // with the substituted impl type.
+ // This also occurs for an enum variant on a type alias.
+ let ty = tcx.type_of(impl_def_id);
+
+ let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
+ match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
+ Ok(ok) => self.register_infer_ok_obligations(ok),
+ Err(_) => {
+ self.tcx.sess.delay_span_bug(
+ span,
+ &format!(
+ "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
+ self_ty,
+ impl_ty,
+ ),
+ );
+ }
+ }
+ }
+
+ self.check_rustc_args_require_const(def_id, hir_id, span);
+
+ debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
+ self.write_substs(hir_id, substs);
+
+ (ty_substituted, res)
+ }
+
+ /// Add all the obligations that are required, substituting and normalized appropriately.
+ fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
+ let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
+
+ for (i, mut obligation) in traits::predicates_for_generics(
+ traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
+ self.param_env,
+ bounds,
+ )
+ .enumerate()
+ {
+ // This makes the error point at the bound, but we want to point at the argument
+ if let Some(span) = spans.get(i) {
+ obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
+ }
+ self.register_predicate(obligation);
+ }
+ }
+
+ /// Resolves `typ` by a single level if `typ` is a type variable.
+ /// If no resolution is possible, then an error is reported.
+ /// Numeric inference variables may be left unresolved.
+ pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let ty = self.resolve_vars_with_obligations(ty);
+ if !ty.is_ty_var() {
+ ty
+ } else {
+ if !self.is_tainted_by_errors() {
+ self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282)
+ .note("type must be known at this point")
+ .emit();
+ }
+ let err = self.tcx.ty_error();
+ self.demand_suptype(sp, err, ty);
+ err
+ }
+ }
+
+ pub(in super::super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
+ &self,
+ id: hir::HirId,
+ ctxt: BreakableCtxt<'tcx>,
+ f: F,
+ ) -> (BreakableCtxt<'tcx>, R) {
+ let index;
+ {
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ index = enclosing_breakables.stack.len();
+ enclosing_breakables.by_id.insert(id, index);
+ enclosing_breakables.stack.push(ctxt);
+ }
+ let result = f();
+ let ctxt = {
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ debug_assert!(enclosing_breakables.stack.len() == index + 1);
+ enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+ enclosing_breakables.stack.pop().expect("missing breakable context")
+ };
+ (ctxt, result)
+ }
+
+ /// Instantiate a QueryResponse in a probe context, without a
+ /// good ObligationCause.
+ pub(in super::super) fn probe_instantiate_query_response(
+ &self,
+ span: Span,
+ original_values: &OriginalQueryValues<'tcx>,
+ query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+ ) -> InferResult<'tcx, Ty<'tcx>> {
+ self.instantiate_query_response_and_region_obligations(
+ &traits::ObligationCause::misc(span, self.body_id),
+ self.param_env,
+ original_values,
+ query_result,
+ )
+ }
+
+ /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
+ pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
+ let mut contained_in_place = false;
+
+ while let hir::Node::Expr(parent_expr) =
+ self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
+ {
+ match &parent_expr.kind {
+ hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
+ if lhs.hir_id == expr_id {
+ contained_in_place = true;
+ break;
+ }
+ }
+ _ => (),
+ }
+ expr_id = parent_expr.hir_id;
+ }
+
+ contained_in_place
+ }
+}
--- /dev/null
+use crate::astconv::AstConv;
+use crate::check::coercion::CoerceMany;
+use crate::check::method::MethodCallee;
+use crate::check::Expectation::*;
+use crate::check::TupleArgumentsFlag::*;
+use crate::check::{
+ potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt,
+ LocalTy, Needs, TupleArgumentsFlag,
+};
+
+use rustc_ast as ast;
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{ExprKind, Node, QPath};
+use rustc_middle::ty::adjustment::AllowTwoPhase;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{self, Ty};
+use rustc_session::Session;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{self, Span};
+use rustc_trait_selection::traits::{self, ObligationCauseCode};
+
+use std::mem::replace;
+use std::slice;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(in super::super) fn check_casts(&self) {
+ let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
+ for cast in deferred_cast_checks.drain(..) {
+ cast.check(self);
+ }
+ }
+
+ pub(in super::super) fn check_method_argument_types(
+ &self,
+ sp: Span,
+ expr: &'tcx hir::Expr<'tcx>,
+ method: Result<MethodCallee<'tcx>, ()>,
+ args_no_rcvr: &'tcx [hir::Expr<'tcx>],
+ tuple_arguments: TupleArgumentsFlag,
+ expected: Expectation<'tcx>,
+ ) -> Ty<'tcx> {
+ let has_error = match method {
+ Ok(method) => method.substs.references_error() || method.sig.references_error(),
+ Err(_) => true,
+ };
+ if has_error {
+ let err_inputs = self.err_args(args_no_rcvr.len());
+
+ let err_inputs = match tuple_arguments {
+ DontTupleArguments => err_inputs,
+ TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
+ };
+
+ self.check_argument_types(
+ sp,
+ expr,
+ &err_inputs[..],
+ &[],
+ args_no_rcvr,
+ false,
+ tuple_arguments,
+ None,
+ );
+ return self.tcx.ty_error();
+ }
+
+ let method = method.unwrap();
+ // HACK(eddyb) ignore self in the definition (see above).
+ let expected_arg_tys = self.expected_inputs_for_expected_output(
+ sp,
+ expected,
+ method.sig.output(),
+ &method.sig.inputs()[1..],
+ );
+ self.check_argument_types(
+ sp,
+ expr,
+ &method.sig.inputs()[1..],
+ &expected_arg_tys[..],
+ args_no_rcvr,
+ method.sig.c_variadic,
+ tuple_arguments,
+ self.tcx.hir().span_if_local(method.def_id),
+ );
+ method.sig.output()
+ }
+
+ /// Generic function that factors out common logic from function calls,
+ /// method calls and overloaded operators.
+ pub(in super::super) fn check_argument_types(
+ &self,
+ sp: Span,
+ expr: &'tcx hir::Expr<'tcx>,
+ fn_inputs: &[Ty<'tcx>],
+ expected_arg_tys: &[Ty<'tcx>],
+ args: &'tcx [hir::Expr<'tcx>],
+ c_variadic: bool,
+ tuple_arguments: TupleArgumentsFlag,
+ def_span: Option<Span>,
+ ) {
+ let tcx = self.tcx;
+ // Grab the argument types, supplying fresh type variables
+ // if the wrong number of arguments were supplied
+ let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
+
+ // All the input types from the fn signature must outlive the call
+ // so as to validate implied bounds.
+ for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
+ self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
+ }
+
+ let expected_arg_count = fn_inputs.len();
+
+ let param_count_error = |expected_count: usize,
+ arg_count: usize,
+ error_code: &str,
+ c_variadic: bool,
+ sugg_unit: bool| {
+ let (span, start_span, args) = match &expr.kind {
+ hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
+ hir::ExprKind::MethodCall(path_segment, span, args, _) => (
+ *span,
+ // `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
+ path_segment
+ .args
+ .and_then(|args| args.args.iter().last())
+ // Account for `foo.bar::<T>()`.
+ .map(|arg| {
+ // Skip the closing `>`.
+ tcx.sess
+ .source_map()
+ .next_point(tcx.sess.source_map().next_point(arg.span()))
+ })
+ .unwrap_or(*span),
+ &args[1..], // Skip the receiver.
+ ),
+ k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
+ };
+ let arg_spans = if args.is_empty() {
+ // foo()
+ // ^^^-- supplied 0 arguments
+ // |
+ // expected 2 arguments
+ vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
+ } else {
+ // foo(1, 2, 3)
+ // ^^^ - - - supplied 3 arguments
+ // |
+ // expected 2 arguments
+ args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
+ };
+
+ let mut err = tcx.sess.struct_span_err_with_code(
+ span,
+ &format!(
+ "this function takes {}{} but {} {} supplied",
+ if c_variadic { "at least " } else { "" },
+ potentially_plural_count(expected_count, "argument"),
+ potentially_plural_count(arg_count, "argument"),
+ if arg_count == 1 { "was" } else { "were" }
+ ),
+ DiagnosticId::Error(error_code.to_owned()),
+ );
+ let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
+ for (i, span) in arg_spans.into_iter().enumerate() {
+ err.span_label(
+ span,
+ if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
+ );
+ }
+
+ if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
+ err.span_label(def_s, "defined here");
+ }
+ if sugg_unit {
+ let sugg_span = tcx.sess.source_map().end_point(expr.span);
+ // remove closing `)` from the span
+ let sugg_span = sugg_span.shrink_to_lo();
+ err.span_suggestion(
+ sugg_span,
+ "expected the unit value `()`; create it with empty parentheses",
+ String::from("()"),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(
+ span,
+ format!(
+ "expected {}{}",
+ if c_variadic { "at least " } else { "" },
+ potentially_plural_count(expected_count, "argument")
+ ),
+ );
+ }
+ err.emit();
+ };
+
+ let mut expected_arg_tys = expected_arg_tys.to_vec();
+
+ let formal_tys = if tuple_arguments == TupleArguments {
+ let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
+ match tuple_type.kind() {
+ ty::Tuple(arg_types) if arg_types.len() != args.len() => {
+ param_count_error(arg_types.len(), args.len(), "E0057", false, false);
+ expected_arg_tys = vec![];
+ self.err_args(args.len())
+ }
+ ty::Tuple(arg_types) => {
+ expected_arg_tys = match expected_arg_tys.get(0) {
+ Some(&ty) => match ty.kind() {
+ ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
+ _ => vec![],
+ },
+ None => vec![],
+ };
+ arg_types.iter().map(|k| k.expect_ty()).collect()
+ }
+ _ => {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0059,
+ "cannot use call notation; the first type parameter \
+ for the function trait is neither a tuple nor unit"
+ )
+ .emit();
+ expected_arg_tys = vec![];
+ self.err_args(args.len())
+ }
+ }
+ } else if expected_arg_count == supplied_arg_count {
+ fn_inputs.to_vec()
+ } else if c_variadic {
+ if supplied_arg_count >= expected_arg_count {
+ fn_inputs.to_vec()
+ } else {
+ param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
+ expected_arg_tys = vec![];
+ self.err_args(supplied_arg_count)
+ }
+ } else {
+ // is the missing argument of type `()`?
+ let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
+ self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
+ } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
+ self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
+ } else {
+ false
+ };
+ param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
+
+ expected_arg_tys = vec![];
+ self.err_args(supplied_arg_count)
+ };
+
+ debug!(
+ "check_argument_types: formal_tys={:?}",
+ formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
+ );
+
+ // If there is no expectation, expect formal_tys.
+ let expected_arg_tys =
+ if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
+
+ let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
+
+ // Check the arguments.
+ // We do this in a pretty awful way: first we type-check any arguments
+ // that are not closures, then we type-check the closures. This is so
+ // that we have more information about the types of arguments when we
+ // type-check the functions. This isn't really the right way to do this.
+ for &check_closures in &[false, true] {
+ debug!("check_closures={}", check_closures);
+
+ // More awful hacks: before we check argument types, try to do
+ // an "opportunistic" trait resolution of any trait bounds on
+ // the call. This helps coercions.
+ if check_closures {
+ self.select_obligations_where_possible(false, |errors| {
+ self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
+ self.point_at_arg_instead_of_call_if_possible(
+ errors,
+ &final_arg_types[..],
+ sp,
+ &args,
+ );
+ })
+ }
+
+ // For C-variadic functions, we don't have a declared type for all of
+ // the arguments hence we only do our usual type checking with
+ // the arguments who's types we do know.
+ let t = if c_variadic {
+ expected_arg_count
+ } else if tuple_arguments == TupleArguments {
+ args.len()
+ } else {
+ supplied_arg_count
+ };
+ for (i, arg) in args.iter().take(t).enumerate() {
+ // Warn only for the first loop (the "no closures" one).
+ // Closure arguments themselves can't be diverging, but
+ // a previous argument can, e.g., `foo(panic!(), || {})`.
+ if !check_closures {
+ self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
+ }
+
+ let is_closure = match arg.kind {
+ ExprKind::Closure(..) => true,
+ _ => false,
+ };
+
+ if is_closure != check_closures {
+ continue;
+ }
+
+ debug!("checking the argument");
+ let formal_ty = formal_tys[i];
+
+ // The special-cased logic below has three functions:
+ // 1. Provide as good of an expected type as possible.
+ let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
+
+ let checked_ty = self.check_expr_with_expectation(&arg, expected);
+
+ // 2. Coerce to the most detailed type that could be coerced
+ // to, which is `expected_ty` if `rvalue_hint` returns an
+ // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+ let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
+ // We're processing function arguments so we definitely want to use
+ // two-phase borrows.
+ self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
+ final_arg_types.push((i, checked_ty, coerce_ty));
+
+ // 3. Relate the expected type and the formal one,
+ // if the expected type was used for the coercion.
+ self.demand_suptype(arg.span, formal_ty, coerce_ty);
+ }
+ }
+
+ // We also need to make sure we at least write the ty of the other
+ // arguments which we skipped above.
+ if c_variadic {
+ fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
+ use crate::structured_errors::{StructuredDiagnostic, VariadicError};
+ VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
+ }
+
+ for arg in args.iter().skip(expected_arg_count) {
+ let arg_ty = self.check_expr(&arg);
+
+ // There are a few types which get autopromoted when passed via varargs
+ // in C but we just error out instead and require explicit casts.
+ let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
+ match arg_ty.kind() {
+ ty::Float(ast::FloatTy::F32) => {
+ variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
+ }
+ ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
+ variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
+ }
+ ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
+ variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
+ }
+ ty::FnDef(..) => {
+ let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
+ let ptr_ty = self.resolve_vars_if_possible(&ptr_ty);
+ variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+
+ // AST fragment checking
+ pub(in super::super) fn check_lit(
+ &self,
+ lit: &hir::Lit,
+ expected: Expectation<'tcx>,
+ ) -> Ty<'tcx> {
+ let tcx = self.tcx;
+
+ match lit.node {
+ ast::LitKind::Str(..) => tcx.mk_static_str(),
+ ast::LitKind::ByteStr(ref v) => {
+ tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
+ }
+ ast::LitKind::Byte(_) => tcx.types.u8,
+ ast::LitKind::Char(_) => tcx.types.char,
+ ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
+ ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t),
+ ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
+ let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
+ ty::Int(_) | ty::Uint(_) => Some(ty),
+ ty::Char => Some(tcx.types.u8),
+ ty::RawPtr(..) => Some(tcx.types.usize),
+ ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
+ _ => None,
+ });
+ opt_ty.unwrap_or_else(|| self.next_int_var())
+ }
+ ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
+ ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
+ let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
+ ty::Float(_) => Some(ty),
+ _ => None,
+ });
+ opt_ty.unwrap_or_else(|| self.next_float_var())
+ }
+ ast::LitKind::Bool(_) => tcx.types.bool,
+ ast::LitKind::Err(_) => tcx.ty_error(),
+ }
+ }
+
+ pub fn check_struct_path(
+ &self,
+ qpath: &QPath<'_>,
+ hir_id: hir::HirId,
+ ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
+ let path_span = qpath.qself_span();
+ let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
+ let variant = match def {
+ Res::Err => {
+ self.set_tainted_by_errors();
+ return None;
+ }
+ Res::Def(DefKind::Variant, _) => match ty.kind() {
+ ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
+ _ => bug!("unexpected type: {:?}", ty),
+ },
+ Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
+ | Res::SelfTy(..) => match ty.kind() {
+ ty::Adt(adt, substs) if !adt.is_enum() => {
+ Some((adt.non_enum_variant(), adt.did, substs))
+ }
+ _ => None,
+ },
+ _ => bug!("unexpected definition: {:?}", def),
+ };
+
+ if let Some((variant, did, substs)) = variant {
+ debug!("check_struct_path: did={:?} substs={:?}", did, substs);
+ self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
+
+ // Check bounds on type arguments used in the path.
+ let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
+ let cause =
+ traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
+ self.add_obligations_for_parameters(cause, bounds);
+
+ Some((variant, ty))
+ } else {
+ struct_span_err!(
+ self.tcx.sess,
+ path_span,
+ E0071,
+ "expected struct, variant or union type, found {}",
+ ty.sort_string(self.tcx)
+ )
+ .span_label(path_span, "not a struct")
+ .emit();
+ None
+ }
+ }
+
+ pub fn check_decl_initializer(
+ &self,
+ local: &'tcx hir::Local<'tcx>,
+ init: &'tcx hir::Expr<'tcx>,
+ ) -> Ty<'tcx> {
+ // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
+ // for #42640 (default match binding modes).
+ //
+ // See #44848.
+ let ref_bindings = local.pat.contains_explicit_ref_binding();
+
+ let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
+ if let Some(m) = ref_bindings {
+ // Somewhat subtle: if we have a `ref` binding in the pattern,
+ // we want to avoid introducing coercions for the RHS. This is
+ // both because it helps preserve sanity and, in the case of
+ // ref mut, for soundness (issue #23116). In particular, in
+ // the latter case, we need to be clear that the type of the
+ // referent for the reference that results is *equal to* the
+ // type of the place it is referencing, and not some
+ // supertype thereof.
+ let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
+ self.demand_eqtype(init.span, local_ty, init_ty);
+ init_ty
+ } else {
+ self.check_expr_coercable_to_type(init, local_ty, None)
+ }
+ }
+
+ /// Type check a `let` statement.
+ pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+ // Determine and write the type which we'll check the pattern against.
+ let ty = self.local_ty(local.span, local.hir_id).decl_ty;
+ self.write_ty(local.hir_id, ty);
+
+ // Type check the initializer.
+ if let Some(ref init) = local.init {
+ let init_ty = self.check_decl_initializer(local, &init);
+ self.overwrite_local_ty_if_err(local, ty, init_ty);
+ }
+
+ // Does the expected pattern type originate from an expression and what is the span?
+ let (origin_expr, ty_span) = match (local.ty, local.init) {
+ (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
+ (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
+ _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
+ };
+
+ // Type check the pattern. Override if necessary to avoid knock-on errors.
+ self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
+ let pat_ty = self.node_ty(local.pat.hir_id);
+ self.overwrite_local_ty_if_err(local, ty, pat_ty);
+ }
+
+ pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
+ // Don't do all the complex logic below for `DeclItem`.
+ match stmt.kind {
+ hir::StmtKind::Item(..) => return,
+ hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+ }
+
+ self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
+
+ // Hide the outer diverging and `has_errors` flags.
+ let old_diverges = self.diverges.replace(Diverges::Maybe);
+ let old_has_errors = self.has_errors.replace(false);
+
+ match stmt.kind {
+ hir::StmtKind::Local(ref l) => {
+ self.check_decl_local(&l);
+ }
+ // Ignore for now.
+ hir::StmtKind::Item(_) => {}
+ hir::StmtKind::Expr(ref expr) => {
+ // Check with expected type of `()`.
+ self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
+ self.suggest_semicolon_at_end(expr.span, err);
+ });
+ }
+ hir::StmtKind::Semi(ref expr) => {
+ self.check_expr(&expr);
+ }
+ }
+
+ // Combine the diverging and `has_error` flags.
+ self.diverges.set(self.diverges.get() | old_diverges);
+ self.has_errors.set(self.has_errors.get() | old_has_errors);
+ }
+
+ pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
+ let unit = self.tcx.mk_unit();
+ let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
+
+ // if the block produces a `!` value, that can always be
+ // (effectively) coerced to unit.
+ if !ty.is_never() {
+ self.demand_suptype(blk.span, unit, ty);
+ }
+ }
+
+ pub(in super::super) fn check_block_with_expected(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected: Expectation<'tcx>,
+ ) -> Ty<'tcx> {
+ let prev = {
+ let mut fcx_ps = self.ps.borrow_mut();
+ let unsafety_state = fcx_ps.recurse(blk);
+ replace(&mut *fcx_ps, unsafety_state)
+ };
+
+ // In some cases, blocks have just one exit, but other blocks
+ // can be targeted by multiple breaks. This can happen both
+ // with labeled blocks as well as when we desugar
+ // a `try { ... }` expression.
+ //
+ // Example 1:
+ //
+ // 'a: { if true { break 'a Err(()); } Ok(()) }
+ //
+ // Here we would wind up with two coercions, one from
+ // `Err(())` and the other from the tail expression
+ // `Ok(())`. If the tail expression is omitted, that's a
+ // "forced unit" -- unless the block diverges, in which
+ // case we can ignore the tail expression (e.g., `'a: {
+ // break 'a 22; }` would not force the type of the block
+ // to be `()`).
+ let tail_expr = blk.expr.as_ref();
+ let coerce_to_ty = expected.coercion_target_type(self, blk.span);
+ let coerce = if blk.targeted_by_break {
+ CoerceMany::new(coerce_to_ty)
+ } else {
+ let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
+ Some(e) => slice::from_ref(e),
+ None => &[],
+ };
+ CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
+ };
+
+ let prev_diverges = self.diverges.get();
+ let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
+
+ let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
+ for s in blk.stmts {
+ self.check_stmt(s);
+ }
+
+ // check the tail expression **without** holding the
+ // `enclosing_breakables` lock below.
+ let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
+
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
+ let coerce = ctxt.coerce.as_mut().unwrap();
+ if let Some(tail_expr_ty) = tail_expr_ty {
+ let tail_expr = tail_expr.unwrap();
+ let span = self.get_expr_coercion_span(tail_expr);
+ let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
+ coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
+ } else {
+ // Subtle: if there is no explicit tail expression,
+ // that is typically equivalent to a tail expression
+ // of `()` -- except if the block diverges. In that
+ // case, there is no value supplied from the tail
+ // expression (assuming there are no other breaks,
+ // this implies that the type of the block will be
+ // `!`).
+ //
+ // #41425 -- label the implicit `()` as being the
+ // "found type" here, rather than the "expected type".
+ if !self.diverges.get().is_always() {
+ // #50009 -- Do not point at the entire fn block span, point at the return type
+ // span, as it is the cause of the requirement, and
+ // `consider_hint_about_removing_semicolon` will point at the last expression
+ // if it were a relevant part of the error. This improves usability in editors
+ // that highlight errors inline.
+ let mut sp = blk.span;
+ let mut fn_span = None;
+ if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
+ let ret_sp = decl.output.span();
+ if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
+ // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
+ // output would otherwise be incorrect and even misleading. Make sure
+ // the span we're aiming at correspond to a `fn` body.
+ if block_sp == blk.span {
+ sp = ret_sp;
+ fn_span = Some(ident.span);
+ }
+ }
+ }
+ coerce.coerce_forced_unit(
+ self,
+ &self.misc(sp),
+ &mut |err| {
+ if let Some(expected_ty) = expected.only_has_type(self) {
+ self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
+ }
+ if let Some(fn_span) = fn_span {
+ err.span_label(
+ fn_span,
+ "implicitly returns `()` as its body has no tail or `return` \
+ expression",
+ );
+ }
+ },
+ false,
+ );
+ }
+ }
+ });
+
+ if ctxt.may_break {
+ // If we can break from the block, then the block's exit is always reachable
+ // (... as long as the entry is reachable) - regardless of the tail of the block.
+ self.diverges.set(prev_diverges);
+ }
+
+ let mut ty = ctxt.coerce.unwrap().complete(self);
+
+ if self.has_errors.get() || ty.references_error() {
+ ty = self.tcx.ty_error()
+ }
+
+ self.write_ty(blk.hir_id, ty);
+
+ *self.ps.borrow_mut() = prev;
+ ty
+ }
+
+ pub(in super::super) fn check_rustc_args_require_const(
+ &self,
+ def_id: DefId,
+ hir_id: hir::HirId,
+ span: Span,
+ ) {
+ // We're only interested in functions tagged with
+ // #[rustc_args_required_const], so ignore anything that's not.
+ if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
+ return;
+ }
+
+ // If our calling expression is indeed the function itself, we're good!
+ // If not, generate an error that this can only be called directly.
+ if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
+ if let ExprKind::Call(ref callee, ..) = expr.kind {
+ if callee.hir_id == hir_id {
+ return;
+ }
+ }
+ }
+
+ self.tcx.sess.span_err(
+ span,
+ "this function can only be invoked directly, not through a function pointer",
+ );
+ }
+
+ /// A common error is to add an extra semicolon:
+ ///
+ /// ```
+ /// fn foo() -> usize {
+ /// 22;
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the final statement in a block is an
+ /// expression with an explicit semicolon whose type is compatible
+ /// with `expected_ty`. If so, it suggests removing the semicolon.
+ fn consider_hint_about_removing_semicolon(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ err: &mut DiagnosticBuilder<'_>,
+ ) {
+ if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
+ err.span_suggestion(
+ span_semi,
+ "consider removing this semicolon",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
+ let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
+ match node {
+ Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
+ | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
+ let body = self.tcx.hir().body(body_id);
+ if let ExprKind::Block(block, _) = &body.value.kind {
+ return Some(block.span);
+ }
+ }
+ _ => {}
+ }
+ None
+ }
+
+ /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
+ fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
+ let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
+ self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
+ }
+
+ /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
+ /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
+ /// when given code like the following:
+ /// ```text
+ /// if false { return 0i32; } else { 1u32 }
+ /// // ^^^^ point at this instead of the whole `if` expression
+ /// ```
+ fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
+ if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
+ let arm_spans: Vec<Span> = arms
+ .iter()
+ .filter_map(|arm| {
+ self.in_progress_typeck_results
+ .and_then(|typeck_results| {
+ typeck_results.borrow().node_type_opt(arm.body.hir_id)
+ })
+ .and_then(|arm_ty| {
+ if arm_ty.is_never() {
+ None
+ } else {
+ Some(match &arm.body.kind {
+ // Point at the tail expression when possible.
+ hir::ExprKind::Block(block, _) => {
+ block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
+ }
+ _ => arm.body.span,
+ })
+ }
+ })
+ })
+ .collect();
+ if arm_spans.len() == 1 {
+ return arm_spans[0];
+ }
+ }
+ expr.span
+ }
+
+ fn overwrite_local_ty_if_err(
+ &self,
+ local: &'tcx hir::Local<'tcx>,
+ decl_ty: Ty<'tcx>,
+ ty: Ty<'tcx>,
+ ) {
+ if ty.references_error() {
+ // Override the types everywhere with `err()` to avoid knock on errors.
+ self.write_ty(local.hir_id, ty);
+ self.write_ty(local.pat.hir_id, ty);
+ let local_ty = LocalTy { decl_ty, revealed_ty: ty };
+ self.locals.borrow_mut().insert(local.hir_id, local_ty);
+ self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
+ }
+ }
+
+ // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
+ // The newly resolved definition is written into `type_dependent_defs`.
+ fn finish_resolving_struct_path(
+ &self,
+ qpath: &QPath<'_>,
+ path_span: Span,
+ hir_id: hir::HirId,
+ ) -> (Res, Ty<'tcx>) {
+ match *qpath {
+ QPath::Resolved(ref maybe_qself, ref path) => {
+ let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
+ let ty = AstConv::res_to_ty(self, self_ty, path, true);
+ (path.res, ty)
+ }
+ QPath::TypeRelative(ref qself, ref segment) => {
+ let ty = self.to_ty(qself);
+
+ let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
+ path.res
+ } else {
+ Res::Err
+ };
+ let result =
+ AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
+ let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
+ let result = result.map(|(_, kind, def_id)| (kind, def_id));
+
+ // Write back the new resolution.
+ self.write_resolution(hir_id, result);
+
+ (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
+ }
+ QPath::LangItem(lang_item, span) => {
+ self.resolve_lang_item_path(lang_item, span, hir_id)
+ }
+ }
+ }
+
+ /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
+ /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
+ /// reference a type argument. The reason to walk also the checked type is that the coerced type
+ /// can be not easily comparable with predicate type (because of coercion). If the types match
+ /// for either checked or coerced type, and there's only *one* argument that does, we point at
+ /// the corresponding argument's expression span instead of the `fn` call path span.
+ fn point_at_arg_instead_of_call_if_possible(
+ &self,
+ errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+ final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
+ call_sp: Span,
+ args: &'tcx [hir::Expr<'tcx>],
+ ) {
+ // We *do not* do this for desugared call spans to keep good diagnostics when involving
+ // the `?` operator.
+ if call_sp.desugaring_kind().is_some() {
+ return;
+ }
+
+ for error in errors {
+ // Only if the cause is somewhere inside the expression we want try to point at arg.
+ // Otherwise, it means that the cause is somewhere else and we should not change
+ // anything because we can break the correct span.
+ if !call_sp.contains(error.obligation.cause.span) {
+ continue;
+ }
+
+ if let ty::PredicateAtom::Trait(predicate, _) =
+ error.obligation.predicate.skip_binders()
+ {
+ // Collect the argument position for all arguments that could have caused this
+ // `FulfillmentError`.
+ let mut referenced_in = final_arg_types
+ .iter()
+ .map(|&(i, checked_ty, _)| (i, checked_ty))
+ .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
+ .flat_map(|(i, ty)| {
+ let ty = self.resolve_vars_if_possible(&ty);
+ // We walk the argument type because the argument's type could have
+ // been `Option<T>`, but the `FulfillmentError` references `T`.
+ if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
+ Some(i)
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<usize>>();
+
+ // Both checked and coerced types could have matched, thus we need to remove
+ // duplicates.
+
+ // We sort primitive type usize here and can use unstable sort
+ referenced_in.sort_unstable();
+ referenced_in.dedup();
+
+ if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
+ // We make sure that only *one* argument matches the obligation failure
+ // and we assign the obligation's span to its expression's.
+ error.obligation.cause.make_mut().span = args[ref_in].span;
+ error.points_at_arg_span = true;
+ }
+ }
+ }
+ }
+
+ /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
+ /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
+ /// were caused by them. If they were, we point at the corresponding type argument's span
+ /// instead of the `fn` call path span.
+ fn point_at_type_arg_instead_of_call_if_possible(
+ &self,
+ errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ ) {
+ if let hir::ExprKind::Call(path, _) = &call_expr.kind {
+ if let hir::ExprKind::Path(qpath) = &path.kind {
+ if let hir::QPath::Resolved(_, path) = &qpath {
+ for error in errors {
+ if let ty::PredicateAtom::Trait(predicate, _) =
+ error.obligation.predicate.skip_binders()
+ {
+ // If any of the type arguments in this path segment caused the
+ // `FullfillmentError`, point at its span (#61860).
+ for arg in path
+ .segments
+ .iter()
+ .filter_map(|seg| seg.args.as_ref())
+ .flat_map(|a| a.args.iter())
+ {
+ if let hir::GenericArg::Type(hir_ty) = &arg {
+ if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
+ &hir_ty.kind
+ {
+ // Avoid ICE with associated types. As this is best
+ // effort only, it's ok to ignore the case. It
+ // would trigger in `is_send::<T::AssocType>();`
+ // from `typeck-default-trait-impl-assoc-type.rs`.
+ } else {
+ let ty = AstConv::ast_ty_to_ty(self, hir_ty);
+ let ty = self.resolve_vars_if_possible(&ty);
+ if ty == predicate.self_ty() {
+ error.obligation.cause.make_mut().span = hir_ty.span;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+mod _impl;
+mod checks;
+mod suggestions;
+
+pub use _impl::*;
+pub use checks::*;
+pub use suggestions::*;
+
+use crate::astconv::AstConv;
+use crate::check::coercion::DynamicCoerceMany;
+use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
+
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::hir::map::blocks::FnLikeNode;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, Const, Ty, TyCtxt};
+use rustc_session::Session;
+use rustc_span::{self, Span};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
+
+use std::cell::{Cell, RefCell};
+use std::ops::Deref;
+
+pub struct FnCtxt<'a, 'tcx> {
+ pub(super) body_id: hir::HirId,
+
+ /// The parameter environment used for proving trait obligations
+ /// in this function. This can change when we descend into
+ /// closures (as they bring new things into scope), hence it is
+ /// not part of `Inherited` (as of the time of this writing,
+ /// closures do not yet change the environment, but they will
+ /// eventually).
+ pub(super) param_env: ty::ParamEnv<'tcx>,
+
+ /// Number of errors that had been reported when we started
+ /// checking this function. On exit, if we find that *more* errors
+ /// have been reported, we will skip regionck and other work that
+ /// expects the types within the function to be consistent.
+ // FIXME(matthewjasper) This should not exist, and it's not correct
+ // if type checking is run in parallel.
+ err_count_on_creation: usize,
+
+ /// If `Some`, this stores coercion information for returned
+ /// expressions. If `None`, this is in a context where return is
+ /// inappropriate, such as a const expression.
+ ///
+ /// This is a `RefCell<DynamicCoerceMany>`, which means that we
+ /// can track all the return expressions and then use them to
+ /// compute a useful coercion from the set, similar to a match
+ /// expression or other branching context. You can use methods
+ /// like `expected_ty` to access the declared return type (if
+ /// any).
+ pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
+
+ pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,
+
+ pub(super) ret_type_span: Option<Span>,
+
+ /// Used exclusively to reduce cost of advanced evaluation used for
+ /// more helpful diagnostics.
+ pub(super) in_tail_expr: bool,
+
+ /// First span of a return site that we find. Used in error messages.
+ pub(super) ret_coercion_span: RefCell<Option<Span>>,
+
+ pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
+
+ pub(super) ps: RefCell<UnsafetyState>,
+
+ /// Whether the last checked node generates a divergence (e.g.,
+ /// `return` will set this to `Always`). In general, when entering
+ /// an expression or other node in the tree, the initial value
+ /// indicates whether prior parts of the containing expression may
+ /// have diverged. It is then typically set to `Maybe` (and the
+ /// old value remembered) for processing the subparts of the
+ /// current expression. As each subpart is processed, they may set
+ /// the flag to `Always`, etc. Finally, at the end, we take the
+ /// result and "union" it with the original value, so that when we
+ /// return the flag indicates if any subpart of the parent
+ /// expression (up to and including this part) has diverged. So,
+ /// if you read it after evaluating a subexpression `X`, the value
+ /// you get indicates whether any subexpression that was
+ /// evaluating up to and including `X` diverged.
+ ///
+ /// We currently use this flag only for diagnostic purposes:
+ ///
+ /// - To warn about unreachable code: if, after processing a
+ /// sub-expression but before we have applied the effects of the
+ /// current node, we see that the flag is set to `Always`, we
+ /// can issue a warning. This corresponds to something like
+ /// `foo(return)`; we warn on the `foo()` expression. (We then
+ /// update the flag to `WarnedAlways` to suppress duplicate
+ /// reports.) Similarly, if we traverse to a fresh statement (or
+ /// tail expression) from a `Always` setting, we will issue a
+ /// warning. This corresponds to something like `{return;
+ /// foo();}` or `{return; 22}`, where we would warn on the
+ /// `foo()` or `22`.
+ ///
+ /// An expression represents dead code if, after checking it,
+ /// the diverges flag is set to something other than `Maybe`.
+ pub(super) diverges: Cell<Diverges>,
+
+ /// Whether any child nodes have any type errors.
+ pub(super) has_errors: Cell<bool>,
+
+ pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
+
+ pub(super) inh: &'a Inherited<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub fn new(
+ inh: &'a Inherited<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ ) -> FnCtxt<'a, 'tcx> {
+ FnCtxt {
+ body_id,
+ param_env,
+ err_count_on_creation: inh.tcx.sess.err_count(),
+ ret_coercion: None,
+ ret_coercion_impl_trait: None,
+ ret_type_span: None,
+ in_tail_expr: false,
+ ret_coercion_span: RefCell::new(None),
+ resume_yield_tys: None,
+ ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
+ diverges: Cell::new(Diverges::Maybe),
+ has_errors: Cell::new(false),
+ enclosing_breakables: RefCell::new(EnclosingBreakables {
+ stack: Vec::new(),
+ by_id: Default::default(),
+ }),
+ inh,
+ }
+ }
+
+ pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
+ ObligationCause::new(span, self.body_id, code)
+ }
+
+ pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
+ self.cause(span, ObligationCauseCode::MiscObligation)
+ }
+
+ pub fn sess(&self) -> &Session {
+ &self.tcx.sess
+ }
+
+ pub fn errors_reported_since_creation(&self) -> bool {
+ self.tcx.sess.err_count() > self.err_count_on_creation
+ }
+}
+
+impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
+ type Target = Inherited<'a, 'tcx>;
+ fn deref(&self) -> &Self::Target {
+ &self.inh
+ }
+}
+
+impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn item_def_id(&self) -> Option<DefId> {
+ None
+ }
+
+ fn default_constness_for_trait_bounds(&self) -> hir::Constness {
+ // FIXME: refactor this into a method
+ let node = self.tcx.hir().get(self.body_id);
+ if let Some(fn_like) = FnLikeNode::from_node(node) {
+ fn_like.constness()
+ } else {
+ hir::Constness::NotConst
+ }
+ }
+
+ fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
+ let tcx = self.tcx;
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ let item_id = tcx.hir().ty_param_owner(hir_id);
+ let item_def_id = tcx.hir().local_def_id(item_id);
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ ty::GenericPredicates {
+ parent: None,
+ predicates: tcx.arena.alloc_from_iter(
+ self.param_env.caller_bounds().iter().filter_map(|predicate| {
+ match predicate.skip_binders() {
+ ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
+ // HACK(eddyb) should get the original `Span`.
+ let span = tcx.def_span(def_id);
+ Some((predicate, span))
+ }
+ _ => None,
+ }
+ }),
+ ),
+ }
+ }
+
+ fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
+ let v = match def {
+ Some(def) => infer::EarlyBoundRegion(span, def.name),
+ None => infer::MiscVariable(span),
+ };
+ Some(self.next_region_var(v))
+ }
+
+ fn allow_ty_infer(&self) -> bool {
+ true
+ }
+
+ fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
+ if let Some(param) = param {
+ if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
+ return ty;
+ }
+ unreachable!()
+ } else {
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::TypeInference,
+ span,
+ })
+ }
+ }
+
+ fn ct_infer(
+ &self,
+ ty: Ty<'tcx>,
+ param: Option<&ty::GenericParamDef>,
+ span: Span,
+ ) -> &'tcx Const<'tcx> {
+ if let Some(param) = param {
+ if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
+ return ct;
+ }
+ unreachable!()
+ } else {
+ self.next_const_var(
+ ty,
+ ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
+ )
+ }
+ }
+
+ fn projected_ty_from_poly_trait_ref(
+ &self,
+ span: Span,
+ item_def_id: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ poly_trait_ref: ty::PolyTraitRef<'tcx>,
+ ) -> Ty<'tcx> {
+ let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
+ span,
+ infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
+ &poly_trait_ref,
+ );
+
+ let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
+ self,
+ self.tcx,
+ span,
+ item_def_id,
+ item_segment,
+ trait_ref.substs,
+ );
+
+ self.tcx().mk_projection(item_def_id, item_substs)
+ }
+
+ fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if ty.has_escaping_bound_vars() {
+ ty // FIXME: normalization and escaping regions
+ } else {
+ self.normalize_associated_types_in(span, &ty)
+ }
+ }
+
+ fn set_tainted_by_errors(&self) {
+ self.infcx.set_tainted_by_errors()
+ }
+
+ fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+ self.write_ty(hir_id, ty)
+ }
+}
--- /dev/null
+use super::FnCtxt;
+use crate::astconv::AstConv;
+
+use rustc_ast::util::parser::ExprPrecedence;
+use rustc_span::{self, Span};
+use rustc_trait_selection::traits;
+
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ExprKind, ItemKind, Node};
+use rustc_infer::infer;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::kw;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+
+use std::iter;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(in super::super) fn suggest_semicolon_at_end(
+ &self,
+ span: Span,
+ err: &mut DiagnosticBuilder<'_>,
+ ) {
+ err.span_suggestion_short(
+ span.shrink_to_hi(),
+ "consider using a semicolon here",
+ ";".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ /// On implicit return expressions with mismatched types, provides the following suggestions:
+ ///
+ /// - Points out the method's return type as the reason for the expected type.
+ /// - Possible missing semicolon.
+ /// - Possible missing return type if the return type is the default, and not `fn main()`.
+ pub fn suggest_mismatched_types_on_tail(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &'tcx hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ cause_span: Span,
+ blk_id: hir::HirId,
+ ) -> bool {
+ let expr = expr.peel_drop_temps();
+ self.suggest_missing_semicolon(err, expr, expected, cause_span);
+ let mut pointing_at_return_type = false;
+ if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
+ pointing_at_return_type =
+ self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
+ }
+ pointing_at_return_type
+ }
+
+ /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
+ /// the ctor would successfully solve the type mismatch and if so, suggest it:
+ /// ```
+ /// fn foo(x: usize) -> usize { x }
+ /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
+ /// ```
+ fn suggest_fn_call(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> bool {
+ let hir = self.tcx.hir();
+ let (def_id, sig) = match *found.kind() {
+ ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
+ ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
+ _ => return false,
+ };
+
+ let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
+ let sig = self.normalize_associated_types_in(expr.span, &sig);
+ if self.can_coerce(sig.output(), expected) {
+ let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
+ (String::new(), Applicability::MachineApplicable)
+ } else {
+ ("...".to_string(), Applicability::HasPlaceholders)
+ };
+ let mut msg = "call this function";
+ match hir.get_if_local(def_id) {
+ Some(
+ Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
+ | Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(_, body_id), ..
+ })
+ | Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
+ ..
+ }),
+ ) => {
+ let body = hir.body(*body_id);
+ sugg_call = body
+ .params
+ .iter()
+ .map(|param| match ¶m.pat.kind {
+ hir::PatKind::Binding(_, _, ident, None)
+ if ident.name != kw::SelfLower =>
+ {
+ ident.to_string()
+ }
+ _ => "_".to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(", ");
+ }
+ Some(Node::Expr(hir::Expr {
+ kind: ExprKind::Closure(_, _, body_id, _, _),
+ span: full_closure_span,
+ ..
+ })) => {
+ if *full_closure_span == expr.span {
+ return false;
+ }
+ msg = "call this closure";
+ let body = hir.body(*body_id);
+ sugg_call = body
+ .params
+ .iter()
+ .map(|param| match ¶m.pat.kind {
+ hir::PatKind::Binding(_, _, ident, None)
+ if ident.name != kw::SelfLower =>
+ {
+ ident.to_string()
+ }
+ _ => "_".to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(", ");
+ }
+ Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
+ sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
+ match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
+ Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
+ msg = "instantiate this tuple variant";
+ }
+ Some(DefKind::Ctor(CtorOf::Struct, _)) => {
+ msg = "instantiate this tuple struct";
+ }
+ _ => {}
+ }
+ }
+ Some(Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Fn(_, idents, _),
+ ..
+ })) => {
+ sugg_call = idents
+ .iter()
+ .map(|ident| {
+ if ident.name != kw::SelfLower {
+ ident.to_string()
+ } else {
+ "_".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+ Some(Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
+ ..
+ })) => {
+ sugg_call = idents
+ .iter()
+ .map(|ident| {
+ if ident.name != kw::SelfLower {
+ ident.to_string()
+ } else {
+ "_".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+ _ => {}
+ }
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ &format!("use parentheses to {}", msg),
+ format!("({})", sugg_call),
+ applicability,
+ );
+ return true;
+ }
+ false
+ }
+
+ pub fn suggest_deref_ref_or_into(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ ) {
+ if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
+ err.span_suggestion(sp, msg, suggestion, applicability);
+ } else if let (ty::FnDef(def_id, ..), true) =
+ (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+ {
+ if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
+ let sp = self.sess().source_map().guess_head_span(sp);
+ err.span_label(sp, &format!("{} defined here", found));
+ }
+ } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+ let is_struct_pat_shorthand_field =
+ self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
+ let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+ if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+ let mut suggestions = iter::repeat(&expr_text)
+ .zip(methods.iter())
+ .filter_map(|(receiver, method)| {
+ let method_call = format!(".{}()", method.ident);
+ if receiver.ends_with(&method_call) {
+ None // do not suggest code that is already there (#53348)
+ } else {
+ let method_call_list = [".to_vec()", ".to_string()"];
+ let sugg = if receiver.ends_with(".clone()")
+ && method_call_list.contains(&method_call.as_str())
+ {
+ let max_len = receiver.rfind('.').unwrap();
+ format!("{}{}", &receiver[..max_len], method_call)
+ } else {
+ if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
+ format!("({}){}", receiver, method_call)
+ } else {
+ format!("{}{}", receiver, method_call)
+ }
+ };
+ Some(if is_struct_pat_shorthand_field {
+ format!("{}: {}", receiver, sugg)
+ } else {
+ sugg
+ })
+ }
+ })
+ .peekable();
+ if suggestions.peek().is_some() {
+ err.span_suggestions(
+ expr.span,
+ "try using a conversion method",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+
+ /// When encountering the expected boxed value allocated in the stack, suggest allocating it
+ /// in the heap by calling `Box::new()`.
+ pub(in super::super) fn suggest_boxing_when_appropriate(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+ // Do not suggest `Box::new` in const context.
+ return;
+ }
+ if !expected.is_box() || found.is_box() {
+ return;
+ }
+ let boxed_found = self.tcx.mk_box(found);
+ if let (true, Ok(snippet)) = (
+ self.can_coerce(boxed_found, expected),
+ self.sess().source_map().span_to_snippet(expr.span),
+ ) {
+ err.span_suggestion(
+ expr.span,
+ "store this in the heap by calling `Box::new`",
+ format!("Box::new({})", snippet),
+ Applicability::MachineApplicable,
+ );
+ err.note(
+ "for more on the distinction between the stack and the heap, read \
+ https://doc.rust-lang.org/book/ch15-01-box.html, \
+ https://doc.rust-lang.org/rust-by-example/std/box.html, and \
+ https://doc.rust-lang.org/std/boxed/index.html",
+ );
+ }
+ }
+
+ /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
+ pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> bool {
+ // Handle #68197.
+
+ if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+ // Do not suggest `Box::new` in const context.
+ return false;
+ }
+ let pin_did = self.tcx.lang_items().pin_type();
+ match expected.kind() {
+ ty::Adt(def, _) if Some(def.did) != pin_did => return false,
+ // This guards the `unwrap` and `mk_box` below.
+ _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
+ _ => {}
+ }
+ let boxed_found = self.tcx.mk_box(found);
+ let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
+ if let (true, Ok(snippet)) = (
+ self.can_coerce(new_found, expected),
+ self.sess().source_map().span_to_snippet(expr.span),
+ ) {
+ match found.kind() {
+ ty::Adt(def, _) if def.is_box() => {
+ err.help("use `Box::pin`");
+ }
+ _ => {
+ err.span_suggestion(
+ expr.span,
+ "you need to pin and box this expression",
+ format!("Box::pin({})", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ true
+ } else {
+ false
+ }
+ }
+
+ /// A common error is to forget to add a semicolon at the end of a block, e.g.,
+ ///
+ /// ```
+ /// fn foo() {
+ /// bar_that_returns_u32()
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the return expression in a block would make sense on its own as a
+ /// statement and the return type has been left as default or has been specified as `()`. If so,
+ /// it suggests adding a semicolon.
+ fn suggest_missing_semicolon(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expression: &'tcx hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ cause_span: Span,
+ ) {
+ if expected.is_unit() {
+ // `BlockTailExpression` only relevant if the tail expr would be
+ // useful on its own.
+ match expression.kind {
+ ExprKind::Call(..)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Loop(..)
+ | ExprKind::Match(..)
+ | ExprKind::Block(..) => {
+ err.span_suggestion(
+ cause_span.shrink_to_hi(),
+ "try adding a semicolon",
+ ";".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ _ => (),
+ }
+ }
+ }
+
+ /// A possible error is to forget to add a return type that is needed:
+ ///
+ /// ```
+ /// fn foo() {
+ /// bar_that_returns_u32()
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the return type is left as default, the method is not part of an
+ /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
+ /// type.
+ pub(in super::super) fn suggest_missing_return_type(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ fn_decl: &hir::FnDecl<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ can_suggest: bool,
+ ) -> bool {
+ // Only suggest changing the return type for methods that
+ // haven't set a return type at all (and aren't `fn main()` or an impl).
+ match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+ (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
+ err.span_suggestion(
+ span,
+ "try adding a return type",
+ format!("-> {} ", self.resolve_vars_with_obligations(found)),
+ Applicability::MachineApplicable,
+ );
+ true
+ }
+ (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
+ err.span_label(span, "possibly return type missing here?");
+ true
+ }
+ (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
+ // `fn main()` must return `()`, do not suggest changing return type
+ err.span_label(span, "expected `()` because of default return type");
+ true
+ }
+ // expectation was caused by something else, not the default return
+ (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
+ (&hir::FnRetTy::Return(ref ty), _, _, _) => {
+ // Only point to return type if the expected type is the return type, as if they
+ // are not, the expectation must have been caused by something else.
+ debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
+ let sp = ty.span;
+ let ty = AstConv::ast_ty_to_ty(self, ty);
+ debug!("suggest_missing_return_type: return type {:?}", ty);
+ debug!("suggest_missing_return_type: expected type {:?}", ty);
+ if ty.kind() == expected.kind() {
+ err.span_label(sp, format!("expected `{}` because of return type", expected));
+ return true;
+ }
+ false
+ }
+ }
+ }
+
+ /// A possible error is to forget to add `.await` when using futures:
+ ///
+ /// ```
+ /// async fn make_u32() -> u32 {
+ /// 22
+ /// }
+ ///
+ /// fn take_u32(x: u32) {}
+ ///
+ /// async fn foo() {
+ /// let x = make_u32();
+ /// take_u32(x);
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+ /// `.await` to the tail of the expression.
+ pub(in super::super) fn suggest_missing_await(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
+ // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
+ // body isn't `async`.
+ let item_id = self.tcx().hir().get_parent_node(self.body_id);
+ if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
+ let body = self.tcx().hir().body(body_id);
+ if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+ let sp = expr.span;
+ // Check for `Future` implementations by constructing a predicate to
+ // prove: `<T as Future>::Output == U`
+ let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
+ let item_def_id = self
+ .tcx
+ .associated_items(future_trait)
+ .in_definition_order()
+ .next()
+ .unwrap()
+ .def_id;
+ // `<T as Future>::Output`
+ let projection_ty = ty::ProjectionTy {
+ // `T`
+ substs: self
+ .tcx
+ .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
+ // `Future::Output`
+ item_def_id,
+ };
+
+ let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
+ projection_ty,
+ ty: expected,
+ })
+ .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
+ let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+
+ debug!("suggest_missing_await: trying obligation {:?}", obligation);
+
+ if self.infcx.predicate_may_hold(&obligation) {
+ debug!("suggest_missing_await: obligation held: {:?}", obligation);
+ if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
+ err.span_suggestion(
+ sp,
+ "consider using `.await` here",
+ format!("{}.await", code),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ debug!("suggest_missing_await: no snippet for {:?}", sp);
+ }
+ } else {
+ debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
+ }
+ }
+ }
+ }
+
+ pub(in super::super) fn suggest_missing_parentheses(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ ) {
+ let sp = self.tcx.sess.source_map().start_point(expr.span);
+ if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
+ // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+ self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
+ }
+ }
+}
pub use check::{check_item_type, check_wf_new};
pub use diverges::Diverges;
pub use expectation::Expectation;
-pub use fn_ctxt::FnCtxt;
+pub use fn_ctxt::*;
pub use inherited::{Inherited, InheritedBuilder};
use crate::astconv::AstConv;
extern "Rust" {
// These are the magic symbols to call the global allocator. rustc generates
- // them to call `__rg_alloc` etc if there is a `#[global_allocator]` attribute
+ // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
// (the code expanding that attribute macro generates those functions), or to call
- // the default implementations in libstd (`__rdl_alloc` etc in `src/libstd/alloc.rs`)
+ // the default implementations in libstd (`__rdl_alloc` etc. in `library/std/src/alloc.rs`)
// otherwise.
#[rustc_allocator]
#[rustc_allocator_nounwind]
/// if there is one, or the `std` crate’s default.
///
/// Note: while this type is unstable, the functionality it provides can be
-/// accessed through the [free functions in `alloc`](index.html#functions).
+/// accessed through the [free functions in `alloc`](self#functions).
#[unstable(feature = "allocator_api", issue = "32838")]
#[derive(Copy, Clone, Default, Debug)]
pub struct Global;
/// assert_eq!(my_struct.special_field.get(), new_value);
/// ```
///
-/// See the [module-level documentation](index.html) for more.
+/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(transparent)]
pub struct Cell<T: ?Sized> {
/// A mutable memory location with dynamically checked borrow rules
///
-/// See the [module-level documentation](index.html) for more.
+/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
/// Wraps a borrowed reference to a value in a `RefCell` box.
/// A wrapper type for an immutably borrowed value from a `RefCell<T>`.
///
-/// See the [module-level documentation](index.html) for more.
+/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Ref<'b, T: ?Sized + 'b> {
value: &'b T,
/// A wrapper type for a mutably borrowed value from a `RefCell<T>`.
///
-/// See the [module-level documentation](index.html) for more.
+/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RefMut<'b, T: ?Sized + 'b> {
value: &'b mut T,
///
/// # Examples
///
-/// Let’s re-implement the counter iterator from [module-level documentation]:
+/// Let’s re-implement the counter iterator from the [module-level documentation]:
///
-/// [module-level documentation]: index.html
+/// [module-level documentation]: super
///
/// ```
/// let mut count = 0;
///
/// See the [module-level documentation] for more.
///
- /// [module-level documentation]: index.html
+ /// [module-level documentation]: crate::iter
///
/// # Examples
///
/// collection of some kind.
///
/// One benefit of implementing `IntoIterator` is that your type will [work
-/// with Rust's `for` loop syntax](index.html#for-loops-and-intoiterator).
+/// with Rust's `for` loop syntax](crate::iter#for-loops-and-intoiterator).
///
/// See also: [`FromIterator`].
///
///
/// See the [module-level documentation] for more.
///
- /// [module-level documentation]: index.html
+ /// [module-level documentation]: crate::iter
///
/// # Examples
///
/// assert_eq!(5, five.len());
/// ```
///
-/// In the [module level docs][moddocs], we implemented an [`Iterator`],
-/// `Counter`. Let's implement `ExactSizeIterator` for it as well:
+/// In the [module-level docs], we implemented an [`Iterator`], `Counter`.
+/// Let's implement `ExactSizeIterator` for it as well:
///
-/// [moddocs]: index.html
+/// [module-level docs]: crate::iter
///
/// ```
/// # struct Counter {
/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
///
-/// See the [`std::result`](index.html) module documentation for details.
+/// See the [module documentation](self) for details.
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
#[rustc_diagnostic_item = "result_type"]
/// [`&OsStr`]: OsStr
/// [`&str`]: str
/// [`CStr`]: crate::ffi::CStr
-/// [conversions]: index.html#conversions
+/// [conversions]: super#conversions
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OsString {
/// the traits which `OsStr` implements for [conversions] from/to native representations.
///
/// [`&str`]: str
-/// [conversions]: index.html#conversions
+/// [conversions]: super#conversions
#[stable(feature = "rust1", since = "1.0.0")]
// FIXME:
// `OsStr::from_inner` current implementation relies
/// [`set_extension`]: PathBuf::set_extension
///
/// More details about the overall approach can be found in
-/// the [module documentation](index.html).
+/// the [module documentation](self).
///
/// # Examples
///
/// see [`PathBuf`].
///
/// More details about the overall approach can be found in
-/// the [module documentation](index.html).
+/// the [module documentation](self).
///
/// # Examples
///
// Note that `libprofiler_builtins/build.rs` also computes this so if
// you're changing something here please also change that.
cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
- " compiler-builtins-c".to_string()
+ " compiler-builtins-c"
} else {
- String::new()
+ ""
};
if builder.no_std(target) == Some(true) {
let mut features = "compiler-builtins-mem".to_string();
- features.push_str(&compiler_builtins_c_feature);
+ features.push_str(compiler_builtins_c_feature);
// for no-std targets we only compile a few no_std crates
cargo
.arg("--manifest-path")
.arg(builder.src.join("library/alloc/Cargo.toml"))
.arg("--features")
- .arg("compiler-builtins-mem compiler-builtins-c");
+ .arg(features);
} else {
let mut features = builder.std_features();
- features.push_str(&compiler_builtins_c_feature);
+ features.push_str(compiler_builtins_c_feature);
cargo
.arg("--features")
--set rust.jemalloc
--set llvm.ninja=false
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
- SELECT_XCODE: /Applications/Xcode_12_beta.app
+ SELECT_XCODE: /Applications/Xcode_12.2.app
USE_XCODE_CLANG: 1
MACOSX_DEPLOYMENT_TARGET: 11.0
MACOSX_STD_DEPLOYMENT_TARGET: 11.0
yield "borrow", self.borrow
-# Yield each key (and optionally value) from a BoxedNode.
-def children_of_node(boxed_node, height, want_values):
+# Yields children (in a provider's sense of the word) for a tree headed by a BoxedNode.
+# In particular, yields each key/value pair in the node and in any child nodes.
+def children_of_node(boxed_node, height):
def cast_to_internal(node):
- internal_type_name = str(node.type.target()).replace("LeafNode", "InternalNode", 1)
+ internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
internal_type = lookup_type(internal_type_name)
return node.cast(internal_type.pointer())
node_ptr = unwrap_unique_or_non_null(boxed_node["ptr"])
- node_ptr = cast_to_internal(node_ptr) if height > 0 else node_ptr
- leaf = node_ptr["data"] if height > 0 else node_ptr.dereference()
+ leaf = node_ptr.dereference()
keys = leaf["keys"]
- values = leaf["vals"]
+ vals = leaf["vals"]
+ edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
length = int(leaf["len"])
for i in xrange(0, length + 1):
if height > 0:
- child_ptr = node_ptr["edges"][i]["value"]["value"]
- for child in children_of_node(child_ptr, height - 1, want_values):
+ boxed_child_node = edges[i]["value"]["value"]
+ for child in children_of_node(boxed_child_node, height - 1):
yield child
if i < length:
- if want_values:
- yield keys[i]["value"]["value"], values[i]["value"]["value"]
- else:
- yield keys[i]["value"]["value"]
+ # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
+ key = keys[i]["value"]["value"] if keys.type.sizeof > 0 else None
+ val = vals[i]["value"]["value"] if vals.type.sizeof > 0 else None
+ yield key, val
+
+
+# Yields children for a BTreeMap.
+def children_of_map(map):
+ if map["length"] > 0:
+ root = map["root"]
+ if root.type.name.startswith("core::option::Option<"):
+ root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
+ boxed_root_node = root["node"]
+ height = root["height"]
+ for i, (key, val) in enumerate(children_of_node(boxed_root_node, height)):
+ if key is not None:
+ yield "key{}".format(i), key
+ if val is not None:
+ yield "val{}".format(i), val
class StdBTreeSetProvider:
def children(self):
inner_map = self.valobj["map"]
- if inner_map["length"] > 0:
- root = inner_map["root"]
- if "core::option::Option<" in root.type.name:
- type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1]
- root = root.cast(gdb.lookup_type(type_name))
-
- node_ptr = root["node"]
- for i, child in enumerate(children_of_node(node_ptr, root["height"], False)):
- yield "[{}]".format(i), child
+ for child in children_of_map(inner_map):
+ yield child
@staticmethod
def display_hint():
return "BTreeMap(size={})".format(self.valobj["length"])
def children(self):
- if self.valobj["length"] > 0:
- root = self.valobj["root"]
- if "core::option::Option<" in root.type.name:
- type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1]
- root = root.cast(gdb.lookup_type(type_name))
-
- node_ptr = root["node"]
- for i, child in enumerate(children_of_node(node_ptr, root["height"], True)):
- yield "key{}".format(i), child[0]
- yield "val{}".format(i), child[1]
+ for child in children_of_map(self.valobj):
+ yield child
@staticmethod
def display_hint():
attrs,
inner,
visibility: clean::Public,
- stability: cx.tcx.lookup_stability(did).clean(cx),
+ stability: cx.tcx.lookup_stability(did).cloned(),
deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
def_id: did,
});
name: None,
attrs,
visibility: clean::Inherited,
- stability: tcx.lookup_stability(did).clean(cx),
+ stability: tcx.lookup_stability(did).cloned(),
deprecation: tcx.lookup_deprecation(did).clean(cx),
def_id: did,
});
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::bug;
use rustc_middle::middle::resolve_lifetime as rl;
-use rustc_middle::middle::stability;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
attrs,
source: span.clean(cx),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
inner: ModuleItem(Module { is_crate: self.is_crate, items }),
attrs: self.attrs.clean(cx),
source: self.span.clean(cx),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
def_id: did.to_def_id(),
inner: FunctionItem(Function {
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: TraitItem(Trait {
auto: self.is_auto.clean(cx),
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: TraitAliasItem(TraitAlias {
generics: self.generics.clean(cx),
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: StructItem(Struct {
struct_type: self.struct_type,
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: UnionItem(Union {
struct_type: self.struct_type,
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: EnumItem(Enum {
variants: self.variants.iter().map(|v| v.clean(cx)).collect(),
attrs: self.attrs.clean(cx),
source: self.span.clean(cx),
visibility: Inherited,
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
inner: VariantItem(Variant { kind: self.def.clean(cx) }),
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
}
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: OpaqueTyItem(OpaqueTy {
bounds: self.opaque_ty.bounds.clean(cx),
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: StaticItem(Static {
type_: self.type_.clean(cx),
source: self.span.clean(cx),
def_id: def_id.to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: ConstantItem(Constant {
type_: self.type_.clean(cx),
source: self.span.clean(cx),
def_id: def_id.to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner: ImplItem(Impl {
unsafety: self.unsafety,
source: self.span.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
visibility: self.vis.clean(cx),
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
inner,
}
attrs: self.attrs.clean(cx),
source: self.span.clean(cx),
visibility: Public,
- stability: cx.stability(self.hid).clean(cx),
+ stability: cx.stability(self.hid),
deprecation: cx.deprecation(self.hid).clean(cx),
def_id: self.def_id,
inner: MacroItem(Macro {
attrs: self.attrs.clean(cx),
source: self.span.clean(cx),
visibility: Public,
- stability: cx.stability(self.id).clean(cx),
+ stability: cx.stability(self.id),
deprecation: cx.deprecation(self.id).clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
}
}
-impl Clean<Stability> for attr::Stability {
- fn clean(&self, _: &DocContext<'_>) -> Stability {
- Stability {
- level: stability::StabilityLevel::from_attr_level(&self.level),
- feature: self.feature.to_string(),
- since: match self.level {
- attr::Stable { ref since } => since.to_string(),
- _ => String::new(),
- },
- unstable_reason: match self.level {
- attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()),
- _ => None,
- },
- issue: match self.level {
- attr::Unstable { issue, .. } => issue,
- _ => None,
- },
- }
- }
-}
-
impl Clean<Deprecation> for attr::Deprecation {
fn clean(&self, _: &DocContext<'_>) -> Deprecation {
Deprecation {
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::lazy::SyncOnceCell as OnceCell;
-use std::num::NonZeroU32;
use std::rc::Rc;
use std::sync::Arc;
use std::{slice, vec};
use rustc_ast::util::comments::beautify_doc_string;
use rustc_ast::{self as ast, AttrStyle};
use rustc_ast::{FloatTy, IntTy, UintTy};
+use rustc_attr::{Stability, StabilityLevel};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::lang_items::LangItem;
use rustc_hir::Mutability;
use rustc_index::vec::IndexVec;
-use rustc_middle::middle::stability;
use rustc_middle::ty::{AssocKind, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::DUMMY_SP;
-use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr};
use rustc_span::{self, FileName};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
self.stability.as_ref().and_then(|ref s| {
let mut classes = Vec::with_capacity(2);
- if s.level == stability::Unstable {
+ if s.level.is_unstable() {
classes.push("unstable");
}
})
}
- pub fn stable_since(&self) -> Option<&str> {
- self.stability.as_ref().map(|s| &s.since[..])
+ pub fn stable_since(&self) -> Option<SymbolStr> {
+ match self.stability?.level {
+ StabilityLevel::Stable { since, .. } => Some(since.as_str()),
+ StabilityLevel::Unstable { .. } => None,
+ }
}
pub fn is_non_exhaustive(&self) -> bool {
pub helpers: Vec<String>,
}
-#[derive(Clone, Debug)]
-pub struct Stability {
- pub level: stability::StabilityLevel,
- pub feature: String,
- pub since: String,
- pub unstable_reason: Option<String>,
- pub issue: Option<NonZeroU32>,
-}
-
#[derive(Clone, Debug)]
pub struct Deprecation {
pub since: Option<String>,
use crate::clean::{
inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg,
GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime,
- MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type,
- TypeBinding, TypeKind, Visibility, WherePredicate,
+ MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding,
+ TypeKind, Visibility, WherePredicate,
};
use crate::core::DocContext;
use itertools::Itertools;
+use rustc_attr::Stability;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
// extract the stability index for a node from tcx, if possible
pub fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option<Stability> {
- cx.tcx.lookup_stability(def_id).clean(cx)
+ cx.tcx.lookup_stability(def_id).cloned()
}
pub fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option<Deprecation> {
use itertools::Itertools;
use rustc_ast_pretty::pprust;
+use rustc_attr::StabilityLevel;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_feature::UnstableFeatures;
}
let s1 = i1.stability.as_ref().map(|s| s.level);
let s2 = i2.stability.as_ref().map(|s| s.level);
- match (s1, s2) {
- (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater,
- (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less,
- _ => {}
+ if let (Some(a), Some(b)) = (s1, s2) {
+ match (a.is_stable(), b.is_stable()) {
+ (true, true) | (false, false) => {}
+ (false, true) => return Ordering::Less,
+ (true, false) => return Ordering::Greater,
+ }
}
let lhs = i1.name.as_ref().map_or("", |s| &**s);
let rhs = i2.name.as_ref().map_or("", |s| &**s);
// The "rustc_private" crates are permanently unstable so it makes no sense
// to render "unstable" everywhere.
- if item
- .stability
- .as_ref()
- .map(|s| s.level == stability::Unstable && s.feature != "rustc_private")
+ if item.stability.as_ref().map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
== Some(true)
{
tags += &tag_html("unstable", "", "Experimental");
// Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
// Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
- if let Some(stab) = item
+ if let Some((StabilityLevel::Unstable { reason, issue, .. }, feature)) = item
.stability
.as_ref()
- .filter(|stab| stab.level == stability::Unstable && stab.feature != "rustc_private")
+ .filter(|stab| stab.feature != sym::rustc_private)
+ .map(|stab| (stab.level, stab.feature))
{
let mut message =
"<span class='emoji'>🔬</span> This is a nightly-only experimental API.".to_owned();
- let mut feature = format!("<code>{}</code>", Escape(&stab.feature));
- if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) {
+ let mut feature = format!("<code>{}</code>", Escape(&feature.as_str()));
+ if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
feature.push_str(&format!(
" <a href=\"{url}{issue}\">#{issue}</a>",
url = url,
message.push_str(&format!(" ({})", feature));
- if let Some(unstable_reason) = &stab.unstable_reason {
+ if let Some(unstable_reason) = reason {
let mut ids = cx.id_map.borrow_mut();
message = format!(
"<details><summary>{}</summary>{}</details>",
message,
MarkdownHtml(
- &unstable_reason,
+ &unstable_reason.as_str(),
&mut ids,
error_codes,
cx.shared.edition,
implementor,
AssocItemLink::Anchor(None),
RenderMode::Normal,
- implementor.impl_item.stable_since(),
+ implementor.impl_item.stable_since().as_deref(),
false,
Some(use_absolute),
false,
i,
assoc_link,
RenderMode::Normal,
- containing_item.stable_since(),
+ containing_item.stable_since().as_deref(),
true,
None,
false,
&implementor,
assoc_link,
RenderMode::Normal,
- implementor.impl_item.stable_since(),
+ implementor.impl_item.stable_since().as_deref(),
false,
None,
true,
}
fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) {
- render_stability_since_raw(w, item.stable_since(), containing_item.stable_since())
+ render_stability_since_raw(
+ w,
+ item.stable_since().as_deref(),
+ containing_item.stable_since().as_deref(),
+ )
}
fn render_assoc_item(
i,
AssocItemLink::Anchor(None),
render_mode,
- containing_item.stable_since(),
+ containing_item.stable_since().as_deref(),
true,
None,
false,
);
}
write!(w, "<a href='#{}' class='anchor'></a>", id);
- let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
- render_stability_since_raw(w, since, outer_version);
+ let since = i.impl_item.stability.as_ref().and_then(|s| match s.level {
+ StabilityLevel::Stable { since } => Some(since.as_str()),
+ StabilityLevel::Unstable { .. } => None,
+ });
+ render_stability_since_raw(w, since.as_deref(), outer_version);
if let Some(l) = cx.src_href(&i.impl_item, cache) {
write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", l, "goto source code");
}
write!(w, "<code>");
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl);
write!(w, "</code>");
- render_stability_since_raw(w, item.stable_since(), outer_version);
+ render_stability_since_raw(w, item.stable_since().as_deref(), outer_version);
if let Some(l) = cx.src_href(item, cache) {
write!(
w,
write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class);
assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "");
write!(w, "</code>");
- render_stability_since_raw(w, item.stable_since(), outer_version);
+ render_stability_since_raw(w, item.stable_since().as_deref(), outer_version);
if let Some(l) = cx.src_href(item, cache) {
write!(
w,
//! Contains information about "passes", used to modify crate information during the documentation
//! process.
-use rustc_hir::def_id::{DefId, DefIdSet};
-use rustc_middle::middle::privacy::AccessLevels;
use rustc_span::{InnerSpan, Span, DUMMY_SP};
-use std::mem;
use std::ops::Range;
use self::Condition::*;
-use crate::clean::{self, DocFragmentKind, GetDefId, Item};
+use crate::clean::{self, DocFragmentKind};
use crate::core::DocContext;
-use crate::fold::{DocFolder, StripItem};
+
+mod stripper;
+pub use stripper::*;
mod collapse_docs;
pub use self::collapse_docs::COLLAPSE_DOCS;
PASSES.iter().find(|p| p.name == pass_name).copied()
}
-struct Stripper<'a> {
- retained: &'a mut DefIdSet,
- access_levels: &'a AccessLevels<DefId>,
- update_retained: bool,
-}
-
-impl<'a> DocFolder for Stripper<'a> {
- fn fold_item(&mut self, i: Item) -> Option<Item> {
- match i.inner {
- clean::StrippedItem(..) => {
- // We need to recurse into stripped modules to strip things
- // like impl methods but when doing so we must not add any
- // items to the `retained` set.
- debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
- let old = mem::replace(&mut self.update_retained, false);
- let ret = self.fold_item_recur(i);
- self.update_retained = old;
- return ret;
- }
- // These items can all get re-exported
- clean::OpaqueTyItem(..)
- | clean::TypedefItem(..)
- | clean::StaticItem(..)
- | clean::StructItem(..)
- | clean::EnumItem(..)
- | clean::TraitItem(..)
- | clean::FunctionItem(..)
- | clean::VariantItem(..)
- | clean::MethodItem(..)
- | clean::ForeignFunctionItem(..)
- | clean::ForeignStaticItem(..)
- | clean::ConstantItem(..)
- | clean::UnionItem(..)
- | clean::AssocConstItem(..)
- | clean::TraitAliasItem(..)
- | clean::ForeignTypeItem => {
- if i.def_id.is_local() {
- if !self.access_levels.is_exported(i.def_id) {
- debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
- return None;
- }
- }
- }
-
- clean::StructFieldItem(..) => {
- if i.visibility != clean::Public {
- return StripItem(i).strip();
- }
- }
-
- clean::ModuleItem(..) => {
- if i.def_id.is_local() && i.visibility != clean::Public {
- debug!("Stripper: stripping module {:?}", i.name);
- let old = mem::replace(&mut self.update_retained, false);
- let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
- self.update_retained = old;
- return ret;
- }
- }
-
- // handled in the `strip-priv-imports` pass
- clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
-
- clean::ImplItem(..) => {}
-
- // tymethods/macros have no control over privacy
- clean::MacroItem(..) | clean::TyMethodItem(..) => {}
-
- // Proc-macros are always public
- clean::ProcMacroItem(..) => {}
-
- // Primitives are never stripped
- clean::PrimitiveItem(..) => {}
-
- // Associated types are never stripped
- clean::AssocTypeItem(..) => {}
-
- // Keywords are never stripped
- clean::KeywordItem(..) => {}
- }
-
- let fastreturn = match i.inner {
- // nothing left to do for traits (don't want to filter their
- // methods out, visibility controlled by the trait)
- clean::TraitItem(..) => true,
-
- // implementations of traits are always public.
- clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
- // Struct variant fields have inherited visibility
- clean::VariantItem(clean::Variant { kind: clean::VariantKind::Struct(..) }) => true,
- _ => false,
- };
-
- let i = if fastreturn {
- if self.update_retained {
- self.retained.insert(i.def_id);
- }
- return Some(i);
- } else {
- self.fold_item_recur(i)
- };
-
- if let Some(ref i) = i {
- if self.update_retained {
- self.retained.insert(i.def_id);
- }
- }
- i
- }
-}
-
-// This stripper discards all impls which reference stripped items
-struct ImplStripper<'a> {
- retained: &'a DefIdSet,
-}
-
-impl<'a> DocFolder for ImplStripper<'a> {
- fn fold_item(&mut self, i: Item) -> Option<Item> {
- if let clean::ImplItem(ref imp) = i.inner {
- // emptied none trait impls can be stripped
- if imp.trait_.is_none() && imp.items.is_empty() {
- return None;
- }
- if let Some(did) = imp.for_.def_id() {
- if did.is_local() && !imp.for_.is_generic() && !self.retained.contains(&did) {
- debug!("ImplStripper: impl item for stripped type; removing");
- return None;
- }
- }
- if let Some(did) = imp.trait_.def_id() {
- if did.is_local() && !self.retained.contains(&did) {
- debug!("ImplStripper: impl item for stripped trait; removing");
- return None;
- }
- }
- if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
- for typaram in generics {
- if let Some(did) = typaram.def_id() {
- if did.is_local() && !self.retained.contains(&did) {
- debug!(
- "ImplStripper: stripped item in trait's generics; removing impl"
- );
- return None;
- }
- }
- }
- }
- }
- self.fold_item_recur(i)
- }
-}
-
-// This stripper discards all private import statements (`use`, `extern crate`)
-struct ImportStripper;
-impl DocFolder for ImportStripper {
- fn fold_item(&mut self, i: Item) -> Option<Item> {
- match i.inner {
- clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
- None
- }
- _ => self.fold_item_recur(i),
- }
- }
-}
-
/// Returns a span encompassing all the given attributes.
crate fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> {
if attrs.doc_strings.is_empty() {
--- /dev/null
+use rustc_hir::def_id::{DefId, DefIdSet};
+use rustc_middle::middle::privacy::AccessLevels;
+use std::mem;
+
+use crate::clean::{self, GetDefId, Item};
+use crate::fold::{DocFolder, StripItem};
+
+pub struct Stripper<'a> {
+ pub retained: &'a mut DefIdSet,
+ pub access_levels: &'a AccessLevels<DefId>,
+ pub update_retained: bool,
+}
+
+impl<'a> DocFolder for Stripper<'a> {
+ fn fold_item(&mut self, i: Item) -> Option<Item> {
+ match i.inner {
+ clean::StrippedItem(..) => {
+ // We need to recurse into stripped modules to strip things
+ // like impl methods but when doing so we must not add any
+ // items to the `retained` set.
+ debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
+ let old = mem::replace(&mut self.update_retained, false);
+ let ret = self.fold_item_recur(i);
+ self.update_retained = old;
+ return ret;
+ }
+ // These items can all get re-exported
+ clean::OpaqueTyItem(..)
+ | clean::TypedefItem(..)
+ | clean::StaticItem(..)
+ | clean::StructItem(..)
+ | clean::EnumItem(..)
+ | clean::TraitItem(..)
+ | clean::FunctionItem(..)
+ | clean::VariantItem(..)
+ | clean::MethodItem(..)
+ | clean::ForeignFunctionItem(..)
+ | clean::ForeignStaticItem(..)
+ | clean::ConstantItem(..)
+ | clean::UnionItem(..)
+ | clean::AssocConstItem(..)
+ | clean::TraitAliasItem(..)
+ | clean::ForeignTypeItem => {
+ if i.def_id.is_local() {
+ if !self.access_levels.is_exported(i.def_id) {
+ debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
+ return None;
+ }
+ }
+ }
+
+ clean::StructFieldItem(..) => {
+ if i.visibility != clean::Public {
+ return StripItem(i).strip();
+ }
+ }
+
+ clean::ModuleItem(..) => {
+ if i.def_id.is_local() && i.visibility != clean::Public {
+ debug!("Stripper: stripping module {:?}", i.name);
+ let old = mem::replace(&mut self.update_retained, false);
+ let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
+ self.update_retained = old;
+ return ret;
+ }
+ }
+
+ // handled in the `strip-priv-imports` pass
+ clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
+
+ clean::ImplItem(..) => {}
+
+ // tymethods/macros have no control over privacy
+ clean::MacroItem(..) | clean::TyMethodItem(..) => {}
+
+ // Proc-macros are always public
+ clean::ProcMacroItem(..) => {}
+
+ // Primitives are never stripped
+ clean::PrimitiveItem(..) => {}
+
+ // Associated types are never stripped
+ clean::AssocTypeItem(..) => {}
+
+ // Keywords are never stripped
+ clean::KeywordItem(..) => {}
+ }
+
+ let fastreturn = match i.inner {
+ // nothing left to do for traits (don't want to filter their
+ // methods out, visibility controlled by the trait)
+ clean::TraitItem(..) => true,
+
+ // implementations of traits are always public.
+ clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
+ // Struct variant fields have inherited visibility
+ clean::VariantItem(clean::Variant { kind: clean::VariantKind::Struct(..) }) => true,
+ _ => false,
+ };
+
+ let i = if fastreturn {
+ if self.update_retained {
+ self.retained.insert(i.def_id);
+ }
+ return Some(i);
+ } else {
+ self.fold_item_recur(i)
+ };
+
+ if let Some(ref i) = i {
+ if self.update_retained {
+ self.retained.insert(i.def_id);
+ }
+ }
+ i
+ }
+}
+
+/// This stripper discards all impls which reference stripped items
+pub struct ImplStripper<'a> {
+ pub retained: &'a DefIdSet,
+}
+
+impl<'a> DocFolder for ImplStripper<'a> {
+ fn fold_item(&mut self, i: Item) -> Option<Item> {
+ if let clean::ImplItem(ref imp) = i.inner {
+ // emptied none trait impls can be stripped
+ if imp.trait_.is_none() && imp.items.is_empty() {
+ return None;
+ }
+ if let Some(did) = imp.for_.def_id() {
+ if did.is_local() && !imp.for_.is_generic() && !self.retained.contains(&did) {
+ debug!("ImplStripper: impl item for stripped type; removing");
+ return None;
+ }
+ }
+ if let Some(did) = imp.trait_.def_id() {
+ if did.is_local() && !self.retained.contains(&did) {
+ debug!("ImplStripper: impl item for stripped trait; removing");
+ return None;
+ }
+ }
+ if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
+ for typaram in generics {
+ if let Some(did) = typaram.def_id() {
+ if did.is_local() && !self.retained.contains(&did) {
+ debug!(
+ "ImplStripper: stripped item in trait's generics; removing impl"
+ );
+ return None;
+ }
+ }
+ }
+ }
+ }
+ self.fold_item_recur(i)
+ }
+}
+
+/// This stripper discards all private import statements (`use`, `extern crate`)
+pub struct ImportStripper;
+
+impl DocFolder for ImportStripper {
+ fn fold_item(&mut self, i: Item) -> Option<Item> {
+ match i.inner {
+ clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
+ None
+ }
+ _ => self.fold_item_recur(i),
+ }
+ }
+}
// gdb-check:$6 = BTreeMap(size=15) = {[0] = pretty_std_collections::MyLeafNode (0), [...]}
// (abbreviated because it's boring but we need enough elements to include internal nodes)
+// gdb-command: print zst_btree_map
+// gdb-check:$7 = BTreeMap(size=1)
+
// gdb-command: print vec_deque
-// gdb-check:$7 = VecDeque(size=3) = {5, 3, 7}
+// gdb-check:$8 = VecDeque(size=3) = {5, 3, 7}
// gdb-command: print vec_deque2
-// gdb-check:$8 = VecDeque(size=7) = {2, 3, 4, 5, 6, 7, 8}
+// gdb-check:$9 = VecDeque(size=7) = {2, 3, 4, 5, 6, 7, 8}
// gdb-command: print hash_map
-// gdb-check:$9 = HashMap(size=4) = {[1] = 10, [2] = 20, [3] = 30, [4] = 40}
+// gdb-check:$10 = HashMap(size=4) = {[1] = 10, [2] = 20, [3] = 30, [4] = 40}
// gdb-command: print hash_set
-// gdb-check:$10 = HashSet(size=4) = {1, 2, 3, 4}
+// gdb-check:$11 = HashSet(size=4) = {1, 2, 3, 4}
// === LLDB TESTS ==================================================================================
#![allow(unused_variables)]
use std::collections::BTreeMap;
use std::collections::BTreeSet;
-use std::collections::VecDeque;
use std::collections::HashMap;
use std::collections::HashSet;
+use std::collections::VecDeque;
use std::hash::{BuildHasherDefault, Hasher};
struct MyLeafNode(i32); // helps to ensure we don't blindly replace substring "LeafNode"
nasty_btree_map.insert(i, MyLeafNode(i));
}
+ let mut zst_btree_map: BTreeMap<(), ()> = BTreeMap::new();
+ zst_btree_map.insert((), ());
+
// VecDeque
let mut vec_deque = VecDeque::new();
vec_deque.push_back(5);
extern crate rustc_codegen_ssa;
extern crate rustc_errors;
extern crate rustc_middle;
-#[macro_use]
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_symbol_mangling;
extern crate rustc_target;
+use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::traits::CodegenBackend;
-use rustc_data_structures::owning_ref::OwningRef;
+use rustc_codegen_ssa::{CodegenResults, CrateInfo};
+use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::MetadataRef;
use rustc_errors::ErrorReported;
use rustc_middle::dep_graph::DepGraph;
+use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::OutputFilenames;
use rustc_session::Session;
-use rustc_span::symbol::Symbol;
use rustc_target::spec::Target;
use std::any::Any;
use std::path::Path;
impl MetadataLoader for NoLlvmMetadataLoader {
fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
- let buf =
- std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?;
- let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf);
- Ok(rustc_erase_owner!(buf.map_owner_box()))
+ unreachable!("some_crate.rs shouldn't depend on any external crates");
}
fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
- self.get_rlib_metadata(target, filename)
+ unreachable!("some_crate.rs shouldn't depend on any external crates");
}
}
Box::new(NoLlvmMetadataLoader)
}
- fn provide(&self, providers: &mut Providers) {
- rustc_symbol_mangling::provide(providers);
-
- providers.supported_target_features = |tcx, _cnum| {
- Default::default() // Just a dummy
- };
- providers.is_reachable_non_generic = |_tcx, _defid| true;
- providers.exported_symbols = |_tcx, _crate| &[];
- }
-
- fn provide_extern(&self, providers: &mut Providers) {
- providers.is_reachable_non_generic = |_tcx, _defid| true;
- }
+ fn provide(&self, providers: &mut Providers) {}
+ fn provide_extern(&self, providers: &mut Providers) {}
fn codegen_crate<'a, 'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- _metadata: EncodedMetadata,
+ metadata: EncodedMetadata,
_need_metadata_module: bool,
) -> Box<dyn Any> {
use rustc_hir::def_id::LOCAL_CRATE;
- Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol)
+ Box::new(CodegenResults {
+ crate_name: tcx.crate_name(LOCAL_CRATE),
+ modules: vec![],
+ allocator_module: None,
+ metadata_module: None,
+ metadata,
+ windows_subsystem: None,
+ linker_info: LinkerInfo::new(tcx),
+ crate_info: CrateInfo::new(tcx),
+ })
}
fn join_codegen(
&self,
ongoing_codegen: Box<dyn Any>,
_sess: &Session,
- _dep_graph: &DepGraph,
- ) -> Result<Box<dyn Any>, ErrorReported> {
- let crate_name = ongoing_codegen
- .downcast::<Symbol>()
- .expect("in join_codegen: ongoing_codegen is not a Symbol");
- Ok(crate_name)
+ ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
+ let codegen_results = ongoing_codegen
+ .downcast::<CodegenResults>()
+ .expect("in join_codegen: ongoing_codegen is not a CodegenResults");
+ Ok((*codegen_results, FxHashMap::default()))
}
fn link(
&self,
sess: &Session,
- codegen_results: Box<dyn Any>,
+ codegen_results: CodegenResults,
outputs: &OutputFilenames,
) -> Result<(), ErrorReported> {
use rustc_session::{config::CrateType, output::out_filename};
use std::io::Write;
- let crate_name =
- codegen_results.downcast::<Symbol>().expect("in link: codegen_results is not a Symbol");
+ let crate_name = codegen_results.crate_name;
for &crate_type in sess.opts.crate_types.iter() {
if crate_type != CrateType::Rlib {
sess.fatal(&format!("Crate type is {:?}", crate_type));
--- /dev/null
+// exact-check
+
+const QUERY = [
+ 'Demon Lord',
+];
+
+const EXPECTED = [
+ {
+ 'others': [
+ {
+ 'path': 'doc_alias_whitespace',
+ 'name': 'Struct',
+ 'alias': 'Demon Lord',
+ 'href': '../doc_alias_whitespace/struct.Struct.html',
+ 'is_alias': true
+ },
+ ],
+ },
+];
--- /dev/null
+#![feature(doc_alias)]
+
+#[doc(alias = "Demon Lord")]
+pub struct Struct;
#[doc(alias = "\n")] //~ ERROR
#[doc(alias = "
")] //~^ ERROR
-#[doc(alias = " ")] //~ ERROR
#[doc(alias = "\t")] //~ ERROR
+#[doc(alias = " hello")] //~ ERROR
+#[doc(alias = "hello ")] //~ ERROR
pub struct Foo;
LL | | ")]
| |_^
-error: ' ' character isn't allowed in `#[doc(alias = "...")]`
+error: '\t' character isn't allowed in `#[doc(alias = "...")]`
--> $DIR/check-doc-alias-attr.rs:14:7
|
-LL | #[doc(alias = " ")]
- | ^^^^^^^^^^^
+LL | #[doc(alias = "\t")]
+ | ^^^^^^^^^^^^
-error: '\t' character isn't allowed in `#[doc(alias = "...")]`
+error: `#[doc(alias = "...")]` cannot start or end with ' '
--> $DIR/check-doc-alias-attr.rs:15:7
|
-LL | #[doc(alias = "\t")]
- | ^^^^^^^^^^^^
+LL | #[doc(alias = " hello")]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[doc(alias = "...")]` cannot start or end with ' '
+ --> $DIR/check-doc-alias-attr.rs:16:7
+ |
+LL | #[doc(alias = "hello ")]
+ | ^^^^^^^^^^^^^^^^
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
--- /dev/null
+// check-pass
+
+#![feature(associated_type_bounds)]
+
+fn foo<F>(_: F)
+where
+ F: for<'a> Trait<Output: 'a>,
+{
+}
+
+trait Trait {
+ type Output;
+}
+
+impl<T> Trait for T {
+ type Output = ();
+}
+
+fn main() {
+ foo(());
+}
--- /dev/null
+#![feature(associated_type_bounds)]
+
+struct Incorrect;
+
+fn hello<F: for<'a> Iterator<Item: 'a>>() {
+ Incorrect //~ERROR: mismatched types
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-71443-1.rs:6:5
+ |
+LL | fn hello<F: for<'a> Iterator<Item: 'a>>() {
+ | - help: try adding a return type: `-> Incorrect`
+LL | Incorrect
+ | ^^^^^^^^^ expected `()`, found struct `Incorrect`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// check-pass
+
+#![feature(associated_type_bounds)]
+
+fn hello<'b, F>()
+where
+ for<'a> F: Iterator<Item: 'a> + 'b,
+{
+}
+
+fn main() {}
--- /dev/null
+use std::ops::Add;
+
+pub trait Encoder {
+ type Size: Add<Output = Self::Size>;
+
+ fn foo(&self) -> Self::Size;
+}
+
+pub trait SubEncoder: Encoder {
+ type ActualSize;
+
+ fn bar(&self) -> Self::Size;
+}
+
+impl<T> Encoder for T
+where
+ T: SubEncoder,
+{
+ type Size = <Self as SubEncoder>::ActualSize;
+ //~^ ERROR: cannot add `<T as SubEncoder>::ActualSize` to `<T as SubEncoder>::ActualSize`
+
+ fn foo(&self) -> Self::Size {
+ self.bar() + self.bar()
+ }
+}
+
+pub struct UnitEncoder;
+
+impl SubEncoder for UnitEncoder {
+ type ActualSize = ();
+
+ fn bar(&self) {}
+}
+
+pub fn fun<R: Encoder>(encoder: &R) {
+ encoder.foo();
+}
+
+fn main() {
+ fun(&UnitEncoder {});
+}
--- /dev/null
+error[E0277]: cannot add `<T as SubEncoder>::ActualSize` to `<T as SubEncoder>::ActualSize`
+ --> $DIR/issue-54108.rs:19:5
+ |
+LL | type Size: Add<Output = Self::Size>;
+ | ------------------------ required by this bound in `Encoder::Size`
+...
+LL | type Size = <Self as SubEncoder>::ActualSize;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `<T as SubEncoder>::ActualSize + <T as SubEncoder>::ActualSize`
+ |
+ = help: the trait `Add` is not implemented for `<T as SubEncoder>::ActualSize`
+help: consider further restricting the associated type
+ |
+LL | T: SubEncoder, <T as SubEncoder>::ActualSize: Add
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// check-pass
+
+trait Trait {
+ type Assoc;
+}
+
+impl Trait for () {
+ type Assoc = ();
+}
+
+fn unit() -> impl Into<<() as Trait>::Assoc> {}
+
+pub fn ice() {
+ Into::into(unit());
+}
+
+fn main() {}
#[doc(alias = "\n")] //~ ERROR
#[doc(alias = "
")] //~^ ERROR
-#[doc(alias = " ")] //~ ERROR
#[doc(alias = "\t")] //~ ERROR
+#[doc(alias = " hello")] //~ ERROR
+#[doc(alias = "hello ")] //~ ERROR
pub struct Foo;
LL | | ")]
| |_^
-error: ' ' character isn't allowed in `#[doc(alias = "...")]`
+error: '\t' character isn't allowed in `#[doc(alias = "...")]`
--> $DIR/check-doc-alias-attr.rs:14:7
|
-LL | #[doc(alias = " ")]
- | ^^^^^^^^^^^
+LL | #[doc(alias = "\t")]
+ | ^^^^^^^^^^^^
-error: '\t' character isn't allowed in `#[doc(alias = "...")]`
+error: `#[doc(alias = "...")]` cannot start or end with ' '
--> $DIR/check-doc-alias-attr.rs:15:7
|
-LL | #[doc(alias = "\t")]
- | ^^^^^^^^^^^^
+LL | #[doc(alias = " hello")]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[doc(alias = "...")]` cannot start or end with ' '
+ --> $DIR/check-doc-alias-attr.rs:16:7
+ |
+LL | #[doc(alias = "hello ")]
+ | ^^^^^^^^^^^^^^^^
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
--- /dev/null
+// check-pass
+
+#![allow(dead_code)]
+
+trait Trait1<T, U> {
+ fn f1(self) -> U;
+}
+
+trait Trait2 {
+ type T;
+ type U: Trait2<T = Self::T>;
+ fn f2(f: impl FnOnce(&Self::U));
+}
+
+fn f3<T: Trait2>() -> impl Trait1<T, T::T> {
+ Struct1
+}
+
+struct Struct1;
+
+impl<T: Trait2> Trait1<T, T::T> for Struct1 {
+ fn f1(self) -> T::T {
+ unimplemented!()
+ }
+}
+
+fn f4<T: Trait2>() {
+ T::f2(|_| {
+ f3::<T::U>().f1();
+ });
+}
+
+fn main() {}
--- /dev/null
+// ignore-test this is not a test
+
+macro_rules! tuple_from_req {
+ ($T:ident) => {
+ #[my_macro] struct Three($T);
+ }
+}
--- /dev/null
+// ignore-test this is not a test
+
+macro_rules! tuple_from_req {
+ ($T:ident) => {
+ #[my_macro] struct Three($T);
+ }
+}
--- /dev/null
+// ignore-test this is not a test
+
+macro_rules! tuple_from_req {
+ ($T:ident) => {
+ #[my_macro] struct Four($T);
+ }
+}
--- /dev/null
+// ignore-test this is not a test
+
+macro_rules! tuple_from_req {
+ ($T:ident) => {
+ #[my_macro] struct Four($T);
+ }
+}
other!(Foo);
}
+mod actix_web_test {
+ include!("actix-web/src/extract.rs");
+
+ struct Foo;
+ tuple_from_req!(Foo);
+}
+
+mod actix_web_version_test {
+ include!("actix-web-2.0.0/src/extract.rs");
+
+ struct Foo;
+ tuple_from_req!(Foo);
+}
+
+mod actori_web_test {
+ include!("actori-web/src/extract.rs");
+
+ struct Foo;
+ tuple_from_req!(Foo);
+}
+
+mod actori_web_version_test {
+ include!("actori-web-2.0.0/src/extract.rs");
+
+ struct Foo;
+ tuple_from_req!(Foo);
+}
+
fn main() {}
Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:43:18: 43:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }]
Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:13: 44:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }]
Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:38:25: 38:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:38:32: 38:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:45:12: 45:15 (#0) }], span: $DIR/group-compat-hack.rs:38:38: 38:43 (#28) }], span: $DIR/group-compat-hack.rs:38:37: 38:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:38:44: 38:45 (#28) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web/src/extract.rs:5:21: 5:27 (#33) }, Ident { ident: "Three", span: $DIR/actix-web/src/extract.rs:5:28: 5:33 (#33) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:52:21: 52:24 (#0) }], span: $DIR/actix-web/src/extract.rs:5:33: 5:37 (#33) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web/src/extract.rs:5:37: 5:38 (#33) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web-2.0.0/src/extract.rs:5:21: 5:27 (#38) }, Ident { ident: "Three", span: $DIR/actix-web-2.0.0/src/extract.rs:5:28: 5:33 (#38) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:59:21: 59:24 (#0) }], span: $DIR/actix-web-2.0.0/src/extract.rs:5:33: 5:37 (#38) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web-2.0.0/src/extract.rs:5:37: 5:38 (#38) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web/src/extract.rs:5:21: 5:27 (#43) }, Ident { ident: "Four", span: $DIR/actori-web/src/extract.rs:5:28: 5:32 (#43) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:66:21: 66:24 (#0) }], span: $DIR/actori-web/src/extract.rs:5:32: 5:36 (#43) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web/src/extract.rs:5:36: 5:37 (#43) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web-2.0.0/src/extract.rs:5:21: 5:27 (#48) }, Ident { ident: "Four", span: $DIR/actori-web-2.0.0/src/extract.rs:5:28: 5:32 (#48) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:73:21: 73:24 (#0) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:32: 5:36 (#48) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web-2.0.0/src/extract.rs:5:36: 5:37 (#48) }]
--- /dev/null
+// Regression test for issue #75734
+// Ensures that we don't lose tokens when pretty-printing would
+// normally insert extra parentheses.
+
+// check-pass
+// aux-build:test-macros.rs
+// compile-flags: -Z span-debug
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
+#[macro_use]
+extern crate test_macros;
+
+macro_rules! mul_2 {
+ ($val:expr) => {
+ print_bang!($val * 2);
+ };
+}
+
+
+#[print_attr]
+fn main() {
+ &|_: u8| {};
+ mul_2!(1 + 1);
+}
--- /dev/null
+PRINT-ATTR INPUT (DISPLAY): fn main() { & | _ : u8 | { } ; mul_2 ! (1 + 1) ; }
+PRINT-ATTR INPUT (DEBUG): TokenStream [
+ Ident {
+ ident: "fn",
+ span: $DIR/issue-75734-pp-paren.rs:23:1: 23:3 (#0),
+ },
+ Ident {
+ ident: "main",
+ span: $DIR/issue-75734-pp-paren.rs:23:4: 23:8 (#0),
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [],
+ span: $DIR/issue-75734-pp-paren.rs:23:8: 23:10 (#0),
+ },
+ Group {
+ delimiter: Brace,
+ stream: TokenStream [
+ Punct {
+ ch: '&',
+ spacing: Joint,
+ span: $DIR/issue-75734-pp-paren.rs:24:5: 24:6 (#0),
+ },
+ Punct {
+ ch: '|',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:24:6: 24:7 (#0),
+ },
+ Ident {
+ ident: "_",
+ span: $DIR/issue-75734-pp-paren.rs:24:7: 24:8 (#0),
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:24:8: 24:9 (#0),
+ },
+ Ident {
+ ident: "u8",
+ span: $DIR/issue-75734-pp-paren.rs:24:10: 24:12 (#0),
+ },
+ Punct {
+ ch: '|',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:24:12: 24:13 (#0),
+ },
+ Group {
+ delimiter: Brace,
+ stream: TokenStream [],
+ span: $DIR/issue-75734-pp-paren.rs:24:14: 24:16 (#0),
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:24:16: 24:17 (#0),
+ },
+ Ident {
+ ident: "mul_2",
+ span: $DIR/issue-75734-pp-paren.rs:25:5: 25:10 (#0),
+ },
+ Punct {
+ ch: '!',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:25:10: 25:11 (#0),
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Literal {
+ kind: Integer,
+ symbol: "1",
+ suffix: None,
+ span: $DIR/issue-75734-pp-paren.rs:25:12: 25:13 (#0),
+ },
+ Punct {
+ ch: '+',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:25:14: 25:15 (#0),
+ },
+ Literal {
+ kind: Integer,
+ symbol: "1",
+ suffix: None,
+ span: $DIR/issue-75734-pp-paren.rs:25:16: 25:17 (#0),
+ },
+ ],
+ span: $DIR/issue-75734-pp-paren.rs:25:11: 25:18 (#0),
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:25:18: 25:19 (#0),
+ },
+ ],
+ span: $DIR/issue-75734-pp-paren.rs:23:11: 26:2 (#0),
+ },
+]
+PRINT-BANG INPUT (DISPLAY): 1 + 1 * 2
+PRINT-BANG INPUT (DEBUG): TokenStream [
+ Group {
+ delimiter: None,
+ stream: TokenStream [
+ Literal {
+ kind: Integer,
+ symbol: "1",
+ suffix: None,
+ span: $DIR/issue-75734-pp-paren.rs:25:12: 25:13 (#0),
+ },
+ Punct {
+ ch: '+',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:25:14: 25:15 (#0),
+ },
+ Literal {
+ kind: Integer,
+ symbol: "1",
+ suffix: None,
+ span: $DIR/issue-75734-pp-paren.rs:25:16: 25:17 (#0),
+ },
+ ],
+ span: $DIR/issue-75734-pp-paren.rs:17:21: 17:25 (#7),
+ },
+ Punct {
+ ch: '*',
+ spacing: Alone,
+ span: $DIR/issue-75734-pp-paren.rs:17:26: 17:27 (#7),
+ },
+ Literal {
+ kind: Integer,
+ symbol: "2",
+ suffix: None,
+ span: $DIR/issue-75734-pp-paren.rs:17:28: 17:29 (#7),
+ },
+]
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl Default;
+//~^ ERROR: the trait bound `T: Default` is not satisfied
+
+#[allow(unused)]
+fn foo<T: Default>(t: T) -> Foo<T> {
+ t
+}
+
+struct NotDefault;
+
+fn main() {
+ let _ = Foo::<NotDefault>::default();
+}
--- /dev/null
+error[E0277]: the trait bound `T: Default` is not satisfied
+ --> $DIR/issue-52843.rs:3:15
+ |
+LL | type Foo<T> = impl Default;
+ | ^^^^^^^^^^^^ the trait `Default` is not implemented for `T`
+ |
+help: consider restricting type parameter `T`
+ |
+LL | type Foo<T: Default> = impl Default;
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(unboxed_closures)]
+
+trait Lt<'a> {
+ type T;
+}
+impl<'a> Lt<'a> for () {
+ type T = ();
+}
+
+fn main() {
+ let v: <() as Lt<'_>>::T = ();
+ let f: &mut dyn FnMut<(_,), Output = ()> = &mut |_: <() as Lt<'_>>::T| {};
+ //~^ ERROR: the size for values of type `<() as Lt<'_>>::T` cannot be known
+ f(v);
+}
--- /dev/null
+error[E0277]: the size for values of type `<() as Lt<'_>>::T` cannot be known at compilation time
+ --> $DIR/issue-53448.rs:12:54
+ |
+LL | let f: &mut dyn FnMut<(_,), Output = ()> = &mut |_: <() as Lt<'_>>::T| {};
+ | ^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `<() as Lt<'_>>::T`
+ = help: unsized locals are gated as an unstable feature
+help: consider further restricting the associated type
+ |
+LL | fn main() where <() as Lt<'_>>::T: Sized {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | let f: &mut dyn FnMut<(_,), Output = ()> = &mut |_: &<() as Lt<'_>>::T| {};
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.