@@ -117,51 +117,63 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
117117 override string toString ( ) { result = "boolean(" + value + ")" }
118118}
119119
120- /** Holds if `pat` is guaranteed to match at the point in the AST where it occurs. */
121- pragma [ nomagic]
122- private predicate isExhaustiveMatch ( Pat pat ) {
123- (
124- pat instanceof WildcardPat
125- or
126- pat = any ( IdentPat ip | not ip .hasPat ( ) and ip = any ( Variable v ) .getPat ( ) )
127- or
128- pat instanceof RestPat
129- or
130- // `let` statements without an `else` branch must be exhaustive
131- pat = any ( LetStmt let | not let .hasLetElse ( ) ) .getPat ( )
132- or
133- // `match` expressions must be exhaustive, so last arm cannot fail
134- pat = any ( MatchExpr me ) .getLastArm ( ) .getPat ( )
135- or
136- // macro invocations are exhaustive if their expansion is
137- pat = any ( MacroPat mp | isExhaustiveMatch ( mp .getMacroCall ( ) .getExpanded ( ) ) )
138- or
139- // parameter patterns must be exhaustive
140- pat = any ( Param p ) .getPat ( )
141- ) and
142- not pat = any ( ForExpr for ) .getPat ( ) // workaround until `for` loops are desugared
120+ /**
121+ * Holds if `pat` can not _itself_ be the cause of a pattern match failure. This
122+ * does not mean that `pat` is irrefutable, as its children might be the cause
123+ * of a failure.
124+ */
125+ private predicate canCauseMatchFailure ( Pat pat ) {
126+ pat instanceof LiteralPat
143127 or
144- exists ( Pat parent | isExhaustiveMatch ( parent ) |
145- pat = parent .( BoxPat ) .getPat ( )
146- or
147- pat = parent .( IdentPat ) .getPat ( )
148- or
149- pat = parent .( MacroPat ) .getMacroCall ( ) .getExpanded ( )
150- or
151- pat = parent .( ParenPat ) .getPat ( )
152- or
153- pat = parent .( RecordPat ) .getRecordPatFieldList ( ) .getField ( _) .getPat ( )
154- or
155- pat = parent .( RefPat ) .getPat ( )
156- or
157- pat = parent .( TuplePat ) .getAField ( )
128+ // NOTE: a `TupleStructPat` can cause a failure if it resolves to a an enum
129+ // variant but not when it resolves to a tuple struct.
130+ pat instanceof TupleStructPat
131+ or
132+ pat instanceof SlicePat
133+ or
134+ pat instanceof PathPat
135+ or
136+ pat instanceof OrPat
137+ or
138+ // Identifier patterns that are in fact path patterns can cause failures. For
139+ // instance `None`. Only if a `@ ...` part is present can we be sure that it's
140+ // an actual identifier pattern.
141+ pat = any ( IdentPat p | not p .hasPat ( ) )
142+ }
143+
144+ /**
145+ * Holds if `pat` is guaranteed to match at the point in the AST where it occurs
146+ * due to Rust's exhaustiveness checks.
147+ */
148+ private predicate guaranteedMatchPosition ( Pat pat ) {
149+ // `let` statements without an `else` branch must match
150+ pat = any ( LetStmt let | not let .hasLetElse ( ) ) .getPat ( )
151+ or
152+ // `match` expressions must be exhaustive, so last arm must match
153+ pat = any ( MatchExpr me ) .getLastArm ( ) .getPat ( )
154+ or
155+ // parameter patterns must match
156+ pat = any ( Param p ) .getPat ( )
157+ or
158+ exists ( Pat parent | guaranteedMatchPosition ( parent ) |
159+ // propagate to all children except for or patterns
160+ parent = pat .getParentPat ( ) and not parent instanceof OrPat
158161 or
159- pat = parent .( TupleStructPat ) .getAField ( )
162+ // for or patterns only the last child inherits the property
163+ parent .( OrPat ) .getLastPat ( ) = pat
160164 or
161- pat = parent .( OrPat ) .getLastPat ( )
165+ // for macro patterns we propagate to the expanded pattern
166+ parent .( MacroPat ) .getMacroCall ( ) .getExpanded ( ) = pat
162167 )
163168}
164169
170+ private predicate guaranteedMatch ( Pat pat ) {
171+ ( not canCauseMatchFailure ( pat ) or guaranteedMatchPosition ( pat ) ) and
172+ // In `for` loops we use a no-match edge from the pattern to terminate the
173+ // loop, hence we special case and always allow the no-match edge.
174+ not pat = any ( ForExpr for ) .getPat ( )
175+ }
176+
165177/**
166178 * A completion that represents the result of a pattern match.
167179 */
@@ -170,7 +182,7 @@ class MatchCompletion extends TMatchCompletion, ConditionalCompletion {
170182
171183 override predicate isValidForSpecific ( AstNode e ) {
172184 e instanceof Pat and
173- if isExhaustiveMatch ( e ) then value = true else any ( )
185+ if guaranteedMatch ( e ) then value = true else any ( )
174186 or
175187 e instanceof TryExpr and value = true
176188 }
0 commit comments