Skip to content

Commit d6b4c3e

Browse files
authored
RangeInfo: unbox switch statement to ensure all branches agree on return information. rdar://32289109 (#9788)
1 parent 5f34931 commit d6b4c3e

File tree

4 files changed

+107
-31
lines changed

4 files changed

+107
-31
lines changed

include/swift/IDE/Utils.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,24 @@ enum class OrphanKind : int8_t {
246246
Continue,
247247
};
248248

249-
typedef llvm::PointerIntPair<TypeBase*, 1, bool> ReturnTyAndWhetherExit;
249+
enum class ExitState: int8_t {
250+
Positive,
251+
Negative,
252+
Unsure,
253+
};
254+
255+
struct ReturnInfo {
256+
TypeBase* ReturnType;
257+
ExitState Exit;
258+
ReturnInfo(): ReturnInfo(nullptr, ExitState::Unsure) {}
259+
ReturnInfo(TypeBase* ReturnType, ExitState Exit):
260+
ReturnType(ReturnType), Exit(Exit) {}
261+
ReturnInfo(ASTContext &Ctx, ArrayRef<ReturnInfo> Branches);
262+
};
250263

251264
struct ResolvedRangeInfo {
252265
RangeKind Kind;
253-
ReturnTyAndWhetherExit ExitInfo;
266+
ReturnInfo ExitInfo;
254267
CharSourceRange Content;
255268
bool HasSingleEntry;
256269
bool ThrowingUnhandledError;
@@ -263,7 +276,7 @@ struct ResolvedRangeInfo {
263276
DeclContext* RangeContext;
264277
Expr* CommonExprParent;
265278

266-
ResolvedRangeInfo(RangeKind Kind, ReturnTyAndWhetherExit ExitInfo,
279+
ResolvedRangeInfo(RangeKind Kind, ReturnInfo ExitInfo,
267280
CharSourceRange Content, DeclContext* RangeContext,
268281
Expr *CommonExprParent, bool HasSingleEntry,
269282
bool ThrowingUnhandledError,
@@ -279,13 +292,14 @@ struct ResolvedRangeInfo {
279292
RangeContext(RangeContext),
280293
CommonExprParent(CommonExprParent) {}
281294
ResolvedRangeInfo(CharSourceRange Content) :
282-
ResolvedRangeInfo(RangeKind::Invalid, {nullptr, false}, Content,
295+
ResolvedRangeInfo(RangeKind::Invalid, {nullptr, ExitState::Unsure}, Content,
283296
nullptr, /*Commom Expr Parent*/nullptr,
284297
/*Single entry*/true, /*unhandled error*/false,
285298
OrphanKind::None, {}, {}, {}) {}
299+
ResolvedRangeInfo(): ResolvedRangeInfo(CharSourceRange()) {}
286300
void print(llvm::raw_ostream &OS);
287-
bool exit() const { return ExitInfo.getInt(); }
288-
Type getType() const { return ExitInfo.getPointer(); }
301+
ExitState exit() const { return ExitInfo.Exit; }
302+
Type getType() const { return ExitInfo.ReturnType; }
289303
};
290304

291305
class RangeResolver : public SourceEntityWalker {

lib/IDE/SwiftSourceDocInfo.cpp

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,14 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
229229

230230
OS << "<Content>" << Content.str() << "</Content>\n";
231231

232-
if (auto Ty = ExitInfo.getPointer()) {
232+
if (auto Ty = getType()) {
233233
OS << "<Type>";
234234
Ty->print(OS);
235235
OS << "</Type>";
236-
if (ExitInfo.getInt()) {
237-
OS << "<Exit>true</Exit>";
236+
switch(exit()) {
237+
case ExitState::Positive: OS << "<Exit>true</Exit>"; break;
238+
case ExitState::Unsure: OS << "<Exit>unsure</Exit>"; break;
239+
case ExitState::Negative: OS << "<Exit>false</Exit>"; break;
238240
}
239241
OS << "\n";
240242
}
@@ -335,6 +337,23 @@ static bool hasUnhandledError(ArrayRef<ASTNode> Nodes) {
335337
});
336338
}
337339

340+
ReturnInfo::
341+
ReturnInfo(ASTContext &Ctx, ArrayRef<ReturnInfo> Branches):
342+
ReturnType(Ctx.TheErrorType.getPointer()), Exit(ExitState::Unsure) {
343+
std::set<TypeBase*> AllTypes;
344+
std::set<ExitState> AllExitStates;
345+
for (auto I : Branches) {
346+
AllTypes.insert(I.ReturnType);
347+
AllExitStates.insert(I.Exit);
348+
}
349+
if (AllTypes.size() == 1) {
350+
ReturnType = *AllTypes.begin();
351+
}
352+
if (AllExitStates.size() == 1) {
353+
Exit = *AllExitStates.begin();
354+
}
355+
}
356+
338357
struct RangeResolver::Implementation {
339358
SourceFile &File;
340359
ASTContext &Ctx;
@@ -394,10 +413,10 @@ struct RangeResolver::Implementation {
394413
std::vector<ASTNode> ContainedASTNodes;
395414

396415
/// Collect the type that an ASTNode should be evaluated to.
397-
ReturnTyAndWhetherExit resolveNodeType(ASTNode N, RangeKind Kind) {
416+
ReturnInfo resolveNodeType(ASTNode N, RangeKind Kind) {
398417
auto *VoidTy = Ctx.getVoidDecl()->getDeclaredInterfaceType().getPointer();
399418
if (N.isNull())
400-
return {VoidTy, false};
419+
return {VoidTy, ExitState::Negative};
401420
switch(Kind) {
402421
case RangeKind::Invalid:
403422
case RangeKind::SingleDecl:
@@ -406,7 +425,7 @@ struct RangeResolver::Implementation {
406425

407426
// For a single expression, its type is apparent.
408427
case RangeKind::SingleExpression:
409-
return {N.get<Expr*>()->getType().getPointer(), false};
428+
return {N.get<Expr*>()->getType().getPointer(), ExitState::Negative};
410429

411430
// For statements, we either resolve to the returning type or Void.
412431
case RangeKind::SingleStatement:
@@ -415,9 +434,7 @@ struct RangeResolver::Implementation {
415434
if (auto RS = dyn_cast<ReturnStmt>(N.get<Stmt*>())) {
416435
return {
417436
resolveNodeType(RS->hasResult() ? RS->getResult() : nullptr,
418-
RangeKind::SingleExpression).getPointer(),
419-
true
420-
};
437+
RangeKind::SingleExpression).ReturnType, ExitState::Positive };
421438
}
422439

423440
// Unbox the brace statement to find its type.
@@ -430,22 +447,26 @@ struct RangeResolver::Implementation {
430447

431448
// Unbox the if statement to find its type.
432449
if (auto *IS = dyn_cast<IfStmt>(N.get<Stmt*>())) {
433-
auto ThenTy = resolveNodeType(IS->getThenStmt(),
434-
RangeKind::SingleStatement);
435-
auto ElseTy = resolveNodeType(IS->getElseStmt(),
436-
RangeKind::SingleStatement);
437-
438-
// If two branches agree on the return type, return that type.
439-
if (ThenTy.getPointer()->isEqual(ElseTy.getPointer()) &&
440-
ThenTy.getInt() == ElseTy.getInt())
441-
return ThenTy;
442-
443-
// Otherwise, return the error type.
444-
return {Ctx.TheErrorType.getPointer(), false};
450+
llvm::SmallVector<ReturnInfo, 2> Branches;
451+
Branches.push_back(resolveNodeType(IS->getThenStmt(),
452+
RangeKind::SingleStatement));
453+
Branches.push_back(resolveNodeType(IS->getElseStmt(),
454+
RangeKind::SingleStatement));
455+
return {Ctx, Branches};
456+
}
457+
458+
// Unbox switch statement to find return information.
459+
if (auto *SWS = dyn_cast<SwitchStmt>(N.get<Stmt*>())) {
460+
llvm::SmallVector<ReturnInfo, 4> Branches;
461+
for (auto *CS : SWS->getCases()) {
462+
Branches.push_back(resolveNodeType(CS->getBody(),
463+
RangeKind::SingleStatement));
464+
}
465+
return {Ctx, Branches};
445466
}
446467
}
447468
// For other statements, the type should be void.
448-
return {VoidTy, false};
469+
return {VoidTy, ExitState::Negative};
449470
}
450471
}
451472
}
@@ -480,7 +501,8 @@ struct RangeResolver::Implementation {
480501
llvm::makeArrayRef(ReferencedDecls));
481502
else {
482503
assert(Node.is<Decl*>());
483-
return ResolvedRangeInfo(RangeKind::SingleDecl, {nullptr, false}, Content,
504+
return ResolvedRangeInfo(RangeKind::SingleDecl,
505+
ReturnInfo(), Content,
484506
getImmediateContext(),
485507
/*Common Parent Expr*/nullptr,
486508
SingleEntry,
@@ -540,7 +562,7 @@ struct RangeResolver::Implementation {
540562
if (!hasResult() && !Node.isImplicit() && nodeContainSelection(Node)) {
541563
if (auto Parent = Node.is<Expr*>() ? Node.get<Expr*>() : nullptr) {
542564
Result = {
543-
RangeKind::PartOfExpression, {nullptr, false}, Content,
565+
RangeKind::PartOfExpression, ReturnInfo(), Content,
544566
getImmediateContext(),
545567
Parent,
546568
hasSingleEntryPoint(ContainedASTNodes),

test/IDE/range_info_branches.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,47 @@ func foo1(_ a: Bool) {
2121
}
2222
}
2323

24+
enum MyEnum {
25+
case Case1
26+
case Case2
27+
case Case3
28+
}
29+
30+
func foo2(_ e : MyEnum) -> Int {
31+
switch e {
32+
case .Case1:
33+
break
34+
case .Case2:
35+
break
36+
case .Case3:
37+
break
38+
}
39+
switch e {
40+
case .Case1:
41+
return 1
42+
case .Case2:
43+
return 2
44+
case .Case3:
45+
return 3
46+
}
47+
switch e {
48+
case .Case1:
49+
return 1
50+
case .Case2:
51+
return 2
52+
case .Case3:
53+
break
54+
}
55+
}
56+
2457
// RUN: %target-swift-ide-test -range -pos=2:1 -end-pos 5:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-ERR
2558
// RUN: %target-swift-ide-test -range -pos=6:1 -end-pos 10:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-INT
2659
// RUN: %target-swift-ide-test -range -pos=14:1 -end-pos 14:10 -source-filename %s | %FileCheck %s -check-prefix=CHECK-VOID-NO-RETURN
2760
// RUN: %target-swift-ide-test -range -pos=15:1 -end-pos 16:10 -source-filename %s | %FileCheck %s -check-prefix=CHECK-VOID-NO-RETURN
2861
// RUN: %target-swift-ide-test -range -pos=17:1 -end-pos 21:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-VOID-RETURN
62+
// RUN: %target-swift-ide-test -range -pos=31:1 -end-pos 38:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-SWITCH1
63+
// RUN: %target-swift-ide-test -range -pos=39:1 -end-pos 46:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-SWITCH2
64+
// RUN: %target-swift-ide-test -range -pos=47:1 -end-pos 54:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-SWITCH3
2965

3066
// CHECK-ERR: <Type><<error type>></Type>
3167
// CHECK-ERR-NOT: <Exit>true</Exit>
@@ -38,3 +74,7 @@ func foo1(_ a: Bool) {
3874

3975
// CHECK-VOID-RETURN: <Type>Void</Type>
4076
// CHECK-VOID-RETURN: <Exit>true</Exit>
77+
78+
// CHECK-SWITCH1: <Type>Void</Type><Exit>false</Exit>
79+
// CHECK-SWITCH2: <Type>Int</Type><Exit>true</Exit>
80+
// CHECK-SWITCH3: <Type><<error type>></Type><Exit>unsure</Exit>

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ static void resolveRange(SwiftLangSupport &Lang,
13461346
case RangeKind::SingleExpression: {
13471347
SmallString<64> SS;
13481348
llvm::raw_svector_ostream OS(SS);
1349-
Info.ExitInfo.getPointer()->print(OS);
1349+
Info.ExitInfo.ReturnType->print(OS);
13501350
Result.ExprType = OS.str();
13511351
Receiver(Result);
13521352
return;

0 commit comments

Comments
 (0)