Skip to content

Commit d800f35

Browse files
committed
[api-digester] Add '-protocol-requirement-white-list' option to the digester for when diagnosing API breakage in SDKs
This option access a file that lists protocol names that are 'whitelisted' for getting new protocol requirements. This allows the SDK checker to avoid flagging protocol method additions for protocols that we know are not intended for user classes to conform to.
1 parent 953d5e8 commit d800f35

File tree

6 files changed

+80
-6
lines changed

6 files changed

+80
-6
lines changed

test/api-digester/Inputs/Foo-new-version/foo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
-(void) someOptionalFunctionFromProt;
1010
@end
1111

12+
@protocol AnotherObjcProt
13+
-(void) anotherFunctionFromProt;
14+
-(void) anotherFunctionFromProt2;
15+
@end
16+
1217
@interface ClangInterface: NSObject <ObjcProt>
1318
- (void)someFunction;
1419
@end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AnotherObjcProt

test/api-digester/Inputs/Foo/foo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
-(void) someFunctionFromProt;
55
@end
66

7+
@protocol AnotherObjcProt
8+
-(void) anotherFunctionFromProt;
9+
@end
10+
711
@interface ClangInterface: NSObject <ObjcProt>
812
- (void)someFunction;
913
@end

test/api-digester/Outputs/clang-module-dump.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,47 @@
33
"name": "TopLevel",
44
"printedName": "TopLevel",
55
"children": [
6+
{
7+
"kind": "TypeDecl",
8+
"name": "AnotherObjcProt",
9+
"printedName": "AnotherObjcProt",
10+
"children": [
11+
{
12+
"kind": "Function",
13+
"name": "anotherFunctionFromProt",
14+
"printedName": "anotherFunctionFromProt()",
15+
"children": [
16+
{
17+
"kind": "TypeNameAlias",
18+
"name": "Void",
19+
"printedName": "Void",
20+
"children": [
21+
{
22+
"kind": "TypeNominal",
23+
"name": "Void",
24+
"printedName": "()"
25+
}
26+
]
27+
}
28+
],
29+
"declKind": "Func",
30+
"usr": "c:objc(pl)AnotherObjcProt(im)anotherFunctionFromProt",
31+
"moduleName": "Foo",
32+
"genericSig": "<Self where Self : AnotherObjcProt>",
33+
"protocolReq": true,
34+
"declAttributes": [
35+
"ObjC"
36+
],
37+
"funcSelfKind": "NonMutating"
38+
}
39+
],
40+
"declKind": "Protocol",
41+
"usr": "c:objc(pl)AnotherObjcProt",
42+
"moduleName": "Foo",
43+
"declAttributes": [
44+
"ObjC"
45+
]
46+
},
647
{
748
"kind": "TypeDecl",
849
"name": "ClangInterface",

test/api-digester/compare-clang-dump.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t.module-cache)
22
// RUN: %api-digester -dump-sdk -module Foo -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %S/Inputs/Foo -avoid-location
33
// RUN: %api-digester -dump-sdk -module Foo -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %S/Inputs/Foo-new-version -avoid-location
4-
// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result
4+
// RUN: %api-digester -diagnose-sdk -protocol-requirement-white-list %S/Inputs/Foo-prot-whitelist.txt -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result
55

66
// RUN: %clang -E -P -x c %S/Outputs/Foo-diff.txt -o - | sed '/^\s*$/d' > %t.expected
77
// RUN: %clang -E -P -x c %t.result -o - | sed '/^\s*$/d' > %t.result.tmp

tools/swift-api-digester/swift-api-digester.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ static llvm::cl::opt<std::string>
6363
ModuleList("module-list-file",
6464
llvm::cl::desc("File containing a new-line separated list of modules"));
6565

66+
static llvm::cl::opt<std::string>
67+
ProtReqWhiteList("protocol-requirement-white-list",
68+
llvm::cl::desc("File containing a new-line separated list of protocol names"));
69+
6670
static llvm::cl::opt<std::string>
6771
OutputFile("o", llvm::cl::desc("Output file"));
6872

@@ -927,6 +931,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
927931

928932
SDKContext &Ctx;
929933
UpdatedNodesMap &UpdateMap;
934+
llvm::StringSet<> ProtocolReqWhitelist;
930935

931936
static void printSpaces(llvm::raw_ostream &OS, SDKNode *N) {
932937
assert(N);
@@ -959,6 +964,10 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
959964

960965
public:
961966
PrunePass(SDKContext &Ctx): Ctx(Ctx), UpdateMap(Ctx.getNodeUpdateMap()) {}
967+
PrunePass(SDKContext &Ctx, llvm::StringSet<> prWhitelist):
968+
Ctx(Ctx),
969+
UpdateMap(Ctx.getNodeUpdateMap()),
970+
ProtocolReqWhitelist(std::move(prWhitelist)) {}
962971

963972
void foundMatch(NodePtr Left, NodePtr Right, NodeMatchReason Reason) override {
964973
if (options::Verbose)
@@ -985,6 +994,12 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
985994
if (ATD->getDefault())
986995
ShouldComplain = false;
987996
}
997+
if (ShouldComplain &&
998+
ProtocolReqWhitelist.count(D->getParent()->getAs<SDKNodeDecl>()->getFullyQualifiedName())) {
999+
// Ignore protocol requirement additions if the protocol has been added
1000+
// to the whitelist.
1001+
ShouldComplain = false;
1002+
}
9881003
if (ShouldComplain)
9891004
Ctx.getDiags().diagnose(SourceLoc(), diag::protocol_req_added,
9901005
D->getScreenInfo());
@@ -2027,7 +2042,8 @@ static void findTypeMemberDiffs(NodePtr leftSDKRoot, NodePtr rightSDKRoot,
20272042

20282043
static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
20292044
StringRef OutputPath,
2030-
CheckerOptions Opts) {
2045+
CheckerOptions Opts,
2046+
llvm::StringSet<> ProtocolReqWhitelist) {
20312047
if (!fs::exists(LeftPath)) {
20322048
llvm::errs() << LeftPath << " does not exist\n";
20332049
return 1;
@@ -2055,7 +2071,7 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath,
20552071
auto RightModule = RightCollector.getSDKRoot();
20562072
TypeAliasDiffFinder(LeftModule, RightModule,
20572073
Ctx.getTypeAliasUpdateMap()).search();
2058-
PrunePass Prune(Ctx);
2074+
PrunePass Prune(Ctx, std::move(ProtocolReqWhitelist));
20592075
Prune.pass(LeftModule, RightModule);
20602076
ChangeRefinementPass RefinementPass(Ctx.getNodeUpdateMap());
20612077
RefinementPass.pass(LeftModule, RightModule);
@@ -2182,7 +2198,7 @@ static int compareSDKs(StringRef LeftPath, StringRef RightPath,
21822198
static int readFileLineByLine(StringRef Path, llvm::StringSet<> &Lines) {
21832199
auto FileBufOrErr = llvm::MemoryBuffer::getFile(Path);
21842200
if (!FileBufOrErr) {
2185-
llvm::errs() << "error opening file: "
2201+
llvm::errs() << "error opening file '" << Path << "': "
21862202
<< FileBufOrErr.getError().message() << '\n';
21872203
return 1;
21882204
}
@@ -2347,19 +2363,26 @@ int main(int argc, char *argv[]) {
23472363
return (prepareForDump(argv[0], InitInvok, Modules)) ? 1 :
23482364
dumpSDKContent(InitInvok, Modules, options::OutputFile, Opts);
23492365
case ActionType::CompareSDKs:
2350-
case ActionType::DiagnoseSDKs:
2366+
case ActionType::DiagnoseSDKs: {
23512367
if (options::SDKJsonPaths.size() != 2) {
23522368
llvm::errs() << "Only two SDK versions can be compared\n";
23532369
llvm::cl::PrintHelpMessage();
23542370
return 1;
23552371
}
2372+
llvm::StringSet<> protocolWhitelist;
2373+
if (!options::ProtReqWhiteList.empty()) {
2374+
if (readFileLineByLine(options::ProtReqWhiteList, protocolWhitelist))
2375+
return 1;
2376+
}
23562377
if (options::Action == ActionType::CompareSDKs)
23572378
return compareSDKs(options::SDKJsonPaths[0], options::SDKJsonPaths[1],
23582379
options::OutputFile, IgnoredUsrs, Opts);
23592380
else
23602381
return diagnoseModuleChange(options::SDKJsonPaths[0],
23612382
options::SDKJsonPaths[1],
2362-
options::OutputFile, Opts);
2383+
options::OutputFile, Opts,
2384+
std::move(protocolWhitelist));
2385+
}
23632386
case ActionType::DeserializeSDK:
23642387
case ActionType::DeserializeDiffItems: {
23652388
if (options::SDKJsonPaths.size() != 1) {

0 commit comments

Comments
 (0)