[Async Refactoring] Better handle known condition paths

If we're going to be classifying nodes in either
an `else if` block or after a `guard` statement
with a known condition path, be more lenient with
unhandled conditions, as we already know what path
they should be classified along.

rdar://78564388
This commit is contained in:
Hamish Knight
2021-06-09 21:27:08 +01:00
parent 107780c5ff
commit 0c21310ab6
4 changed files with 295 additions and 24 deletions

View File

@@ -4933,6 +4933,12 @@ private:
const ParamDecl *ErrParam;
bool IsResultParam;
/// This is set to \c true if we're currently classifying on a known condition
/// path, where \c CurrentBlock is set to the appropriate block. This lets us
/// be more lenient with unhandled conditions as we already know the block
/// we're supposed to be in.
bool IsKnownConditionPath = false;
CallbackClassifier(ClassifiedBlocks &Blocks, const FuncDecl *Callee,
ArrayRef<const ParamDecl *> SuccessParams,
llvm::DenseSet<SwitchStmt *> &HandledSwitches,
@@ -5248,7 +5254,12 @@ private:
// placeholders added (ideally we'd remove them)
// TODO: Remove known conditions and split the `if` statement
if (CallbackConditions.empty()) {
if (IsKnownConditionPath) {
// If we're on a known condition path, we can be lenient as we already
// know what block we're in and can therefore just add the conditional
// straight to it.
CurrentBlock->addNode(Statement);
} else if (CallbackConditions.empty()) {
// Technically this has a similar problem, ie. the else could have
// conditions that should be in either success/error
CurrentBlock->addNode(Statement);
@@ -5311,19 +5322,45 @@ private:
if (ElseStmt) {
if (auto *BS = dyn_cast<BraceStmt>(ElseStmt)) {
setNodes(ElseBlock, ThenBlock, NodesToPrint::inBraceStmt(BS));
// If this is a guard statement, we know that we'll always exit,
// allowing us to classify any additional nodes into the opposite block.
auto AlwaysExits = isa<GuardStmt>(Statement);
setNodes(ElseBlock, ThenBlock, NodesToPrint::inBraceStmt(BS),
AlwaysExits);
} else {
// If we reached here, we should have an else if statement. Given we
// know we're in the else of a known condition, temporarily flip the
// current block, and set that we know what path we're on.
llvm::SaveAndRestore<bool> CondScope(IsKnownConditionPath, true);
llvm::SaveAndRestore<ClassifiedBlock *> BlockScope(CurrentBlock,
ElseBlock);
classifyNodes(ArrayRef<ASTNode>(ElseStmt),
/*endCommentLoc*/ SourceLoc());
}
}
}
/// Adds \p Nodes to \p Block, potentially flipping the current block if we
/// can determine that the nodes being added will cause control flow to leave
/// the scope.
///
/// \param Block The block to add the nodes to.
/// \param OtherBlock The block for the opposing condition path.
/// \param Nodes The nodes to add.
/// \param AlwaysExitsScope Whether the nodes being added always exit the
/// scope, and therefore whether the current block should be flipped.
void setNodes(ClassifiedBlock *Block, ClassifiedBlock *OtherBlock,
NodesToPrint Nodes) {
if (Nodes.hasTrailingReturnOrBreak()) {
CurrentBlock = OtherBlock;
NodesToPrint Nodes, bool AlwaysExitsScope = false) {
// Drop an explicit trailing 'return' or 'break' if we can.
bool HasTrailingReturnOrBreak = Nodes.hasTrailingReturnOrBreak();
if (HasTrailingReturnOrBreak)
Nodes.dropTrailingReturnOrBreakIfPossible();
// If we know we're exiting the scope, we can set IsKnownConditionPath, as
// we know any future nodes should be classified into the other block.
if (HasTrailingReturnOrBreak || AlwaysExitsScope) {
CurrentBlock = OtherBlock;
IsKnownConditionPath = true;
Block->addAllNodes(std::move(Nodes));
} else {
Block->addAllNodes(std::move(Nodes));