mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
swift-module-digester: diagnose adding/removing @escaping as ABI breakage.
This commit is contained in:
@@ -80,6 +80,8 @@ ERROR(var_let_changed,none,"%0 changes from %select{var|let}1 to %select{let|var
|
|||||||
|
|
||||||
ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef))
|
ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef))
|
||||||
|
|
||||||
|
ERROR(func_type_escaping_changed,none,"%0 has %select{removed|added}2 @escaping in %1", (StringRef, StringRef, bool))
|
||||||
|
|
||||||
#ifndef DIAG_NO_UNDEF
|
#ifndef DIAG_NO_UNDEF
|
||||||
# if defined(DIAG)
|
# if defined(DIAG)
|
||||||
# undef DIAG
|
# undef DIAG
|
||||||
|
|||||||
@@ -120,3 +120,8 @@ public class ClassWithOpenMember {
|
|||||||
open var property: Int {get { return 1}}
|
open var property: Int {get { return 1}}
|
||||||
open func bar() {}
|
open func bar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EscapingFunctionType {
|
||||||
|
public func removedEscaping(_ a: @escaping ()->()) {}
|
||||||
|
public func addedEscaping(_ a: ()->()) {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -130,3 +130,8 @@ public class ClassWithOpenMember {
|
|||||||
public var property: Int {get { return 1}}
|
public var property: Int {get { return 1}}
|
||||||
public func bar() {}
|
public func bar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EscapingFunctionType {
|
||||||
|
public func removedEscaping(_ a: ()->()) {}
|
||||||
|
public func addedEscaping(_ a: @escaping ()->()) {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ cake1: Constructor S1.init(_:) has parameter 0 type change from Int to Double
|
|||||||
cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> ()
|
cake1: Func C1.foo2(_:) has parameter 0 type change from Int to () -> ()
|
||||||
cake1: Func C7.foo(_:_:) has removed default argument from parameter 0
|
cake1: Func C7.foo(_:_:) has removed default argument from parameter 0
|
||||||
cake1: Func C7.foo(_:_:) has removed default argument from parameter 1
|
cake1: Func C7.foo(_:_:) has removed default argument from parameter 1
|
||||||
|
cake1: Func EscapingFunctionType.addedEscaping(_:) has added @escaping in parameter 0
|
||||||
|
cake1: Func EscapingFunctionType.removedEscaping(_:) has removed @escaping in parameter 0
|
||||||
cake1: Func Somestruct2.foo1(_:) has parameter 0 type change from C3 to C1
|
cake1: Func Somestruct2.foo1(_:) has parameter 0 type change from C3 to C1
|
||||||
|
|
||||||
/* Decl Attribute changes */
|
/* Decl Attribute changes */
|
||||||
|
|||||||
@@ -352,13 +352,19 @@ ArrayRef<DeclAttrKind> SDKNodeDecl::getDeclAttributes() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const {
|
bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const {
|
||||||
if (getDeclAttributes().size() != Another.getDeclAttributes().size())
|
std::set<DeclAttrKind> Left(getDeclAttributes().begin(),
|
||||||
return true;
|
getDeclAttributes().end());
|
||||||
for (auto K: getDeclAttributes()) {
|
std::set<DeclAttrKind> Right(Another.getDeclAttributes().begin(),
|
||||||
if (!Another.hasDeclAttribute(K))
|
Another.getDeclAttributes().end());
|
||||||
return true;
|
return Left != Right;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
bool SDKNodeType::hasAttributeChange(const SDKNodeType &Another) const {
|
||||||
|
std::set<TypeAttrKind> Left(getTypeAttributes().begin(),
|
||||||
|
getTypeAttributes().end());
|
||||||
|
std::set<TypeAttrKind> Right(Another.getTypeAttributes().begin(),
|
||||||
|
Another.getTypeAttributes().end());
|
||||||
|
return Left != Right;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDKNodeDecl *SDKNodeType::getClosestParentDecl() const {
|
SDKNodeDecl *SDKNodeType::getClosestParentDecl() const {
|
||||||
@@ -614,7 +620,7 @@ bool SDKNode::operator==(const SDKNode &Other) const {
|
|||||||
case SDKNodeKind::TypeFunc: {
|
case SDKNodeKind::TypeFunc: {
|
||||||
auto Left = this->getAs<SDKNodeType>();
|
auto Left = this->getAs<SDKNodeType>();
|
||||||
auto Right = (&Other)->getAs<SDKNodeType>();
|
auto Right = (&Other)->getAs<SDKNodeType>();
|
||||||
if (!Left->getTypeAttributes().equals(Right->getTypeAttributes()))
|
if (Left->hasAttributeChange(*Right))
|
||||||
return false;
|
return false;
|
||||||
if (Left->hasDefaultArgument() != Right->hasDefaultArgument())
|
if (Left->hasDefaultArgument() != Right->hasDefaultArgument())
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -348,10 +348,10 @@ class SDKNodeType : public SDKNode {
|
|||||||
bool HasDefaultArg;
|
bool HasDefaultArg;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool hasTypeAttribute(TypeAttrKind DAKind) const;
|
|
||||||
SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind);
|
SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind);
|
||||||
~SDKNodeType() = default;
|
~SDKNodeType() = default;
|
||||||
public:
|
public:
|
||||||
|
bool hasTypeAttribute(TypeAttrKind DAKind) const;
|
||||||
KnownTypeKind getTypeKind() const;
|
KnownTypeKind getTypeKind() const;
|
||||||
void addTypeAttribute(TypeAttrKind AttrKind);
|
void addTypeAttribute(TypeAttrKind AttrKind);
|
||||||
ArrayRef<TypeAttrKind> getTypeAttributes() const;
|
ArrayRef<TypeAttrKind> getTypeAttributes() const;
|
||||||
@@ -363,6 +363,7 @@ public:
|
|||||||
bool isTopLevelType() const { return !isa<SDKNodeType>(getParent()); }
|
bool isTopLevelType() const { return !isa<SDKNodeType>(getParent()); }
|
||||||
static bool classof(const SDKNode *N);
|
static bool classof(const SDKNode *N);
|
||||||
virtual void jsonize(json::Output &Out) override;
|
virtual void jsonize(json::Output &Out) override;
|
||||||
|
bool hasAttributeChange(const SDKNodeType &Another) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SDKNodeTypeNominal : public SDKNodeType {
|
class SDKNodeTypeNominal : public SDKNodeType {
|
||||||
@@ -378,7 +379,7 @@ public:
|
|||||||
class SDKNodeTypeFunc : public SDKNodeType {
|
class SDKNodeTypeFunc : public SDKNodeType {
|
||||||
public:
|
public:
|
||||||
SDKNodeTypeFunc(SDKNodeInitInfo Info);
|
SDKNodeTypeFunc(SDKNodeInitInfo Info);
|
||||||
bool isEscaping() const { return !hasTypeAttribute(TypeAttrKind::TAK_noescape); }
|
bool isEscaping() const { return hasTypeAttribute(TypeAttrKind::TAK_noescape); }
|
||||||
static bool classof(const SDKNode *N);
|
static bool classof(const SDKNode *N);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ static StringRef getCategoryName(uint32_t ID) {
|
|||||||
return "/* Decl Attribute changes */";
|
return "/* Decl Attribute changes */";
|
||||||
case LocalDiagID::default_arg_removed:
|
case LocalDiagID::default_arg_removed:
|
||||||
case LocalDiagID::decl_type_change:
|
case LocalDiagID::decl_type_change:
|
||||||
|
case LocalDiagID::func_type_escaping_changed:
|
||||||
return "/* Type Changes */";
|
return "/* Type Changes */";
|
||||||
case LocalDiagID::raw_type_change:
|
case LocalDiagID::raw_type_change:
|
||||||
return "/* RawRepresentable Changes */";
|
return "/* RawRepresentable Changes */";
|
||||||
|
|||||||
@@ -811,14 +811,69 @@ static void diagnoseTypeChange(SDKNode* L, SDKNode* R) {
|
|||||||
auto *RT = dyn_cast<SDKNodeType>(R);
|
auto *RT = dyn_cast<SDKNodeType>(R);
|
||||||
if (!LT || !RT)
|
if (!LT || !RT)
|
||||||
return;
|
return;
|
||||||
|
assert(LT->isTopLevelType() == RT->isTopLevelType());
|
||||||
|
if (!LT->isTopLevelType())
|
||||||
|
return;
|
||||||
|
StringRef Descriptor;
|
||||||
|
auto LParent = cast<SDKNodeDecl>(LT->getParent());
|
||||||
|
auto RParent = cast<SDKNodeDecl>(RT->getParent());
|
||||||
|
assert(LParent->getKind() == RParent->getKind());
|
||||||
|
if (LParent->isSDKPrivate())
|
||||||
|
return;
|
||||||
|
switch(LParent->getKind()) {
|
||||||
|
case SDKNodeKind::Root:
|
||||||
|
case SDKNodeKind::TypeNominal:
|
||||||
|
case SDKNodeKind::TypeFunc:
|
||||||
|
case SDKNodeKind::TypeAlias:
|
||||||
|
case SDKNodeKind::DeclType:
|
||||||
|
llvm_unreachable("Type Parent is wrong");
|
||||||
|
case SDKNodeKind::DeclFunction:
|
||||||
|
case SDKNodeKind::DeclConstructor:
|
||||||
|
case SDKNodeKind::DeclGetter:
|
||||||
|
case SDKNodeKind::DeclSetter:
|
||||||
|
case SDKNodeKind::DeclSubscript:
|
||||||
|
Descriptor = SDKNodeDeclAbstractFunc::
|
||||||
|
getTypeRoleDescription(Ctx, LParent->getChildIndex(LT));
|
||||||
|
break;
|
||||||
|
case SDKNodeKind::DeclVar:
|
||||||
|
Descriptor = "declared";
|
||||||
|
break;
|
||||||
|
case SDKNodeKind::DeclTypeAlias:
|
||||||
|
Descriptor = "underlying";
|
||||||
|
break;
|
||||||
|
case SDKNodeKind::DeclAssociatedType:
|
||||||
|
Descriptor = "default";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LT->getPrintedName() != RT->getPrintedName()) {
|
||||||
|
Diags.diagnose(SourceLoc(), diag::decl_type_change, LParent->getScreenInfo(),
|
||||||
|
Descriptor, LT->getPrintedName(), RT->getPrintedName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (LT->hasDefaultArgument() && !RT->hasDefaultArgument()) {
|
if (LT->hasDefaultArgument() && !RT->hasDefaultArgument()) {
|
||||||
auto *Func = cast<SDKNodeDeclAbstractFunc>(LT->getClosestParentDecl());
|
Diags.diagnose(SourceLoc(), diag::default_arg_removed,
|
||||||
Diags.diagnose(SourceLoc(), diag::default_arg_removed, Func->getScreenInfo(),
|
LParent->getScreenInfo(), Descriptor);
|
||||||
Func->getTypeRoleDescription(Ctx, Func->getChildIndex(LT)));
|
}
|
||||||
|
if (LT->getKind() != RT->getKind())
|
||||||
|
return;
|
||||||
|
assert(LT->getKind() == RT->getKind());
|
||||||
|
switch (LT->getKind()) {
|
||||||
|
case SDKNodeKind::TypeFunc: {
|
||||||
|
auto *LFT = cast<SDKNodeTypeFunc>(LT);
|
||||||
|
auto *RFT = cast<SDKNodeTypeFunc>(RT);
|
||||||
|
if (Ctx.checkingABI() && LFT->isEscaping() != RFT->isEscaping()) {
|
||||||
|
Diags.diagnose(SourceLoc(), diag::func_type_escaping_changed,
|
||||||
|
LParent->getScreenInfo(), Descriptor, LFT->isEscaping());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is first pass on two given SDKNode trees. This pass removes the common part
|
// This is first pass on two given SDKNode trees. This pass removes the common part
|
||||||
// of two versions of SDK, leaving only the changed part.
|
// of two versions of SDK, leaving only the changed part.
|
||||||
class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
|
class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
|
||||||
@@ -1656,7 +1711,6 @@ public:
|
|||||||
|
|
||||||
class DiagnosisEmitter : public SDKNodeVisitor {
|
class DiagnosisEmitter : public SDKNodeVisitor {
|
||||||
void handle(const SDKNodeDecl *D, NodeAnnotation Anno);
|
void handle(const SDKNodeDecl *D, NodeAnnotation Anno);
|
||||||
void visitType(SDKNodeType *T);
|
|
||||||
void visitDecl(SDKNodeDecl *D);
|
void visitDecl(SDKNodeDecl *D);
|
||||||
void visit(NodePtr Node) override;
|
void visit(NodePtr Node) override;
|
||||||
SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node);
|
SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node);
|
||||||
@@ -1796,42 +1850,10 @@ void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) {
|
|||||||
handle(Node, Anno);
|
handle(Node, Anno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagnosisEmitter::visitType(SDKNodeType *Node) {
|
|
||||||
auto *Parent = dyn_cast<SDKNodeDecl>(Node->getParent());
|
|
||||||
if (!Parent || Parent->isSDKPrivate())
|
|
||||||
return;
|
|
||||||
SDKContext &Ctx = Node->getSDKContext();
|
|
||||||
if (Node->isAnnotatedAs(NodeAnnotation::Updated)) {
|
|
||||||
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeType>();
|
|
||||||
StringRef Descriptor;
|
|
||||||
switch (Parent->getKind()) {
|
|
||||||
case SDKNodeKind::DeclConstructor:
|
|
||||||
case SDKNodeKind::DeclFunction:
|
|
||||||
case SDKNodeKind::DeclVar:
|
|
||||||
Descriptor = isa<SDKNodeDeclAbstractFunc>(Parent) ?
|
|
||||||
SDKNodeDeclAbstractFunc::getTypeRoleDescription(Ctx, Parent->getChildIndex(Node)) :
|
|
||||||
Ctx.buffer("declared");
|
|
||||||
if (Node->getPrintedName() != Count->getPrintedName())
|
|
||||||
Diags.diagnose(SourceLoc(), diag::decl_type_change, Parent->getScreenInfo(),
|
|
||||||
Descriptor, Node->getPrintedName(), Count->getPrintedName());
|
|
||||||
break;
|
|
||||||
case SDKNodeKind::DeclAssociatedType:
|
|
||||||
Diags.diagnose(SourceLoc(), diag::decl_type_change, Parent->getScreenInfo(),
|
|
||||||
"default", Node->getPrintedName(), Count->getPrintedName());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiagnosisEmitter::visit(NodePtr Node) {
|
void DiagnosisEmitter::visit(NodePtr Node) {
|
||||||
if (auto *DNode = dyn_cast<SDKNodeDecl>(Node)) {
|
if (auto *DNode = dyn_cast<SDKNodeDecl>(Node)) {
|
||||||
visitDecl(DNode);
|
visitDecl(DNode);
|
||||||
}
|
}
|
||||||
if (auto *TNode = dyn_cast<SDKNodeType>(Node)) {
|
|
||||||
visitType(TNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector;
|
typedef std::vector<NoEscapeFuncParam> NoEscapeFuncParamVector;
|
||||||
|
|||||||
Reference in New Issue
Block a user