//===--- DataflowDiagnostics.cpp - Emits diagnostics based on SIL analysis ===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILLocation.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILVisitor.h" using namespace swift; template static void diagnose(ASTContext &Context, SourceLoc loc, Diag diag, U &&...args) { Context.Diags.diagnose(loc, diag, std::forward(args)...); } 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()) { ResTy = FD->getResultType(); } else if (auto *CE = FLoc.getAsASTNode()) { 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() ? 1 : 0); } static void diagnoseUnreachable(const SILInstruction *I, ASTContext &Context) { if (auto *UI = dyn_cast(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 || L.is()) 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() || L.isASTNode()) { diagnoseMissingReturn(UI, Context); return; } // A non-exhaustive switch would also produce an unreachable instruction. if (L.isASTNode()) { diagnose(Context, L.getEndSourceLoc(), diag::non_exhaustive_switch); return; } if (auto *Guard = L.getAsASTNode()) { diagnose(Context, Guard->getBody()->getEndLoc(), diag::guard_body_must_not_fallthrough); return; } } } static void diagnoseReturn(const SILInstruction *I, ASTContext &Context) { auto *TI = dyn_cast(I); if (!TI || !(isa(TI) || isa(TI))) return; const SILBasicBlock *BB = TI->getParent(); const SILFunction *F = BB->getParent(); // Warn if we reach a return inside a noreturn function. if (F->getLoweredFunctionType()->isNoReturn()) { SILLocation L = TI->getLoc(); if (L.is()) diagnose(Context, L.getSourceLoc(), diag::return_from_noreturn); if (L.is()) diagnose(Context, L.getSourceLoc(), diag::return_from_noreturn); } } /// \brief Issue diagnostics whenever we see Builtin.static_report(1, ...). static void diagnoseStaticReports(const SILInstruction *I, SILModule &M) { // Find out if we are dealing with Builtin.staticReport(). if (auto *BI = dyn_cast(I)) { const BuiltinInfo &B = BI->getBuiltinInfo(); if (B.ID == BuiltinValueKind::StaticReport) { // Report diagnostic if the first argument has been folded to '1'. OperandValueArrayRef Args = BI->getArguments(); IntegerLiteralInst *V = dyn_cast(Args[0]); if (!V || V->getValue() != 1) return; diagnose(M.getASTContext(), I->getLoc().getSourceLoc(), diag::static_report_error); } } } namespace { class EmitDFDiagnostics : public SILFunctionTransform { virtual ~EmitDFDiagnostics() {} StringRef getName() override { return "Emit Dataflow Diagnostics"; } /// The entry point to the transformation. void run() override { SILModule &M = getFunction()->getModule(); for (auto &BB : *getFunction()) for (auto &I : BB) { diagnoseUnreachable(&I, M.getASTContext()); diagnoseReturn(&I, M.getASTContext()); diagnoseStaticReports(&I, M); } } }; } // end anonymous namespace SILTransform *swift::createEmitDFDiagnostics() { return new EmitDFDiagnostics(); }