|
1 | 1 | use super::ty::AllowPlus;
|
2 |
| -use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; |
| 2 | +use super::TokenType; |
| 3 | +use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; |
3 | 4 |
|
4 | 5 | use rustc_ast::ptr::P;
|
5 | 6 | use rustc_ast::token::{self, Lit, LitKind, TokenKind};
|
6 | 7 | use rustc_ast::util::parser::AssocOp;
|
7 | 8 | use rustc_ast::{
|
8 |
| - self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, |
9 |
| - ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, |
10 |
| - TyKind, |
| 9 | + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, |
| 10 | + Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, |
| 11 | + PatKind, Path, PathSegment, QSelf, Ty, TyKind, |
11 | 12 | };
|
12 | 13 | use rustc_ast_pretty::pprust;
|
13 | 14 | use rustc_data_structures::fx::FxHashSet;
|
@@ -1780,4 +1781,142 @@ impl<'a> Parser<'a> {
|
1780 | 1781 | }
|
1781 | 1782 | }
|
1782 | 1783 | }
|
| 1784 | + |
| 1785 | + /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this |
| 1786 | + /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks |
| 1787 | + /// like the user has forgotten them. |
| 1788 | + pub fn handle_ambiguous_unbraced_const_arg( |
| 1789 | + &mut self, |
| 1790 | + args: &mut Vec<AngleBracketedArg>, |
| 1791 | + ) -> PResult<'a, bool> { |
| 1792 | + // If we haven't encountered a closing `>`, then the argument is malformed. |
| 1793 | + // It's likely that the user has written a const expression without enclosing it |
| 1794 | + // in braces, so we try to recover here. |
| 1795 | + let arg = args.pop().unwrap(); |
| 1796 | + // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has |
| 1797 | + // adverse side-effects to subsequent errors and seems to advance the parser. |
| 1798 | + // We are causing this error here exclusively in case that a `const` expression |
| 1799 | + // could be recovered from the current parser state, even if followed by more |
| 1800 | + // arguments after a comma. |
| 1801 | + let mut err = self.struct_span_err( |
| 1802 | + self.token.span, |
| 1803 | + &format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)), |
| 1804 | + ); |
| 1805 | + err.span_label(self.token.span, "expected one of `,` or `>`"); |
| 1806 | + match self.recover_const_arg(arg.span(), err) { |
| 1807 | + Ok(arg) => { |
| 1808 | + args.push(AngleBracketedArg::Arg(arg)); |
| 1809 | + if self.eat(&token::Comma) { |
| 1810 | + return Ok(true); // Continue |
| 1811 | + } |
| 1812 | + } |
| 1813 | + Err(mut err) => { |
| 1814 | + args.push(arg); |
| 1815 | + // We will emit a more generic error later. |
| 1816 | + err.delay_as_bug(); |
| 1817 | + } |
| 1818 | + } |
| 1819 | + return Ok(false); // Don't continue. |
| 1820 | + } |
| 1821 | + |
| 1822 | + /// Handle a generic const argument that had not been enclosed in braces, and suggest enclosing |
| 1823 | + /// it braces. In this situation, unlike in `handle_ambiguous_unbraced_const_arg`, this is |
| 1824 | + /// almost certainly a const argument, so we always offer a suggestion. |
| 1825 | + pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> { |
| 1826 | + let start = self.token.span; |
| 1827 | + let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { |
| 1828 | + err.span_label( |
| 1829 | + start.shrink_to_lo(), |
| 1830 | + "while parsing a const generic argument starting here", |
| 1831 | + ); |
| 1832 | + err |
| 1833 | + })?; |
| 1834 | + if !self.expr_is_valid_const_arg(&expr) { |
| 1835 | + self.struct_span_err( |
| 1836 | + expr.span, |
| 1837 | + "expressions must be enclosed in braces to be used as const generic \ |
| 1838 | + arguments", |
| 1839 | + ) |
| 1840 | + .multipart_suggestion( |
| 1841 | + "enclose the `const` expression in braces", |
| 1842 | + vec![ |
| 1843 | + (expr.span.shrink_to_lo(), "{ ".to_string()), |
| 1844 | + (expr.span.shrink_to_hi(), " }".to_string()), |
| 1845 | + ], |
| 1846 | + Applicability::MachineApplicable, |
| 1847 | + ) |
| 1848 | + .emit(); |
| 1849 | + } |
| 1850 | + Ok(expr) |
| 1851 | + } |
| 1852 | + |
| 1853 | + /// Try to recover from possible generic const argument without `{` and `}`. |
| 1854 | + /// |
| 1855 | + /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest |
| 1856 | + /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion |
| 1857 | + /// if we think that that the resulting expression would be well formed. |
| 1858 | + pub fn recover_const_arg( |
| 1859 | + &mut self, |
| 1860 | + start: Span, |
| 1861 | + mut err: DiagnosticBuilder<'a>, |
| 1862 | + ) -> PResult<'a, GenericArg> { |
| 1863 | + let is_op = AssocOp::from_token(&self.token) |
| 1864 | + .and_then(|op| { |
| 1865 | + if let AssocOp::Greater |
| 1866 | + | AssocOp::Less |
| 1867 | + | AssocOp::ShiftRight |
| 1868 | + | AssocOp::GreaterEqual |
| 1869 | + // Don't recover from `foo::<bar = baz>`, because this could be an attempt to |
| 1870 | + // assign a value to a defaulted generic parameter. |
| 1871 | + | AssocOp::Assign |
| 1872 | + | AssocOp::AssignOp(_) = op |
| 1873 | + { |
| 1874 | + None |
| 1875 | + } else { |
| 1876 | + Some(op) |
| 1877 | + } |
| 1878 | + }) |
| 1879 | + .is_some(); |
| 1880 | + // This will be true when a trait object type `Foo +` or a path which was a `const fn` with |
| 1881 | + // type params has been parsed. |
| 1882 | + let was_op = |
| 1883 | + matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt); |
| 1884 | + if !is_op && !was_op { |
| 1885 | + // We perform these checks and early return to avoid taking a snapshot unnecessarily. |
| 1886 | + return Err(err); |
| 1887 | + } |
| 1888 | + let snapshot = self.clone(); |
| 1889 | + if is_op { |
| 1890 | + self.bump(); |
| 1891 | + } |
| 1892 | + match self.parse_expr_res(Restrictions::CONST_EXPR, None) { |
| 1893 | + Ok(expr) => { |
| 1894 | + if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() { |
| 1895 | + // Avoid the following output by checking that we consumed a full const arg: |
| 1896 | + // help: expressions must be enclosed in braces to be used as const generic |
| 1897 | + // arguments |
| 1898 | + // | |
| 1899 | + // LL | let sr: Vec<{ (u32, _, _) = vec![] }; |
| 1900 | + // | ^ ^ |
| 1901 | + err.multipart_suggestion( |
| 1902 | + "expressions must be enclosed in braces to be used as const generic \ |
| 1903 | + arguments", |
| 1904 | + vec![ |
| 1905 | + (start.shrink_to_lo(), "{ ".to_string()), |
| 1906 | + (expr.span.shrink_to_hi(), " }".to_string()), |
| 1907 | + ], |
| 1908 | + Applicability::MaybeIncorrect, |
| 1909 | + ); |
| 1910 | + let value = self.mk_expr_err(start.to(expr.span)); |
| 1911 | + err.emit(); |
| 1912 | + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); |
| 1913 | + } |
| 1914 | + } |
| 1915 | + Err(mut err) => { |
| 1916 | + err.cancel(); |
| 1917 | + } |
| 1918 | + } |
| 1919 | + *self = snapshot; |
| 1920 | + Err(err) |
| 1921 | + } |
1783 | 1922 | }
|
0 commit comments