Skip to content

[APIDigester] Allow outputting module diff diagnostics as JSON for other tools to consume #36435

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

Closed
wants to merge 1 commit into from
Closed
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
23 changes: 23 additions & 0 deletions include/swift/APIDigester/ModuleDiagsConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "swift/AST/DiagnosticConsumer.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"

#include "llvm/ADT/StringSet.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <set>

Expand All @@ -42,6 +44,27 @@ class ModuleDifferDiagsConsumer: public PrintingDiagnosticConsumer {
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;
};

struct ModuleDifferDiagnosticInfo {
DiagID ID;
llvm::SmallString<256> Text;

ModuleDifferDiagnosticInfo(DiagID ID, llvm::SmallString<256> Text)
: ID(ID), Text(Text) {}

void serialize(llvm::json::OStream &JSON);
};

/// Diagnostic consumer that outputs module differ diags as JSON.
class ModuleDifferDiagsJSONConsumer : public DiagnosticConsumer {
llvm::raw_ostream &OS;
std::vector<ModuleDifferDiagnosticInfo> AllDiags;

public:
ModuleDifferDiagsJSONConsumer(llvm::raw_ostream &OS) : OS(OS){};
~ModuleDifferDiagsJSONConsumer();
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;
};

class FilteringDiagnosticConsumer: public DiagnosticConsumer {
bool HasError = false;
std::unique_ptr<DiagnosticConsumer> subConsumer;
Expand Down
117 changes: 116 additions & 1 deletion lib/APIDigester/ModuleDiagsConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
//
//===----------------------------------------------------------------------===//

#include "swift/APIDigester/ModuleDiagsConsumer.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsModuleDiffer.h"
#include "swift/APIDigester/ModuleDiagsConsumer.h"
#include "swift/Basic/SourceManager.h"

using namespace swift;

Expand Down Expand Up @@ -81,6 +82,81 @@ static StringRef getCategoryName(uint32_t ID) {
return "/* Others */";
}
}

static StringRef getSerializedIdentifier(uint32_t ID) {
switch (ID) {
case LocalDiagID::removed_decl:
return "removedDecl";
case LocalDiagID::moved_decl:
return "movedDecl";
case LocalDiagID::decl_kind_changed:
return "declKindChanged";
case LocalDiagID::renamed_decl:
return "renamedDecl";
case LocalDiagID::objc_name_change:
return "objcNameChange";
case LocalDiagID::decl_attr_change:
return "declAttrChange";
case LocalDiagID::decl_new_attr:
return "declNewAttr";
case LocalDiagID::func_self_access_change:
return "funcSelfAccessChange";
case LocalDiagID::new_decl_without_intro:
return "newDeclWithoutIntro";
case LocalDiagID::default_arg_removed:
return "defaultArgRemoved";
case LocalDiagID::decl_type_change:
return "declTypeChange";
case LocalDiagID::func_type_escaping_changed:
return "funcTypeEscapingChanged";
case LocalDiagID::param_ownership_change:
return "paramOwnershipChange";
case LocalDiagID::type_witness_change:
return "typeWitnessChange";
case LocalDiagID::raw_type_change:
return "rawTypeChange";
case LocalDiagID::generic_sig_change:
return "genericSigChange";
case LocalDiagID::enum_case_added:
return "enumCaseAdded";
case LocalDiagID::decl_added:
return "declAdded";
case LocalDiagID::decl_reorder:
return "declReorder";
case LocalDiagID::var_has_fixed_order_change:
return "varHasFixedOrderChange";
case LocalDiagID::func_has_fixed_order_change:
return "funcHasFixedOrderChange";
case LocalDiagID::conformance_added:
return "conformanceAdded";
case LocalDiagID::conformance_removed:
return "conformanceRemoved";
case LocalDiagID::optional_req_changed:
return "optionalReqChanged";
case LocalDiagID::existing_conformance_added:
return "existingConformanceAdded";
case LocalDiagID::default_associated_type_removed:
return "defaultAssociatedTypeRemoved";
case LocalDiagID::protocol_req_added:
return "protocolReqAdded";
case LocalDiagID::decl_new_witness_table_entry:
return "declNewWitnessTableEntry";
case LocalDiagID::super_class_removed:
return "superClassRemoved";
case LocalDiagID::super_class_changed:
return "superClassChanged";
case LocalDiagID::no_longer_open:
return "noLongerOpen";
case LocalDiagID::desig_init_added:
return "desigInitAdded";
case LocalDiagID::added_invisible_designated_init:
return "addedInvisibleDesignatedInit";
case LocalDiagID::not_inheriting_convenience_inits:
return "notInheritingConvenienceInits";
default:
return "other";
}
}
}

swift::ide::api::
Expand Down Expand Up @@ -123,6 +199,45 @@ swift::ide::api::ModuleDifferDiagsConsumer::~ModuleDifferDiagsConsumer() {
}
}

void swift::ide::api::ModuleDifferDiagnosticInfo::serialize(
llvm::json::OStream &JSON) {
JSON.object([&]() {
JSON.attribute("identifier", getSerializedIdentifier((uint32_t)ID));
JSON.attribute("text", Text);
});
}

void swift::ide::api::ModuleDifferDiagsJSONConsumer::handleDiagnostic(
SourceManager &SM, const DiagnosticInfo &Info) {
llvm::SmallString<256> Text;
{
llvm::raw_svector_ostream Out(Text);
DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString,
Info.FormatArgs);
}

AllDiags.push_back(ModuleDifferDiagnosticInfo(Info.ID, Text));
}

swift::ide::api::ModuleDifferDiagsJSONConsumer::
~ModuleDifferDiagsJSONConsumer() {
llvm::json::OStream JSON(OS, 2);

JSON.object([&]() {
JSON.attributeObject("version", [&]() {
JSON.attribute("major", 0);
JSON.attribute("minor", 1);
});
JSON.attributeArray("diagnostics", [&]() {
for (auto &Info : AllDiags) {
Info.serialize(JSON);
}
});
});

JSON.flush();
}

bool swift::ide::api::
FilteringDiagnosticConsumer::shouldProceed(const DiagnosticInfo &Info) {
if (allowedBreakages->empty()) {
Expand Down
220 changes: 220 additions & 0 deletions test/api-digester/Outputs/Cake-diff-diags.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
{
"version": {
"major": 0,
"minor": 1
},
"diagnostics": [
{
"identifier": "declTypeChange",
"text": "cake: Constructor S1.init(_:) has parameter 0 type change from Swift.Int to Swift.Double"
},
{
"identifier": "funcSelfAccessChange",
"text": "cake: Func S1.foo1() has self access kind changing from NonMutating to Mutating"
},
{
"identifier": "declNewAttr",
"text": "cake: Func S1.foo3() is now static"
},
{
"identifier": "declNewAttr",
"text": "cake: Func C1.foo1() is now not static"
},
{
"identifier": "declTypeChange",
"text": "cake: Func C1.foo2(_:) has parameter 0 type change from Swift.Int to () -> ()"
},
{
"identifier": "declAttrChange",
"text": "cake: Var C1.CIIns1 changes from weak to strong"
},
{
"identifier": "declAttrChange",
"text": "cake: Var C1.CIIns2 changes from strong to weak"
},
{
"identifier": "superClassChanged",
"text": "cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType"
},
{
"identifier": "declNewAttr",
"text": "cake: Enum IceKind is now without @frozen"
},
{
"identifier": "conformanceRemoved",
"text": "cake: Enum IceKind has removed conformance to Sendable"
},
{
"identifier": "genericSigChange",
"text": "cake: Func P1.P1Constraint() has generic signature change from <Self where Self : cake.P1, Self : cake.P2> to <Self where Self : cake.P1>"
},
{
"identifier": "conformanceRemoved",
"text": "cake: Struct fixedLayoutStruct has removed conformance to P1"
},
{
"identifier": "enumCaseAdded",
"text": "cake: EnumElement FrozenKind.AddedCase has been added as a new enum case"
},
{
"identifier": "defaultArgRemoved",
"text": "cake: Func C7.foo(_:_:) has removed default argument from parameter 0"
},
{
"identifier": "defaultArgRemoved",
"text": "cake: Func C7.foo(_:_:) has removed default argument from parameter 1"
},
{
"identifier": "genericSigChange",
"text": "cake: Protocol P3 has generic signature change from <Self : cake.P1, Self : cake.P2> to <Self : cake.P1, Self : cake.P4>"
},
{
"identifier": "conformanceRemoved",
"text": "cake: Protocol P3 has removed inherited protocol P2"
},
{
"identifier": "conformanceAdded",
"text": "cake: Protocol P3 has added inherited protocol P4"
},
{
"identifier": "defaultAssociatedTypeRemoved",
"text": "cake: AssociatedType AssociatedTypePro.T1 has removed default type Swift.Int"
},
{
"identifier": "declTypeChange",
"text": "cake: AssociatedType AssociatedTypePro.T3 has default type change from cake.C1 to cake.C6"
},
{
"identifier": "removedDecl",
"text": "cake: Accessor RemoveSetters.Value.Set() has been removed"
},
{
"identifier": "removedDecl",
"text": "cake: Accessor RemoveSetters.subscript(_:).Set() has been removed"
},
{
"identifier": "protocolReqAdded",
"text": "cake: AssociatedType RequiementChanges.addedTypeWithoutDefault has been added as a protocol requirement"
},
{
"identifier": "protocolReqAdded",
"text": "cake: Func RequiementChanges.addedFunc() has been added as a protocol requirement"
},
{
"identifier": "protocolReqAdded",
"text": "cake: Var RequiementChanges.addedVar has been added as a protocol requirement"
},
{
"identifier": "superClassRemoved",
"text": "cake: Class SuperClassRemoval has removed its super class cake.C3"
},
{
"identifier": "notInheritingConvenienceInits",
"text": "cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass"
},
{
"identifier": "declKindChanged",
"text": "cake: Class ClassToStruct has been changed to a Struct"
},
{
"identifier": "desigInitAdded",
"text": "cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class"
},
{
"identifier": "declKindChanged",
"text": "cake: Protocol ProtocolToEnum has been changed to a Enum"
},
{
"identifier": "superClassChanged",
"text": "cake: Class SubGenericClass has changed its super class from cake.GenericClass<cake.P1> to cake.GenericClass<cake.P2>"
},
{
"identifier": "optionalReqChanged",
"text": "cake: Func ObjCProtocol.removeOptional() is no longer an optional requirement"
},
{
"identifier": "removedDecl",
"text": "cake: Accessor GlobalVarChangedToLet.Set() has been removed"
},
{
"identifier": "noLongerOpen",
"text": "cake: Func ClassWithOpenMember.foo() is no longer open for subclassing"
},
{
"identifier": "noLongerOpen",
"text": "cake: Var ClassWithOpenMember.property is no longer open for subclassing"
},
{
"identifier": "noLongerOpen",
"text": "cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclassing"
},
{
"identifier": "noLongerOpen",
"text": "cake: Func ClassWithOpenMember.bar() is no longer open for subclassing"
},
{
"identifier": "declKindChanged",
"text": "cake: InfixOperator ..*.. has been changed to a PrefixOperator"
},
{
"identifier": "paramOwnershipChange",
"text": "cake: Func ownershipChange(_:_:) has parameter 0 changing from InOut to Default"
},
{
"identifier": "paramOwnershipChange",
"text": "cake: Func ownershipChange(_:_:) has parameter 1 changing from Shared to Owned"
},
{
"identifier": "declTypeChange",
"text": "cake: TypeAlias TChangesFromIntToString.T has underlying type change from Swift.Int to Swift.String"
},
{
"identifier": "funcSelfAccessChange",
"text": "cake: Func HasMutatingMethodClone.foo() has self access kind changing from Mutating to NonMutating"
},
{
"identifier": "objcNameChange",
"text": "cake: Class SwiftObjcClass has ObjC name change from OldObjCClass to NewObjCClass"
},
{
"identifier": "objcNameChange",
"text": "cake: Func SwiftObjcClass.foo(a:b:c:) has ObjC name change from OldObjCFool:OldObjCA:OldObjCB: to NewObjCFool:NewObjCA:NewObjCB:"
},
{
"identifier": "desigInitAdded",
"text": "cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class"
},
{
"identifier": "renamedDecl",
"text": "cake: Func S1.foo5(x:y:) has been renamed to Func foo5(x:y:z:)"
},
{
"identifier": "removedDecl",
"text": "cake: Constructor Somestruct2.init(_:) has been removed"
},
{
"identifier": "renamedDecl",
"text": "cake: Struct Somestruct2 has been renamed to Struct NSSomestruct2"
},
{
"identifier": "removedDecl",
"text": "cake: Func C4.foo() has been removed"
},
{
"identifier": "removedDecl",
"text": "cake: Func RequiementChanges.removedFunc() has been removed"
},
{
"identifier": "removedDecl",
"text": "cake: AssociatedType RequiementChanges.removedType has been removed"
},
{
"identifier": "removedDecl",
"text": "cake: Var RequiementChanges.removedVar has been removed"
},
{
"identifier": "removedDecl",
"text": "cake: Func Int.IntEnhancer() has been removed"
}
]
}
Loading