diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index fafa4c4c768ff..664345b2080cd 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -223,7 +223,7 @@ ERROR(unknown_attribute,none, NOTE(in_macro_expansion,none, "in expansion of macro %0 here", (DeclName)) ERROR(macro_experimental,none, - "%0 macros are an experimental feature that is not enabled (%1)", + "%0 macros are an experimental feature that is not enabled %select{|(%1)}1", (StringRef, StringRef)) ERROR(ambiguous_macro_reference,none, "ambiguous reference to macro %0", (DeclName)) diff --git a/include/swift/AST/MacroDeclaration.h b/include/swift/AST/MacroDeclaration.h index 1134dba7be70a..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; @@ -83,6 +88,9 @@ bool isAttachedMacro(MacroRoles contexts); MacroRoles getAttachedMacroRoles(); +/// Checks if the macro is supported or guarded behind an experimental flag. +bool isMacroSupported(MacroRole role, ASTContext &ctx); + enum class MacroIntroducedDeclNameKind { Named, Overloaded, 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/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 08f353c8f6a01..0538a61557ec0 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1019,9 +1019,10 @@ class Parser { PatternBindingInitializer *initContext); /// Parse the optional modifiers before a declaration. - bool parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc, - StaticSpellingKind &StaticSpelling, - bool isFromClangAttribute = false); + ParserStatus parseDeclModifierList(DeclAttributes &Attributes, + SourceLoc &StaticLoc, + StaticSpellingKind &StaticSpelling, + bool isFromClangAttribute = false); /// Parse an availability attribute of the form /// @available(*, introduced: 1.0, deprecated: 3.1). @@ -1133,9 +1134,9 @@ class Parser { ParserResult parseCustomAttribute( SourceLoc atLoc, PatternBindingInitializer *&initContext); - bool parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, - DeclAttrKind DK, - bool isFromClangAttribute = false); + ParserStatus parseNewDeclAttribute(DeclAttributes &Attributes, + SourceLoc AtLoc, DeclAttrKind DK, + bool isFromClangAttribute = false); /// Parse a version tuple of the form x[.y[.z]]. Returns true if there was /// an error parsing. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5d65e90d92396..56592ca4174ba 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10091,6 +10091,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: @@ -10182,6 +10190,21 @@ MacroRoles swift::getAttachedMacroRoles() { return attachedMacroRoles; } +bool swift::isMacroSupported(MacroRole role, ASTContext &ctx) { + switch (role) { + case MacroRole::Expression: + case MacroRole::Declaration: + case MacroRole::Accessor: + case MacroRole::MemberAttribute: + case MacroRole::Member: + case MacroRole::Peer: + case MacroRole::Conformance: + return true; + case MacroRole::CodeItem: + return ctx.LangOpts.hasFeature(Feature::CodeItemMacros); + } +} + void MissingDecl::forEachMacroExpandedDecl(MacroExpandedDeclCallback callback) { auto macroRef = unexpandedMacro.macroRef; auto *baseDecl = unexpandedMacro.baseDecl; diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 92e6b60ea2026..8e21168b3cda0 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7658,9 +7658,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { } else { SourceLoc staticLoc; StaticSpellingKind staticSpelling; - hadError = parser.parseDeclModifierList( - MappedDecl->getAttrs(), staticLoc, staticSpelling, - /*isFromClangAttribute=*/true); + hadError = parser + .parseDeclModifierList(MappedDecl->getAttrs(), staticLoc, + staticSpelling, + /*isFromClangAttribute=*/true) + .isError(); } if (hadError) { 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..912ff6ffafae7 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 declared 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 f574dcd844841..1f79ff882f49b 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) @@ -2218,6 +2219,26 @@ Parser::parseMacroRoleAttribute( /*AllowSepAfterLast=*/false, diag::expected_rparen_expr_list, [&] { + 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 (this->CodeCompletionCallbacks) { + this->CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 0); + } + } else if (!sawNames) { + if (this->CodeCompletionCallbacks) { + this->CodeCompletionCallbacks->completeDeclAttrParam(attributeKind, 1); + } + } + } + // Parse the argment label, if there is one. Identifier fieldName; SourceLoc fieldNameLoc; @@ -2228,7 +2249,8 @@ Parser::parseMacroRoleAttribute( diagnose( fieldNameLoc, diag::macro_attribute_unknown_label, isAttached, fieldName); - return makeParserError(); + status.setIsParseError(); + return status; } // If there is no field name and we haven't seen either names or the role, @@ -2244,13 +2266,20 @@ Parser::parseMacroRoleAttribute( SourceLoc roleNameLoc; if (parseIdentifier(roleName, roleNameLoc, diagKind, /*diagnoseDollarPrefix=*/true)) { - return makeParserError(); + status.setIsParseError(); + return status; } role = getMacroRole(roleName.str()); if (!role) { diagnose(roleNameLoc, diag::macro_role_attr_expected_kind, isAttached); - return makeParserError(); + status.setIsParseError(); + return status; + } + if (!isMacroSupported(*role, Context)) { + diagnose(roleNameLoc, diag::macro_experimental, roleName.str(), ""); + status.setIsParseError(); + return status; } // Check that the role makes sense. @@ -2259,10 +2288,11 @@ Parser::parseMacroRoleAttribute( roleNameLoc, diag::macro_role_syntax_mismatch, isAttached, roleName ); - return makeParserError(); + status.setIsParseError(); + return status; } - return makeParserSuccess(); + return status; } // If the field name is empty and we haved seen "names", or the field name @@ -2283,7 +2313,8 @@ Parser::parseMacroRoleAttribute( introducedNameKind, introducedNameKindLoc, diag::macro_attribute_unknown_argument_form, /*diagnoseDollarPrefix=*/true)) { - return makeParserError(); + status.setIsParseError(); + return status; } auto introducedKind = getMacroIntroducedDeclNameKind(introducedNameKind); @@ -2292,7 +2323,8 @@ Parser::parseMacroRoleAttribute( introducedNameKindLoc, diag::macro_attribute_unknown_name_kind, introducedNameKind ); - return makeParserError(); + status.setIsParseError(); + return status; } // If we don't need an argument, we're done. @@ -2306,14 +2338,15 @@ Parser::parseMacroRoleAttribute( } names.push_back(MacroIntroducedDeclName(*introducedKind)); - return makeParserSuccess(); + return status; } if (!Tok.is(tok::l_paren)) { diagnose( Tok, diag::macro_attribute_introduced_name_requires_argument, introducedNameKind); - return makeParserError(); + status.setIsParseError(); + return status; } // Parse the name. @@ -2326,8 +2359,10 @@ Parser::parseMacroRoleAttribute( DeclNameFlag::AllowKeywordsUsingSpecialNames | DeclNameFlag::AllowCompoundNames | DeclNameFlag::AllowZeroArgCompoundNames)); - if (!name) - return makeParserError(); + if (!name) { + status.setIsParseError(); + return status; + } SourceLoc rParenLoc; if (!consumeIf(tok::r_paren, rParenLoc)) { @@ -2339,7 +2374,7 @@ Parser::parseMacroRoleAttribute( names.push_back( MacroIntroducedDeclName(*introducedKind, name.getFullName())); - return makeParserSuccess(); + return status; }); if (argumentsStatus.isErrorOrHasCompletion()) @@ -2470,8 +2505,9 @@ parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange, return None; } -bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, - DeclAttrKind DK, bool isFromClangAttribute) { +ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, + SourceLoc AtLoc, DeclAttrKind DK, + bool isFromClangAttribute) { // Ok, it is a valid attribute, eat it, and then process it. StringRef AttrName = Tok.getText(); SourceLoc Loc = consumeToken(); @@ -2548,14 +2584,15 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, case DAK_Effects: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, - DeclAttribute::isDeclModifier(DK)); return false; + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); } EffectsKind kind = EffectsKind::Unspecified; SourceLoc customStart, customEnd; { if (Tok.isNot(tok::identifier)) { diagnose(Loc, diag::error_in_effects_attribute, "expected identifier"); - return false; + return makeParserSuccess(); } if (Tok.getText() == "readonly") @@ -2583,7 +2620,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } if (!DiscardAttribute) { @@ -2606,7 +2643,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, { Context.Id__always, InlineKind::Always } }); if (!kind) - return false; + return makeParserSuccess(); if (!DiscardAttribute) Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, *kind)); @@ -2622,7 +2659,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, { Context.Id_none, OptimizationMode::NoOptimization } }); if (!optMode) - return false; + return makeParserSuccess(); if (!DiscardAttribute) Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, *optMode)); @@ -2637,7 +2674,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, { Context.Id_unchecked, ExclusivityAttr::Mode::Unchecked } }); if (!mode) - return false; + return makeParserSuccess(); if (!DiscardAttribute) Attributes.add(new (Context) ExclusivityAttr(AtLoc, AttrRange, *mode)); @@ -2677,7 +2714,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, { Context.Id_assumed, NonSendableKind::Assumed } }, NonSendableKind::Specific); if (!kind) - return false; + return makeParserSuccess(); if (!DiscardAttribute) Attributes.add(new (Context) NonSendableAttr(AtLoc, AttrRange, *kind)); @@ -2724,7 +2761,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, consumeToken(tok::r_paren); } } - return false; + return makeParserSuccess(); } AttrRange = SourceRange(Loc, Tok.getLoc()); @@ -2732,7 +2769,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } DuplicateAttribute = Attributes.getAttribute(); @@ -2747,7 +2784,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SmallVector spiGroups; @@ -2757,7 +2794,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, diagnose(getEndOfPreviousLoc(), diag::attr_access_expected_spi_name); consumeToken(); consumeIf(tok::r_paren); - return false; + return makeParserSuccess(); } auto text = Tok.getText(); @@ -2769,7 +2806,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } Attributes.add(SPIAccessControlAttr::create(Context, AtLoc, AttrRange, @@ -2783,7 +2820,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } bool ParseSymbolName = true; @@ -2792,7 +2829,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, diagnose(Tok.getLoc(), diag::attr_expected_option_such_as, AttrName, "Cxx"); if (Tok.isNot(tok::identifier)) - return false; + return makeParserSuccess(); DiscardAttribute = true; } consumeToken(tok::identifier); @@ -2803,7 +2840,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (ParseSymbolName) { if (Tok.isNot(tok::string_literal)) { diagnose(Loc, diag::attr_expected_string_literal, AttrName); - return false; + return makeParserSuccess(); } AsmName = @@ -2821,7 +2858,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } // Diagnose using @_silgen_name in a local scope. These don't @@ -2854,19 +2891,19 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } if (Tok.isNot(tok::integer_literal)) { diagnose(Loc, diag::alignment_must_be_positive_integer); - return false; + return makeParserSuccess(); } StringRef alignmentText = Tok.getText(); unsigned alignmentValue; if (alignmentText.getAsInteger(0, alignmentValue)) { diagnose(Loc, diag::alignment_must_be_positive_integer); - return false; + return makeParserSuccess(); } consumeToken(tok::integer_literal); @@ -2876,7 +2913,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } Attributes.add(new (Context) AlignmentAttr(alignmentValue, AtLoc, range, @@ -2890,7 +2927,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) - return false; + return makeParserSuccess(); Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(*name, AtLoc, range, /*implicit*/ false)); @@ -2901,12 +2938,12 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } if (Tok.isNot(tok::string_literal)) { diagnose(Loc, diag::attr_expected_string_literal, AttrName); - return false; + return makeParserSuccess(); } auto Value = getStringLiteralIfNotInterpolated( @@ -2922,7 +2959,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::r_paren)) { diagnose(Loc, diag::attr_expected_rparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } if (!DiscardAttribute) @@ -2936,7 +2973,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SourceLoc RightLoc; enum class NextSegmentKind: uint8_t { @@ -2998,15 +3035,15 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } llvm_unreachable("invalid next segment kind"); }).isErrorOrHasCompletion() || SuppressLaterDiags) { - return false; + return makeParserSuccess(); } if (OriginalModuleName.empty()) { diagnose(AtLoc, diag::originally_defined_in_need_nonempty_module_name); - return false; + return makeParserSuccess(); } if (PlatformAndVersions.empty()) { diagnose(AtLoc, diag::attr_availability_need_platform_version, AttrName); - return false; + return makeParserSuccess(); } assert(!OriginalModuleName.empty()); @@ -3026,12 +3063,12 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } if (!parseAvailability(false, AttrName, DiscardAttribute, AttrRange, AtLoc, Loc, [&](AvailableAttr *attr) { Attributes.add(attr); })) - return false; + return makeParserSuccess(); break; } case DAK_PrivateImport: { @@ -3039,7 +3076,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Tok.isNot(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SourceLoc LParenLoc = consumeToken(tok::l_paren); Optional filename; @@ -3047,26 +3084,26 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, // Parse 'sourceFile'. if (Tok.getText() != "sourceFile") { diagnose(LParenLoc, diag::attr_private_import_expected_sourcefile); - return false; + return makeParserSuccess(); } auto ForLoc = consumeToken(); // Parse ':'. if (Tok.getKind() != tok::colon) { diagnose(ForLoc, diag::attr_private_import_expected_colon); - return false; + return makeParserSuccess(); } auto ColonLoc = consumeToken(tok::colon); // Parse '"'function-name'"' if (Tok.isNot(tok::string_literal)) { diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name); - return false; + return makeParserSuccess(); } filename = getStringLiteralIfNotInterpolated(Loc, "_private"); if (!filename.has_value()) { diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name); - return false; + return makeParserSuccess(); } consumeToken(tok::string_literal); } @@ -3076,7 +3113,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, diag::attr_private_import_expected_rparen, LParenLoc); if (Invalid) - return false; + return makeParserSuccess(); auto *attr = PrivateImportAttr::create(Context, AtLoc, Loc, LParenLoc, *filename, RParenLoc); Attributes.add(attr); @@ -3132,7 +3169,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK, /*allowOmitted=*/true); if (!name) - return false; + return makeParserSuccess(); Attributes.add(new (Context) ObjCImplementationAttr(*name, AtLoc, range)); break; @@ -3142,7 +3179,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) - return false; + return makeParserSuccess(); Attributes.add(new (Context) ObjCRuntimeNameAttr(name->str(), AtLoc, range, /*implicit*/ false)); @@ -3155,7 +3192,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Tok.isNot(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SourceLoc LParenLoc = consumeToken(tok::l_paren); @@ -3164,14 +3201,14 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, // Parse 'for'. if (Tok.getText() != "for") { diagnose(Loc, diag::attr_dynamic_replacement_expected_for); - return false; + return makeParserSuccess(); } auto ForLoc = consumeToken(); // Parse ':'. if (Tok.getText() != ":") { diagnose(ForLoc, diag::attr_dynamic_replacement_expected_colon); - return false; + return makeParserSuccess(); } consumeToken(tok::colon); { @@ -3191,7 +3228,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, tok::r_paren, RParenLoc, diag::attr_dynamic_replacement_expected_rparen, LParenLoc); if (Invalid) { - return false; + return makeParserSuccess(); } @@ -3206,7 +3243,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Tok.isNot(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SourceLoc LParenLoc = consumeToken(tok::l_paren); @@ -3224,7 +3261,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, diag::attr_type_eraser_expected_rparen, LParenLoc); if (invalid) - return false; + return makeParserSuccess(); auto *TE = new (Context) TypeExpr(ErasedType.get()); Attributes.add(TypeEraserAttr::create(Context, AtLoc, {Loc, RParenLoc}, TE)); @@ -3235,11 +3272,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Tok.isNot(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, DeclAttribute::isDeclModifier(DK)); - return false; + return makeParserSuccess(); } SpecializeAttr *Attr; if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr, nullptr)) - return false; + return makeParserSuccess(); Attributes.add(Attr); break; @@ -3287,7 +3324,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK); if (!name) - return false; + return makeParserSuccess(); Attributes.add(new (Context) ProjectedValuePropertyAttr( *name, AtLoc, range, /*implicit*/ false)); @@ -3305,13 +3342,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (flag != "message") { diagnose(Tok.getLoc(), diag::attr_unknown_option, flag, AttrName); - return true; + return makeParserError(); } consumeToken(); if (!consumeIf(tok::colon)) { if (!Tok.is(tok::equal)) { diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, flag); - return false; + return makeParserSuccess(); } diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value) .fixItReplace(Tok.getLoc(), ": "); @@ -3319,13 +3356,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } if (!Tok.is(tok::string_literal)) { diagnose(Tok.getLoc(), diag::attr_expected_string_literal, AttrName); - return false; + return makeParserSuccess(); } Optional value = getStringLiteralIfNotInterpolated( Tok.getLoc(), flag); if (!value) - return false; + return makeParserSuccess(); Token stringTok = Tok; consumeToken(); message = *value; @@ -3342,7 +3379,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } case DAK_BackDeployed: { if (!parseBackDeployedAttribute(Attributes, AttrName, AtLoc, Loc)) - return false; + return makeParserSuccess(); break; } case DAK_Documentation: { @@ -3350,7 +3387,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Attr.isNonNull()) Attributes.add(Attr.get()); else - return false; + return makeParserSuccess(); break; } case DAK_MacroRole: { @@ -3360,7 +3397,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, if (Attr.isNonNull()) Attributes.add(Attr.get()); else - return false; + return Attr; break; } } @@ -3378,8 +3415,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, // with a fixit. if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK)) diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc); - - return false; + + return makeParserSuccess(); } bool Parser::parseVersionTuple(llvm::VersionTuple &Version, @@ -3726,8 +3763,7 @@ ParserStatus Parser::parseDeclAttribute( } if (DK != DAK_Count && !DeclAttribute::shouldBeRejectedByParser(DK)) { - parseNewDeclAttribute(Attributes, AtLoc, DK, isFromClangAttribute); - return makeParserSuccess(); + return parseNewDeclAttribute(Attributes, AtLoc, DK, isFromClangAttribute); } if (TypeAttributes::getAttrKindFromString(Tok.getText()) != TAK_Count) @@ -4300,11 +4336,11 @@ ParserStatus Parser::parseDeclAttributeList( // 'convenience' // 'actor' // 'distributed' -bool Parser::parseDeclModifierList(DeclAttributes &Attributes, - SourceLoc &StaticLoc, - StaticSpellingKind &StaticSpelling, - bool isFromClangAttribute) { - bool isError = false; +ParserStatus Parser::parseDeclModifierList(DeclAttributes &Attributes, + SourceLoc &StaticLoc, + StaticSpellingKind &StaticSpelling, + bool isFromClangAttribute) { + ParserStatus status; while (true) { switch (Tok.getKind()) { @@ -4313,7 +4349,7 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, case tok::kw_internal: case tok::kw_public: { // We still model these specifiers as attributes. - isError |= + status |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl); continue; } @@ -4360,7 +4396,7 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, // Actor is a standalone keyword now, so it can't be used // as a modifier. Let's diagnose and recover. - isError = true; + status.setIsParseError(); consumeToken(); // consume 'actor' @@ -4368,8 +4404,8 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, continue; } - isError |= parseNewDeclAttribute( - Attributes, /*AtLoc=*/{}, Kind, isFromClangAttribute); + status |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, Kind, + isFromClangAttribute); continue; } @@ -4431,7 +4467,7 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, } // If we 'break' out of the switch, modifier list has ended. - return isError; + return status; } } @@ -5073,15 +5109,19 @@ Parser::parseDecl(ParseDeclOptions Flags, // Keep track of where and whether we see a contextual keyword on the decl. SourceLoc StaticLoc; StaticSpellingKind StaticSpelling = StaticSpellingKind::None; - parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); + auto ModifierResult = + parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); + if (ModifierResult.hasCodeCompletion()) { + return ModifierResult; + } + + ParserResult DeclResult; // We emit diagnostics for 'try let ...' in parseDeclVar(). SourceLoc tryLoc; if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var)) tryLoc = consumeToken(tok::kw_try); - ParserResult DeclResult; - // Save the original token, in case code-completion needs it. auto OrigTok = Tok; bool MayNeedOverrideCompletion = false; diff --git a/test/IDE/complete_macro_attribute.swift b/test/IDE/complete_macro_attribute.swift new file mode 100644 index 0000000000000..0c3e61eb72a0c --- /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 declared names#]; name=names diff --git a/test/Macros/macro_codeitems_disabled.swift b/test/Macros/macro_codeitems_disabled.swift new file mode 100644 index 0000000000000..d3056bcfb498a --- /dev/null +++ b/test/Macros/macro_codeitems_disabled.swift @@ -0,0 +1,4 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 %s + +@freestanding(codeItem) // expected-error {{codeItem macros are an experimental feature that is not enabled}} +macro codeItems() // expected-error {{macro 'codeItems()' requires a definition}} diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 29b1be888864e..cbae960c02a8e 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -1,6 +1,6 @@ // REQUIRES: swift_swift_parser -// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-experimental-feature FreestandingMacros -module-name MacrosTest +// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-experimental-feature FreestandingMacros -enable-experimental-feature CodeItemMacros -module-name MacrosTest @expression macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") // expected-note@-1 2{{'stringify' declared here}}