diff --git a/src/main/grammars/RustLexer.flex b/src/main/grammars/RustLexer.flex index 4cb2ce5f0e2..79f347705e3 100644 --- a/src/main/grammars/RustLexer.flex +++ b/src/main/grammars/RustLexer.flex @@ -153,6 +153,7 @@ OUTER_EOL_DOC = ({EOL_DOC_LINE}{EOL_WS})*{EOL_DOC_LINE} "." { return DOT; } ".." { return DOTDOT; } "..." { return DOTDOTDOT; } + "..=" { return DOTDOTEQ; } "=" { return EQ; } "!=" { return EXCLEQ; } "==" { return EQEQ; } diff --git a/src/main/grammars/RustParser.bnf b/src/main/grammars/RustParser.bnf index fe838cbd63e..490e6601625 100644 --- a/src/main/grammars/RustParser.bnf +++ b/src/main/grammars/RustParser.bnf @@ -58,6 +58,7 @@ DOT = '.' DOTDOT = '..' DOTDOTDOT = '...' + DOTDOTEQ = '..=' FAT_ARROW = '=>' ARROW = '->' Q = '?' @@ -401,7 +402,7 @@ Pat ::= PatWild | PatStruct | PatEnum | PatIdent - | (PatConst !('..' | '...')) + | (PatConst !('..' | '...' | '..=')) | PatRange | PatUniq @@ -427,7 +428,7 @@ private Pat_with_recover ::= Pat (',' | &(')' | ']' | '..')) private PatField_with_recover ::= PatField (',' | & '}') PatConst ::= PathExpr | LitExpr | &('-' LitExpr) UnaryExpr -PatRange ::= PatConst ('..' | '...') PatConst { pin = 2 } +PatRange ::= PatConst ('..' | '...' | '..=') PatConst { pin = 2 } PatTup ::= '(' SeqPat ')' PatVec ::= '[' SeqPat ']' @@ -847,7 +848,7 @@ Expr ::= RetExpr stubClass = "org.rust.lang.core.stubs.RsPlaceholderStub" } -private Expr_first ::= return | '|' | Path_first | '{' | '[' | '(' | '..' | '...' | true | false | box | QUOTE_IDENTIFIER +private Expr_first ::= return | '|' | Path_first | '{' | '[' | '(' | '..' | '...' | '..=' | true | false | box | QUOTE_IDENTIFIER | '-' | '*' | '!' | '&' | move | LitExpr | while | if | for | continue | break | loop | match | unsafe // https://github.com/rust-lang/rfcs/blob/master/text/0092-struct-grammar.md @@ -1044,12 +1045,12 @@ ArrayExpr ::= OuterAttr* '[' ArrayInitializer ']' { } private ArrayInitializer ::= [ AnyExpr ( ';' AnyExpr | (',' AnyExpr)* ','? ) ] -fake RangeExpr ::= Expr? ('..' | '...') Expr? { +fake RangeExpr ::= Expr? ('..' | '...' | '..=') Expr? { elementTypeFactory = "org.rust.lang.core.stubs.StubImplementationsKt.factory" } -FullRangeExpr ::= Expr ( '..' (<> Expr)? | '...' (<> Expr) ) { elementType = RangeExpr } -OpenRangeExpr ::= ( '..' (<> Expr)? | '...' (<> Expr) ) { elementType = RangeExpr } +FullRangeExpr ::= Expr ( '..' (<> Expr)? | '...' (<> Expr) | '..=' (<> Expr) ) { elementType = RangeExpr } +OpenRangeExpr ::= ( '..' (<> Expr)? | '...' (<> Expr) | '..=' (<> Expr) ) { elementType = RangeExpr } IndexExpr ::= Expr IndexArg { elementTypeFactory = "org.rust.lang.core.stubs.StubImplementationsKt.factory" diff --git a/src/main/kotlin/org/rust/ide/formatter/impl/utils.kt b/src/main/kotlin/org/rust/ide/formatter/impl/utils.kt index 9bd914451f8..d50817b89cb 100644 --- a/src/main/kotlin/org/rust/ide/formatter/impl/utils.kt +++ b/src/main/kotlin/org/rust/ide/formatter/impl/utils.kt @@ -23,7 +23,7 @@ import com.intellij.psi.tree.TokenSet.create as ts val SPECIAL_MACRO_ARGS = ts(FORMAT_MACRO_ARGUMENT, LOG_MACRO_ARGUMENT, TRY_MACRO_ARGUMENT, VEC_MACRO_ARGUMENT, ASSERT_MACRO_ARGUMENT) -val NO_SPACE_AROUND_OPS = ts(COLONCOLON, DOT, DOTDOT, DOTDOTDOT) +val NO_SPACE_AROUND_OPS = ts(COLONCOLON, DOT, DOTDOT, DOTDOTDOT, DOTDOTEQ) val SPACE_AROUND_OPS = TokenSet.andNot(RS_OPERATORS, NO_SPACE_AROUND_OPS) val UNARY_OPS = ts(MINUS, MUL, EXCL, AND, ANDAND) diff --git a/src/main/kotlin/org/rust/lang/core/psi/RsTokenType.kt b/src/main/kotlin/org/rust/lang/core/psi/RsTokenType.kt index 7103ef306b0..d5512c7be93 100644 --- a/src/main/kotlin/org/rust/lang/core/psi/RsTokenType.kt +++ b/src/main/kotlin/org/rust/lang/core/psi/RsTokenType.kt @@ -40,7 +40,7 @@ val RS_KEYWORDS = tokenSetOf( ) val RS_OPERATORS = tokenSetOf( - AND, ANDEQ, ARROW, FAT_ARROW, SHA, COLON, COLONCOLON, COMMA, DIV, DIVEQ, DOT, DOTDOT, DOTDOTDOT, EQ, EQEQ, EXCL, + AND, ANDEQ, ARROW, FAT_ARROW, SHA, COLON, COLONCOLON, COMMA, DIV, DIVEQ, DOT, DOTDOT, DOTDOTDOT, DOTDOTEQ, EQ, EQEQ, EXCL, EXCLEQ, GT, LT, MINUS, MINUSEQ, MUL, MULEQ, OR, OREQ, PLUS, PLUSEQ, REM, REMEQ, SEMICOLON, XOR, XOREQ, Q, AT, DOLLAR, GTGTEQ, GTGT, GTEQ, LTLTEQ, LTLT, LTEQ, OROR, ANDAND ) diff --git a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt index f0525af0f79..62a0bbb0afc 100644 --- a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt +++ b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt @@ -1248,7 +1248,7 @@ private class RsFnInferenceContext( private fun inferRangeType(expr: RsRangeExpr): Ty { val el = expr.exprList val dot2 = expr.dotdot - val dot3 = expr.dotdotdot + val dot3 = expr.dotdotdot ?: expr.dotdoteq val (rangeName, indexType) = when { dot2 != null && el.size == 0 -> "RangeFull" to null diff --git a/src/test/kotlin/org/rust/ide/formatter/RsFormatterTest.kt b/src/test/kotlin/org/rust/ide/formatter/RsFormatterTest.kt index efb1e21b984..14b0a14c84f 100644 --- a/src/test/kotlin/org/rust/ide/formatter/RsFormatterTest.kt +++ b/src/test/kotlin/org/rust/ide/formatter/RsFormatterTest.kt @@ -309,11 +309,41 @@ class RsFormatterTest : RsFormatterTestBase() { fn main() { if let - 10 .. - 1 = - 8 {} if let - 10 ... - 1 = - 8 {} + if let - 10 ..= - 1 = - 8 {} } """, """ fn main() { if let -10..-1 = -8 {} if let -10...-1 = -8 {} + if let -10..=-1 = -8 {} + } + """) + + fun `test ranges`() = doTextTest(""" + fn main() { + let r = .. ; + let r = .. 1; + let r = 0 .. ; + let r = 0 .. 1; + + let r = ... 1; + let r = 0 ... 1; + + let r = ..= 1; + let r = 0 ..= 1; + } + """, """ + fn main() { + let r = ..; + let r = ..1; + let r = 0..; + let r = 0..1; + + let r = ...1; + let r = 0...1; + + let r = ..=1; + let r = 0..=1; } """) diff --git a/src/test/kotlin/org/rust/lang/core/type/RsNumericLiteralTypeInferenceTest.kt b/src/test/kotlin/org/rust/lang/core/type/RsNumericLiteralTypeInferenceTest.kt index b981b3aa4a8..6bf7a852a0e 100644 --- a/src/test/kotlin/org/rust/lang/core/type/RsNumericLiteralTypeInferenceTest.kt +++ b/src/test/kotlin/org/rust/lang/core/type/RsNumericLiteralTypeInferenceTest.kt @@ -424,6 +424,16 @@ class RsNumericLiteralTypeInferenceTest : RsTypificationTestBase() { } """) + fun `test unify match pattern inclusive range new syntax`() = testExpr(""" + fn main() { + match 0u8 { + 0..=1 => {}, + //^ u8 + _ => {}, + }; + } + """) + fun `test unify match pattern exclusive range`() = testExpr(""" fn main() { match 0u8 { diff --git a/src/test/kotlin/org/rust/lang/core/type/RsStdlibExpressionTypeInferenceTest.kt b/src/test/kotlin/org/rust/lang/core/type/RsStdlibExpressionTypeInferenceTest.kt index f4bc54c70c3..611986125e3 100644 --- a/src/test/kotlin/org/rust/lang/core/type/RsStdlibExpressionTypeInferenceTest.kt +++ b/src/test/kotlin/org/rust/lang/core/type/RsStdlibExpressionTypeInferenceTest.kt @@ -74,6 +74,15 @@ class RsStdlibExpressionTypeInferenceTest : RsTypificationTestBase() { } """) + fun `test RangeToInclusive new syntax`() = stubOnlyTypeInfer(""" + //- main.rs + fn main() { + let x = ..=42u16; + x + //^ RangeToInclusive + } + """) + fun `test RangeInclusive 1`() = stubOnlyTypeInfer(""" //- main.rs fn main() { @@ -92,6 +101,15 @@ class RsStdlibExpressionTypeInferenceTest : RsTypificationTestBase() { } """) + fun `test RangeInclusive new syntax`() = stubOnlyTypeInfer(""" + //- main.rs + fn main() { + let x = 0..=42u16; + x + //^ RangeInclusive + } + """) + fun `test vec!`() = stubOnlyTypeInfer(""" //- main.rs fn main() { diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.rs b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.rs index c3e95104784..a22581c2f54 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.rs +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.rs @@ -7,6 +7,9 @@ fn main() { r = 1...10; r = 1 ... 10; r = ... 10; + r = 1..=10; + r = 1 ..= 10; + r = ..= 10; for i in 0.. { 2 diff --git a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.txt b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.txt index 1010a54186e..032c5165eef 100644 --- a/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.txt +++ b/src/test/resources/org/rust/lang/core/parser/fixtures/complete/ranges.txt @@ -148,6 +148,58 @@ FILE RsLitExprImpl(LIT_EXPR) PsiElement(INTEGER_LITERAL)('10') PsiElement(;)(';') + PsiWhiteSpace('\n ') + RsExprStmtImpl(EXPR_STMT) + RsBinaryExprImpl(BINARY_EXPR) + RsPathExprImpl(PATH_EXPR) + RsPathImpl(PATH) + PsiElement(identifier)('r') + PsiWhiteSpace(' ') + RsBinaryOpImpl(BINARY_OP) + PsiElement(=)('=') + PsiWhiteSpace(' ') + RsRangeExprImpl(RANGE_EXPR) + RsLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiElement(..=)('..=') + RsLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('10') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + RsExprStmtImpl(EXPR_STMT) + RsBinaryExprImpl(BINARY_EXPR) + RsPathExprImpl(PATH_EXPR) + RsPathImpl(PATH) + PsiElement(identifier)('r') + PsiWhiteSpace(' ') + RsBinaryOpImpl(BINARY_OP) + PsiElement(=)('=') + PsiWhiteSpace(' ') + RsRangeExprImpl(RANGE_EXPR) + RsLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + PsiElement(..=)('..=') + PsiWhiteSpace(' ') + RsLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('10') + PsiElement(;)(';') + PsiWhiteSpace('\n ') + RsExprStmtImpl(EXPR_STMT) + RsBinaryExprImpl(BINARY_EXPR) + RsPathExprImpl(PATH_EXPR) + RsPathImpl(PATH) + PsiElement(identifier)('r') + PsiWhiteSpace(' ') + RsBinaryOpImpl(BINARY_OP) + PsiElement(=)('=') + PsiWhiteSpace(' ') + RsRangeExprImpl(RANGE_EXPR) + PsiElement(..=)('..=') + PsiWhiteSpace(' ') + RsLitExprImpl(LIT_EXPR) + PsiElement(INTEGER_LITERAL)('10') + PsiElement(;)(';') PsiWhiteSpace('\n\n ') RsForExprImpl(FOR_EXPR) PsiElement(for)('for')