mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Detect and fix situations when (force) unwrap is used on a non-optional type, this helps to diagnose invalid unwraps precisely and provide fix-its. Resolves: [SR-8977](https://bugs.swift.org/browse/SR-8977) Resolves: rdar://problem/45218255
637 lines
21 KiB
C++
637 lines
21 KiB
C++
//===--- CSDiagnostics.h - Constraint Diagnostics -------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file provides necessary abstractions for constraint system diagnostics.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef SWIFT_SEMA_CSDIAGNOSTICS_H
|
|
#define SWIFT_SEMA_CSDIAGNOSTICS_H
|
|
|
|
#include "Constraint.h"
|
|
#include "ConstraintSystem.h"
|
|
#include "OverloadChoice.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/DiagnosticEngine.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/SourceLoc.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include <tuple>
|
|
|
|
namespace swift {
|
|
namespace constraints {
|
|
|
|
/// Base class for all of the possible diagnostics,
|
|
/// provides most basic information such as location of
|
|
/// the problem, parent expression and some utility methods.
|
|
class FailureDiagnostic {
|
|
Expr *E;
|
|
ConstraintSystem &CS;
|
|
ConstraintLocator *Locator;
|
|
|
|
/// The original anchor before any simplification.
|
|
Expr *RawAnchor;
|
|
/// Simplified anchor associated with the given locator.
|
|
Expr *Anchor;
|
|
/// Indicates whether locator could be simplified
|
|
/// down to anchor expression.
|
|
bool HasComplexLocator;
|
|
|
|
public:
|
|
FailureDiagnostic(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator)
|
|
: E(expr), CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) {
|
|
std::tie(Anchor, HasComplexLocator) = computeAnchor();
|
|
}
|
|
|
|
virtual ~FailureDiagnostic();
|
|
|
|
/// Try to diagnose a problem given affected expression,
|
|
/// failure location, types and declarations deduced by
|
|
/// constraint system, and other auxiliary information.
|
|
///
|
|
/// \param asNote In ambiguity cases it's beneficial to
|
|
/// produce diagnostic as a note instead of an error if possible.
|
|
///
|
|
/// \returns true If the problem has been successfully diagnosed
|
|
/// and diagnostic message emitted, false otherwise.
|
|
bool diagnose(bool asNote = false);
|
|
|
|
/// Try to produce an error diagnostic for the problem at hand.
|
|
virtual bool diagnoseAsError() = 0;
|
|
|
|
/// Instead of producing an error diagnostic, attempt to
|
|
/// produce a "note" to complement some other diagnostic
|
|
/// e.g. ambiguity error.
|
|
virtual bool diagnoseAsNote();
|
|
|
|
ConstraintSystem &getConstraintSystem() const {
|
|
return CS;
|
|
}
|
|
|
|
Expr *getParentExpr() const { return E; }
|
|
|
|
Expr *getRawAnchor() const { return RawAnchor; }
|
|
|
|
Expr *getAnchor() const { return Anchor; }
|
|
|
|
ConstraintLocator *getLocator() const { return Locator; }
|
|
|
|
Type getType(Expr *expr) const;
|
|
|
|
/// Resolve type variables present in the raw type, if any.
|
|
Type resolveType(Type rawType) const {
|
|
return CS.simplifyType(rawType);
|
|
}
|
|
|
|
template <typename... ArgTypes>
|
|
InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const;
|
|
|
|
protected:
|
|
TypeChecker &getTypeChecker() const { return CS.TC; }
|
|
|
|
DeclContext *getDC() const { return CS.DC; }
|
|
|
|
ASTContext &getASTContext() const { return CS.getASTContext(); }
|
|
|
|
Optional<std::pair<Type, ConversionRestrictionKind>>
|
|
getRestrictionForType(Type type) const {
|
|
for (auto &restriction : CS.ConstraintRestrictions) {
|
|
if (std::get<0>(restriction)->isEqual(type))
|
|
return std::pair<Type, ConversionRestrictionKind>(
|
|
std::get<1>(restriction), std::get<2>(restriction));
|
|
}
|
|
return None;
|
|
}
|
|
|
|
ValueDecl *getResolvedMemberRef(UnresolvedDotExpr *member) {
|
|
auto locator = CS.getConstraintLocator(member, ConstraintLocator::Member);
|
|
return CS.findResolvedMemberRef(locator);
|
|
}
|
|
|
|
Optional<SelectedOverload>
|
|
getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
|
|
if (auto *overload = getResolvedOverload(locator))
|
|
return Optional<SelectedOverload>(
|
|
{overload->Choice, overload->OpenedFullType, overload->ImpliedType});
|
|
return None;
|
|
}
|
|
|
|
/// Retrieve overload choice resolved for given locator
|
|
/// by the constraint solver.
|
|
ResolvedOverloadSetListItem *
|
|
getResolvedOverload(ConstraintLocator *locator) const {
|
|
auto resolvedOverload = CS.getResolvedOverloadSets();
|
|
while (resolvedOverload) {
|
|
if (resolvedOverload->Locator == locator)
|
|
return resolvedOverload;
|
|
resolvedOverload = resolvedOverload->Previous;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// \returns true is locator hasn't been simplified down to expression.
|
|
bool hasComplexLocator() const { return HasComplexLocator; }
|
|
|
|
private:
|
|
/// Compute anchor expression associated with current diagnostic.
|
|
std::pair<Expr *, bool> computeAnchor() const;
|
|
};
|
|
|
|
/// Base class for all of the diagnostics related to generic requirement
|
|
/// failures, provides common information like failed requirement,
|
|
/// declaration where such requirement comes from, etc.
|
|
class RequirementFailure : public FailureDiagnostic {
|
|
protected:
|
|
using PathEltKind = ConstraintLocator::PathElementKind;
|
|
using DiagOnDecl = Diag<DescriptiveDeclKind, DeclName, Type, Type>;
|
|
using DiagInReference = Diag<DescriptiveDeclKind, DeclName, Type, Type, Type>;
|
|
using DiagAsNote = Diag<Type, Type, Type, Type, StringRef>;
|
|
|
|
const ValueDecl *AffectedDecl;
|
|
/// If possible, find application expression associated
|
|
/// with current generic requirement failure, that helps
|
|
/// to diagnose failures related to arguments.
|
|
const ApplyExpr *Apply = nullptr;
|
|
|
|
public:
|
|
RequirementFailure(ConstraintSystem &cs, Expr *expr, RequirementKind kind,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(expr, cs, locator), AffectedDecl(getDeclRef()) {
|
|
assert(locator);
|
|
assert(AffectedDecl);
|
|
|
|
auto path = locator->getPath();
|
|
assert(!path.empty());
|
|
|
|
auto &last = path.back();
|
|
assert(last.getKind() == ConstraintLocator::TypeParameterRequirement);
|
|
assert(static_cast<RequirementKind>(last.getValue2()) == kind);
|
|
|
|
// It's possible sometimes not to have no base expression.
|
|
if (!expr)
|
|
return;
|
|
|
|
auto *anchor = getAnchor();
|
|
expr->forEachChildExpr([&](Expr *subExpr) -> Expr * {
|
|
auto *AE = dyn_cast<ApplyExpr>(subExpr);
|
|
if (!AE || AE->getFn() != anchor)
|
|
return subExpr;
|
|
|
|
Apply = AE;
|
|
return nullptr;
|
|
});
|
|
}
|
|
|
|
unsigned getRequirementIndex() const {
|
|
auto path = getLocator()->getPath();
|
|
assert(!path.empty());
|
|
|
|
auto &requirementLoc = path.back();
|
|
assert(requirementLoc.getKind() == PathEltKind::TypeParameterRequirement);
|
|
return requirementLoc.getValue();
|
|
}
|
|
|
|
/// The generic base type where failing requirement comes from.
|
|
Type getOwnerType() const;
|
|
|
|
/// Generic context associated with the failure.
|
|
const GenericContext *getGenericContext() const;
|
|
|
|
/// Generic requirement associated with the failure.
|
|
const Requirement &getRequirement() const;
|
|
|
|
virtual Type getLHS() const = 0;
|
|
virtual Type getRHS() const = 0;
|
|
|
|
bool diagnoseAsError() override;
|
|
bool diagnoseAsNote() override;
|
|
|
|
protected:
|
|
/// Retrieve declaration contextual where current
|
|
/// requirement has been introduced.
|
|
const DeclContext *getRequirementDC() const;
|
|
|
|
virtual DiagOnDecl getDiagnosticOnDecl() const = 0;
|
|
virtual DiagInReference getDiagnosticInRereference() const = 0;
|
|
virtual DiagAsNote getDiagnosticAsNote() const = 0;
|
|
|
|
/// Determine whether it would be possible to diagnose
|
|
/// current requirement failure.
|
|
bool canDiagnoseFailure() const {
|
|
// For static/initializer calls there is going to be
|
|
// a separate fix, attached to the argument, which is
|
|
// much easier to diagnose.
|
|
// For operator calls we can't currently produce a good
|
|
// diagnostic, so instead let's refer to expression diagnostics.
|
|
return !(Apply && (isOperator(Apply) || isa<TypeExpr>(getAnchor())));
|
|
}
|
|
|
|
static bool isOperator(const ApplyExpr *apply) {
|
|
return isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
|
|
isa<BinaryExpr>(apply);
|
|
}
|
|
|
|
private:
|
|
/// Retrieve declaration associated with failing generic requirement.
|
|
ValueDecl *getDeclRef() const;
|
|
|
|
void emitRequirementNote(const Decl *anchor) const;
|
|
};
|
|
|
|
/// Diagnostics for failed conformance checks originating from
|
|
/// generic requirements e.g.
|
|
/// ```swift
|
|
/// struct S {}
|
|
/// func foo<T: Hashable>(_ t: T) {}
|
|
/// foo(S())
|
|
/// ```
|
|
class MissingConformanceFailure final : public RequirementFailure {
|
|
Type NonConformingType;
|
|
ProtocolDecl *Protocol;
|
|
|
|
public:
|
|
MissingConformanceFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator,
|
|
std::pair<Type, ProtocolDecl *> conformance)
|
|
: RequirementFailure(cs, expr, RequirementKind::Conformance, locator),
|
|
NonConformingType(conformance.first), Protocol(conformance.second) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
|
|
private:
|
|
/// The type which was expected, by one of the generic requirements,
|
|
/// to conform to associated protocol.
|
|
Type getLHS() const override { return NonConformingType; }
|
|
|
|
/// The protocol generic requirement expected associated type to conform to.
|
|
Type getRHS() const override { return Protocol->getDeclaredType(); }
|
|
|
|
protected:
|
|
DiagOnDecl getDiagnosticOnDecl() const override {
|
|
return diag::type_does_not_conform_decl_owner;
|
|
}
|
|
|
|
DiagInReference getDiagnosticInRereference() const override {
|
|
return diag::type_does_not_conform_in_decl_ref;
|
|
}
|
|
|
|
DiagAsNote getDiagnosticAsNote() const override {
|
|
return diag::candidate_types_conformance_requirement;
|
|
}
|
|
};
|
|
|
|
/// Diagnose failures related to same-type generic requirements, e.g.
|
|
/// ```swift
|
|
/// protocol P {
|
|
/// associatedtype T
|
|
/// }
|
|
///
|
|
/// struct S : P {
|
|
/// typealias T = String
|
|
/// }
|
|
///
|
|
/// func foo<U: P>(_ t: [U]) where U.T == Int {}
|
|
/// foo([S()])
|
|
/// ```
|
|
///
|
|
/// `S.T` is not the same type as `Int`, which is required by `foo`.
|
|
class SameTypeRequirementFailure final : public RequirementFailure {
|
|
Type LHS, RHS;
|
|
|
|
public:
|
|
SameTypeRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs,
|
|
Type rhs, ConstraintLocator *locator)
|
|
: RequirementFailure(cs, expr, RequirementKind::SameType, locator),
|
|
LHS(lhs), RHS(rhs) {}
|
|
|
|
Type getLHS() const override { return LHS; }
|
|
Type getRHS() const override { return RHS; }
|
|
|
|
protected:
|
|
DiagOnDecl getDiagnosticOnDecl() const override {
|
|
return diag::types_not_equal_decl;
|
|
}
|
|
|
|
DiagInReference getDiagnosticInRereference() const override {
|
|
return diag::types_not_equal_in_decl_ref;
|
|
}
|
|
|
|
DiagAsNote getDiagnosticAsNote() const override {
|
|
return diag::candidate_types_equal_requirement;
|
|
}
|
|
};
|
|
|
|
/// Diagnose failures related to superclass generic requirements, e.g.
|
|
/// ```swift
|
|
/// class A {
|
|
/// }
|
|
///
|
|
/// class B {
|
|
/// }
|
|
///
|
|
/// func foo<T>(_ t: [T]) where T: A {}
|
|
/// foo([B()])
|
|
/// ```
|
|
///
|
|
/// `A` is not the superclass of `B`, which is required by `foo<T>`.
|
|
class SuperclassRequirementFailure final : public RequirementFailure {
|
|
Type LHS, RHS;
|
|
|
|
public:
|
|
SuperclassRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs,
|
|
Type rhs, ConstraintLocator *locator)
|
|
: RequirementFailure(cs, expr, RequirementKind::Superclass, locator),
|
|
LHS(lhs), RHS(rhs) {}
|
|
|
|
Type getLHS() const override { return LHS; }
|
|
Type getRHS() const override { return RHS; }
|
|
|
|
protected:
|
|
DiagOnDecl getDiagnosticOnDecl() const override {
|
|
return diag::types_not_inherited_decl;
|
|
}
|
|
|
|
DiagInReference getDiagnosticInRereference() const override {
|
|
return diag::types_not_inherited_in_decl_ref;
|
|
}
|
|
|
|
DiagAsNote getDiagnosticAsNote() const override {
|
|
return diag::candidate_types_inheritance_requirement;
|
|
}
|
|
};
|
|
|
|
/// Diagnose errors associated with missing, extraneous
|
|
/// or incorrect labels supplied by arguments, e.g.
|
|
/// ```swift
|
|
/// func foo(q: String, _ a: Int) {}
|
|
/// foo("ultimate quesiton", a: 42)
|
|
/// ```
|
|
/// Call to `foo` is going to be diagnosed as missing `q:`
|
|
/// and having extraneous `a:` labels, with appropriate fix-its added.
|
|
class LabelingFailure final : public FailureDiagnostic {
|
|
ArrayRef<Identifier> CorrectLabels;
|
|
|
|
public:
|
|
LabelingFailure(ConstraintSystem &cs, ConstraintLocator *locator,
|
|
ArrayRef<Identifier> labels)
|
|
: FailureDiagnostic(nullptr, cs, locator), CorrectLabels(labels) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose errors related to converting function type which
|
|
/// isn't explicitly '@escaping' to some other type.
|
|
class NoEscapeFuncToTypeConversionFailure final : public FailureDiagnostic {
|
|
Type ConvertTo;
|
|
|
|
public:
|
|
NoEscapeFuncToTypeConversionFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator,
|
|
Type toType = Type())
|
|
: FailureDiagnostic(expr, cs, locator), ConvertTo(toType) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
class MissingForcedDowncastFailure final : public FailureDiagnostic {
|
|
public:
|
|
MissingForcedDowncastFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(expr, cs, locator) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose failures related to passing value of some type
|
|
/// to `inout` parameter, without explicitly specifying `&`.
|
|
class MissingAddressOfFailure final : public FailureDiagnostic {
|
|
public:
|
|
MissingAddressOfFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(expr, cs, locator) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose failures related attempt to implicitly convert types which
|
|
/// do not support such implicit converstion.
|
|
/// "as" or "as!" has to be specified explicitly in cases like that.
|
|
class MissingExplicitConversionFailure final : public FailureDiagnostic {
|
|
Type ConvertingTo;
|
|
|
|
public:
|
|
MissingExplicitConversionFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator, Type toType)
|
|
: FailureDiagnostic(expr, cs, locator), ConvertingTo(toType) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
|
|
private:
|
|
bool exprNeedsParensBeforeAddingAs(Expr *expr) {
|
|
auto *DC = getDC();
|
|
auto &TC = getTypeChecker();
|
|
|
|
auto asPG = TC.lookupPrecedenceGroup(
|
|
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
|
|
if (!asPG)
|
|
return true;
|
|
return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG);
|
|
}
|
|
|
|
bool exprNeedsParensAfterAddingAs(Expr *expr, Expr *rootExpr) {
|
|
auto *DC = getDC();
|
|
auto &TC = getTypeChecker();
|
|
|
|
auto asPG = TC.lookupPrecedenceGroup(
|
|
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
|
|
if (!asPG)
|
|
return true;
|
|
|
|
return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr,
|
|
asPG);
|
|
}
|
|
};
|
|
|
|
/// Diagnose failures related to attempting member access on optional base
|
|
/// type without optional chaining or force-unwrapping it first.
|
|
class MemberAccessOnOptionalBaseFailure final : public FailureDiagnostic {
|
|
DeclName Member;
|
|
bool ResultTypeIsOptional;
|
|
|
|
public:
|
|
MemberAccessOnOptionalBaseFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator,
|
|
DeclName memberName, bool resultOptional)
|
|
: FailureDiagnostic(expr, cs, locator), Member(memberName),
|
|
ResultTypeIsOptional(resultOptional) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose failures related to use of the unwrapped optional types,
|
|
/// which require some type of force-unwrap e.g. "!" or "try!".
|
|
class MissingOptionalUnwrapFailure final : public FailureDiagnostic {
|
|
public:
|
|
MissingOptionalUnwrapFailure(Expr *expr, ConstraintSystem &cs,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(expr, cs, locator) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose errors associated with rvalues in positions
|
|
/// where an lvalue is required, such as inout arguments.
|
|
class RValueTreatedAsLValueFailure final : public FailureDiagnostic {
|
|
|
|
public:
|
|
RValueTreatedAsLValueFailure(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: FailureDiagnostic(nullptr, cs, locator) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
class TrailingClosureAmbiguityFailure final : public FailureDiagnostic {
|
|
ArrayRef<OverloadChoice> Choices;
|
|
|
|
public:
|
|
TrailingClosureAmbiguityFailure(Expr *root, ConstraintSystem &cs,
|
|
Expr *anchor,
|
|
ArrayRef<OverloadChoice> choices)
|
|
: FailureDiagnostic(root, cs, cs.getConstraintLocator(anchor)),
|
|
Choices(choices) {}
|
|
|
|
bool diagnoseAsError() override { return false; }
|
|
|
|
bool diagnoseAsNote() override;
|
|
};
|
|
|
|
/// Diagnose errors related to assignment expressions e.g.
|
|
/// trying to assign something to immutable value, or trying
|
|
/// to access mutating member on immutable base.
|
|
class AssignmentFailure final : public FailureDiagnostic {
|
|
SourceLoc Loc;
|
|
Diag<StringRef> DeclDiagnostic;
|
|
Diag<Type> TypeDiagnostic;
|
|
|
|
public:
|
|
AssignmentFailure(Expr *destExpr, ConstraintSystem &cs,
|
|
SourceLoc diagnosticLoc);
|
|
|
|
AssignmentFailure(Expr *destExpr, ConstraintSystem &cs,
|
|
SourceLoc diagnosticLoc, Diag<StringRef> declDiag,
|
|
Diag<Type> typeDiag)
|
|
: FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)),
|
|
Loc(diagnosticLoc), DeclDiagnostic(declDiag), TypeDiagnostic(typeDiag) {
|
|
}
|
|
|
|
bool diagnoseAsError() override;
|
|
|
|
private:
|
|
void fixItChangeInoutArgType(const Expr *arg, Type actualType,
|
|
Type neededType) const;
|
|
|
|
/// Given an expression that has a non-lvalue type, dig into it until
|
|
/// we find the part of the expression that prevents the entire subexpression
|
|
/// from being mutable. For example, in a sequence like "x.v.v = 42" we want
|
|
/// to complain about "x" being a let property if "v.v" are both mutable.
|
|
///
|
|
/// \returns The base subexpression that looks immutable (or that can't be
|
|
/// analyzed any further) along with a decl extracted from it if we could.
|
|
std::pair<Expr *, ValueDecl *> resolveImmutableBase(Expr *expr) const;
|
|
|
|
static Diag<StringRef> findDeclDiagonstic(ASTContext &ctx, Expr *destExpr);
|
|
|
|
static bool isLoadedLValue(Expr *expr) {
|
|
expr = expr->getSemanticsProvidingExpr();
|
|
if (isa<LoadExpr>(expr))
|
|
return true;
|
|
if (auto ifExpr = dyn_cast<IfExpr>(expr))
|
|
return isLoadedLValue(ifExpr->getThenExpr()) &&
|
|
isLoadedLValue(ifExpr->getElseExpr());
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// Intended to diagnose any possible contextual failure
|
|
/// e.g. argument/parameter, closure result, conversions etc.
|
|
class ContextualFailure final : public FailureDiagnostic {
|
|
Type FromType, ToType;
|
|
|
|
public:
|
|
ContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(root, cs, locator), FromType(resolve(lhs)),
|
|
ToType(resolve(rhs)) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
|
|
// If we're trying to convert something of type "() -> T" to T,
|
|
// then we probably meant to call the value.
|
|
bool diagnoseMissingFunctionCall() const;
|
|
|
|
/// Try to add a fix-it when converting between a collection and its slice
|
|
/// type, such as String <-> Substring or (eventually) Array <-> ArraySlice
|
|
static bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
|
|
ConstraintSystem &CS, Type fromType,
|
|
Type toType, Expr *expr);
|
|
|
|
private:
|
|
Type resolve(Type rawType) {
|
|
auto type = resolveType(rawType)->getWithoutSpecifierType();
|
|
if (auto *BGT = type->getAs<BoundGenericType>()) {
|
|
if (BGT->hasUnresolvedType())
|
|
return BGT->getDecl()->getDeclaredInterfaceType();
|
|
}
|
|
return type;
|
|
}
|
|
};
|
|
|
|
/// Diagnose situations when @autoclosure argument is passed to @autoclosure
|
|
/// parameter directly without calling it first.
|
|
class AutoClosureForwardingFailure final : public FailureDiagnostic {
|
|
public:
|
|
AutoClosureForwardingFailure(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: FailureDiagnostic(nullptr, cs, locator) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
/// Diagnose situations when there was an attempt to unwrap entity
|
|
/// of non-optional type e.g.
|
|
///
|
|
/// ```swift
|
|
/// let i: Int = 0
|
|
/// _ = i!
|
|
///
|
|
/// struct A { func foo() {} }
|
|
/// func foo(_ a: A) {
|
|
/// a?.foo()
|
|
/// }
|
|
/// ```
|
|
class NonOptionalUnwrapFailure final : public FailureDiagnostic {
|
|
Type BaseType;
|
|
|
|
public:
|
|
NonOptionalUnwrapFailure(Expr *root, ConstraintSystem &cs, Type baseType,
|
|
ConstraintLocator *locator)
|
|
: FailureDiagnostic(root, cs, locator), BaseType(baseType) {}
|
|
|
|
bool diagnoseAsError() override;
|
|
};
|
|
|
|
} // end namespace constraints
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_SEMA_CSDIAGNOSTICS_H
|