static void diagnoseMissingReturn(const UnreachableInst *UI, ASTContext &Context) { const SILBasicBlock *BB = UI->getParent(); const SILFunction *F = BB->getParent(); SILLocation FLoc = F->getLocation(); Type ResTy; if (auto *FD = FLoc.getAsASTNode<FuncDecl>()) { ResTy = FD->getResultType(); } else if (auto *CE = FLoc.getAsASTNode<ClosureExpr>()) { ResTy = CE->getResultType(); } else { llvm_unreachable("unhandled case in MissingReturn"); } bool isNoReturn = F->getLoweredFunctionType()->isNoReturn(); // No action required if the function returns 'Void' or that the // function is marked 'noreturn'. if (ResTy->isVoid() || isNoReturn) return; SILLocation L = UI->getLoc(); assert(L && ResTy); diagnose(Context, L.getEndSourceLoc(), diag::missing_return, ResTy, FLoc.isASTNode<ClosureExpr>() ? 1 : 0); }
static void diagnoseMissingReturn(const UnreachableInst *UI, ASTContext &Context) { const SILBasicBlock *BB = UI->getParent(); const SILFunction *F = BB->getParent(); SILLocation FLoc = F->getLocation(); Type ResTy; BraceStmt *BS; if (auto *FD = FLoc.getAsASTNode<FuncDecl>()) { ResTy = FD->getResultInterfaceType(); BS = FD->getBody(/*canSynthesize=*/false); } else if (auto *CD = FLoc.getAsASTNode<ConstructorDecl>()) { ResTy = CD->getResultInterfaceType(); BS = FD->getBody(); } else if (auto *CE = FLoc.getAsASTNode<ClosureExpr>()) { ResTy = CE->getResultType(); BS = CE->getBody(); } else { llvm_unreachable("unhandled case in MissingReturn"); } SILLocation L = UI->getLoc(); assert(L && ResTy); auto numElements = BS->getNumElements(); if (numElements > 0) { auto element = BS->getElement(numElements - 1); if (auto expr = element.dyn_cast<Expr *>()) { if (expr->getType()->getCanonicalType() == ResTy->getCanonicalType()) { Context.Diags.diagnose( expr->getStartLoc(), diag::missing_return_last_expr, ResTy, FLoc.isASTNode<ClosureExpr>() ? 1 : 0) .fixItInsert(expr->getStartLoc(), "return "); return; } } } auto diagID = F->isNoReturnFunction() ? diag::missing_never_call : diag::missing_return; diagnose(Context, L.getEndSourceLoc(), diagID, ResTy, FLoc.isASTNode<ClosureExpr>() ? 1 : 0); }
static void diagnoseUnreachable(const SILInstruction *I, ASTContext &Context) { if (auto *UI = dyn_cast<UnreachableInst>(I)){ SILLocation L = UI->getLoc(); // Invalid location means that the instruction has been generated by SIL // passes, such as DCE. FIXME: we might want to just introduce a separate // instruction kind, instead of keeping this invariant. // // We also do not want to emit diagnostics for code that was // transparently inlined. We should have already emitted these // diagnostics when we process the callee function prior to // inlining it. if (!L.hasASTLocation() || L.is<MandatoryInlinedLocation>()) return; // The most common case of getting an unreachable instruction is a // missing return statement. In this case, we know that the instruction // location will be the enclosing function. if (L.isASTNode<AbstractFunctionDecl>() || L.isASTNode<ClosureExpr>()) { diagnoseMissingReturn(UI, Context); return; } // A non-exhaustive switch would also produce an unreachable instruction. if (L.isASTNode<SwitchStmt>()) { diagnose(Context, L.getEndSourceLoc(), diag::non_exhaustive_switch); return; } if (auto *Guard = L.getAsASTNode<GuardStmt>()) { diagnose(Context, Guard->getBody()->getEndLoc(), diag::guard_body_must_not_fallthrough); return; } } }