From 673d6a1d9f755222e3eef44b901fde237234ca76 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Wed, 12 Mar 2025 21:44:45 +0200 Subject: [PATCH 01/12] [IDE] Rename `CustomSyntaxAttributeKind` to `ParameterizedDeclAttributeKind` --- include/swift/IDE/CompletionLookup.h | 2 +- include/swift/Parse/IDEInspectionCallbacks.h | 4 ++-- lib/IDE/CodeCompletion.cpp | 8 +++++--- lib/IDE/CompletionLookup.cpp | 16 ++++++++-------- lib/Parse/ParseDecl.cpp | 20 ++++++++++---------- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 9dca0b2bf7003..cdf47c718e7a8 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -612,7 +612,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void getAttributeDeclCompletions(bool IsInSil, std::optional DK); - void getAttributeDeclParamCompletions(CustomSyntaxAttributeKind AttrKind, + void getAttributeDeclParamCompletions(ParameterizedDeclAttributeKind AttrKind, int ParamIndex, bool HasLabel); void getTypeAttributeKeywordCompletions(CompletionKind completionKind); diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 809dde85833af..61183dd8c6405 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -32,7 +32,7 @@ enum class ObjCSelectorContext { /// Attributes that have syntax which can't be modelled using a function call. /// This can't be \c DeclAttrKind because '@freestandig' and '@attached' have /// the same attribute kind but take different macro roles as arguemnts. -enum class CustomSyntaxAttributeKind { +enum class ParameterizedDeclAttributeKind { Available, FreestandingMacro, AttachedMacro, @@ -228,7 +228,7 @@ class CodeCompletionCallbacks { /// @available. /// If `HasLabel` is `true`, then the argument already has a label specified, /// e.g. we're completing after `names: ` in a macro declaration. - virtual void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index, + virtual void completeDeclAttrParam(ParameterizedDeclAttributeKind DK, int Index, bool HasLabel){}; /// Complete 'async' and 'throws' at effects specifier position. diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index dc3b2ef54fbe1..1cc4aebf4a00c 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -114,7 +114,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, SourceLoc DotLoc; TypeLoc ParsedTypeLoc; DeclContext *CurDeclContext = nullptr; - CustomSyntaxAttributeKind AttrKind; + ParameterizedDeclAttributeKind AttrKind; /// When the code completion token occurs in a custom attribute, the attribute /// it occurs in. Used so we can complete inside the attribute even if it's @@ -272,7 +272,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, void completeCaseStmtKeyword() override; void completeCaseStmtBeginning(CodeCompletionExpr *E) override; void completeDeclAttrBeginning(bool Sil, bool isIndependent) override; - void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index, + void completeDeclAttrParam(ParameterizedDeclAttributeKind DK, int Index, bool HasLabel) override; void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override; void completeInPrecedenceGroup( @@ -460,7 +460,9 @@ void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { } void CodeCompletionCallbacksImpl::completeDeclAttrParam( - CustomSyntaxAttributeKind DK, int Index, bool HasLabel) { + ParameterizedDeclAttributeKind DK, int Index, bool HasLabel) { + assert(P.Tok.is(tok::code_complete)); + Kind = CompletionKind::AttributeDeclParen; AttrKind = DK; AttrParamIndex = Index; diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 91939902395c2..014118387619f 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3103,9 +3103,9 @@ void CompletionLookup::getAttributeDeclCompletions(bool IsInSil, } void CompletionLookup::getAttributeDeclParamCompletions( - CustomSyntaxAttributeKind AttrKind, int ParamIndex, bool HasLabel) { + ParameterizedDeclAttributeKind AttrKind, int ParamIndex, bool HasLabel) { switch (AttrKind) { - case CustomSyntaxAttributeKind::Available: + case ParameterizedDeclAttributeKind::Available: if (ParamIndex == 0) { addDeclAttrParamKeyword("*", /*Parameters=*/{}, "Platform", false); @@ -3126,15 +3126,15 @@ void CompletionLookup::getAttributeDeclParamCompletions( "Specify version number", true); } break; - case CustomSyntaxAttributeKind::FreestandingMacro: - case CustomSyntaxAttributeKind::AttachedMacro: + case ParameterizedDeclAttributeKind::FreestandingMacro: + case ParameterizedDeclAttributeKind::AttachedMacro: switch (ParamIndex) { case 0: for (auto role : getAllMacroRoles()) { bool isRoleSupported = isMacroSupported(role, Ctx); - if (AttrKind == CustomSyntaxAttributeKind::FreestandingMacro) { + if (AttrKind == ParameterizedDeclAttributeKind::FreestandingMacro) { isRoleSupported &= isFreestandingMacro(role); - } else if (AttrKind == CustomSyntaxAttributeKind::AttachedMacro) { + } else if (AttrKind == ParameterizedDeclAttributeKind::AttachedMacro) { isRoleSupported &= isAttachedMacro(role); } if (isRoleSupported) { @@ -3162,7 +3162,7 @@ void CompletionLookup::getAttributeDeclParamCompletions( break; } break; - case CustomSyntaxAttributeKind::StorageRestrictions: { + case ParameterizedDeclAttributeKind::StorageRestrictions: { bool suggestInitializesLabel = false; bool suggestAccessesLabel = false; bool suggestArgument = false; @@ -3295,7 +3295,7 @@ void CompletionLookup::getPrecedenceGroupCompletions( void CompletionLookup::getPoundAvailablePlatformCompletions() { // The platform names should be identical to those in @available. - getAttributeDeclParamCompletions(CustomSyntaxAttributeKind::Available, 0, + getAttributeDeclParamCompletions(ParameterizedDeclAttributeKind::Available, 0, /*HasLabel=*/false); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 534f70d9fd5dd..c4bffa6f79d98 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -396,7 +396,7 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( .highlight(SourceRange(ArgumentLoc)); if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::Available, ParamIndex, + ParameterizedDeclAttributeKind::Available, ParamIndex, /*HasLabel=*/false); consumeToken(tok::code_complete); } else { @@ -737,7 +737,7 @@ bool Parser::parseAvailability( !(Tok.isAnyOperator() && Tok.getText() == "*")) { if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::Available, 0, /*HasLabel=*/false); + ParameterizedDeclAttributeKind::Available, 0, /*HasLabel=*/false); consumeToken(tok::code_complete); } diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) @@ -993,7 +993,7 @@ Parser::parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc) { } } this->CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::StorageRestrictions, + ParameterizedDeclAttributeKind::StorageRestrictions, static_cast(completionKind), /*HasLabel=*/false); } } else if (parseIdentifier(propertyName, propertyNameLoc, @@ -1026,7 +1026,7 @@ Parser::parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc) { if (consumeIf(tok::code_complete)) { if (this->CodeCompletionCallbacks) { this->CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::StorageRestrictions, + ParameterizedDeclAttributeKind::StorageRestrictions, static_cast(StorageRestrictionsCompletionKind::Label), /*HasLabel=*/false); } @@ -2214,11 +2214,11 @@ std::optional getMacroRole(StringRef roleName) { .Default(std::nullopt); } -static CustomSyntaxAttributeKind getCustomSyntaxAttributeKind(bool isAttached) { +static ParameterizedDeclAttributeKind getParameterizedDeclAttributeKind(bool isAttached) { if (isAttached) { - return CustomSyntaxAttributeKind::AttachedMacro; + return ParameterizedDeclAttributeKind::AttachedMacro; } else { - return CustomSyntaxAttributeKind::FreestandingMacro; + return ParameterizedDeclAttributeKind::FreestandingMacro; } } @@ -2265,13 +2265,13 @@ Parser::parseMacroRoleAttribute( sawRole = true; if (this->CodeCompletionCallbacks) { this->CodeCompletionCallbacks->completeDeclAttrParam( - getCustomSyntaxAttributeKind(isAttached), 0, + getParameterizedDeclAttributeKind(isAttached), 0, /*HasLabel=*/false); } } else if (!sawNames) { if (this->CodeCompletionCallbacks) { this->CodeCompletionCallbacks->completeDeclAttrParam( - getCustomSyntaxAttributeKind(isAttached), 1, + getParameterizedDeclAttributeKind(isAttached), 1, /*HasLabel=*/false); } } @@ -2373,7 +2373,7 @@ Parser::parseMacroRoleAttribute( status.setHasCodeCompletionAndIsError(); if (this->CodeCompletionCallbacks) { this->CodeCompletionCallbacks->completeDeclAttrParam( - getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/true); + getParameterizedDeclAttributeKind(isAttached), 1, /*HasLabel=*/true); } } else if (parseIdentifier(introducedNameKind, introducedNameKindLoc, diag::macro_attribute_unknown_argument_form, From 78b4e851a71a54a26e58f08fc0d080c4cc39899d Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Thu, 13 Mar 2025 00:19:18 +0200 Subject: [PATCH 02/12] [IDE] Complete `unowned`, `nonisolated`, `access-control` parameter --- include/swift/Parse/IDEInspectionCallbacks.h | 7 +- lib/IDE/CompletionLookup.cpp | 10 ++ lib/Parse/ParseDecl.cpp | 106 +++++++++++++++---- 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 61183dd8c6405..938083795af51 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -29,10 +29,13 @@ enum class ObjCSelectorContext { SetterSelector }; -/// Attributes that have syntax which can't be modelled using a function call. -/// This can't be \c DeclAttrKind because '@freestandig' and '@attached' have +/// Parameterized attributes that have code completion. +/// This can't be \c DeclAttrKind because '@freestanding' and '@attached' have /// the same attribute kind but take different macro roles as arguemnts. enum class ParameterizedDeclAttributeKind { + AccessControl, + Nonisolated, + Unowned, Available, FreestandingMacro, AttachedMacro, diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 014118387619f..c259a6fe01596 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3105,6 +3105,16 @@ void CompletionLookup::getAttributeDeclCompletions(bool IsInSil, void CompletionLookup::getAttributeDeclParamCompletions( ParameterizedDeclAttributeKind AttrKind, int ParamIndex, bool HasLabel) { switch (AttrKind) { + case ParameterizedDeclAttributeKind::Unowned: + addDeclAttrParamKeyword("safe", /*Parameters=*/{}, "", false); + addDeclAttrParamKeyword("unsafe", /*Parameters=*/{}, "", false); + break; + case ParameterizedDeclAttributeKind::Nonisolated: + addDeclAttrParamKeyword("unsafe", /*Parameters=*/{}, "", false); + break; + case ParameterizedDeclAttributeKind::AccessControl: + addDeclAttrParamKeyword("set", /*Parameters=*/{}, "", false); + break; case ParameterizedDeclAttributeKind::Available: if (ParamIndex == 0) { addDeclAttrParamKeyword("*", /*Parameters=*/{}, "Platform", false); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c4bffa6f79d98..28d1e3fa6704b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2473,8 +2473,9 @@ Parser::parseMacroRoleAttribute( /// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly /// omitted; the identifier written by the user otherwise. static std::optional parseSingleAttrOptionImpl( - Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, - DeclAttrKind DK, bool allowOmitted, DiagRef nonIdentifierDiagnostic) { + Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, DeclAttrKind DK, + ParserStatus &Status, bool allowOmitted, DiagRef nonIdentifierDiagnostic, + llvm::function_ref codeCompletionCallback = {}) { SWIFT_DEFER { AttrRange = SourceRange(Loc, P.PreviousLoc); }; @@ -2484,19 +2485,32 @@ static std::optional parseSingleAttrOptionImpl( if (allowOmitted) return Identifier(); + Status.setIsParseError(); P.diagnose(Loc, diag::attr_expected_lparen, AttrName, isDeclModifier); return std::nullopt; } P.consumeAttributeLParen(); + + if (P.Tok.is(tok::code_complete)) { + Status.setHasCodeCompletion(); + codeCompletionCallback(); + } StringRef parsedName = P.Tok.getText(); if (!P.consumeIf(tok::identifier)) { + Status.setIsParseError(); P.diagnose(Loc, nonIdentifierDiagnostic); return std::nullopt; } + if (P.Tok.is(tok::code_complete)) { + Status.setHasCodeCompletion(); + codeCompletionCallback(); + } + if (!P.consumeIf(tok::r_paren)) { + Status.setIsParseError(); P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isDeclModifier); return std::nullopt; } @@ -2590,11 +2604,11 @@ ParserResult Parser::parseLifetimeAttribute(SourceLoc atLoc, /// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly /// omitted; the identifier written by the user otherwise. static std::optional -parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc, +parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc, ParserStatus &Status, SourceRange &AttrRange, StringRef AttrName, DeclAttrKind DK, bool allowOmitted = false) { return parseSingleAttrOptionImpl( - P, Loc, AttrRange, AttrName, DK, allowOmitted, + P, Loc, AttrRange, AttrName, DK, Status, allowOmitted, {diag::attr_expected_option_identifier, {AttrName}}); } @@ -2616,13 +2630,17 @@ template static std::optional parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, DeclAttrKind DK, + ParserStatus& Status, ArrayRef> options, - std::optional valueIfOmitted = std::nullopt) { + std::optional valueIfOmitted = std::nullopt, + llvm::function_ref codeCompletionCallback = {}) { auto parsedIdentifier = parseSingleAttrOptionImpl( - P, Loc, AttrRange,AttrName, DK, + P, Loc, AttrRange, AttrName, DK, Status, /*allowOmitted=*/valueIfOmitted.has_value(), {diag::attr_expected_option_such_as, - {AttrName, options.front().first.str()}}); + {AttrName, options.front().first.str()}}, + codeCompletionCallback); + if (!parsedIdentifier) return std::nullopt; @@ -2755,8 +2773,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Inline: { + ParserStatus optionStatus; auto kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, { + *this, Loc, AttrRange, AttrName, DK, optionStatus, { { Context.Id_never, InlineKind::Never }, { Context.Id__always, InlineKind::Always } }); @@ -2770,8 +2789,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Optimize: { + ParserStatus optionStatus; auto optMode = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, { + *this, Loc, AttrRange, AttrName, DK, optionStatus, { { Context.Id_speed, OptimizationMode::ForSpeed }, { Context.Id_size, OptimizationMode::ForSize }, { Context.Id_none, OptimizationMode::NoOptimization } @@ -2786,8 +2806,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Exclusivity: { + ParserStatus optionStatus; auto mode = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, { + *this, Loc, AttrRange, AttrName, DK, optionStatus, { { Context.Id_checked, ExclusivityAttr::Mode::Checked }, { Context.Id_unchecked, ExclusivityAttr::Mode::Unchecked } }); @@ -2807,11 +2828,19 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, if (Kind == ReferenceOwnership::Unowned) { // Parse an optional specifier after unowned. + ParserStatus optionStatus; Kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, { + *this, Loc, AttrRange, AttrName, DK, optionStatus, { { Context.Id_unsafe, ReferenceOwnership::Unmanaged }, { Context.Id_safe, ReferenceOwnership::Unowned } - }, ReferenceOwnership::Unowned) + }, ReferenceOwnership::Unowned, + [&] () { + if (CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::Unowned, 0, false); + consumeToken(tok::code_complete); + } + }) // Recover from errors by going back to Unowned. .value_or(ReferenceOwnership::Unowned); } @@ -2827,8 +2856,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::NonSendable: { + ParserStatus optionStatus; auto kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, { + *this, Loc, AttrRange, AttrName, DK, optionStatus, { { Context.Id_assumed, NonSendableKind::Assumed } }, NonSendableKind::Specific); if (!kind) @@ -2882,11 +2912,34 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, consumeAttributeLParen(); + if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::AccessControl, 0, false); + consumeToken(tok::code_complete); + } + // Parse the subject. if (Tok.isContextualKeyword("set")) { consumeToken(); } else { diagnose(Loc, diag::attr_access_expected_set, AttrName); + + const Token &Tok2 = peekToken(); + + if (CodeCompletionCallbacks) { + if (Tok.is(tok::code_complete)) { + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::AccessControl, 0, false); + consumeToken(tok::code_complete); + } else if (Tok2.is(tok::code_complete) && Tok.is(tok::identifier) && + !Tok.isContextualDeclKeyword()) { + consumeToken(tok::identifier); + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::AccessControl, 0, false); + consumeToken(tok::code_complete); + } + } + // Minimal recovery: if there's a single token and then an r_paren, // consume them both. If there's just an r_paren, consume that. if (!consumeIf(tok::r_paren)) { @@ -3155,8 +3208,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::SwiftNativeObjCRuntimeBase: { SourceRange range; - auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, - DK); + ParserStatus optionStatus; + auto name = parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, + AttrName, DK); if (!name) return makeParserSuccess(); @@ -3448,7 +3502,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::ObjCImplementation: { SourceRange range; - auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK, + ParserStatus optionStatus; + auto name = parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK, /*allowOmitted=*/true); if (!name) return makeParserSuccess(); @@ -3460,8 +3515,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::ObjCRuntimeName: { SourceRange range; + ParserStatus optionStatus; auto name = - parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); + parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK); if (!name) return makeParserSuccess(); @@ -3618,8 +3674,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::ProjectedValueProperty: { SourceRange range; + ParserStatus optionStatus; auto name = - parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); + parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK); if (!name) return makeParserSuccess(); @@ -3719,9 +3776,17 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::Nonisolated: { std::optional isUnsafe(false); if (EnableParameterizedNonisolated) { + ParserStatus optionStatus; isUnsafe = parseSingleAttrOption(*this, Loc, AttrRange, AttrName, DK, - {{Context.Id_unsafe, true}}, *isUnsafe); + optionStatus, {{Context.Id_unsafe, true}}, + *isUnsafe, [&] () { + if (CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::Nonisolated, 0, false); + consumeToken(tok::code_complete); + } + }); if (!isUnsafe) { return makeParserSuccess(); } @@ -3920,8 +3985,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Execution: { + ParserStatus optionStatus; auto behavior = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, + *this, Loc, AttrRange, AttrName, DK, optionStatus, {{Context.Id_concurrent, ExecutionKind::Concurrent}, {Context.Id_caller, ExecutionKind::Caller}}); if (!behavior) From 951988c10098bacdc8f6c81d0a84692704a03f0a Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Thu, 13 Mar 2025 01:38:01 +0200 Subject: [PATCH 03/12] [IDE] Return Code Completion Status in Access Control Completion --- lib/Parse/ParseDecl.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 28d1e3fa6704b..40b3706f25bda 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2912,10 +2912,13 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, consumeAttributeLParen(); - if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::AccessControl, 0, false); + if (Tok.is(tok::code_complete)) { + if (CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam( + ParameterizedDeclAttributeKind::AccessControl, 0, false); + } consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); } // Parse the subject. @@ -2926,18 +2929,24 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, const Token &Tok2 = peekToken(); - if (CodeCompletionCallbacks) { - if (Tok.is(tok::code_complete)) { + if (Tok.is(tok::code_complete)) { + if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( ParameterizedDeclAttributeKind::AccessControl, 0, false); - consumeToken(tok::code_complete); - } else if (Tok2.is(tok::code_complete) && Tok.is(tok::identifier) && - !Tok.isContextualDeclKeyword()) { - consumeToken(tok::identifier); + } + consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); + } + + if (Tok2.is(tok::code_complete) && Tok.is(tok::identifier) && + !Tok.isContextualDeclKeyword()) { + consumeToken(tok::identifier); + if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( ParameterizedDeclAttributeKind::AccessControl, 0, false); - consumeToken(tok::code_complete); } + consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); } // Minimal recovery: if there's a single token and then an r_paren, From c871e4962fcfb45880d940014a96cdc0a2868132 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Thu, 13 Mar 2025 02:16:53 +0200 Subject: [PATCH 04/12] [IDE] Recover from incomplete `unowned` and `nonisolated` in `isStartOfSwiftDecl` --- lib/Parse/ParseDecl.cpp | 76 ++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 40b3706f25bda..7cc963cc835c4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5691,33 +5691,61 @@ bool swift::isKeywordPossibleDeclStart(const LangOptions &options, } static bool -isParenthesizedModifier(Parser &P, StringRef name, - std::initializer_list allowedArguments) { +consumeIfParenthesizedModifier(Parser &P, StringRef name, + std::initializer_list allowedArguments) { assert((P.Tok.getText() == name) && P.peekToken().is(tok::l_paren) && "Invariant violated"); // Look ahead to parse the parenthesized expression. - Parser::BacktrackingScope Backtrack(P); + Parser::CancellableBacktrackingScope backtrack(P); P.consumeToken(tok::identifier); P.consumeToken(tok::l_paren); + + const Token &Tok2 = P.peekToken(); + if (P.consumeIf(tok::code_complete)) { + // If a code complete token is present, recover from missing/incorrect argument and missing '(' + P.consumeIf(tok::identifier); + P.consumeIf(tok::r_paren); + backtrack.cancelBacktrack(); + return true; + } + + if (P.Tok.is(tok::identifier) && !P.Tok.isContextualDeclKeyword() && Tok2.is(tok::code_complete)) { + P.consumeToken(tok::identifier); + P.consumeToken(tok::code_complete); + + // If a code complete is present with a non-keyword token before it, recover from missing/incorrect argument and missing '(' + if (!P.Tok.isContextualDeclKeyword()) { + P.consumeIf(tok::identifier); + } + P.consumeIf(tok::r_paren); + backtrack.cancelBacktrack(); + return true; + } const bool argumentIsAllowed = std::find(allowedArguments.begin(), allowedArguments.end(), P.Tok.getText()) != allowedArguments.end(); - return argumentIsAllowed && P.Tok.is(tok::identifier) && - P.peekToken().is(tok::r_paren); + + if (argumentIsAllowed && P.Tok.is(tok::identifier) && + P.peekToken().is(tok::r_paren)) { + backtrack.cancelBacktrack(); + return true; + } + + return false; } /// Given a current token of 'unowned', check to see if it is followed by a -/// "(safe)" or "(unsafe)" specifier. -static bool isParenthesizedUnowned(Parser &P) { - return isParenthesizedModifier(P, "unowned", {"safe", "unsafe"}); +/// "(safe)" or "(unsafe)" specifier and consumes if it is. +static bool consumeIfParenthesizedUnowned(Parser &P) { + return consumeIfParenthesizedModifier(P, "unowned", {"safe", "unsafe"}); } /// Given a current token of 'nonisolated', check to see if it is followed by an -/// "(unsafe)" specifier. -static bool isParenthesizedNonisolated(Parser &P) { - return isParenthesizedModifier(P, "nonisolated", {"unsafe"}); +/// "(unsafe)" specifier and consumes if it is. +static bool consumeIfParenthesizedNonisolated(Parser &P) { + return consumeIfParenthesizedModifier(P, "nonisolated", {"unsafe"}); } static void skipAttribute(Parser &P) { @@ -5878,27 +5906,21 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes, // If it might be, we do some more digging. // If this is 'unowned', check to see if it is valid. - if (Tok.getText() == "unowned" && Tok2.is(tok::l_paren) && - isParenthesizedUnowned(*this)) { + if (Tok.getText() == "unowned" && Tok2.is(tok::l_paren)) { Parser::BacktrackingScope Backtrack(*this); - consumeToken(tok::identifier); - consumeToken(tok::l_paren); - consumeToken(tok::identifier); - consumeToken(tok::r_paren); - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, - /*hadAttrsOrModifiers=*/true); + if (consumeIfParenthesizedUnowned(*this)) { + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); + } } // If this is 'nonisolated', check to see if it is valid. - if (Tok.isContextualKeyword("nonisolated") && Tok2.is(tok::l_paren) && - isParenthesizedNonisolated(*this)) { + if (Tok.isContextualKeyword("nonisolated") && Tok2.is(tok::l_paren)) { BacktrackingScope backtrack(*this); - consumeToken(tok::identifier); - consumeToken(tok::l_paren); - consumeToken(tok::identifier); - consumeToken(tok::r_paren); - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, - /*hadAttrsOrModifiers=*/true); + if (consumeIfParenthesizedNonisolated(*this)) { + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); + } } if (Tok.isContextualKeyword("actor")) { From 9767cc11589d6e65ba4ab33104aae4ee1c435aff Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Thu, 13 Mar 2025 21:45:40 +0200 Subject: [PATCH 05/12] [IDE] Remove duplicate code & add parameter label --- lib/Parse/ParseDecl.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7cc963cc835c4..4d36f6e3686ba 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2837,7 +2837,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, [&] () { if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::Unowned, 0, false); + ParameterizedDeclAttributeKind::Unowned, 0, /*HasLabel=*/false); consumeToken(tok::code_complete); } }) @@ -2929,15 +2929,6 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, const Token &Tok2 = peekToken(); - if (Tok.is(tok::code_complete)) { - if (CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::AccessControl, 0, false); - } - consumeToken(tok::code_complete); - return makeParserCodeCompletionStatus(); - } - if (Tok2.is(tok::code_complete) && Tok.is(tok::identifier) && !Tok.isContextualDeclKeyword()) { consumeToken(tok::identifier); From ab6ad401b38c82c3493d6f4ec0f32deb47ac015f Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 02:34:04 +0200 Subject: [PATCH 06/12] [IDE] Consume identifier and ')' after parenthesized modifier This fixes an issue where valid parenthesized modifiers like nonisolated(unsafe) get misinterpreted as function calls. --- lib/Parse/ParseDecl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 4d36f6e3686ba..032fba81cd5e9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5718,8 +5718,8 @@ consumeIfParenthesizedModifier(Parser &P, StringRef name, std::find(allowedArguments.begin(), allowedArguments.end(), P.Tok.getText()) != allowedArguments.end(); - if (argumentIsAllowed && P.Tok.is(tok::identifier) && - P.peekToken().is(tok::r_paren)) { + if (argumentIsAllowed && P.consumeIf(tok::identifier) && + P.consumeIf(tok::r_paren)) { backtrack.cancelBacktrack(); return true; } From 3bb2da0d0d9539215eb1f3ca1da896074609d2d9 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 04:44:26 +0200 Subject: [PATCH 07/12] [IDE] Remove unnecessary code complete checks --- lib/Parse/ParseDecl.cpp | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 032fba81cd5e9..cc49f38076ed9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2504,11 +2504,6 @@ static std::optional parseSingleAttrOptionImpl( return std::nullopt; } - if (P.Tok.is(tok::code_complete)) { - Status.setHasCodeCompletion(); - codeCompletionCallback(); - } - if (!P.consumeIf(tok::r_paren)) { Status.setIsParseError(); P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isDeclModifier); @@ -2926,20 +2921,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, consumeToken(); } else { diagnose(Loc, diag::attr_access_expected_set, AttrName); - - const Token &Tok2 = peekToken(); - - if (Tok2.is(tok::code_complete) && Tok.is(tok::identifier) && - !Tok.isContextualDeclKeyword()) { - consumeToken(tok::identifier); - if (CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::AccessControl, 0, false); - } - consumeToken(tok::code_complete); - return makeParserCodeCompletionStatus(); - } - + // Minimal recovery: if there's a single token and then an r_paren, // consume them both. If there's just an r_paren, consume that. if (!consumeIf(tok::r_paren)) { From 704e9fcc3528cb8de3c160f8cd68568ba892b9d0 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 04:45:07 +0200 Subject: [PATCH 08/12] [IDE] Fix access control set completion as a declaration item --- lib/Parse/ParseDecl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index cc49f38076ed9..7e4c8054ff1ed 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2911,9 +2911,9 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( ParameterizedDeclAttributeKind::AccessControl, 0, false); + consumeToken(tok::code_complete); } - consumeToken(tok::code_complete); - return makeParserCodeCompletionStatus(); + return makeParserSuccess(); } // Parse the subject. From 6d163a5b0c763509f11366fbdab0dcc2dd3f45e8 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 04:45:56 +0200 Subject: [PATCH 09/12] [Test] Add tests for `unowned`, `nonisolated`, and access control `set` completion --- test/IDE/complete_access_control_set.swift | 27 ++++++++++++++++++++++ test/IDE/complete_nonisolated_unsafe.swift | 9 ++++++++ test/IDE/complete_unowned_parameter.swift | 10 ++++++++ 3 files changed, 46 insertions(+) create mode 100644 test/IDE/complete_access_control_set.swift create mode 100644 test/IDE/complete_nonisolated_unsafe.swift create mode 100644 test/IDE/complete_unowned_parameter.swift diff --git a/test/IDE/complete_access_control_set.swift b/test/IDE/complete_access_control_set.swift new file mode 100644 index 0000000000000..d006fd10b6e05 --- /dev/null +++ b/test/IDE/complete_access_control_set.swift @@ -0,0 +1,27 @@ +// RUN: %batch-code-completion + +// ACCESS_CONTROL_SET: Keyword/None: set; name=set + +public(#^PUBLIC_TOP_LEVEL?check=ACCESS_CONTROL_SET^#) var var1 = 0 + +private(#^PRIVATE_TOP_LEVEL?check=ACCESS_CONTROL_SET^#) var var2 = 0 + +internal(#^INTERNAL_TOP_LEVEL?check=ACCESS_CONTROL_SET^#) var var3 = 0 + +fileprivate(#^FILEPRIVATE_TOP_LEVEL?check=ACCESS_CONTROL_SET^#) var var4 = 0 + +package(#^PACKAGE_TOP_LEVEL?check=ACCESS_CONTROL_SET^#) var var5 = 0 + +struct MyStruct { + public(#^PUBLIC_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop1: Int = 0 + + private(#^PRIVATE_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop2: Int = 0 + + open(#^OPEN_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop3: Int = 0 + + internal(#^INTERNAL_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop4: Int = 0 + + fileprivate(#^FILEPRIVATE_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop5: Int = 0 + + package(#^PACKAGE_IN_STRUCT?check=ACCESS_CONTROL_SET^#) var prop6: Int = 0 +} diff --git a/test/IDE/complete_nonisolated_unsafe.swift b/test/IDE/complete_nonisolated_unsafe.swift new file mode 100644 index 0000000000000..54edd05f1ac14 --- /dev/null +++ b/test/IDE/complete_nonisolated_unsafe.swift @@ -0,0 +1,9 @@ +// RUN: %batch-code-completion + +// NONISOLATED_UNSAFE: Keyword/None: unsafe; name=unsafe + +nonisolated(#^NONISOLATED_UNSAFE_TOP_LEVEL?check=NONISOLATED_UNSAFE^#) var count = 0 + +struct MyStruct { + nonisolated(#^NONISOLATED_UNSAFE_IN_STRUCT?check=NONISOLATED_UNSAFE^#) var prop = 0 +} diff --git a/test/IDE/complete_unowned_parameter.swift b/test/IDE/complete_unowned_parameter.swift new file mode 100644 index 0000000000000..d3a73a720cf0c --- /dev/null +++ b/test/IDE/complete_unowned_parameter.swift @@ -0,0 +1,10 @@ +// RUN: %batch-code-completion + +// UNOWNED_PARAMETER-DAG: Keyword/None: safe; name=safe +// UNOWNED_PARAMETER-DAG: Keyword/None: unsafe; name=unsafe + +unowned(#^UNOWNED_TOP_LEVEL?check=UNOWNED_PARAMETER^#) var count = 0 + +struct MyStruct { + unowned(#^UNOWNED_IN_STRUCT?check=UNOWNED_PARAMETER^#) var prop: Int = 0 +} From a340dca4b5096e8fe33406c8c4d22380fa45294a Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 04:48:22 +0200 Subject: [PATCH 10/12] [IDE] Add parameter label comment for `HasLabel` --- lib/Parse/ParseDecl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7e4c8054ff1ed..aa667da1bca15 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2910,7 +2910,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, if (Tok.is(tok::code_complete)) { if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::AccessControl, 0, false); + ParameterizedDeclAttributeKind::AccessControl, 0, /*HasLabel=*/false); consumeToken(tok::code_complete); } return makeParserSuccess(); @@ -3765,7 +3765,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, *isUnsafe, [&] () { if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::Nonisolated, 0, false); + ParameterizedDeclAttributeKind::Nonisolated, 0, /*HasLabel=*/false); consumeToken(tok::code_complete); } }); From 78990ffe3f142cc57e99d641c334accf4bffc73f Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 13:19:53 +0200 Subject: [PATCH 11/12] [IDE] Pass `ParameterizedDeclAttributeKind` to `parseSingleAttrOption` rather than completion callback --- lib/Parse/ParseDecl.cpp | 98 ++++++++++++----------------------------- 1 file changed, 29 insertions(+), 69 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index aa667da1bca15..af4dce130d384 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2469,13 +2469,14 @@ Parser::parseMacroRoleAttribute( /// \c Identifier() ; if false, diagnose a missing argument list as an error. /// \param nonIdentifierDiagnostic The diagnostic to emit if something other than a /// \c tok::identifier is used as an argument. +/// \param PDK Specific kind of the parameterized attribute being passed. Used when invoking code completion callbacks. /// /// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly /// omitted; the identifier written by the user otherwise. static std::optional parseSingleAttrOptionImpl( - Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, DeclAttrKind DK, - ParserStatus &Status, bool allowOmitted, DiagRef nonIdentifierDiagnostic, - llvm::function_ref codeCompletionCallback = {}) { + Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, + DeclAttrKind DK, bool allowOmitted, DiagRef nonIdentifierDiagnostic, + std::optional PDK = std::nullopt) { SWIFT_DEFER { AttrRange = SourceRange(Loc, P.PreviousLoc); }; @@ -2484,28 +2485,25 @@ static std::optional parseSingleAttrOptionImpl( if (!P.Tok.is(tok::l_paren)) { if (allowOmitted) return Identifier(); - - Status.setIsParseError(); + P.diagnose(Loc, diag::attr_expected_lparen, AttrName, isDeclModifier); return std::nullopt; } P.consumeAttributeLParen(); - if (P.Tok.is(tok::code_complete)) { - Status.setHasCodeCompletion(); - codeCompletionCallback(); + if (P.Tok.is(tok::code_complete) && P.CodeCompletionCallbacks && PDK) { + P.CodeCompletionCallbacks->completeDeclAttrParam(*PDK, 0, /*HasLabel=*/false); + P.consumeToken(tok::code_complete); } StringRef parsedName = P.Tok.getText(); if (!P.consumeIf(tok::identifier)) { - Status.setIsParseError(); P.diagnose(Loc, nonIdentifierDiagnostic); return std::nullopt; } if (!P.consumeIf(tok::r_paren)) { - Status.setIsParseError(); P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isDeclModifier); return std::nullopt; } @@ -2599,11 +2597,11 @@ ParserResult Parser::parseLifetimeAttribute(SourceLoc atLoc, /// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly /// omitted; the identifier written by the user otherwise. static std::optional -parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc, ParserStatus &Status, - SourceRange &AttrRange, StringRef AttrName, - DeclAttrKind DK, bool allowOmitted = false) { +parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc, SourceRange &AttrRange, + StringRef AttrName, DeclAttrKind DK, + bool allowOmitted = false) { return parseSingleAttrOptionImpl( - P, Loc, AttrRange, AttrName, DK, Status, allowOmitted, + P, Loc, AttrRange, AttrName, DK, allowOmitted, {diag::attr_expected_option_identifier, {AttrName}}); } @@ -2618,6 +2616,7 @@ parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc, ParserStatus &Status, /// \param options The set of permitted keywords and their corresponding values. /// \param valueIfOmitted If present, treat a missing argument list as permitted and return /// the provided value; if absent, diagnose a missing argument list as an error. +/// \param PDK Specific kind of the parameterized attribute being passed. Used when invoking code completion callbacks. /// /// \returns \c None if an error was diagnosed; the value corresponding to the identifier written by the /// user otherwise. @@ -2625,16 +2624,14 @@ template static std::optional parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange, StringRef AttrName, DeclAttrKind DK, - ParserStatus& Status, ArrayRef> options, std::optional valueIfOmitted = std::nullopt, - llvm::function_ref codeCompletionCallback = {}) { + std::optional PDK = std::nullopt) { auto parsedIdentifier = parseSingleAttrOptionImpl( - P, Loc, AttrRange, AttrName, DK, Status, + P, Loc, AttrRange, AttrName, DK, /*allowOmitted=*/valueIfOmitted.has_value(), {diag::attr_expected_option_such_as, - {AttrName, options.front().first.str()}}, - codeCompletionCallback); + {AttrName, options.front().first.str()}}, PDK); if (!parsedIdentifier) return std::nullopt; @@ -2768,9 +2765,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Inline: { - ParserStatus optionStatus; auto kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, { + *this, Loc, AttrRange, AttrName, DK, { { Context.Id_never, InlineKind::Never }, { Context.Id__always, InlineKind::Always } }); @@ -2784,9 +2780,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Optimize: { - ParserStatus optionStatus; auto optMode = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, { + *this, Loc, AttrRange, AttrName, DK, { { Context.Id_speed, OptimizationMode::ForSpeed }, { Context.Id_size, OptimizationMode::ForSize }, { Context.Id_none, OptimizationMode::NoOptimization } @@ -2801,9 +2796,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Exclusivity: { - ParserStatus optionStatus; auto mode = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, { + *this, Loc, AttrRange, AttrName, DK, { { Context.Id_checked, ExclusivityAttr::Mode::Checked }, { Context.Id_unchecked, ExclusivityAttr::Mode::Unchecked } }); @@ -2823,19 +2817,12 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, if (Kind == ReferenceOwnership::Unowned) { // Parse an optional specifier after unowned. - ParserStatus optionStatus; Kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, { + *this, Loc, AttrRange, AttrName, DK, { { Context.Id_unsafe, ReferenceOwnership::Unmanaged }, { Context.Id_safe, ReferenceOwnership::Unowned } }, ReferenceOwnership::Unowned, - [&] () { - if (CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::Unowned, 0, /*HasLabel=*/false); - consumeToken(tok::code_complete); - } - }) + ParameterizedDeclAttributeKind::Unowned) // Recover from errors by going back to Unowned. .value_or(ReferenceOwnership::Unowned); } @@ -2851,9 +2838,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::NonSendable: { - ParserStatus optionStatus; auto kind = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, { + *this, Loc, AttrRange, AttrName, DK, { { Context.Id_assumed, NonSendableKind::Assumed } }, NonSendableKind::Specific); if (!kind) @@ -3190,8 +3176,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::SwiftNativeObjCRuntimeBase: { SourceRange range; - ParserStatus optionStatus; - auto name = parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, + auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) return makeParserSuccess(); @@ -3484,8 +3469,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::ObjCImplementation: { SourceRange range; - ParserStatus optionStatus; - auto name = parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK, + auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK, /*allowOmitted=*/true); if (!name) return makeParserSuccess(); @@ -3497,9 +3481,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::ObjCRuntimeName: { SourceRange range; - ParserStatus optionStatus; auto name = - parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK); + parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) return makeParserSuccess(); @@ -3656,9 +3639,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::ProjectedValueProperty: { SourceRange range; - ParserStatus optionStatus; auto name = - parseSingleAttrOptionIdentifier(*this, Loc, optionStatus, range, AttrName, DK); + parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) return makeParserSuccess(); @@ -3758,17 +3740,10 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::Nonisolated: { std::optional isUnsafe(false); if (EnableParameterizedNonisolated) { - ParserStatus optionStatus; isUnsafe = parseSingleAttrOption(*this, Loc, AttrRange, AttrName, DK, - optionStatus, {{Context.Id_unsafe, true}}, - *isUnsafe, [&] () { - if (CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam( - ParameterizedDeclAttributeKind::Nonisolated, 0, /*HasLabel=*/false); - consumeToken(tok::code_complete); - } - }); + {{Context.Id_unsafe, true}}, *isUnsafe, + ParameterizedDeclAttributeKind::Nonisolated); if (!isUnsafe) { return makeParserSuccess(); } @@ -3967,9 +3942,8 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Execution: { - ParserStatus optionStatus; auto behavior = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, optionStatus, + *this, Loc, AttrRange, AttrName, DK, {{Context.Id_concurrent, ExecutionKind::Concurrent}, {Context.Id_caller, ExecutionKind::Caller}}); if (!behavior) @@ -5674,7 +5648,6 @@ consumeIfParenthesizedModifier(Parser &P, StringRef name, P.consumeToken(tok::identifier); P.consumeToken(tok::l_paren); - const Token &Tok2 = P.peekToken(); if (P.consumeIf(tok::code_complete)) { // If a code complete token is present, recover from missing/incorrect argument and missing '(' P.consumeIf(tok::identifier); @@ -5683,19 +5656,6 @@ consumeIfParenthesizedModifier(Parser &P, StringRef name, return true; } - if (P.Tok.is(tok::identifier) && !P.Tok.isContextualDeclKeyword() && Tok2.is(tok::code_complete)) { - P.consumeToken(tok::identifier); - P.consumeToken(tok::code_complete); - - // If a code complete is present with a non-keyword token before it, recover from missing/incorrect argument and missing '(' - if (!P.Tok.isContextualDeclKeyword()) { - P.consumeIf(tok::identifier); - } - P.consumeIf(tok::r_paren); - backtrack.cancelBacktrack(); - return true; - } - const bool argumentIsAllowed = std::find(allowedArguments.begin(), allowedArguments.end(), P.Tok.getText()) != allowedArguments.end(); From c74e2710e9f97f623726665ff74d1895ad857ee3 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmoud Date: Fri, 14 Mar 2025 13:20:14 +0200 Subject: [PATCH 12/12] [IDE] Remove invalid assertion in `completeDeclAttrParam` --- lib/IDE/CodeCompletion.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 1cc4aebf4a00c..bcca73daaca5a 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -461,8 +461,6 @@ void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { void CodeCompletionCallbacksImpl::completeDeclAttrParam( ParameterizedDeclAttributeKind DK, int Index, bool HasLabel) { - assert(P.Tok.is(tok::code_complete)); - Kind = CompletionKind::AttributeDeclParen; AttrKind = DK; AttrParamIndex = Index;