Skip to content

Commit 9c9ba68

Browse files
committed
Parse/Sema: Fix diagnostics for missing wildcards in @available.
The previous algorithm failed to correctly handle the cases where some grouped `@available` attributes could be marked invalid prior to type checking attributes.
1 parent 7e58d82 commit 9c9ba68

File tree

4 files changed

+41
-27
lines changed

4 files changed

+41
-27
lines changed

include/swift/AST/Attr.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,20 @@ class DeclAttribute : public AttributeBase {
165165
/// Whether this attribute was spelled `@_spi_available`.
166166
IsSPI : 1,
167167

168-
/// Whether this attribute belongs to a chain of adjacent `@available` attributes that were generated from a single attribute written in source using short form syntax e.g. (`@available(macOS 15, iOS 18, *)`).
168+
/// Whether this attribute belongs to a chain of adjacent `@available`
169+
/// attributes that were generated from a single attribute written in
170+
/// source using short form syntax, e.g.
171+
///
172+
/// @available(macOS 15, iOS 18, *)
173+
///
169174
IsGroupMember : 1,
170175

171176
/// Whether this attribute is the final one in its group.
172177
IsGroupTerminator : 1,
173178

174-
/// Whether this attribute's specification was followed by `, *` in source.
175-
IsAdjacentToWildcard : 1
179+
/// Whether any members of the group were written as a wildcard
180+
/// specification (`*`) in source.
181+
IsGroupedWithWildcard : 1
176182
);
177183

178184
SWIFT_INLINE_BITFIELD(ClangImporterSynthesizedTypeAttr, DeclAttribute, 1,
@@ -848,12 +854,13 @@ class AvailableAttr : public DeclAttribute {
848854
}
849855
void setIsGroupTerminator() { Bits.AvailableAttr.IsGroupTerminator = true; }
850856

851-
/// Whether this attribute's specification was followed by `, *` in source.
852-
bool isAdjacentToWildcard() const {
853-
return Bits.AvailableAttr.IsAdjacentToWildcard;
857+
/// Whether any members of the group were written as a wildcard specification
858+
/// (`*`) in source.
859+
bool isGroupedWithWildcard() const {
860+
return Bits.AvailableAttr.IsGroupedWithWildcard;
854861
}
855-
void setIsAdjacentToWildcard() {
856-
Bits.AvailableAttr.IsAdjacentToWildcard = true;
862+
void setIsGroupedWithWildcard() {
863+
Bits.AvailableAttr.IsGroupedWithWildcard = true;
857864
}
858865

859866
/// Returns the kind of availability the attribute specifies.

lib/AST/Attr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2146,7 +2146,7 @@ AvailableAttr::AvailableAttr(
21462146
Bits.AvailableAttr.IsSPI = IsSPI;
21472147
Bits.AvailableAttr.IsGroupMember = false;
21482148
Bits.AvailableAttr.IsGroupTerminator = false;
2149-
Bits.AvailableAttr.IsAdjacentToWildcard = false;
2149+
Bits.AvailableAttr.IsGroupedWithWildcard = false;
21502150
}
21512151

21522152
AvailableAttr *AvailableAttr::createUniversallyUnavailable(ASTContext &C,

lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -790,13 +790,13 @@ bool Parser::parseAvailability(
790790
// we will synthesize
791791
// @available(_PackageDescription, introduced: 4.2)
792792

793+
bool groupContainsWildcard = llvm::any_of(
794+
Specs, [](AvailabilitySpec *Spec) { return Spec->isWildcard(); });
795+
793796
AvailableAttr *PrevAttr = nullptr;
794797
for (auto *Spec : Specs) {
795-
if (Spec->isWildcard()) {
796-
if (PrevAttr)
797-
PrevAttr->setIsAdjacentToWildcard();
798+
if (Spec->isWildcard())
798799
continue;
799-
}
800800

801801
std::optional<AvailabilityDomain> Domain = Spec->getDomain();
802802
if (!Domain)
@@ -817,6 +817,8 @@ bool Parser::parseAvailability(
817817
addAttribute(Attr);
818818

819819
Attr->setIsGroupMember();
820+
if (groupContainsWildcard)
821+
Attr->setIsGroupedWithWildcard();
820822
if (!PrevAttr)
821823
Attr->setIsGroupTerminator();
822824

lib/Sema/TypeCheckAttr.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4936,24 +4936,29 @@ void AttributeChecker::checkOriginalDefinedInAttrs(
49364936
/// Find each of the `AvailableAttr`s that represents the first attribute in a
49374937
/// group of attributes what were parsed from a short-form available attribute,
49384938
/// e.g. `@available(macOS , iOS, *)`.
4939-
static llvm::SmallSet<const AvailableAttr *, 8>
4940-
getAvailableAttrGroups(ArrayRef<AvailableAttr *> attrs) {
4941-
llvm::SmallSet<const AvailableAttr *, 8> heads;
4939+
static llvm::SmallVector<const AvailableAttr *, 4>
4940+
getAvailableAttrGroups(ArrayRef<const AvailableAttr *> attrs) {
4941+
llvm::SmallSet<const AvailableAttr *, 8> seen;
49424942

4943-
// Find each attribute that belongs to a group.
4943+
// Collect the of the grouped attributes that are reachable starting from any
4944+
// other attribute.
49444945
for (auto attr : attrs) {
4945-
if (attr->isGroupMember())
4946-
heads.insert(attr);
4946+
auto next = attr;
4947+
while ((next = next->getNextGroupedAvailableAttr())) {
4948+
if (!seen.insert(next).second)
4949+
break;
4950+
}
49474951
}
49484952

4949-
// Remove the interior attributes of each group, leaving only the head.
4953+
// The grouped attributes that are _not_ reachable from any other attribute
4954+
// are the results.
4955+
llvm::SmallVector<const AvailableAttr *, 4> results;
49504956
for (auto attr : attrs) {
4951-
if (auto next = attr->getNextGroupedAvailableAttr()) {
4952-
heads.erase(next);
4953-
}
4957+
if (attr->isGroupMember() && !seen.contains(attr))
4958+
results.push_back(attr);
49544959
}
49554960

4956-
return heads;
4961+
return results;
49574962
}
49584963

49594964
void AttributeChecker::checkAvailableAttrs(ArrayRef<AvailableAttr *> attrs) {
@@ -4982,7 +4987,7 @@ void AttributeChecker::checkAvailableAttrs(ArrayRef<AvailableAttr *> attrs) {
49824987
groupAttrCount++;
49834988
auto loc = groupedAttr->getLocation();
49844989
groupEndLoc = groupedAttr->getEndLoc();
4985-
if (groupedAttr->isAdjacentToWildcard())
4990+
if (groupedAttr->isGroupedWithWildcard())
49864991
foundWildcard = true;
49874992

49884993
auto attr = D->getSemanticAvailableAttr(groupedAttr);
@@ -4997,8 +5002,8 @@ void AttributeChecker::checkAvailableAttrs(ArrayRef<AvailableAttr *> attrs) {
49975002
if (domain.isPlatform())
49985003
requiresWildcard = true;
49995004

5000-
if (groupAttrCount > 1 || groupedAttr->isAdjacentToWildcard() ||
5001-
!groupedAttr->isGroupTerminator()) {
5005+
if (groupAttrCount > 1 || !groupedAttr->isGroupTerminator() ||
5006+
foundWildcard) {
50025007
// Only platform availability is allowed to be written groups with more
50035008
// than one member.
50045009
if (!domain.isPlatform()) {

0 commit comments

Comments
 (0)