diff --git a/include/swift/AST/MacroDeclaration.h b/include/swift/AST/MacroDeclaration.h index e7187fedfb022..1cb937d33423d 100644 --- a/include/swift/AST/MacroDeclaration.h +++ b/include/swift/AST/MacroDeclaration.h @@ -97,8 +97,14 @@ enum class MacroIntroducedDeclNameKind { Prefixed, Suffixed, Arbitrary, + + // NOTE: When adding a new name kind, also add it to + // `getAllMacroIntroducedDeclNameKinds`. }; +/// Returns an enumeratable list of all macro introduced decl name kinds. +std::vector getAllMacroIntroducedDeclNameKinds(); + /// Whether a macro-introduced name of this kind requires an argument. bool macroIntroducedNameRequiresArgument(MacroIntroducedDeclNameKind kind); diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 4d70cb473fb64..70810095f9220 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -449,8 +449,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionKeywordKind KeyKind = CodeCompletionKeywordKind::None, CodeCompletionFlair flair = {}); - void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation, - bool NeedSpecify); + void addDeclAttrParamKeyword(StringRef Name, ArrayRef Parameters, + StringRef Annotation, bool NeedSpecify); void addDeclAttrKeyword(StringRef Name, StringRef Annotation); @@ -586,7 +586,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void getAttributeDeclCompletions(bool IsInSil, Optional DK); void getAttributeDeclParamCompletions(CustomSyntaxAttributeKind AttrKind, - int ParamIndex); + int ParamIndex, bool HasLabel); void getTypeAttributeKeywordCompletions(); diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index bcff4427b4eb3..568263fa33624 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -194,7 +194,10 @@ class CodeCompletionCallbacks { /// Complete the parameters in attribute, for instance, version specifier for /// @available. - virtual void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index){}; + /// 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, + bool HasLabel){}; /// Complete 'async' and 'throws' at effects specifier position. virtual void completeEffectsSpecifier(bool hasAsync, bool hasThrows) {}; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 56f32f53fef6b..fa7ec71e02326 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10149,6 +10149,17 @@ StringRef swift::getMacroRoleString(MacroRole role) { } } +std::vector +swift::getAllMacroIntroducedDeclNameKinds() { + return { + MacroIntroducedDeclNameKind::Named, + MacroIntroducedDeclNameKind::Overloaded, + MacroIntroducedDeclNameKind::Prefixed, + MacroIntroducedDeclNameKind::Suffixed, + MacroIntroducedDeclNameKind::Arbitrary, + }; +} + bool swift::macroIntroducedNameRequiresArgument( MacroIntroducedDeclNameKind kind ) { diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 19ed95d918e12..8d9f727c4c5e2 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -127,6 +127,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, CodeCompletionCallbacks::PrecedenceGroupCompletionKind SyntxKind; int AttrParamIndex; + bool AttrParamHasLabel; bool IsInSil = false; bool HasSpace = false; bool ShouldCompleteCallPatternAfterParen = true; @@ -270,7 +271,8 @@ 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) override; + void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index, + bool HasLabel) override; void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override; void completeInPrecedenceGroup( CodeCompletionCallbacks::PrecedenceGroupCompletionKind SK) override; @@ -457,10 +459,11 @@ void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { } void CodeCompletionCallbacksImpl::completeDeclAttrParam( - CustomSyntaxAttributeKind DK, int Index) { + CustomSyntaxAttributeKind DK, int Index, bool HasLabel) { Kind = CompletionKind::AttributeDeclParen; AttrKind = DK; AttrParamIndex = Index; + AttrParamHasLabel = HasLabel; CurDeclContext = P.CurDeclContext; } @@ -1844,7 +1847,8 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { break; } case CompletionKind::AttributeDeclParen: { - Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex); + Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex, + AttrParamHasLabel); break; } case CompletionKind::PoundAvailablePlatform: { diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 41e7347f925e7..5bfe6ac88e5c3 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -345,10 +345,17 @@ class CodeCompletionResultBuilder { addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind::Equal, "="); } - void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation, - bool NeedSpecify) { + void addDeclAttrParamKeyword(StringRef Name, ArrayRef Parameters, + StringRef Annotation, bool NeedSpecify) { addChunkWithText(CodeCompletionString::Chunk::ChunkKind:: DeclAttrParamKeyword, Name); + if (!Parameters.empty()) { + addLeftParen(); + for (auto Parameter : Parameters) { + addSimpleNamedParameter(Parameter); + } + addRightParen(); + } if (NeedSpecify) addChunkWithText(CodeCompletionString::Chunk::ChunkKind:: DeclAttrParamColon, ": "); diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index d65af2ec1a4aa..2f096b21b5c0c 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -1850,11 +1850,12 @@ void CompletionLookup::addKeyword(StringRef Name, StringRef TypeAnnotation, } void CompletionLookup::addDeclAttrParamKeyword(StringRef Name, + ArrayRef Parameters, StringRef Annotation, bool NeedSpecify) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword, SemanticContextKind::None); - Builder.addDeclAttrParamKeyword(Name, Annotation, NeedSpecify); + Builder.addDeclAttrParamKeyword(Name, Parameters, Annotation, NeedSpecify); } void CompletionLookup::addDeclAttrKeyword(StringRef Name, @@ -3012,23 +3013,27 @@ void CompletionLookup::getAttributeDeclCompletions(bool IsInSil, } void CompletionLookup::getAttributeDeclParamCompletions( - CustomSyntaxAttributeKind AttrKind, int ParamIndex) { + CustomSyntaxAttributeKind AttrKind, int ParamIndex, bool HasLabel) { switch (AttrKind) { case CustomSyntaxAttributeKind::Available: if (ParamIndex == 0) { - addDeclAttrParamKeyword("*", "Platform", false); + addDeclAttrParamKeyword("*", /*Parameters=*/{}, "Platform", false); #define AVAILABILITY_PLATFORM(X, PrettyName) \ - addDeclAttrParamKeyword(swift::platformString(PlatformKind::X), "Platform", \ - false); + addDeclAttrParamKeyword(swift::platformString(PlatformKind::X), \ + /*Parameters=*/{}, "Platform", false); #include "swift/AST/PlatformKinds.def" } else { - addDeclAttrParamKeyword("unavailable", "", false); - addDeclAttrParamKeyword("message", "Specify message", true); - addDeclAttrParamKeyword("renamed", "Specify replacing name", true); - addDeclAttrParamKeyword("introduced", "Specify version number", true); - addDeclAttrParamKeyword("deprecated", "Specify version number", true); + addDeclAttrParamKeyword("unavailable", /*Parameters=*/{}, "", false); + addDeclAttrParamKeyword("message", /*Parameters=*/{}, "Specify message", + true); + addDeclAttrParamKeyword("renamed", /*Parameters=*/{}, + "Specify replacing name", true); + addDeclAttrParamKeyword("introduced", /*Parameters=*/{}, + "Specify version number", true); + addDeclAttrParamKeyword("deprecated", /*Parameters=*/{}, + "Specify version number", true); } break; case CustomSyntaxAttributeKind::FreestandingMacro: @@ -3043,12 +3048,27 @@ void CompletionLookup::getAttributeDeclParamCompletions( isRoleSupported &= isAttachedMacro(role); } if (isRoleSupported) { - addDeclAttrParamKeyword(getMacroRoleString(role), "", false); + addDeclAttrParamKeyword(getMacroRoleString(role), /*Parameters=*/{}, + /*Annotation=*/"", /*NeedsSpecify=*/false); } } break; case 1: - addDeclAttrParamKeyword("names", "Specify declared names", true); + if (HasLabel) { + for (auto kind : getAllMacroIntroducedDeclNameKinds()) { + auto name = getMacroIntroducedDeclNameString(kind); + SmallVector Parameters; + if (macroIntroducedNameRequiresArgument(kind)) { + Parameters = {"name"}; + } + addDeclAttrParamKeyword(name, Parameters, /*Annotation=*/"", + /*NeedsSpecify=*/false); + } + } else { + addDeclAttrParamKeyword("names", /*Parameters=*/{}, + "Specify declared names", + /*NeedsSpecify=*/true); + } break; } break; @@ -3126,7 +3146,8 @@ void CompletionLookup::getPrecedenceGroupCompletions( void CompletionLookup::getPoundAvailablePlatformCompletions() { // The platform names should be identical to those in @available. - getAttributeDeclParamCompletions(CustomSyntaxAttributeKind::Available, 0); + getAttributeDeclParamCompletions(CustomSyntaxAttributeKind::Available, 0, + /*HasLabel=*/false); } void CompletionLookup::getSelfTypeCompletionInDeclContext( diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 04c155fbab7c5..c5197ee1d4a08 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -508,7 +508,8 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( .highlight(SourceRange(ArgumentLoc)); if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::Available, ParamIndex); + CustomSyntaxAttributeKind::Available, ParamIndex, + /*HasLabel=*/false); consumeToken(tok::code_complete); } else { consumeIf(tok::identifier); @@ -941,7 +942,7 @@ bool Parser::parseAvailability( !(Tok.isAnyOperator() && Tok.getText() == "*")) { if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { CodeCompletionCallbacks->completeDeclAttrParam( - CustomSyntaxAttributeKind::Available, 0); + CustomSyntaxAttributeKind::Available, 0, /*HasLabel=*/false); consumeToken(tok::code_complete); } diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) @@ -2185,6 +2186,14 @@ Optional getMacroRole(StringRef roleName) { .Default(None); } +static CustomSyntaxAttributeKind getCustomSyntaxAttributeKind(bool isAttached) { + if (isAttached) { + return CustomSyntaxAttributeKind::AttachedMacro; + } else { + return CustomSyntaxAttributeKind::FreestandingMacro; + } +} + ParserResult Parser::parseMacroRoleAttribute( MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc) @@ -2221,20 +2230,18 @@ Parser::parseMacroRoleAttribute( [&] { ParserStatus status; - if (Tok.is(tok::code_complete)) { - consumeIf(tok::code_complete); + if (consumeIf(tok::code_complete)) { status.setHasCodeCompletionAndIsError(); - CustomSyntaxAttributeKind attributeKind = - isAttached ? CustomSyntaxAttributeKind::AttachedMacro - : CustomSyntaxAttributeKind::FreestandingMacro; if (!sawRole) { sawRole = true; if (this->CodeCompletionCallbacks) { - this->CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 0); + this->CodeCompletionCallbacks->completeDeclAttrParam( + getCustomSyntaxAttributeKind(isAttached), 0, /*HasLabel=*/false); } } else if (!sawNames) { if (this->CodeCompletionCallbacks) { - this->CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 1); + this->CodeCompletionCallbacks->completeDeclAttrParam( + getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/false); } } } @@ -2309,10 +2316,15 @@ Parser::parseMacroRoleAttribute( // Parse the introduced name kind. Identifier introducedNameKind; SourceLoc introducedNameKindLoc; - if (parseIdentifier( - introducedNameKind, introducedNameKindLoc, - diag::macro_attribute_unknown_argument_form, - /*diagnoseDollarPrefix=*/true)) { + if (consumeIf(tok::code_complete)) { + status.setHasCodeCompletionAndIsError(); + if (this->CodeCompletionCallbacks) { + this->CodeCompletionCallbacks->completeDeclAttrParam( + getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/true); + } + } else if (parseIdentifier(introducedNameKind, introducedNameKindLoc, + diag::macro_attribute_unknown_argument_form, + /*diagnoseDollarPrefix=*/true)) { status.setIsParseError(); return status; } diff --git a/test/IDE/complete_macro_attribute.swift b/test/IDE/complete_macro_attribute.swift index 0c3e61eb72a0c..1d7024fddbb0c 100644 --- a/test/IDE/complete_macro_attribute.swift +++ b/test/IDE/complete_macro_attribute.swift @@ -24,3 +24,13 @@ macro FreestandingDeclarationMacro // NAMES_POSITION: Begin completions, 1 item // NAMES_POSITION-DAG: Keyword/None: names: [#Specify declared names#]; name=names + +@attached(member, names: #^NAMES_ARGUMENT^#) + +// NAMES_ARGUMENT: Begin completions, 5 items +// NAMES_ARGUMENT-DAG: Keyword/None: named({#(name)#}); name=named() +// NAMES_ARGUMENT-DAG: Keyword/None: overloaded; name=overloaded +// NAMES_ARGUMENT-DAG: Keyword/None: prefixed({#(name)#}); name=prefixed() +// NAMES_ARGUMENT-DAG: Keyword/None: suffixed({#(name)#}); name=suffixed() +// NAMES_ARGUMENT-DAG: Keyword/None: arbitrary; name=arbitrary +