Skip to content

Commit bde58e7

Browse files
authored
Merge pull request #60208 from DougGregor/has-attribute
2 parents f680ddc + 3f8d80a commit bde58e7

File tree

9 files changed

+374
-50
lines changed

9 files changed

+374
-50
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2632,6 +2632,10 @@ inline SourceLoc extractNearestSourceLoc(const DeclAttribute *attr) {
26322632
return attr->getLocation();
26332633
}
26342634

2635+
/// Determine whether the given attribute is available, looking up the
2636+
/// attribute by name.
2637+
bool hasAttribute(const LangOptions &langOpts, llvm::StringRef attributeName);
2638+
26352639
} // end namespace swift
26362640

26372641
#endif

include/swift/Parse/Parser.h

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ class ConsumeTokenReceiver {
100100
virtual ~ConsumeTokenReceiver() = default;
101101
};
102102

103+
/// The role of the elements in a given #if
104+
enum class IfConfigElementsRole {
105+
// Parse normally.
106+
Normal,
107+
// Parse, but only for syntax. Throw away the results.
108+
SyntaxOnly,
109+
// Already skipped; no need to parse anything.
110+
Skipped
111+
};
112+
103113
/// The main class used for parsing a source file (.swift or .sil).
104114
///
105115
/// Rather than instantiating a Parser yourself, use one of the parsing APIs
@@ -684,7 +694,8 @@ class Parser {
684694
while (Tok.isNot(K..., tok::eof, tok::r_brace, tok::pound_endif,
685695
tok::pound_else, tok::pound_elseif,
686696
tok::code_complete) &&
687-
!isStartOfStmt() && !isStartOfSwiftDecl()) {
697+
!isStartOfStmt() &&
698+
!isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) {
688699
skipSingle();
689700
}
690701
}
@@ -945,7 +956,7 @@ class Parser {
945956
// Decl Parsing
946957

947958
/// Returns true if parser is at the start of a Swift decl or decl-import.
948-
bool isStartOfSwiftDecl();
959+
bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true);
949960

950961
/// Returns true if the parser is at the start of a SIL decl.
951962
bool isStartOfSILDecl();
@@ -982,6 +993,7 @@ class Parser {
982993

983994
ParserResult<Decl> parseDecl(ParseDeclOptions Flags,
984995
bool IsAtStartOfLineOrPreviousHadSemi,
996+
bool IfConfigsAreDeclAttrs,
985997
llvm::function_ref<void(Decl*)> Handler);
986998

987999
std::pair<std::vector<Decl *>, Optional<Fingerprint>>
@@ -1004,12 +1016,29 @@ class Parser {
10041016

10051017
ParserResult<TypeDecl> parseDeclAssociatedType(ParseDeclOptions Flags,
10061018
DeclAttributes &Attributes);
1007-
1019+
1020+
/// Parse a #if ... #endif directive.
1021+
/// Delegate callback function to parse elements in the blocks and record
1022+
/// them however they wish. The parsing function will be provided with the
1023+
/// location of the clause token (`#if`, `#else`, etc.), the condition,
1024+
/// whether this is the active clause, and the role of the elements.
1025+
template<typename Result>
1026+
Result parseIfConfigRaw(
1027+
llvm::function_ref<void(SourceLoc clauseLoc, Expr *condition,
1028+
bool isActive, IfConfigElementsRole role)>
1029+
parseElements,
1030+
llvm::function_ref<Result(SourceLoc endLoc, bool hadMissingEnd)> finish);
1031+
10081032
/// Parse a #if ... #endif directive.
10091033
/// Delegate callback function to parse elements in the blocks.
10101034
ParserResult<IfConfigDecl> parseIfConfig(
10111035
llvm::function_ref<void(SmallVectorImpl<ASTNode> &, bool)> parseElements);
10121036

1037+
/// Parse an #if ... #endif containing only attributes.
1038+
ParserStatus parseIfConfigDeclAttributes(
1039+
DeclAttributes &attributes, bool ifConfigsAreDeclAttrs,
1040+
PatternBindingInitializer *initContext);
1041+
10131042
/// Parse a #error or #warning diagnostic.
10141043
ParserResult<PoundDiagnosticDecl> parseDeclPoundDiagnostic();
10151044

@@ -1020,8 +1049,26 @@ class Parser {
10201049
void setLocalDiscriminator(ValueDecl *D);
10211050
void setLocalDiscriminatorToParamList(ParameterList *PL);
10221051

1052+
/// Skip an `#if` configuration block containing only attributes.
1053+
///
1054+
/// \returns true if the skipping was successful, false otherwise.
1055+
bool skipIfConfigOfAttributes(bool &sawAnyAttributes);
1056+
1057+
/// Determine whether the `#if` at which the parser occurs only contains
1058+
/// attributes (in all branches), in which case it is treated as part of
1059+
/// an attribute list.
1060+
bool ifConfigContainsOnlyAttributes();
1061+
10231062
/// Parse the optional attributes before a declaration.
1024-
ParserStatus parseDeclAttributeList(DeclAttributes &Attributes);
1063+
ParserStatus parseDeclAttributeList(DeclAttributes &Attributes,
1064+
bool IfConfigsAreDeclAttrs = false);
1065+
1066+
/// Parse the optional attributes before a declaration.
1067+
///
1068+
/// This is the inner loop, which can be called recursively.
1069+
ParserStatus parseDeclAttributeList(DeclAttributes &Attributes,
1070+
bool IfConfigsAreDeclAttrs,
1071+
PatternBindingInitializer *initContext);
10251072

10261073
/// Parse the optional modifiers before a declaration.
10271074
bool parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc,

lib/AST/Attr.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,3 +2285,23 @@ void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
22852285
if (attr)
22862286
attr->print(out);
22872287
}
2288+
2289+
bool swift::hasAttribute(
2290+
const LangOptions &langOpts, llvm::StringRef attributeName) {
2291+
DeclAttrKind kind = DeclAttribute::getAttrKindFromString(attributeName);
2292+
if (kind == DAK_Count)
2293+
return false;
2294+
2295+
if (DeclAttribute::isUserInaccessible(kind))
2296+
return false;
2297+
if (DeclAttribute::isDeclModifier(kind))
2298+
return false;
2299+
if (DeclAttribute::shouldBeRejectedByParser(kind))
2300+
return false;
2301+
if (DeclAttribute::isSilOnly(kind))
2302+
return false;
2303+
if (DeclAttribute::isConcurrencyOnly(kind))
2304+
return false;
2305+
2306+
return true;
2307+
}

lib/Parse/ParseDecl.cpp

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3804,26 +3804,41 @@ ParserStatus Parser::parseTypeAttribute(TypeAttributes &Attributes,
38043804
return makeParserSuccess();
38053805
}
38063806

3807+
ParserStatus Parser::parseDeclAttributeList(
3808+
DeclAttributes &Attributes, bool ifConfigsAreDeclAttrs,
3809+
PatternBindingInitializer *initContext) {
3810+
ParserStatus Status;
3811+
while (Tok.isAny(tok::at_sign, tok::pound_if)) {
3812+
if (Tok.is(tok::at_sign)) {
3813+
SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute);
3814+
SourceLoc AtLoc = consumeToken();
3815+
Status |= parseDeclAttribute(Attributes, AtLoc, initContext);
3816+
} else {
3817+
if (!ifConfigsAreDeclAttrs && !ifConfigContainsOnlyAttributes())
3818+
break;
3819+
3820+
Status |= parseIfConfigDeclAttributes(
3821+
Attributes, ifConfigsAreDeclAttrs, initContext);
3822+
}
3823+
}
3824+
return Status;
3825+
}
3826+
38073827
/// \verbatim
38083828
/// attribute-list:
38093829
/// /*empty*/
38103830
/// attribute-list-clause attribute-list
38113831
/// attribute-list-clause:
38123832
/// '@' attribute
38133833
/// \endverbatim
3814-
ParserStatus Parser::parseDeclAttributeList(DeclAttributes &Attributes) {
3815-
if (Tok.isNot(tok::at_sign))
3834+
ParserStatus Parser::parseDeclAttributeList(
3835+
DeclAttributes &Attributes, bool IfConfigsAreDeclAttrs) {
3836+
if (Tok.isNot(tok::at_sign, tok::pound_if))
38163837
return makeParserSuccess();
38173838

38183839
PatternBindingInitializer *initContext = nullptr;
3819-
ParserStatus Status;
38203840
SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList);
3821-
do {
3822-
SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute);
3823-
SourceLoc AtLoc = consumeToken();
3824-
Status |= parseDeclAttribute(Attributes, AtLoc, initContext);
3825-
} while (Tok.is(tok::at_sign));
3826-
return Status;
3841+
return parseDeclAttributeList(Attributes, IfConfigsAreDeclAttrs, initContext);
38273842
}
38283843

38293844
/// \verbatim
@@ -3913,7 +3928,8 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes,
39133928
BacktrackingScope Scope(*this);
39143929

39153930
consumeToken(); // consume actor
3916-
isActorModifier = isStartOfSwiftDecl();
3931+
isActorModifier = isStartOfSwiftDecl(
3932+
/*allowPoundIfAttributes=*/false);
39173933
}
39183934

39193935
if (!isActorModifier)
@@ -4284,7 +4300,7 @@ static void skipAttribute(Parser &P) {
42844300
}
42854301
}
42864302

4287-
bool Parser::isStartOfSwiftDecl() {
4303+
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
42884304
if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) {
42894305
// @rethrows does not follow the general rule of @<identifier> so
42904306
// it is needed to short circuit this else there will be an infinite
@@ -4318,7 +4334,7 @@ bool Parser::isStartOfSwiftDecl() {
43184334
// check 'let' and 'var' right now.
43194335
if (Tok.is(tok::kw_try))
43204336
return peekToken().isAny(tok::kw_let, tok::kw_var);
4321-
4337+
43224338
// Skip an attribute, since it might be a type attribute. This can't
43234339
// happen at the top level of a scope, but we do use isStartOfSwiftDecl()
43244340
// in positions like generic argument lists.
@@ -4329,10 +4345,20 @@ bool Parser::isStartOfSwiftDecl() {
43294345

43304346
// If this attribute is the last element in the block,
43314347
// consider it is a start of incomplete decl.
4332-
if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif))
4348+
if (Tok.isAny(tok::r_brace, tok::eof) ||
4349+
(Tok.is(tok::pound_endif) && !allowPoundIfAttributes))
43334350
return true;
43344351

4335-
return isStartOfSwiftDecl();
4352+
return isStartOfSwiftDecl(allowPoundIfAttributes);
4353+
}
4354+
4355+
// Skip a #if that contains only attributes in all branches. These will be
4356+
// parsed as attributes of a declaration, not as separate declarations.
4357+
if (Tok.is(tok::pound_if) && allowPoundIfAttributes) {
4358+
BacktrackingScope backtrack(*this);
4359+
bool sawAnyAttributes = false;
4360+
return skipIfConfigOfAttributes(sawAnyAttributes) &&
4361+
(Tok.is(tok::eof) || (sawAnyAttributes && isStartOfSwiftDecl()));
43364362
}
43374363

43384364
// If we have a decl modifying keyword, check if the next token is a valid
@@ -4354,13 +4380,13 @@ bool Parser::isStartOfSwiftDecl() {
43544380
// If we found the start of a decl while trying to skip over the
43554381
// paren, then we have something incomplete like 'private('. Return
43564382
// true for better recovery.
4357-
if (isStartOfSwiftDecl())
4383+
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false))
43584384
return true;
43594385

43604386
skipSingle();
43614387
}
43624388
}
4363-
return isStartOfSwiftDecl();
4389+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
43644390
}
43654391
}
43664392

@@ -4387,7 +4413,7 @@ bool Parser::isStartOfSwiftDecl() {
43874413
consumeToken(tok::l_paren);
43884414
consumeToken(tok::identifier);
43894415
consumeToken(tok::r_paren);
4390-
return isStartOfSwiftDecl();
4416+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
43914417
}
43924418

43934419
if (Tok.isContextualKeyword("actor")) {
@@ -4399,7 +4425,7 @@ bool Parser::isStartOfSwiftDecl() {
43994425
// it's an actor declaration, otherwise, it isn't.
44004426
do {
44014427
consumeToken();
4402-
} while (isStartOfSwiftDecl());
4428+
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false));
44034429
return Tok.is(tok::identifier);
44044430
}
44054431

@@ -4410,7 +4436,7 @@ bool Parser::isStartOfSwiftDecl() {
44104436
// Otherwise, do a recursive parse.
44114437
Parser::BacktrackingScope Backtrack(*this);
44124438
consumeToken(tok::identifier);
4413-
return isStartOfSwiftDecl();
4439+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
44144440
}
44154441

44164442
bool Parser::isStartOfSILDecl() {
@@ -4521,12 +4547,13 @@ setOriginalDeclarationForDifferentiableAttributes(DeclAttributes attrs,
45214547
ParserResult<Decl>
45224548
Parser::parseDecl(ParseDeclOptions Flags,
45234549
bool IsAtStartOfLineOrPreviousHadSemi,
4550+
bool IfConfigsAreDeclAttrs,
45244551
llvm::function_ref<void(Decl*)> Handler) {
45254552
ParserPosition BeginParserPosition;
45264553
if (isCodeCompletionFirstPass())
45274554
BeginParserPosition = getParserPosition();
45284555

4529-
if (Tok.is(tok::pound_if)) {
4556+
if (Tok.is(tok::pound_if) && !ifConfigContainsOnlyAttributes()) {
45304557
auto IfConfigResult = parseIfConfig(
45314558
[&](SmallVectorImpl<ASTNode> &Decls, bool IsActive) {
45324559
ParserStatus Status;
@@ -4585,7 +4612,8 @@ Parser::parseDecl(ParseDeclOptions Flags,
45854612
DeclAttributes Attributes;
45864613
if (Tok.hasComment())
45874614
Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
4588-
ParserStatus AttrStatus = parseDeclAttributeList(Attributes);
4615+
ParserStatus AttrStatus = parseDeclAttributeList(
4616+
Attributes, IfConfigsAreDeclAttrs);
45894617

45904618
// Parse modifiers.
45914619
// Keep track of where and whether we see a contextual keyword on the decl.
@@ -5367,7 +5395,9 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi,
53675395
if (loadCurrentSyntaxNodeFromCache()) {
53685396
return ParserStatus();
53695397
}
5370-
Result = parseDecl(Options, IsAtStartOfLineOrPreviousHadSemi, handler);
5398+
Result = parseDecl(
5399+
Options, IsAtStartOfLineOrPreviousHadSemi,
5400+
/* IfConfigsAreDeclAttrs=*/false, handler);
53715401
if (Result.isParseErrorOrHasCompletion())
53725402
skipUntilDeclRBrace(tok::semi, tok::pound_endif);
53735403
SourceLoc SemiLoc;
@@ -6211,7 +6241,8 @@ void Parser::skipSILUntilSwiftDecl() {
62116241
// Tell the lexer we're about to start lexing SIL.
62126242
Lexer::SILBodyRAII sbr(*L);
62136243

6214-
while (!Tok.is(tok::eof) && !isStartOfSwiftDecl()) {
6244+
while (!Tok.is(tok::eof) &&
6245+
!isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) {
62156246
// SIL pound dotted paths need to be skipped specially as they can contain
62166247
// decl keywords like 'subscript'.
62176248
if (consumeIf(tok::pound)) {

0 commit comments

Comments
 (0)