Exclude private decls from interface token hash.

After parsing a private decl, reset the token hash state to what it was
before the decl was parsed. This way, adding a private decl or editing
its type or name will not trigger a rebuild of downstream files.

<rdar://problem/22239821> Modifying private APIs shouldn't cause dependents to recompile

Swift SVN r31358
This commit is contained in:
Chris Willmore
2015-08-20 03:43:43 +00:00
parent c33e3efe59
commit 0e326dbeeb
4 changed files with 69 additions and 0 deletions

View File

@@ -1083,6 +1083,9 @@ public:
InterfaceHash.update(a);
}
const llvm::MD5 &getInterfaceHashState() { return InterfaceHash; }
void setInterfaceHashState(const llvm::MD5 &state) { InterfaceHash = state; }
void getInterfaceHash(llvm::SmallString<32> &str) {
llvm::MD5::MD5Result result;
InterfaceHash.final(result);

View File

@@ -174,6 +174,36 @@ namespace {
P.markWasHandled(D);
}
};
/// An RAII type to exclude tokens contributing to private decls from the
/// interface hash of the source file. On destruct, it checks if the set of
/// attributes includes the "private" attribute; if so, it resets the MD5
/// hash of the source file to what it was when the IgnorePrivateDeclTokens
/// instance was created, thus excluding from the interface hash all tokens
/// parsed in the meantime.
struct IgnorePrivateDeclTokens {
Parser &TheParser;
DeclAttributes &Attributes;
Optional<llvm::MD5> SavedHashState;
IgnorePrivateDeclTokens(Parser &P, DeclAttributes &Attrs)
: TheParser(P), Attributes(Attrs) {
if (TheParser.IsParsingInterfaceTokens) {
SavedHashState = TheParser.SF.getInterfaceHashState();
}
}
~IgnorePrivateDeclTokens() {
if (!SavedHashState)
return;
if (auto *attr = Attributes.getAttribute<AbstractAccessibilityAttr>()) {
if (attr->getAccess() == Accessibility::Private) {
TheParser.SF.setInterfaceHashState(*SavedHashState);
}
}
}
};
}
/// \brief Main entrypoint for the parser.
@@ -1711,6 +1741,7 @@ void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition,
BeginParserPosition.PreviousLoc);
skipUntil(tok::eof);
}
/// \brief Parse a single syntactic declaration and return a list of decl
/// ASTs. This can return multiple results for var decls that bind to multiple
/// values, structs that define a struct decl and a constructor, etc.
@@ -1742,6 +1773,7 @@ ParserStatus Parser::parseDecl(SmallVectorImpl<Decl*> &Entries,
StructureMarkerKind::Declaration);
DeclAttributes Attributes;
IgnorePrivateDeclTokens IgnoreTokens(*this, Attributes);
if (Tok.hasComment())
Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
bool FoundCCTokenInAttr;

View File

@@ -0,0 +1,23 @@
// RUN: mkdir -p %t
// RUN: %S/../../utils/split_file.py -o %t %s
// RUN: %target-swift-frontend -dump-interface-hash %t/a.swift 2> %t/a.hash
// RUN: %target-swift-frontend -dump-interface-hash %t/b.swift 2> %t/b.hash
// RUN: cmp %t/a.hash %t/b.hash
// BEGIN a.swift
class C {
func f2() -> Int {
return 0
}
}
// BEGIN b.swift
class C {
func f2() -> Int {
return 0
}
private func f3() -> Int {
return 1
}
}

View File

@@ -0,0 +1,11 @@
// RUN: mkdir -p %t
// RUN: %S/../../utils/split_file.py -o %t %s
// RUN: %target-swift-frontend -dump-interface-hash %t/a.swift 2> %t/a.hash
// RUN: %target-swift-frontend -dump-interface-hash %t/b.swift 2> %t/b.hash
// RUN: cmp %t/a.hash %t/b.hash
// BEGIN a.swift
private var x: Int
// BEGIN b.swift
private var x: Float