mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
remove the mixed concept that was SILFileLocation. Also add support for a third type of underlying storage that will be used for deserialized debug lcoations from textual SIL. NFC <rdar://problem/22706994>
165 lines
5.4 KiB
C++
165 lines
5.4 KiB
C++
//===--- 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<typename...T, typename...U>
|
|
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
|
|
U &&...args) {
|
|
Context.Diags.diagnose(loc,
|
|
diag, std::forward<U>(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<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 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 || 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void diagnoseReturn(const SILInstruction *I, ASTContext &Context) {
|
|
auto *TI = dyn_cast<TermInst>(I);
|
|
if (!TI || !(isa<BranchInst>(TI) || isa<ReturnInst>(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<ReturnLocation>())
|
|
diagnose(Context, L.getSourceLoc(), diag::return_from_noreturn);
|
|
if (L.is<ImplicitReturnLocation>())
|
|
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<BuiltinInst>(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<IntegerLiteralInst>(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();
|
|
}
|