swift-module-digester: refactor the ABI/API diagnostics to use DiagnosticEngine. NFC (#18957)

Before this patch, we used to define tooling-specific diagnostics. With adding more
checking logics, we found this mechanism hard to extend. This patch
eliminates the home-made diagnostics model to use the one from compiler, which provides
several benefits: less boiler-templates, better integration with DiagnosticConsumer,
and easy ordering of detected issues.
This commit is contained in:
Xi Ge
2018-08-24 13:11:32 -07:00
committed by GitHub
parent 574b7f45be
commit e4388af6be
15 changed files with 349 additions and 290 deletions

View File

@@ -26,8 +26,11 @@
// can be reflected as source-breaking changes for API users. If they are,
// the output of api-digester will include such changes.
#include "swift/AST/DiagnosticsModuleDiffer.h"
#include "swift/IDE/APIDigesterData.h"
#include <functional>
#include <ModuleAnalyzerNodes.h>
#include "ModuleAnalyzerNodes.h"
#include "ModuleDiagsConsumer.h"
using namespace swift;
using namespace ide;
@@ -1478,16 +1481,6 @@ public:
}
};
// Redefine << so that we can output the name of decl kind.
raw_ostream &operator<<(raw_ostream &Out, const DeclKind Value) {
switch (Value) {
#define DECL(X, PARENT) case DeclKind::X: return Out << #X;
#include "swift/AST/DeclNodes.def"
}
llvm_unreachable("Unhandled DeclKind in switch.");
}
class DiagnosisEmitter : public SDKNodeVisitor {
void handle(const SDKNodeDecl *D, NodeAnnotation Anno);
void visitType(SDKNodeType *T);
@@ -1495,182 +1488,18 @@ class DiagnosisEmitter : public SDKNodeVisitor {
void visit(NodePtr Node) override;
SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node);
bool findTypeAliasDecl(const SDKNodeDecl *Node);
static StringRef printName(StringRef Name);
static StringRef printDiagKeyword(StringRef Name);
static void collectAddedDecls(NodePtr Root, std::set<SDKNodeDecl*> &Results);
template<typename T>
struct DiagBag {
std::vector<T> Diags;
~DiagBag() {
llvm::outs() << "\n/* ";
T::theme(llvm::outs());
llvm::outs() << " */\n";
removeRedundantAndSort(Diags);
std::for_each(Diags.begin(), Diags.end(), [](T &Diag) {
if (Diag.isABISpecific() && !options::Abi)
return;
Diag.outputModule();
Diag.output();
});
}
};
struct MetaInfo {
StringRef ModuleName;
StringRef HeaderName;
DeclKind Kind;
StringRef Name;
bool IsABISpecific;
MetaInfo(const SDKNodeDecl *Node, bool IsABISpecific):
ModuleName(Node->getModuleName()), HeaderName(Node->getHeaderName()),
Kind(Node->getDeclKind()), Name(Node->getFullyQualifiedName()),
IsABISpecific(IsABISpecific) {}
int compare(MetaInfo &Other) const {
if (ModuleName != Other.ModuleName)
return ModuleName.compare(Other.ModuleName);
if (HeaderName != Other.HeaderName)
return HeaderName.compare(Other.HeaderName);
if (Kind != Other.Kind)
return (uint8_t)Kind - (uint8_t)Other.Kind;
return Name.compare(Other.Name);
}
};
class DiagBase {
protected:
MetaInfo Info;
DiagBase(MetaInfo Info): Info(Info) {}
virtual ~DiagBase() = default;
raw_ostream &outputName() const {
return llvm::outs() << Info.Kind << " " << printName(Info.Name);
}
public:
void outputModule() const {
if (options::PrintModule) {
llvm::outs() << Info.ModuleName;
if (!Info.HeaderName.empty())
llvm::outs() << "(" << Info.HeaderName << ")";
llvm::outs() << ": ";
}
}
virtual void output() const = 0;
bool isABISpecific() const { return Info.IsABISpecific; }
};
struct RemovedDeclDiag: public DiagBase {
DeclKind Kind;
StringRef Name;
bool IsDeprecated;
RemovedDeclDiag(MetaInfo Info, bool IsDeprecated): DiagBase(Info),
IsDeprecated(IsDeprecated) {}
bool operator<(RemovedDeclDiag Other) const {
return Info.compare(Other.Info) < 0;
}
void output() const override;
static void theme(raw_ostream &OS) { OS << "Removed Decls"; };
};
struct MovedDeclDiag: public DiagBase {
DeclKind AddedKind;
StringRef AddedName;
MovedDeclDiag(MetaInfo Info, DeclKind AddedKind, StringRef AddedName):
DiagBase(Info), AddedKind(AddedKind), AddedName(AddedName) {}
bool operator<(MovedDeclDiag Other) const {
return Info.compare(Other.Info) < 0;
}
void output() const override;
static void theme(raw_ostream &OS) { OS << "Moved Decls"; };
};
struct RenamedDeclDiag: public DiagBase {
DeclKind KindAfter;
StringRef NameAfter;
RenamedDeclDiag(MetaInfo Info, DeclKind KindAfter, StringRef NameAfter):
DiagBase(Info), KindAfter(KindAfter), NameAfter(NameAfter) {}
bool operator<(RenamedDeclDiag Other) const {
return Info.compare(Other.Info) < 0;
};
void output() const override;
static void theme(raw_ostream &OS) { OS << "Renamed Decls"; };
};
struct DeclAttrDiag: public DiagBase {
StringRef AttrBefore;
StringRef AttrAfter;
DeclAttrDiag(MetaInfo Info, StringRef AttrBefore, StringRef AttrAfter):
DiagBase(Info), AttrBefore(AttrBefore), AttrAfter(AttrAfter) {}
DeclAttrDiag(MetaInfo Info, StringRef AttrAfter): DeclAttrDiag(Info,
StringRef(), AttrAfter) {}
bool operator<(DeclAttrDiag Other) const;
void output() const override;
static void theme(raw_ostream &OS) { OS << "Decl Attribute changes"; };
};
struct DeclTypeChangeDiag: public DiagBase {
StringRef TypeNameBefore;
StringRef TypeNameAfter;
StringRef Description;
DeclTypeChangeDiag(MetaInfo Info, StringRef TypeNameBefore,
StringRef TypeNameAfter,
StringRef Description): DiagBase(Info),
TypeNameBefore(TypeNameBefore), TypeNameAfter(TypeNameAfter),
Description(Description) {}
bool operator<(DeclTypeChangeDiag Other) const {
return Info.compare(Other.Info) < 0;
};
void output() const override;
static void theme(raw_ostream &OS) { OS << "Type Changes"; };
};
struct RawRepresentableChangeDiag: public DiagBase {
StringRef UnderlyingType;
StringRef RawTypeName;
RawRepresentableChangeDiag(MetaInfo Info, StringRef UnderlyingType,
StringRef RawTypeName): DiagBase(Info), UnderlyingType(UnderlyingType),
RawTypeName(RawTypeName) {}
bool operator<(RawRepresentableChangeDiag Other) const {
return Info.compare(Other.Info) < 0;
}
void output() const override {
outputName() << "(" << UnderlyingType << ")"
<< " is now " << RawTypeName << " representable\n";
}
static void theme(raw_ostream &OS) { OS << "RawRepresentable Changes"; };
};
struct GenericSignatureChangeDiag: public DiagBase {
StringRef GSBefore;
StringRef GSAfter;
GenericSignatureChangeDiag(MetaInfo Info, StringRef GSBefore,
StringRef GSAfter): DiagBase(Info),
GSBefore(GSBefore), GSAfter(GSAfter) {}
bool operator<(GenericSignatureChangeDiag Other) const {
return Info.compare(Other.Info) < 0;
}
void output() const override {
outputName() << " has generic signature change from " << GSBefore << " to "
<< GSAfter << "\n";
}
static void theme(raw_ostream &OS) { OS << "Generic Signature Changes"; };
};
std::set<SDKNodeDecl*> AddedDecls;
DiagBag<DeclAttrDiag> AttrChangedDecls;
DiagBag<DeclTypeChangeDiag> TypeChangedDecls;
DiagBag<RenamedDeclDiag> RenamedDecls;
DiagBag<MovedDeclDiag> MovedDecls;
DiagBag<RemovedDeclDiag> RemovedDecls;
DiagBag<RawRepresentableChangeDiag> RawRepresentableDecls;
DiagBag<GenericSignatureChangeDiag> GSChangeDecls;
UpdatedNodesMap &UpdateMap;
NodeMap &TypeAliasUpdateMap;
TypeMemberDiffVector &MemberChanges;
DiagnosticEngine &Diags;
DiagnosisEmitter(SDKContext &Ctx):
UpdateMap(Ctx.getNodeUpdateMap()),
TypeAliasUpdateMap(Ctx.getTypeAliasUpdateMap()),
MemberChanges(Ctx.getTypeMemberDiffs()){}
MemberChanges(Ctx.getTypeMemberDiffs()), Diags(Ctx.getDiags()) {}
public:
static void diagnosis(NodePtr LeftRoot, NodePtr RightRoot,
@@ -1707,58 +1536,6 @@ bool DiagnosisEmitter::findTypeAliasDecl(const SDKNodeDecl *Node) {
});
}
StringRef DiagnosisEmitter::printName(StringRef Name) {
OSColor Color(llvm::outs(), llvm::raw_ostream::CYAN);
Color << Name;
return StringRef();
}
StringRef DiagnosisEmitter::printDiagKeyword(StringRef Name) {
OSColor Color(llvm::outs(), llvm::raw_ostream::YELLOW);
Color << Name;
return StringRef();
}
void DiagnosisEmitter::RemovedDeclDiag::output() const {
outputName() << " has been " << printDiagKeyword("removed");
if (IsDeprecated)
llvm::outs() << " (deprecated)";
llvm::outs() << "\n";
}
void DiagnosisEmitter::MovedDeclDiag::output() const {
outputName() << " has been " << printDiagKeyword("moved") << " to "
<< AddedKind << " " << printName(AddedName) << "\n";
}
void DiagnosisEmitter::RenamedDeclDiag::output() const {
outputName() << " has been " << printDiagKeyword("renamed") << " to "
<< KindAfter << " " << printName(NameAfter) << "\n";
}
void DiagnosisEmitter::DeclTypeChangeDiag::output() const {
outputName() << " has "
<< Description << " type change from "
<< printName(TypeNameBefore) << " to "
<< printName(TypeNameAfter) << "\n";
}
bool DiagnosisEmitter::DeclAttrDiag::operator<(DeclAttrDiag Other) const {
auto result = Info.compare(Other.Info);
if (result)
return result < 0;
return AttrAfter.compare(Other.AttrAfter) < 0;
}
void DiagnosisEmitter::DeclAttrDiag::output() const {
if (AttrBefore.empty())
outputName() << " is now " << printDiagKeyword(AttrAfter)<< "\n";
else
outputName() << " changes from " << printDiagKeyword(AttrBefore)
<< " to "<< printDiagKeyword(AttrAfter)<< "\n";
}
void DiagnosisEmitter::diagnosis(NodePtr LeftRoot, NodePtr RightRoot,
SDKContext &Ctx) {
DiagnosisEmitter Emitter(Ctx);
@@ -1771,16 +1548,15 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
auto &Ctx = Node->getSDKContext();
switch(Anno) {
case NodeAnnotation::Removed: {
MetaInfo ScreenInfo(Node, false);
// If we can find a type alias decl with the same name of this type, we
// consider the type is not removed.
if (findTypeAliasDecl(Node))
return;
if (auto *Added = findAddedDecl(Node)) {
if (Node->getDeclKind() != DeclKind::Constructor) {
MovedDecls.Diags.emplace_back(ScreenInfo,
Added->getDeclKind(),
Added->getFullyQualifiedName());
Diags.diagnose(diag::moved_decl, Node->getScreenInfo(),
Ctx.buffer((Twine(getDeclKindStr(Added->getDeclKind())) + " " +
Added->getFullyQualifiedName()).str()));
return;
}
}
@@ -1790,8 +1566,9 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
auto It = std::find_if(MemberChanges.begin(), MemberChanges.end(),
[&](TypeMemberDiffItem &Item) { return Item.usr == Node->getUsr(); });
if (It != MemberChanges.end()) {
RenamedDecls.Diags.emplace_back(ScreenInfo, Node->getDeclKind(),
Ctx.buffer((Twine(It->newTypeName) + "." + It->newPrintedName).str()));
Diags.diagnose(diag::renamed_decl, Node->getScreenInfo(),
Ctx.buffer((Twine(getDeclKindStr(Node->getDeclKind())) + " " +
It->newTypeName + "." + It->newPrintedName).str()));
return;
}
@@ -1800,7 +1577,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
// refine diagnostics message instead of showing the type alias has been
// removed.
if (TypeAliasUpdateMap.find((SDKNode*)Node) != TypeAliasUpdateMap.end()) {
RawRepresentableDecls.Diags.emplace_back(ScreenInfo,
Diags.diagnose(diag::raw_type_change, Node->getScreenInfo(),
Node->getAs<SDKNodeDeclTypeAlias>()->getUnderlyingType()->getPrintedName(),
TypeAliasUpdateMap[(SDKNode*)Node]->getAs<SDKNodeDeclType>()->
getRawValueType()->getPrintedName());
@@ -1822,53 +1599,47 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
}
if (FoundInSuperclass)
return;
RemovedDecls.Diags.emplace_back(ScreenInfo,
Node->isDeprecated());
Diags.diagnose(diag::removed_decl, Node->getScreenInfo(), Node->isDeprecated());
return;
}
case NodeAnnotation::Rename: {
MetaInfo ScreenInfo(Node, false);
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeDecl>();
RenamedDecls.Diags.emplace_back(ScreenInfo,
Count->getDeclKind(),
Count->getFullyQualifiedName());
Diags.diagnose(diag::renamed_decl, Node->getScreenInfo(),
Ctx.buffer((Twine(getDeclKindStr(Count->getDeclKind())) + " " +
Count->getFullyQualifiedName()).str()));
return;
}
case NodeAnnotation::NowMutating: {
MetaInfo ScreenInfo(Node, false);
AttrChangedDecls.Diags.emplace_back(ScreenInfo, Ctx.buffer("mutating"));
Diags.diagnose(diag::decl_new_attr, Node->getScreenInfo(),
Ctx.buffer("mutating"));
return;
}
case NodeAnnotation::NowThrowing: {
MetaInfo ScreenInfo(Node, false);
AttrChangedDecls.Diags.emplace_back(ScreenInfo, Ctx.buffer("throwing"));
Diags.diagnose(diag::decl_new_attr, Node->getScreenInfo(),
Ctx.buffer("throwing"));
return;
}
case NodeAnnotation::StaticChange: {
MetaInfo ScreenInfo(Node, false);
AttrChangedDecls.Diags.emplace_back(ScreenInfo,
Ctx.buffer(Node->isStatic() ? "not static" : "static"));
Diags.diagnose(diag::decl_new_attr, Node->getScreenInfo(),
Ctx.buffer(Node->isStatic() ? "not static" : "static"));
return;
}
case NodeAnnotation::OwnershipChange: {
MetaInfo ScreenInfo(Node, false);
auto getOwnershipDescription = [&](swift::ReferenceOwnership O) {
if (O == ReferenceOwnership::Strong)
return Ctx.buffer("strong");
return keywordOf(O);
};
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeDecl>();
AttrChangedDecls.Diags.emplace_back(
ScreenInfo,
getOwnershipDescription(Node->getReferenceOwnership()),
getOwnershipDescription(Count->getReferenceOwnership()));
Diags.diagnose(diag::decl_attr_change, Node->getScreenInfo(),
getOwnershipDescription(Node->getReferenceOwnership()),
getOwnershipDescription(Count->getReferenceOwnership()));
return;
}
case NodeAnnotation::ChangeGenericSignature: {
MetaInfo ScreenInfo(Node, false);
GSChangeDecls.Diags.emplace_back(ScreenInfo, Node->getGenericSignature(),
UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeDecl>()->
getGenericSignature());
Diags.diagnose(diag::generic_sig_change, Node->getScreenInfo(),
Node->getGenericSignature(), UpdateMap.findUpdateCounterpart(Node)->
getAs<SDKNodeDecl>()->getGenericSignature());
return;
}
@@ -1879,11 +1650,11 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
[&](const ABIAttributeInfo &I) { return I.Annotation == Anno; });
if (It == Infos.end())
return;
MetaInfo ScreenInfo(Node, true);
auto Desc = Node->hasDeclAttribute(It->Kind) ?
Ctx.buffer((llvm::Twine("without ") + It->Content).str()):
Ctx.buffer((llvm::Twine("with ") + It->Content).str());
AttrChangedDecls.Diags.emplace_back(ScreenInfo, Desc);
if (options::Abi)
Diags.diagnose(diag::decl_new_attr, Node->getScreenInfo(), Desc);
return;
}
}
@@ -1901,7 +1672,6 @@ void DiagnosisEmitter::visitType(SDKNodeType *Node) {
auto *Parent = dyn_cast<SDKNodeDecl>(Node->getParent());
if (!Parent || Parent->isSDKPrivate())
return;
MetaInfo ScreenInfo(Parent, false);
SDKContext &Ctx = Node->getSDKContext();
if (Node->isAnnotatedAs(NodeAnnotation::Updated)) {
auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeType>();
@@ -1914,10 +1684,8 @@ void DiagnosisEmitter::visitType(SDKNodeType *Node) {
SDKNodeDeclAbstractFunc::getTypeRoleDescription(Ctx, Parent->getChildIndex(Node)) :
Ctx.buffer("declared");
if (Node->getPrintedName() != Count->getPrintedName())
TypeChangedDecls.Diags.emplace_back(ScreenInfo,
Node->getPrintedName(),
Count->getPrintedName(),
Descriptor);
Diags.diagnose(diag::decl_type_change, Parent->getScreenInfo(),
Descriptor, Node->getPrintedName(), Count->getPrintedName());
break;
default:
break;
@@ -2043,7 +1811,7 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
llvm::errs() << RightPath << " does not exist\n";
return 1;
}
PrintingDiagnosticConsumer PDC;
ModuleDifferDiagsConsumer PDC;
SDKContext Ctx(Opts);
Ctx.getDiags().addConsumer(PDC);
@@ -2061,6 +1829,7 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
RefinementPass.pass(LeftModule, RightModule);
// Find member hoist changes to help refine diagnostics.
findTypeMemberDiffs(LeftModule, RightModule, Ctx.getTypeMemberDiffs());
DiagnosisEmitter::diagnosis(LeftModule, RightModule, Ctx);
return 0;
}
@@ -2099,7 +1868,7 @@ static int compareSDKs(StringRef LeftPath, StringRef RightPath,
}
llvm::errs() << "Diffing: " << LeftPath << " and " << RightPath << "\n";
PrintingDiagnosticConsumer PDC;
ModuleDifferDiagsConsumer PDC;
SDKContext Ctx(Opts);
Ctx.getDiags().addConsumer(PDC);
@@ -2319,6 +2088,7 @@ static CheckerOptions getCheckOpts() {
Opts.Verbose = options::Verbose;
Opts.AbortOnModuleLoadFailure = options::AbortOnModuleLoadFailure;
Opts.LocationFilter = options::LocationFilter;
Opts.PrintModule = options::PrintModule;
return Opts;
}