Skip to content

Commit 07cdc20

Browse files
committed
[Refactoring] Unwrap optional chains in async transform
Remove an optional chain of a success parameter, as it will no longer be optional, similar to how we remove a force unwrap. Note that while this is a locally valid transform within the optional chain, e.g `foo?.x` -> `foo.x`, it may change the type of the overall chain, which could cause errors elsewhere in the code. However this is generally more useful to the user than just leaving `foo` as a placeholder. Note this is only the case when no other optionals are involved in the chain, e.g `foo?.x?.y` -> `foo.x?.y` is completely valid. Resolves rdar://74014826.
1 parent 9def053 commit 07cdc20

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4701,8 +4701,9 @@ class AsyncConverter : private SourceEntityWalker {
47014701
SmallString<0> Buffer;
47024702
llvm::raw_svector_ostream OS;
47034703

4704-
// Decls where any force-unwrap of that decl should be unwrapped, eg. for a
4705-
// previously optional closure paramter has become a non-optional local
4704+
// Decls where any force unwrap or optional chain of that decl should be
4705+
// elided, e.g for a previously optional closure parameter that has become a
4706+
// non-optional local.
47064707
llvm::DenseSet<const Decl *> Unwraps;
47074708
// Decls whose references should be replaced with, either because they no
47084709
// longer exist or are a different type. Any replaced code should ideally be
@@ -4813,11 +4814,19 @@ class AsyncConverter : private SourceEntityWalker {
48134814
OS << PLACEHOLDER_END;
48144815
});
48154816
}
4816-
} else if (auto *FTE = dyn_cast<ForceValueExpr>(E)) {
4817-
if (auto *D = FTE->getReferencedDecl().getDecl()) {
4817+
} else if (isa<ForceValueExpr>(E) || isa<BindOptionalExpr>(E)) {
4818+
// Remove a force unwrap or optional chain of a returned success value,
4819+
// as it will no longer be optional. For force unwraps, this is always a
4820+
// valid transform. For optional chains, it is a locally valid transform
4821+
// within the optional chain e.g foo?.x -> foo.x, but may change the type
4822+
// of the overall chain, which could cause errors elsewhere in the code.
4823+
// However this is generally more useful to the user than just leaving
4824+
// 'foo' as a placeholder. Note this is only the case when no other
4825+
// optionals are involved in the chain, e.g foo?.x?.y -> foo.x?.y is
4826+
// completely valid.
4827+
if (auto *D = E->getReferencedDecl().getDecl()) {
48184828
if (Unwraps.count(D))
4819-
return addCustom(FTE->getStartLoc(),
4820-
FTE->getEndLoc().getAdvancedLoc(1),
4829+
return addCustom(E->getStartLoc(), E->getEndLoc().getAdvancedLoc(1),
48214830
[&]() { OS << newNameFor(D, true); });
48224831
}
48234832
} else if (NestedExprCount == 0) {

test/refactoring/ConvertAsync/convert_params_single.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,35 @@ withError { res, err in
406406
print("got result \(res!)")
407407
print("after")
408408
}
409+
410+
// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s
411+
withError { str, err in
412+
print("before")
413+
guard err == nil else { return }
414+
_ = str!.count
415+
_ = str?.count
416+
_ = str!.count.bitWidth
417+
_ = str?.count.bitWidth
418+
_ = (str?.count.bitWidth)!
419+
_ = str!.first?.isWhitespace
420+
_ = str?.first?.isWhitespace
421+
_ = (str?.first?.isWhitespace)!
422+
print("after")
423+
}
424+
// UNWRAPPING: let str = try await withError()
425+
// UNWRAPPING-NEXT: print("before")
426+
// UNWRAPPING-NEXT: _ = str.count
427+
// UNWRAPPING-NEXT: _ = str.count
428+
// UNWRAPPING-NEXT: _ = str.count.bitWidth
429+
// UNWRAPPING-NEXT: _ = str.count.bitWidth
430+
431+
// Note this transform results in invalid code as str.count.bitWidth is no
432+
// longer optional, but arguably it's more useful than leaving str as a
433+
// placeholder. In general, the tranform we perform here is locally valid
434+
// within the optional chain, but may change the type of the overall chain.
435+
// UNWRAPPING-NEXT: _ = (str.count.bitWidth)!
436+
437+
// UNWRAPPING-NEXT: _ = str.first?.isWhitespace
438+
// UNWRAPPING-NEXT: _ = str.first?.isWhitespace
439+
// UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)!
440+
// UNWRAPPING-NEXT: print("after")

0 commit comments

Comments
 (0)