@@ -811,59 +811,11 @@ class CompletionContextFinder : public ASTWalker {
811
811
812
812
} // end namespace
813
813
814
- // Determine if the target expression is the implicit BinaryExpr generated for
815
- // pattern-matching in a switch/if/guard case (<completion> ~= matchValue).
816
- static bool isForPatternMatch (SolutionApplicationTarget &target) {
817
- if (target.getExprContextualTypePurpose () != CTP_Condition)
818
- return false ;
819
- Expr *condition = target.getAsExpr ();
820
- if (!condition->isImplicit ())
821
- return false ;
822
- if (auto *BE = dyn_cast<BinaryExpr>(condition)) {
823
- Identifier id;
824
- if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(BE->getFn ())) {
825
- id = ODRE->getDecls ().front ()->getBaseIdentifier ();
826
- } else if (auto *DRE = dyn_cast<DeclRefExpr>(BE->getFn ())) {
827
- id = DRE->getDecl ()->getBaseIdentifier ();
828
- }
829
- if (id != target.getDeclContext ()->getASTContext ().Id_MatchOperator )
830
- return false ;
831
- return isa<CodeCompletionExpr>(BE->getLHS ());
832
- }
833
- return false ;
834
- }
835
-
836
- // / Remove any solutions from the provided vector that both require fixes and have a
837
- // / score worse than the best.
814
+ // / Remove any solutions from the provided vector that both require fixes and
815
+ // / have a score worse than the best.
838
816
static void filterSolutions (SolutionApplicationTarget &target,
839
817
SmallVectorImpl<Solution> &solutions,
840
818
CodeCompletionExpr *completionExpr) {
841
- // FIXME: this is only needed because in pattern matching position, the
842
- // code completion expression always becomes an expression pattern, which
843
- // requires the ~= operator to be defined on the type being matched against.
844
- // Pattern matching against an enum doesn't require that however, so valid
845
- // solutions always end up having fixes. This is a problem because there will
846
- // always be a valid solution as well. Optional defines ~= between Optional
847
- // and _OptionalNilComparisonType (which defines a nilLiteral initializer),
848
- // and the matched-against value can implicitly be made Optional if it isn't
849
- // already, so _OptionalNilComparisonType is always a valid solution for the
850
- // completion. That only generates the 'nil' completion, which is rarely what
851
- // the user intends to write in this position and shouldn't be preferred over
852
- // the other formed solutions (which require fixes). We should generate enum
853
- // pattern completions separately, but for now ignore the
854
- // _OptionalNilComparisonType solution.
855
- if (isForPatternMatch (target) && completionExpr) {
856
- solutions.erase (llvm::remove_if (solutions, [&](const Solution &S) {
857
- ASTContext &ctx = S.getConstraintSystem ().getASTContext ();
858
- if (!S.hasType (completionExpr))
859
- return false ;
860
- if (auto ty = S.getResolvedType (completionExpr))
861
- if (auto *NTD = ty->getAnyNominal ())
862
- return NTD->getBaseIdentifier () == ctx.Id_OptionalNilComparisonType ;
863
- return false ;
864
- }), solutions.end ());
865
- }
866
-
867
819
if (solutions.size () <= 1 )
868
820
return ;
869
821
@@ -1286,6 +1238,69 @@ sawSolution(const constraints::Solution &S) {
1286
1238
}
1287
1239
}
1288
1240
1241
+ // / If the code completion variable occurs in a pattern matching position, we
1242
+ // / have an AST that looks like this.
1243
+ // / \code
1244
+ // / (binary_expr implicit type='$T3'
1245
+ // / (overloaded_decl_ref_expr function_ref=compound decls=[
1246
+ // / Swift.(file).~=,
1247
+ // / Swift.(file).Optional extension.~=])
1248
+ // / (tuple_expr implicit type='($T1, (OtherEnum))'
1249
+ // / (code_completion_expr implicit type='$T1')
1250
+ // / (declref_expr implicit decl=swift_ide_test.(file).foo(x:).$match)))
1251
+ // / \endcode
1252
+ // / If the code completion expression occurs in such an AST, return the
1253
+ // / declaration of the \c $match variable, otherwise return \c nullptr.
1254
+ VarDecl *getMatchVarIfInPatternMatch (CodeCompletionExpr *CompletionExpr,
1255
+ ConstraintSystem &CS) {
1256
+ auto &Context = CS.getASTContext ();
1257
+
1258
+ TupleExpr *ArgTuple =
1259
+ dyn_cast_or_null<TupleExpr>(CS.getParentExpr (CompletionExpr));
1260
+ if (!ArgTuple || !ArgTuple->isImplicit () || ArgTuple->getNumElements () != 2 ) {
1261
+ return nullptr ;
1262
+ }
1263
+
1264
+ auto Binary = dyn_cast_or_null<BinaryExpr>(CS.getParentExpr (ArgTuple));
1265
+ if (!Binary || !Binary->isImplicit ()) {
1266
+ return nullptr ;
1267
+ }
1268
+
1269
+ auto CalledOperator = Binary->getFn ();
1270
+ if (!CalledOperator || !CalledOperator->isImplicit ()) {
1271
+ return nullptr ;
1272
+ }
1273
+ // The reference to the ~= operator might be an OverloadedDeclRefExpr or a
1274
+ // DeclRefExpr, depending on how many ~= operators are viable.
1275
+ if (auto Overloaded =
1276
+ dyn_cast_or_null<OverloadedDeclRefExpr>(CalledOperator)) {
1277
+ if (!llvm::all_of (Overloaded->getDecls (), [&Context](ValueDecl *D) {
1278
+ return D->getBaseName () == Context.Id_MatchOperator ;
1279
+ })) {
1280
+ return nullptr ;
1281
+ }
1282
+ } else if (auto Ref = dyn_cast_or_null<DeclRefExpr>(CalledOperator)) {
1283
+ if (Ref->getDecl ()->getBaseName () != Context.Id_MatchOperator ) {
1284
+ return nullptr ;
1285
+ }
1286
+ } else {
1287
+ return nullptr ;
1288
+ }
1289
+
1290
+ auto MatchArg = dyn_cast_or_null<DeclRefExpr>(ArgTuple->getElement (1 ));
1291
+ if (!MatchArg || !MatchArg->isImplicit ()) {
1292
+ return nullptr ;
1293
+ }
1294
+
1295
+ auto MatchVar = MatchArg->getDecl ();
1296
+ if (MatchVar && MatchVar->isImplicit () &&
1297
+ MatchVar->getBaseName () == Context.Id_PatternMatchVar ) {
1298
+ return dyn_cast<VarDecl>(MatchVar);
1299
+ } else {
1300
+ return nullptr ;
1301
+ }
1302
+ }
1303
+
1289
1304
void UnresolvedMemberTypeCheckCompletionCallback::
1290
1305
sawSolution (const constraints::Solution &S) {
1291
1306
GotCallback = true ;
@@ -1295,18 +1310,34 @@ sawSolution(const constraints::Solution &S) {
1295
1310
// If the type couldn't be determined (e.g. because there isn't any context
1296
1311
// to derive it from), let's not attempt to do a lookup since it wouldn't
1297
1312
// produce any useful results anyway.
1298
- if (!ExpectedTy || ExpectedTy->is <UnresolvedType>())
1299
- return ;
1300
-
1301
- // If ExpectedTy is a duplicate of any other result, ignore this solution.
1302
- if (llvm::any_of (Results, [&](const Result &R) {
1303
- return R.ExpectedTy ->isEqual (ExpectedTy);
1304
- })) {
1305
- return ;
1313
+ if (ExpectedTy && !ExpectedTy->is <UnresolvedType>()) {
1314
+ // If ExpectedTy is a duplicate of any other result, ignore this solution.
1315
+ if (!llvm::any_of (ExprResults, [&](const ExprResult &R) {
1316
+ return R.ExpectedTy ->isEqual (ExpectedTy);
1317
+ })) {
1318
+ bool SingleExprBody =
1319
+ isImplicitSingleExpressionReturn (CS, CompletionExpr);
1320
+ ExprResults.push_back ({ExpectedTy, SingleExprBody});
1321
+ }
1306
1322
}
1307
1323
1308
- bool SingleExprBody = isImplicitSingleExpressionReturn (CS, CompletionExpr);
1309
- Results.push_back ({ExpectedTy, SingleExprBody});
1324
+ if (auto MatchVar = getMatchVarIfInPatternMatch (CompletionExpr, CS)) {
1325
+ Type MatchVarType;
1326
+ // If the MatchVar has an explicit type, it's not part of the solution. But
1327
+ // we can look it up in the constraint system directly.
1328
+ if (auto T = S.getConstraintSystem ().getVarType (MatchVar)) {
1329
+ MatchVarType = T;
1330
+ } else {
1331
+ MatchVarType = S.getResolvedType (MatchVar);
1332
+ }
1333
+ if (MatchVarType && !MatchVarType->is <UnresolvedType>()) {
1334
+ if (!llvm::any_of (EnumPatternTypes, [&](const Type &R) {
1335
+ return R->isEqual (MatchVarType);
1336
+ })) {
1337
+ EnumPatternTypes.push_back (MatchVarType);
1338
+ }
1339
+ }
1340
+ }
1310
1341
}
1311
1342
1312
1343
void KeyPathTypeCheckCompletionCallback::sawSolution (
0 commit comments