Skip to content

Commit

Permalink
GRAM&ANN&TY: Annotate if let guard syntax as experimental
Browse files Browse the repository at this point in the history
  • Loading branch information
mchernyavsky committed Sep 27, 2021
1 parent 9ccb895 commit 23b4c4e
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/main/grammars/RustParser.bnf
Expand Up @@ -1247,7 +1247,7 @@ MatchArm ::= OuterAttr* TopPat MatchArmGuard? '=>' StmtModeExpr (',' | (&'}' | <
pin = 2
implements = [ "org.rust.lang.core.psi.ext.RsOuterAttributeOwner" ]
}
MatchArmGuard ::= if AnyExpr
MatchArmGuard ::= if (AnyExpr | IfLetCondition)

private MatchArm_with_recover ::= !'}' MatchArm {
pin = 1
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/org/rust/ide/annotator/RsErrorAnnotator.kt
Expand Up @@ -67,6 +67,7 @@ class RsErrorAnnotator : AnnotatorBase(), HighlightRangeExtension {
override fun visitImplItem(o: RsImplItem) = checkImpl(rsHolder, o)
override fun visitLabel(o: RsLabel) = checkLabel(rsHolder, o)
override fun visitLifetime(o: RsLifetime) = checkLifetime(rsHolder, o)
override fun visitMatchArmGuard(o: RsMatchArmGuard) = checkMatchArmGuard(rsHolder, o)
override fun visitModDeclItem(o: RsModDeclItem) = checkModDecl(rsHolder, o)
override fun visitModItem(o: RsModItem) = checkDuplicates(rsHolder, o)
override fun visitUseSpeck(o: RsUseSpeck) = checkUseSpeck(rsHolder, o)
Expand Down Expand Up @@ -689,6 +690,12 @@ class RsErrorAnnotator : AnnotatorBase(), HighlightRangeExtension {
}
}

private fun checkMatchArmGuard(holder: RsAnnotationHolder, guard: RsMatchArmGuard) {
if (guard.let != null) {
IF_LET_GUARD.check(holder, guard, "if let guard")
}
}

private fun checkModDecl(holder: RsAnnotationHolder, modDecl: RsModDeclItem) {
checkDuplicates(holder, modDecl)
val pathAttribute = modDecl.pathAttribute
Expand Down
Expand Up @@ -12,29 +12,32 @@ import org.rust.lang.core.psi.RsExpr
import org.rust.lang.core.psi.RsIfExpr
import org.rust.lang.core.psi.RsMatchArmGuard
import org.rust.lang.core.psi.RsPsiFactory
import org.rust.lang.core.psi.ext.parentMatchArm
import org.rust.lang.core.psi.ext.ancestorStrict
import org.rust.lang.core.psi.ext.parentMatchArm

class MoveGuardToMatchArmIntention : RsElementBaseIntentionAction<MoveGuardToMatchArmIntention.Context>() {
override fun getText(): String = "Move guard inside the match arm"
override fun getFamilyName(): String = text

data class Context(
val guard: RsMatchArmGuard,
val guardExpr: RsExpr,
val armBody: RsExpr
)

override fun findApplicableContext(project: Project, editor: Editor, element: PsiElement): Context? {
val guard = element.ancestorStrict<RsMatchArmGuard>() ?: return null
if (guard.let != null) return null // TODO: support `if let guard`
val guardExpr = guard.expr ?: return null
val armBody = guard.parentMatchArm.expr ?: return null
return Context(guard, armBody)
return Context(guard, guardExpr, armBody)
}

override fun invoke(project: Project, editor: Editor, ctx: Context) {
val (guard, oldBody) = ctx
val (guard, guardExpr, oldBody) = ctx
val caretOffsetInGuard = editor.caretModel.offset - guard.textOffset
val psiFactory = RsPsiFactory(project)
var newBody = psiFactory.createIfExpression(guard.expr, oldBody)
var newBody = psiFactory.createIfExpression(guardExpr, oldBody)
newBody = oldBody.replace(newBody) as RsIfExpr
guard.delete()
editor.caretModel.moveToOffset(newBody.textOffset + caretOffsetInGuard)
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt
Expand Up @@ -624,6 +624,7 @@ class CFGBuilder(
}

override fun visitMatchArmGuard(guard: RsMatchArmGuard) {
// TODO: support `if let guard`
val conditionExit = process(guard.expr, pred)
finishWithAstNode(guard, conditionExit)
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/kotlin/org/rust/lang/core/dfa/ExprUseWalker.kt
Expand Up @@ -446,7 +446,10 @@ class ExprUseWalker(private val delegate: Delegate, private val mc: MemoryCatego

private fun walkArm(discriminantCmt: Cmt, arm: RsMatchArm, mode: MatchMode) {
arm.patList.forEach { walkPat(discriminantCmt, it, mode) }
arm.matchArmGuard?.let { consumeExpr(it.expr) }
val guard = arm.matchArmGuard
if (guard?.let == null) { // TODO: support `if let guard`
guard?.expr?.let { consumeExpr(it) }
}
arm.expr?.let { consumeExpr(it) }
}

Expand Down
Expand Up @@ -104,6 +104,6 @@ class RustParserDefinition : ParserDefinition {
/**
* Should be increased after any change of parser rules
*/
const val PARSER_VERSION: Int = LEXER_VERSION + 33
const val PARSER_VERSION: Int = LEXER_VERSION + 34
}
}
Expand Up @@ -883,7 +883,8 @@ class RsTypeInferenceWalker(
for (arm in arms) {
arm.pat.extractBindings(matchingExprTy)
arm.expr?.inferType(expected)
arm.matchArmGuard?.expr?.inferType(TyBool.INSTANCE)
val expectedGuardTy = if (arm.matchArmGuard?.let == null) TyBool.INSTANCE else null
arm.matchArmGuard?.expr?.inferType(expectedGuardTy)
}

return getMoreCompleteType(arms.mapNotNull { it.expr?.let(ctx::getExprType) })
Expand Down
27 changes: 27 additions & 0 deletions src/test/kotlin/org/rust/ide/annotator/RsErrorAnnotatorTest.kt
Expand Up @@ -3004,6 +3004,33 @@ class RsErrorAnnotatorTest : RsAnnotatorTestBase(RsErrorAnnotator::class) {
}
""")

@MockRustcVersion("1.47.0")
fun `test if let guard E0658 1`() = checkErrors("""
fn main() {
fn main() {
let xs = vec![0i32];
match xs.len() {
1 <error descr="if let guard is experimental [E0658]">if let Some(x) = xs.iter().next()</error> => {}
_ => unreachable!(),
}
}
}
""")

@MockRustcVersion("1.47.0-nightly")
fun `test if let guard E0658 2`() = checkErrors("""
#![feature(if_let_guard)]
fn main() {
fn main() {
let xs = vec![0i32];
match xs.len() {
1 if let Some(x) = xs.iter().next() => {}
_ => unreachable!(),
}
}
}
""")

@MockRustcVersion("1.0.0-nightly")
fun `test stable attr on invalid owner E0132`() = checkErrors("""
#![feature(start)]
Expand Down
Expand Up @@ -11,4 +11,10 @@ fn main() {
| 1 => 0,
| _ => 42,
};

match x {
0 => 0,
1 if 1 < 2 => 1,
2 if let Some(_) = Some(42) => 42
};
}
Expand Up @@ -125,5 +125,89 @@ FILE
PsiWhiteSpace('\n ')
PsiElement(})('}')
PsiElement(;)(';')
PsiWhiteSpace('\n\n ')
RsExprStmtImpl(EXPR_STMT)
RsMatchExprImpl(MATCH_EXPR)
PsiElement(match)('match')
PsiWhiteSpace(' ')
RsPathExprImpl(PATH_EXPR)
RsPathImpl(PATH)
PsiElement(identifier)('x')
PsiWhiteSpace(' ')
RsMatchBodyImpl(MATCH_BODY)
PsiElement({)('{')
PsiWhiteSpace('\n ')
RsMatchArmImpl(MATCH_ARM)
RsPatConstImpl(PAT_CONST)
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('0')
PsiWhiteSpace(' ')
PsiElement(=>)('=>')
PsiWhiteSpace(' ')
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('0')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
RsMatchArmImpl(MATCH_ARM)
RsPatConstImpl(PAT_CONST)
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
RsMatchArmGuardImpl(MATCH_ARM_GUARD)
PsiElement(if)('if')
PsiWhiteSpace(' ')
RsBinaryExprImpl(BINARY_EXPR)
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
RsBinaryOpImpl(BINARY_OP)
PsiElement(<)('<')
PsiWhiteSpace(' ')
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('2')
PsiWhiteSpace(' ')
PsiElement(=>)('=>')
PsiWhiteSpace(' ')
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('1')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
RsMatchArmImpl(MATCH_ARM)
RsPatConstImpl(PAT_CONST)
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('2')
PsiWhiteSpace(' ')
RsMatchArmGuardImpl(MATCH_ARM_GUARD)
PsiElement(if)('if')
PsiWhiteSpace(' ')
PsiElement(let)('let')
PsiWhiteSpace(' ')
RsPatTupleStructImpl(PAT_TUPLE_STRUCT)
RsPathImpl(PATH)
PsiElement(identifier)('Some')
PsiElement(()('(')
RsPatWildImpl(PAT_WILD)
PsiElement(_)('_')
PsiElement())(')')
PsiWhiteSpace(' ')
PsiElement(=)('=')
PsiWhiteSpace(' ')
RsCallExprImpl(CALL_EXPR)
RsPathExprImpl(PATH_EXPR)
RsPathImpl(PATH)
PsiElement(identifier)('Some')
RsValueArgumentListImpl(VALUE_ARGUMENT_LIST)
PsiElement(()('(')
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('42')
PsiElement())(')')
PsiWhiteSpace(' ')
PsiElement(=>)('=>')
PsiWhiteSpace(' ')
RsLitExprImpl(LIT_EXPR)
PsiElement(INTEGER_LITERAL)('42')
PsiWhiteSpace('\n ')
PsiElement(})('}')
PsiElement(;)(';')
PsiWhiteSpace('\n')
PsiElement(})('}')

0 comments on commit 23b4c4e

Please sign in to comment.