From 3f412de4af854e5686d53a947a1d6d1c51184685 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 25 Apr 2023 17:44:34 -0700 Subject: [PATCH] [CodeCompletion] Support completion for macro roles and the 'names:' argument label rdar://108163121 --- include/swift/AST/MacroDeclaration.h | 5 ++++ include/swift/IDE/CompletionLookup.h | 3 +- include/swift/Parse/IDEInspectionCallbacks.h | 11 ++++++- lib/AST/Decl.cpp | 8 +++++ lib/IDE/CodeCompletion.cpp | 8 ++--- lib/IDE/CompletionLookup.cpp | 31 +++++++++++++++++--- lib/Parse/ParseDecl.cpp | 25 ++++++++++++++-- test/IDE/complete_macro_attribute.swift | 26 ++++++++++++++++ 8 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 test/IDE/complete_macro_attribute.swift diff --git a/include/swift/AST/MacroDeclaration.h b/include/swift/AST/MacroDeclaration.h index 0097e68ad98f9..e7187fedfb022 100644 --- a/include/swift/AST/MacroDeclaration.h +++ b/include/swift/AST/MacroDeclaration.h @@ -58,8 +58,13 @@ enum class MacroRole: uint32_t { /// A freestanding macro that expands to expressions, statements and /// declarations in a code block. CodeItem = 0x80, + + // NOTE: When adding a new macro role, also add it to `getAllMacroRoles`. }; +/// Returns an enumeratable list of all macro roles. +std::vector getAllMacroRoles(); + /// The contexts in which a particular macro declaration can be used. using MacroRoles = OptionSet; diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 2156a93d69a2d..84f5bcaf95874 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -585,7 +585,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void getAttributeDeclCompletions(bool IsInSil, Optional DK); - void getAttributeDeclParamCompletions(DeclAttrKind AttrKind, int ParamIndex); + void getAttributeDeclParamCompletions(CustomSyntaxAttributeKind AttrKind, + int ParamIndex); void getTypeAttributeKeywordCompletions(); diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 776b8679d9b4b..bcff4427b4eb3 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -29,6 +29,15 @@ 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 +/// the same attribute kind but take different macro roles as arguemnts. +enum class CustomSyntaxAttributeKind { + Available, + FreestandingMacro, + AttachedMacro, +}; + /// Parser's interface to code completion. class CodeCompletionCallbacks { protected: @@ -185,7 +194,7 @@ class CodeCompletionCallbacks { /// Complete the parameters in attribute, for instance, version specifier for /// @available. - virtual void completeDeclAttrParam(DeclAttrKind DK, int Index) {}; + virtual void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index){}; /// 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 ff14d85168734..fd27a51823077 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10049,6 +10049,14 @@ BuiltinTupleDecl::BuiltinTupleDecl(Identifier Name, DeclContext *Parent) : NominalTypeDecl(DeclKind::BuiltinTuple, Parent, Name, SourceLoc(), ArrayRef(), nullptr) {} +std::vector swift::getAllMacroRoles() { + return { + MacroRole::Expression, MacroRole::Declaration, MacroRole::Accessor, + MacroRole::MemberAttribute, MacroRole::Member, MacroRole::Peer, + MacroRole::Conformance, MacroRole::CodeItem, + }; +} + StringRef swift::getMacroRoleString(MacroRole role) { switch (role) { case MacroRole::Expression: diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 5e1d6c8b725d5..19ed95d918e12 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -113,7 +113,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, SourceLoc DotLoc; TypeLoc ParsedTypeLoc; DeclContext *CurDeclContext = nullptr; - DeclAttrKind AttrKind; + CustomSyntaxAttributeKind 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 @@ -270,7 +270,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, void completeCaseStmtKeyword() override; void completeCaseStmtBeginning(CodeCompletionExpr *E) override; void completeDeclAttrBeginning(bool Sil, bool isIndependent) override; - void completeDeclAttrParam(DeclAttrKind DK, int Index) override; + void completeDeclAttrParam(CustomSyntaxAttributeKind DK, int Index) override; void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override; void completeInPrecedenceGroup( CodeCompletionCallbacks::PrecedenceGroupCompletionKind SK) override; @@ -456,8 +456,8 @@ void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { CurDeclContext = P.CurDeclContext; } -void CodeCompletionCallbacksImpl::completeDeclAttrParam(DeclAttrKind DK, - int Index) { +void CodeCompletionCallbacksImpl::completeDeclAttrParam( + CustomSyntaxAttributeKind DK, int Index) { Kind = CompletionKind::AttributeDeclParen; AttrKind = DK; AttrParamIndex = Index; diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 6d1afb85062c3..8b6ecb6537820 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3004,9 +3004,10 @@ void CompletionLookup::getAttributeDeclCompletions(bool IsInSil, #include "swift/AST/Attr.def" } -void CompletionLookup::getAttributeDeclParamCompletions(DeclAttrKind AttrKind, - int ParamIndex) { - if (AttrKind == DAK_Available) { +void CompletionLookup::getAttributeDeclParamCompletions( + CustomSyntaxAttributeKind AttrKind, int ParamIndex) { + switch (AttrKind) { + case CustomSyntaxAttributeKind::Available: if (ParamIndex == 0) { addDeclAttrParamKeyword("*", "Platform", false); @@ -3022,6 +3023,28 @@ void CompletionLookup::getAttributeDeclParamCompletions(DeclAttrKind AttrKind, addDeclAttrParamKeyword("introduced", "Specify version number", true); addDeclAttrParamKeyword("deprecated", "Specify version number", true); } + break; + case CustomSyntaxAttributeKind::FreestandingMacro: + case CustomSyntaxAttributeKind::AttachedMacro: + switch (ParamIndex) { + case 0: + for (auto role : getAllMacroRoles()) { + bool isRoleSupported = isMacroSupported(role, Ctx); + if (AttrKind == CustomSyntaxAttributeKind::FreestandingMacro) { + isRoleSupported &= isFreestandingMacro(role); + } else if (AttrKind == CustomSyntaxAttributeKind::AttachedMacro) { + isRoleSupported &= isAttachedMacro(role); + } + if (isRoleSupported) { + addDeclAttrParamKeyword(getMacroRoleString(role), "", false); + } + } + break; + case 1: + addDeclAttrParamKeyword("names", "Specify names", true); + break; + } + break; } } @@ -3096,7 +3119,7 @@ void CompletionLookup::getPrecedenceGroupCompletions( void CompletionLookup::getPoundAvailablePlatformCompletions() { // The platform names should be identical to those in @available. - getAttributeDeclParamCompletions(DAK_Available, 0); + getAttributeDeclParamCompletions(CustomSyntaxAttributeKind::Available, 0); } void CompletionLookup::getSelfTypeCompletionInDeclContext( diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f9f35cbdaba77..3b66d5c1ef7d7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -507,8 +507,8 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( diagnose(ArgumentLoc, diag::attr_availability_expected_option, AttrName) .highlight(SourceRange(ArgumentLoc)); if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam(DAK_Available, - ParamIndex); + CodeCompletionCallbacks->completeDeclAttrParam( + CustomSyntaxAttributeKind::Available, ParamIndex); consumeToken(tok::code_complete); } else { consumeIf(tok::identifier); @@ -940,7 +940,8 @@ bool Parser::parseAvailability( if (!Tok.is(tok::identifier) && !(Tok.isAnyOperator() && Tok.getText() == "*")) { if (Tok.is(tok::code_complete) && CodeCompletionCallbacks) { - CodeCompletionCallbacks->completeDeclAttrParam(DAK_Available, 0); + CodeCompletionCallbacks->completeDeclAttrParam( + CustomSyntaxAttributeKind::Available, 0); consumeToken(tok::code_complete); } diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) @@ -2220,6 +2221,24 @@ Parser::parseMacroRoleAttribute( [&] { ParserStatus status; + if (Tok.is(tok::code_complete)) { + consumeIf(tok::code_complete); + status.setHasCodeCompletionAndIsError(); + CustomSyntaxAttributeKind attributeKind = + isAttached ? CustomSyntaxAttributeKind::AttachedMacro + : CustomSyntaxAttributeKind::FreestandingMacro; + if (!sawRole) { + sawRole = true; + if (CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 0); + } + } else if (!sawNames) { + if (CodeCompletionCallbacks) { + CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 1); + } + } + } + // Parse the argment label, if there is one. Identifier fieldName; SourceLoc fieldNameLoc; diff --git a/test/IDE/complete_macro_attribute.swift b/test/IDE/complete_macro_attribute.swift new file mode 100644 index 0000000000000..b3e872d34ef55 --- /dev/null +++ b/test/IDE/complete_macro_attribute.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/output + + +@freestanding(#^FREESTANDING_ROLE^#) +macro FreestandingMacro + +// FREESTANDING_ROLE: Begin completions, 2 items +// FREESTANDING_ROLE-DAG: Keyword/None: expression; name=expression +// FREESTANDING_ROLE-DAG: Keyword/None: declaration; name=declaration + +@attached(#^ATTACHED_ROLE^#) +macro AttachedMacro + +// ATTACHED_ROLE: Begin completions, 5 items +// ATTACHED_ROLE-DAG: Keyword/None: accessor; name=accessor +// ATTACHED_ROLE-DAG: Keyword/None: memberAttribute; name=memberAttribute +// ATTACHED_ROLE-DAG: Keyword/None: member; name=member +// ATTACHED_ROLE-DAG: Keyword/None: peer; name=peer +// ATTACHED_ROLE-DAG: Keyword/None: conformance; name=conformance + +@freestanding(declaration, #^NAMES_POSITION^#) +macro FreestandingDeclarationMacro + +// NAMES_POSITION: Begin completions, 1 item +// NAMES_POSITION-DAG: Keyword/None: names: [#Specify names#]; name=names