diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index 815e8f4d3567..4c7ce30097c9 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -384,3 +384,6 @@ parser_fn_ptr_with_generics = function pointer types may not have generic parame
         [true] the
         *[false] a
     } `for` parameter list
+
+parser_invalid_identifier_with_leading_number = expected identifier, found number literal
+    .label = identifiers cannot start with a number
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index a39398950a53..2b17cea97949 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1205,6 +1205,14 @@ pub(crate) struct SelfParamNotFirst {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(parser_invalid_identifier_with_leading_number)]
+pub(crate) struct InvalidIdentiferStartsWithNumber {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(parser_const_generic_without_braces)]
 pub(crate) struct ConstGenericWithoutBraces {
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 9684145ad994..18b661034e07 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -10,8 +10,8 @@ use super::{
 use crate::errors::{
     AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
     DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
-    InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
-    WrapExpressionInParentheses,
+    InvalidExpressionInLetElse, InvalidIdentiferStartsWithNumber, InvalidVariableDeclaration,
+    InvalidVariableDeclarationSub, WrapExpressionInParentheses,
 };
 use crate::maybe_whole;
 
@@ -264,6 +264,7 @@ impl<'a> Parser<'a> {
             self.bump();
         }
 
+        self.report_invalid_identifier_error()?;
         let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?;
 
         let (err, ty) = if colon {
@@ -355,6 +356,17 @@ impl<'a> Parser<'a> {
         Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
     }
 
+    /// report error for `let 1x = 123`
+    pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> {
+        if let token::Literal(lit) = self.token.uninterpolate().kind &&
+            let Err(_) = rustc_ast::Lit::from_token(&self.token) &&
+            (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) &&
+            self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) {
+                return Err(self.sess.create_err(InvalidIdentiferStartsWithNumber { span: self.token.span }));
+        }
+        Ok(())
+    }
+
     fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
         if let ast::ExprKind::Binary(op, ..) = init.kind {
             if op.node.lazy() {
diff --git a/src/test/ui/parser/issues/issue-104088.rs b/src/test/ui/parser/issues/issue-104088.rs
new file mode 100644
index 000000000000..5f794fe2dc92
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-104088.rs
@@ -0,0 +1,26 @@
+fn test() {
+    if let 123 = 123 { println!("yes"); }
+}
+
+fn test_2() {
+    let 1x = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn test_3() {
+    let 2x: i32 = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn test_4() {
+    if let 2e1 = 123 {
+        //~^ ERROR mismatched types
+    }
+}
+
+fn test_5() {
+    let 23name = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn main() {}
diff --git a/src/test/ui/parser/issues/issue-104088.stderr b/src/test/ui/parser/issues/issue-104088.stderr
new file mode 100644
index 000000000000..ff4b4bdb6953
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-104088.stderr
@@ -0,0 +1,29 @@
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:6:9
+   |
+LL |     let 1x = 123;
+   |         ^^ identifiers cannot start with a number
+
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:11:9
+   |
+LL |     let 2x: i32 = 123;
+   |         ^^ identifiers cannot start with a number
+
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:22:9
+   |
+LL |     let 23name = 123;
+   |         ^^^^^^ identifiers cannot start with a number
+
+error[E0308]: mismatched types
+  --> $DIR/issue-104088.rs:16:12
+   |
+LL |     if let 2e1 = 123 {
+   |            ^^^   --- this expression has type `{integer}`
+   |            |
+   |            expected integer, found floating-point number
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/slowparse-bstring.rs b/src/test/ui/parser/slowparse-bstring.rs
similarity index 100%
rename from src/test/ui/slowparse-bstring.rs
rename to src/test/ui/parser/slowparse-bstring.rs
diff --git a/src/test/ui/slowparse-string.rs b/src/test/ui/parser/slowparse-string.rs
similarity index 100%
rename from src/test/ui/slowparse-string.rs
rename to src/test/ui/parser/slowparse-string.rs