Skip to content

ABIChecker: minor refactoring to move code to APIDigester lib. NFC #38969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/APIDigester/ModuleAnalyzerNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -787,6 +788,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer {
void lookupVisibleDecls(ArrayRef<ModuleDecl *> Modules);
};

void detectRename(SDKNode *L, SDKNode *R);

int dumpSwiftModules(const CompilerInvocation &InitInvok,
const llvm::StringSet<> &ModuleNames,
StringRef OutputDir,
Expand All @@ -805,6 +808,8 @@ int dumpSDKContent(const CompilerInvocation &InitInvok,
const llvm::StringSet<> &ModuleNames,
StringRef OutputFile, CheckerOptions Opts);

void dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, bool ABI);

/// Mostly for testing purposes, this function de-serializes the SDK dump in
/// dumpPath and re-serialize them to OutputPath. If the tool performs correctly,
/// the contents in dumpPath and OutputPath should be identical.
Expand Down
314 changes: 314 additions & 0 deletions lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2294,6 +2294,22 @@ int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath
return 0;
}

void swift::ide::api::dumpModuleContent(ModuleDecl *MD, StringRef OutputFile,
bool ABI) {
CheckerOptions opts;
opts.ABI = ABI;
opts.SwiftOnly = true;
opts.AvoidLocation = true;
opts.AvoidToolArgs = true;
opts.Migrator = false;
opts.SkipOSCheck = false;
opts.Verbose = false;
SDKContext ctx(opts);
SwiftDeclCollector collector(ctx);
collector.lookupVisibleDecls({MD});
dumpSDKRoot(collector.getSDKRoot(), OutputFile);
}

int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) {
std::error_code EC;
if (!fs::exists(dumpPath)) {
Expand Down Expand Up @@ -2323,3 +2339,301 @@ int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) {
}
return 0;
}

void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) {
SDKNodeDecl::diagnose(Right);
auto *R = dyn_cast<SDKNodeDeclType>(Right);
if (!R)
return;
auto Loc = R->getLoc();
if (getDeclKind() != R->getDeclKind()) {
emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(),
getSDKContext().getOpts().CompilerStyle));
return;
}

assert(getDeclKind() == R->getDeclKind());
auto DKind = getDeclKind();
switch (DKind) {
case DeclKind::Class: {
auto LSuperClass = getSuperClassName();
auto RSuperClass = R->getSuperClassName();
if (!LSuperClass.empty() && LSuperClass != RSuperClass) {
if (RSuperClass.empty()) {
emitDiag(Loc, diag::super_class_removed, LSuperClass);
} else if (!llvm::is_contained(R->getClassInheritanceChain(), LSuperClass)) {
emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass);
}
}

// Check for @_hasMissingDesignatedInitializers and
// @_inheritsConvenienceInitializers changes.
if (isOpen() && R->isOpen()) {
// It's not safe to add new, invisible designated inits to open
// classes.
if (!hasMissingDesignatedInitializers() &&
R->hasMissingDesignatedInitializers())
R->emitDiag(R->getLoc(), diag::added_invisible_designated_init);
}

// It's not safe to stop inheriting convenience inits, it changes
// the set of initializers that are available.
if (!Ctx.checkingABI() &&
inheritsConvenienceInitializers() &&
!R->inheritsConvenienceInitializers())
R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits);
break;
}
default:
break;
}
}

void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) {
SDKNodeDecl::diagnose(Right);
auto *R = dyn_cast<SDKNodeDeclAbstractFunc>(Right);
if (!R)
return;
auto Loc = R->getLoc();
if (!isThrowing() && R->isThrowing()) {
emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing"));
}
if (Ctx.checkingABI()) {
if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) {
emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry());
}
}
}

void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) {
SDKNodeDeclAbstractFunc::diagnose(Right);
auto *R = dyn_cast<SDKNodeDeclFunction>(Right);
if (!R)
return;
auto Loc = R->getLoc();
if (getSelfAccessKind() != R->getSelfAccessKind()) {
emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(),
R->getSelfAccessKind());
}
if (Ctx.checkingABI()) {
if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) {
emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder());
}
}
}

static StringRef getAttrName(DeclAttrKind Kind) {
switch (Kind) {
#define DECL_ATTR(NAME, CLASS, ...) \
case DAK_##CLASS: \
return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME;
#include "swift/AST/Attr.def"
case DAK_Count:
llvm_unreachable("unrecognized attribute kind.");
}
llvm_unreachable("covered switch");
}

static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) {
return true;
}

static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) {
return true;
}

static bool isOwnershipEquivalent(ReferenceOwnership Left,
ReferenceOwnership Right) {
if (Left == Right)
return true;
if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak)
return true;
if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned)
return true;
return false;
}

void swift::ide::api::detectRename(SDKNode *L, SDKNode *R) {
if (L->getKind() == R->getKind() && isa<SDKNodeDecl>(L) &&
L->getPrintedName() != R->getPrintedName()) {
L->annotate(NodeAnnotation::Rename);
L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName());
L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName());
}
}

void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) {
SDKNode::diagnose(Right);
auto *RD = dyn_cast<SDKNodeDecl>(Right);
if (!RD)
return;
detectRename(this, RD);
auto Loc = RD->getLoc();
if (isOpen() && !RD->isOpen()) {
emitDiag(Loc, diag::no_longer_open);
}

// Diagnose static attribute change.
if (isStatic() ^ RD->isStatic()) {
emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" :
"static"));
}

// Diagnose ownership change.
if (!isOwnershipEquivalent(getReferenceOwnership(),
RD->getReferenceOwnership())) {
auto getOwnershipDescription = [&](swift::ReferenceOwnership O) {
if (O == ReferenceOwnership::Strong)
return Ctx.buffer("strong");
return keywordOf(O);
};
emitDiag(Loc, diag::decl_attr_change,
getOwnershipDescription(getReferenceOwnership()),
getOwnershipDescription(RD->getReferenceOwnership()));
}
// Diagnose generic signature change
if (getGenericSignature() != RD->getGenericSignature()) {
// Prefer sugared signature in diagnostics to be more user-friendly.
if (Ctx.commonVersionAtLeast(2) &&
getSugaredGenericSignature() != RD->getSugaredGenericSignature()) {
emitDiag(Loc, diag::generic_sig_change,
getSugaredGenericSignature(), RD->getSugaredGenericSignature());
} else {
emitDiag(Loc, diag::generic_sig_change,
getGenericSignature(), RD->getGenericSignature());
}
}

// ObjC name changes are considered breakage
if (getObjCName() != RD->getObjCName()) {
if (Ctx.commonVersionAtLeast(4)) {
emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName());
}
}

if (isOptional() != RD->isOptional()) {
if (Ctx.checkingABI()) {
// Both adding/removing optional is ABI-breaking.
emitDiag(Loc, diag::optional_req_changed, isOptional());
} else if (isOptional()) {
// Removing optional is source-breaking.
emitDiag(Loc, diag::optional_req_changed, isOptional());
}
}

// Diagnose removing attributes.
for (auto Kind: getDeclAttributes()) {
if (!RD->hasDeclAttribute(Kind)) {
if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) :
DeclAttribute::isRemovingBreakingAPI(Kind)) &&
shouldDiagnoseRemovingAttribute(this, Kind)) {
emitDiag(Loc, diag::decl_new_attr,
Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str()));
}
}
}

// Diagnose adding attributes.
for (auto Kind: RD->getDeclAttributes()) {
if (!hasDeclAttribute(Kind)) {
if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) :
DeclAttribute::isAddingBreakingAPI(Kind)) &&
shouldDiagnoseAddingAttribute(this, Kind)) {
emitDiag(Loc, diag::decl_new_attr,
Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str()));
}
}
}

if (Ctx.checkingABI()) {
if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() &&
getFixedBinaryOrder() != RD->getFixedBinaryOrder()) {
emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(),
RD->getFixedBinaryOrder());
}
}
}

void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) {
SDKNodeDecl::diagnose(Right);
auto *RO = dyn_cast<SDKNodeDeclOperator>(Right);
if (!RO)
return;
auto Loc = RO->getLoc();
if (getDeclKind() != RO->getDeclKind()) {
emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(),
getSDKContext().getOpts().CompilerStyle));
}
}

void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) {
SDKNodeDecl::diagnose(Right);
auto *RV = dyn_cast<SDKNodeDeclVar>(Right);
if (!RV)
return;
auto Loc = RV->getLoc();
if (Ctx.checkingABI()) {
if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) {
emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder());
}
}
}

static bool shouldDiagnoseType(SDKNodeType *T) {
return T->isTopLevelType();
}

void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) {
SDKNode::diagnose(Right);
auto *RT = dyn_cast<SDKNodeType>(Right);
if (!RT || !shouldDiagnoseType(this))
return;
assert(isTopLevelType());

// Diagnose type witness changes when diagnosing ABI breakages.
if (auto *Wit = dyn_cast<SDKNodeTypeWitness>(getParent())) {
auto *Conform = Wit->getParent()->getAs<SDKNodeConformance>();
if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) {
auto *LD = Conform->getNominalTypeDecl();
LD->emitDiag(SourceLoc(), diag::type_witness_change,
Wit->getWitnessedTypeName(),
getPrintedName(), RT->getPrintedName());
}
return;
}

StringRef Descriptor = getTypeRoleDescription();
assert(isa<SDKNodeDecl>(getParent()));
auto LParent = cast<SDKNodeDecl>(getParent());
assert(LParent->getKind() == RT->getParent()->getAs<SDKNodeDecl>()->getKind());
auto Loc = RT->getParent()->getAs<SDKNodeDecl>()->getLoc();
if (getPrintedName() != RT->getPrintedName()) {
LParent->emitDiag(Loc, diag::decl_type_change,
Descriptor, getPrintedName(), RT->getPrintedName());
}

if (hasDefaultArgument() && !RT->hasDefaultArgument()) {
LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor);
}
if (getParamValueOwnership() != RT->getParamValueOwnership()) {
LParent->emitDiag(Loc, diag::param_ownership_change,
getTypeRoleDescription(),
getParamValueOwnership(),
RT->getParamValueOwnership());
}
}

void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) {
SDKNodeType::diagnose(Right);
auto *RT = dyn_cast<SDKNodeTypeFunc>(Right);
if (!RT || !shouldDiagnoseType(this))
return;
assert(isTopLevelType());
auto Loc = RT->getParent()->getAs<SDKNodeDecl>()->getLoc();
if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) {
getParent()->getAs<SDKNodeDecl>()->emitDiag(Loc,
diag::func_type_escaping_changed,
getTypeRoleDescription(),
isEscaping());
}
}
Loading