diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 141aa27e4e0eb..3aae2b5490690 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -16,6 +16,7 @@ #include "swift/Basic/Mangler.h" #include "swift/AST/Types.h" #include "swift/AST/Decl.h" +#include "swift/AST/FreestandingMacroExpansion.h" #include "swift/Basic/TaggedUnion.h" namespace clang { @@ -367,8 +368,7 @@ class ASTMangler : public Mangler { mangleRuntimeAttributeGeneratorEntity(const ValueDecl *decl, CustomAttr *attr, SymbolKind SKind = SymbolKind::Default); - std::string mangleMacroExpansion(const MacroExpansionExpr *expansion); - std::string mangleMacroExpansion(const MacroExpansionDecl *expansion); + std::string mangleMacroExpansion(const FreestandingMacroExpansion *expansion); std::string mangleAttachedMacroExpansion( const Decl *decl, CustomAttr *attr, MacroRole role); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index bbea62516e69b..a271d4b940970 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -26,6 +26,7 @@ #include "swift/AST/DefaultArgumentKind.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/FreestandingMacroExpansion.h" #include "swift/AST/GenericParamKey.h" #include "swift/AST/IfConfigClause.h" #include "swift/AST/LayoutConstraint.h" @@ -912,7 +913,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated { /// /// Auxiliary declarations can be property wrapper backing variables, /// backing variables for 'lazy' vars, or peer macro expansions. - void visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const; + /// + /// When \p visitFreestandingExpanded is true (the default), this will also + /// visit the declarations produced by a freestanding macro expansion. + void visitAuxiliaryDecls( + AuxiliaryDeclCallback callback, + bool visitFreestandingExpanded = true + ) const; using MacroCallback = llvm::function_ref; @@ -8596,6 +8603,9 @@ class MacroDecl : public GenericContext, public ValueDecl { /// Retrieve the definition of this macro. MacroDefinition getDefinition() const; + /// Set the definition of this macro + void setDefinition(MacroDefinition definition); + /// Retrieve the parameter list of this macro. ParameterList *getParameterList() const { return parameterList; } @@ -8618,69 +8628,29 @@ class MacroDecl : public GenericContext, public ValueDecl { using Decl::getASTContext; }; -/// Information about a macro expansion that is common between macro -/// expansion declarations and expressions. -/// -/// Instances of these types will be shared among paired macro expansion -/// declaration/expression nodes. -struct MacroExpansionInfo : ASTAllocated { - SourceLoc SigilLoc; - DeclNameRef MacroName; - DeclNameLoc MacroNameLoc; - SourceLoc LeftAngleLoc, RightAngleLoc; - ArrayRef GenericArgs; - ArgumentList *ArgList; - - /// The referenced macro. - ConcreteDeclRef macroRef; - - MacroExpansionInfo(SourceLoc sigilLoc, - DeclNameRef macroName, - DeclNameLoc macroNameLoc, - SourceLoc leftAngleLoc, SourceLoc rightAngleLoc, - ArrayRef genericArgs, - ArgumentList *argList) - : SigilLoc(sigilLoc), MacroName(macroName), MacroNameLoc(macroNameLoc), - LeftAngleLoc(leftAngleLoc), RightAngleLoc(rightAngleLoc), - GenericArgs(genericArgs), ArgList(argList) { } -}; - -class MacroExpansionDecl : public Decl { - MacroExpansionInfo *info; +class MacroExpansionDecl : public Decl, public FreestandingMacroExpansion { public: enum : unsigned { InvalidDiscriminator = 0xFFFF }; MacroExpansionDecl(DeclContext *dc, MacroExpansionInfo *info); - MacroExpansionDecl(DeclContext *dc, SourceLoc poundLoc, DeclNameRef macro, - DeclNameLoc macroLoc, - SourceLoc leftAngleLoc, - ArrayRef genericArgs, - SourceLoc rightAngleLoc, - ArgumentList *args); + static MacroExpansionDecl *create(DeclContext *dc, SourceLoc poundLoc, + DeclNameRef macro, DeclNameLoc macroLoc, + SourceLoc leftAngleLoc, + ArrayRef genericArgs, + SourceLoc rightAngleLoc, + ArgumentList *args); - ArrayRef getGenericArgs() const { - return info->GenericArgs; - } + DeclContext *getDeclContext() const { return Decl::getDeclContext(); } - SourceRange getGenericArgsRange() const { - return SourceRange(info->LeftAngleLoc, info->RightAngleLoc); + SourceRange getSourceRange() const { + return getExpansionInfo()->getSourceRange(); } + SourceLoc getLocFromSource() const { return getExpansionInfo()->SigilLoc; } - SourceRange getSourceRange() const; - SourceLoc getLocFromSource() const { return info->SigilLoc; } - SourceLoc getPoundLoc() const { return info->SigilLoc; } - DeclNameLoc getMacroNameLoc() const { return info->MacroNameLoc; } - DeclNameRef getMacroName() const { return info->MacroName; } - ArgumentList *getArgs() const { return info->ArgList; } - void setArgs(ArgumentList *args) { info->ArgList = args; } - using ExprOrStmtExpansionCallback = llvm::function_ref; - void forEachExpandedExprOrStmt(ExprOrStmtExpansionCallback) const; - ConcreteDeclRef getMacroRef() const { return info->macroRef; } - void setMacroRef(ConcreteDeclRef ref) { info->macroRef = ref; } - - MacroExpansionInfo *getExpansionInfo() const { return info; } + /// Enumerate the nodes produced by expanding this macro expansion. + void forEachExpandedNode(llvm::function_ref callback) const; /// Returns a discriminator which determines this macro expansion's index /// in the sequence of macro expansions within the current function. @@ -8703,6 +8673,9 @@ class MacroExpansionDecl : public Decl { static bool classof(const Decl *D) { return D->getKind() == DeclKind::MacroExpansion; } + static bool classof(const FreestandingMacroExpansion *expansion) { + return expansion->getFreestandingMacroKind() == FreestandingMacroKind::Decl; + } }; /// Find references to the given generic parameter in the type signature of the diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 3636517bf7e70..c9092bf925472 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -25,6 +25,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DeclContext.h" #include "swift/AST/DeclNameLoc.h" +#include "swift/AST/FreestandingMacroExpansion.h" #include "swift/AST/FunctionRefKind.h" #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/TypeAlignments.h" @@ -6214,10 +6215,10 @@ class TypeJoinExpr final : public Expr, /// An invocation of a macro expansion, spelled with `#` for freestanding /// macros or `@` for attached macros. -class MacroExpansionExpr final : public Expr { +class MacroExpansionExpr final : public Expr, + public FreestandingMacroExpansion { private: DeclContext *DC; - MacroExpansionInfo *info; Expr *Rewritten; MacroRoles Roles; MacroExpansionDecl *SubstituteDecl; @@ -6226,47 +6227,31 @@ class MacroExpansionExpr final : public Expr { enum : unsigned { InvalidDiscriminator = 0xFFFF }; explicit MacroExpansionExpr(DeclContext *dc, MacroExpansionInfo *info, - MacroRoles roles, - bool isImplicit = false, + MacroRoles roles, bool isImplicit = false, Type ty = Type()) : Expr(ExprKind::MacroExpansion, isImplicit, ty), - DC(dc), info(info), Rewritten(nullptr), Roles(roles), - SubstituteDecl(nullptr) { + FreestandingMacroExpansion(FreestandingMacroKind::Expr, info), DC(dc), + Rewritten(nullptr), Roles(roles), SubstituteDecl(nullptr) { Bits.MacroExpansionExpr.Discriminator = InvalidDiscriminator; } - explicit MacroExpansionExpr(DeclContext *dc, - SourceLoc sigilLoc, DeclNameRef macroName, - DeclNameLoc macroNameLoc, - SourceLoc leftAngleLoc, - ArrayRef genericArgs, - SourceLoc rightAngleLoc, - ArgumentList *argList, - MacroRoles roles, - bool isImplicit = false, - Type ty = Type()); - - DeclNameRef getMacroName() const { return info->MacroName; } - DeclNameLoc getMacroNameLoc() const { return info->MacroNameLoc; } + static MacroExpansionExpr * + create(DeclContext *dc, SourceLoc sigilLoc, DeclNameRef macroName, + DeclNameLoc macroNameLoc, SourceLoc leftAngleLoc, + ArrayRef genericArgs, SourceLoc rightAngleLoc, + ArgumentList *argList, MacroRoles roles, bool isImplicit = false, + Type ty = Type()); Expr *getRewritten() const { return Rewritten; } void setRewritten(Expr *rewritten) { Rewritten = rewritten; } - ArrayRef getGenericArgs() const { return info->GenericArgs; } - - SourceRange getGenericArgsRange() const { - return SourceRange(info->LeftAngleLoc, info->RightAngleLoc); + ArgumentList *getArgs() const { + return FreestandingMacroExpansion::getArgs(); } - ArgumentList *getArgs() const { return info->ArgList; } - void setArgs(ArgumentList *newArgs) { info->ArgList = newArgs; } - MacroRoles getMacroRoles() const { return Roles; } - SourceLoc getLoc() const { return info->SigilLoc; } - - ConcreteDeclRef getMacroRef() const { return info->macroRef; } - void setMacroRef(ConcreteDeclRef ref) { info->macroRef = ref; } + SourceLoc getLoc() const { return getPoundLoc(); } DeclContext *getDeclContext() const { return DC; } void setDeclContext(DeclContext *dc) { DC = dc; } @@ -6289,9 +6274,9 @@ class MacroExpansionExpr final : public Expr { Bits.MacroExpansionExpr.Discriminator = discriminator; } - MacroExpansionInfo *getExpansionInfo() const { return info; } - - SourceRange getSourceRange() const; + SourceRange getSourceRange() const { + return getExpansionInfo()->getSourceRange(); + } MacroExpansionDecl *createSubstituteDecl(); MacroExpansionDecl *getSubstituteDecl() const; @@ -6299,6 +6284,9 @@ class MacroExpansionExpr final : public Expr { static bool classof(const Expr *E) { return E->getKind() == ExprKind::MacroExpansion; } + static bool classof(const FreestandingMacroExpansion *expansion) { + return expansion->getFreestandingMacroKind() == FreestandingMacroKind::Expr; + } }; inline bool Expr::isInfixOperator() const { diff --git a/include/swift/AST/FreestandingMacroExpansion.h b/include/swift/AST/FreestandingMacroExpansion.h new file mode 100644 index 0000000000000..5907d37193e58 --- /dev/null +++ b/include/swift/AST/FreestandingMacroExpansion.h @@ -0,0 +1,113 @@ +//===--- FreestandingMacroExpansion.h ------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_FREESTANDING_MACRO_EXPANSION_H +#define SWIFT_AST_FREESTANDING_MACRO_EXPANSION_H + +#include "swift/AST/ASTAllocated.h" +#include "swift/AST/ASTNode.h" +#include "swift/AST/ConcreteDeclRef.h" +#include "swift/AST/DeclNameLoc.h" +#include "swift/AST/Identifier.h" +#include "swift/Basic/SourceLoc.h" + +namespace swift { +class MacroExpansionDecl; +class MacroExpansionExpr; +class Expr; +class Decl; +class ArgumentList; + +/// Information about a macro expansion that is common between macro +/// expansion declarations and expressions. +/// +/// Instances of these types will be shared among paired macro expansion +/// declaration/expression nodes. +struct MacroExpansionInfo : ASTAllocated { + SourceLoc SigilLoc; + DeclNameRef MacroName; + DeclNameLoc MacroNameLoc; + SourceLoc LeftAngleLoc, RightAngleLoc; + llvm::ArrayRef GenericArgs; + ArgumentList *ArgList; + + /// The referenced macro. + ConcreteDeclRef macroRef; + + MacroExpansionInfo(SourceLoc sigilLoc, DeclNameRef macroName, + DeclNameLoc macroNameLoc, SourceLoc leftAngleLoc, + SourceLoc rightAngleLoc, ArrayRef genericArgs, + ArgumentList *argList) + : SigilLoc(sigilLoc), MacroName(macroName), MacroNameLoc(macroNameLoc), + LeftAngleLoc(leftAngleLoc), RightAngleLoc(rightAngleLoc), + GenericArgs(genericArgs), ArgList(argList) {} + + SourceLoc getLoc() const { return SigilLoc; } + SourceRange getGenericArgsRange() const { + return {LeftAngleLoc, RightAngleLoc}; + } + SourceRange getSourceRange() const; +}; + +enum class FreestandingMacroKind { + Expr, // MacroExpansionExpr. + Decl, // MacroExpansionDecl. +}; + +/// A base class of either 'MacroExpansionExpr' or 'MacroExpansionDecl'. +class FreestandingMacroExpansion { + llvm::PointerIntPair + infoAndKind; + +protected: + FreestandingMacroExpansion(FreestandingMacroKind kind, + MacroExpansionInfo *info) + : infoAndKind(info, kind) {} + +public: + MacroExpansionInfo *getExpansionInfo() const { + return infoAndKind.getPointer(); + } + FreestandingMacroKind getFreestandingMacroKind() const { + return infoAndKind.getInt(); + } + + ASTNode getASTNode(); + + SourceLoc getPoundLoc() const { return getExpansionInfo()->SigilLoc; } + + DeclNameLoc getMacroNameLoc() const { + return getExpansionInfo()->MacroNameLoc; + } + DeclNameRef getMacroName() const { return getExpansionInfo()->MacroName; } + + ArrayRef getGenericArgs() const { + return getExpansionInfo()->GenericArgs; + } + SourceRange getGenericArgsRange() const { + return getExpansionInfo()->getGenericArgsRange(); + } + + ArgumentList *getArgs() const { return getExpansionInfo()->ArgList; } + void setArgs(ArgumentList *args) { getExpansionInfo()->ArgList = args; } + + ConcreteDeclRef getMacroRef() const { return getExpansionInfo()->macroRef; } + void setMacroRef(ConcreteDeclRef ref) { getExpansionInfo()->macroRef = ref; } + + DeclContext *getDeclContext() const; + SourceRange getSourceRange() const; + unsigned getDiscriminator() const; +}; + +} // namespace swift + +#endif // SWIFT_AST_FREESTANDING_MACRO_EXPANSION_H diff --git a/include/swift/AST/MacroDiscriminatorContext.h b/include/swift/AST/MacroDiscriminatorContext.h index ac770a59b7c2d..0e8f80130cdc0 100644 --- a/include/swift/AST/MacroDiscriminatorContext.h +++ b/include/swift/AST/MacroDiscriminatorContext.h @@ -22,12 +22,10 @@ namespace swift { /// Describes the context of a macro expansion for the purpose of /// computing macro expansion discriminators. struct MacroDiscriminatorContext - : public llvm::PointerUnion { + : public llvm::PointerUnion { using PointerUnion::PointerUnion; - static MacroDiscriminatorContext getParentOf(MacroExpansionExpr *expansion); - static MacroDiscriminatorContext getParentOf(MacroExpansionDecl *expansion); + static MacroDiscriminatorContext getParentOf(FreestandingMacroExpansion *expansion); static MacroDiscriminatorContext getParentOf( SourceLoc loc, DeclContext *origDC ); diff --git a/include/swift/AST/PrettyStackTrace.h b/include/swift/AST/PrettyStackTrace.h index 0baaa28354cca..e60dddbf92f40 100644 --- a/include/swift/AST/PrettyStackTrace.h +++ b/include/swift/AST/PrettyStackTrace.h @@ -18,11 +18,12 @@ #ifndef SWIFT_PRETTYSTACKTRACE_H #define SWIFT_PRETTYSTACKTRACE_H -#include "llvm/Support/PrettyStackTrace.h" -#include "swift/Basic/SourceLoc.h" #include "swift/AST/AnyFunctionRef.h" +#include "swift/AST/FreestandingMacroExpansion.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" +#include "swift/Basic/SourceLoc.h" +#include "llvm/Support/PrettyStackTrace.h" namespace clang { class Type; @@ -93,7 +94,21 @@ class PrettyStackTraceAnyFunctionRef : public llvm::PrettyStackTraceEntry { virtual void print(llvm::raw_ostream &OS) const override; }; -void printExprDescription(llvm::raw_ostream &out, Expr *E, +/// PrettyStackTraceFreestandingMacroExpansion - Observe that we are +/// processing a specific freestanding macro expansion. +class PrettyStackTraceFreestandingMacroExpansion + : public llvm::PrettyStackTraceEntry { + const FreestandingMacroExpansion *Expansion; + const char *Action; + +public: + PrettyStackTraceFreestandingMacroExpansion( + const char *action, const FreestandingMacroExpansion *expansion) + : Expansion(expansion), Action(action) {} + virtual void print(llvm::raw_ostream &OS) const override; +}; + +void printExprDescription(llvm::raw_ostream &out, const Expr *E, const ASTContext &Context, bool addNewline = true); /// PrettyStackTraceExpr - Observe that we are processing a specific diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 8a7591b213959..1e1e9a515c36f 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -3223,19 +3223,15 @@ class CheckRedeclarationRequest class UnresolvedMacroReference { private: - llvm::PointerUnion + llvm::PointerUnion pointer; public: - UnresolvedMacroReference(MacroExpansionDecl *decl) : pointer(decl) {} - UnresolvedMacroReference(MacroExpansionExpr *expr) : pointer(expr) {} + UnresolvedMacroReference(FreestandingMacroExpansion *exp) : pointer(exp) {} UnresolvedMacroReference(CustomAttr *attr) : pointer(attr) {} - MacroExpansionDecl *getDecl() const { - return pointer.dyn_cast(); - } - MacroExpansionExpr *getExpr() const { - return pointer.dyn_cast(); + FreestandingMacroExpansion *getFreestanding() const { + return pointer.dyn_cast(); } CustomAttr *getAttr() const { return pointer.dyn_cast(); diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 77b361d2d7be4..a53e9e46c151a 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -578,6 +578,9 @@ class SourceEditConsumer { void insertAfter(SourceManager &SM, SourceLoc Loc, StringRef Text, ArrayRef SubRegions = {}); void accept(SourceManager &SM, Replacement Replacement) { accept(SM, RegionType::ActiveCode, {Replacement}); } void remove(SourceManager &SM, CharSourceRange Range); + void acceptMacroExpansionBuffer(SourceManager &SM, unsigned bufferID, + SourceFile *containingSF, + bool adjustExpansion, bool includeBufferName); }; /// This helper stream inserts text into a SourceLoc by calling functions in diff --git a/include/swift/IDETool/SyntacticMacroExpansion.h b/include/swift/IDETool/SyntacticMacroExpansion.h new file mode 100644 index 0000000000000..2f1c079ae9c28 --- /dev/null +++ b/include/swift/IDETool/SyntacticMacroExpansion.h @@ -0,0 +1,97 @@ +//===--- SyntacticMacroExpansion.h ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_SYNTACTICMACROEXPANSION_H +#define SWIFT_IDE_SYNTACTICMACROEXPANSION_H + +#include "swift/AST/Decl.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/AST/PluginRegistry.h" +#include "swift/Basic/Fingerprint.h" +#include "swift/Frontend/Frontend.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace swift { + +class ASTContext; +class SourceFile; + +namespace ide { +class SourceEditConsumer; + +/// Simple object to specify a syntactic macro expansion. +struct MacroExpansionSpecifier { + unsigned offset; + swift::MacroRoles macroRoles; + swift::MacroDefinition macroDefinition; +}; + +/// Instance of a syntactic macro expansion context. This is created for each +/// list of compiler arguments (i.e. 'argHash'), and reused as long as the +/// compiler arguments are not changed. +class SyntacticMacroExpansionInstance { + CompilerInvocation invocation; + + SourceManager SourceMgr; + DiagnosticEngine Diags{SourceMgr}; + std::unique_ptr Ctx; + ModuleDecl *TheModule = nullptr; + llvm::StringMap MacroDecls; + + /// Create 'SourceFile' using the buffer. + swift::SourceFile *getSourceFile(llvm::MemoryBuffer *inputBuf); + + /// Synthesize 'MacroDecl' AST object to use the expansion. + swift::MacroDecl * + getSynthesizedMacroDecl(swift::Identifier name, + const MacroExpansionSpecifier &expansion); + + /// Expand single 'expansion' in SF. + void expand(swift::SourceFile *SF, + const MacroExpansionSpecifier &expansion, + SourceEditConsumer &consumer); + +public: + SyntacticMacroExpansionInstance() {} + + /// Setup the instance with \p args . + bool setup(StringRef SwiftExecutablePath, ArrayRef args, + std::shared_ptr plugins, std::string &error); + + ASTContext &getASTContext() { return *Ctx; } + + /// Expand all macros in \p inputBuf and send the edit results to \p consumer. + /// Expansions are specified by \p expansions . + void expandAll(llvm::MemoryBuffer *inputBuf, + ArrayRef expansions, + SourceEditConsumer &consumer); +}; + +/// Manager object to vend 'SyntacticMacroExpansionInstance'. +class SyntacticMacroExpansion { + StringRef SwiftExecutablePath; + std::shared_ptr Plugins; + +public: + SyntacticMacroExpansion(StringRef SwiftExecutablePath, + std::shared_ptr Plugins) + : SwiftExecutablePath(SwiftExecutablePath), Plugins(Plugins) {} + + /// Get instance configured with the specified compiler arguments. + std::shared_ptr + getInstance(ArrayRef args, std::string &error); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_SYNTACTICMACROEXPANSION_H diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 2d4da1a016ae5..33825e593cf15 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -37,6 +37,7 @@ namespace swift { enum class DeclRefKind; class Expr; class ExtensionDecl; + class FreestandingMacroExpansion; class FunctionType; class LabeledConditionalStmt; class LookupResult; @@ -355,6 +356,13 @@ namespace swift { SmallVector, 1> getShorthandShadows(LabeledConditionalStmt *CondStmt, DeclContext *DC = nullptr); + + SourceFile *evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminator); + + SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, bool passParentContext, + MacroRole role, StringRef discriminator); } #endif diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 92d879304551d..6b4de6b2e8976 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -4033,26 +4033,23 @@ static StringRef getPrivateDiscriminatorIfNecessary( return discriminator.str(); } -std::string ASTMangler::mangleMacroExpansion( - const MacroExpansionExpr *expansion) { - beginMangling(); - appendMacroExpansionContext(expansion->getLoc(), expansion->getDeclContext()); - auto privateDiscriminator = getPrivateDiscriminatorIfNecessary(expansion); - if (!privateDiscriminator.empty()) { - appendIdentifier(privateDiscriminator); - appendOperator("Ll"); +static StringRef getPrivateDiscriminatorIfNecessary( + const FreestandingMacroExpansion *expansion) { + switch (expansion->getFreestandingMacroKind()) { + case FreestandingMacroKind::Expr: + return getPrivateDiscriminatorIfNecessary( + cast(expansion)); + case FreestandingMacroKind::Decl: + return getPrivateDiscriminatorIfNecessary( + cast(cast(expansion))); } - appendMacroExpansionOperator( - expansion->getMacroName().getBaseName().userFacingName(), - MacroRole::Expression, - expansion->getDiscriminator()); - return finalize(); } -std::string ASTMangler::mangleMacroExpansion( - const MacroExpansionDecl *expansion) { +std::string +ASTMangler::mangleMacroExpansion(const FreestandingMacroExpansion *expansion) { beginMangling(); - appendMacroExpansionContext(expansion->getLoc(), expansion->getDeclContext()); + appendMacroExpansionContext(expansion->getPoundLoc(), + expansion->getDeclContext()); auto privateDiscriminator = getPrivateDiscriminatorIfNecessary(expansion); if (!privateDiscriminator.empty()) { appendIdentifier(privateDiscriminator); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 1238515d24d2a..096a34484f7a1 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -455,22 +455,20 @@ class Traversal : public ASTVisitorvisitAuxiliaryDecls([&](Decl *decl) { - if (alreadyFailed) return; - if (!isa(decl)) - alreadyFailed = inherited::visit(decl); - }); - MED->forEachExpandedExprOrStmt([&](ASTNode expandedNode) { + MED->forEachExpandedNode([&](ASTNode expandedNode) { if (alreadyFailed) return; + if (auto *expr = expandedNode.dyn_cast()) { - if (!doIt(expr)) - alreadyFailed = true; + alreadyFailed = doIt(expr) == nullptr; } else if (auto *stmt = expandedNode.dyn_cast()) { - if (!doIt(stmt)) - alreadyFailed = true; + alreadyFailed = doIt(stmt) == nullptr; + } else { + auto decl = expandedNode.get(); + if (!isa(decl)) + alreadyFailed = doIt(decl); } }); } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index cfb7e1ec73fb1..b2af2b5a56ea3 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -50,6 +50,7 @@ add_swift_host_library(swiftAST STATIC ExtInfo.cpp FineGrainedDependencies.cpp FineGrainedDependencyFormat.cpp + FreestandingMacroExpansion.cpp FrontendSourceFileDepGraphFactory.cpp GenericEnvironment.cpp GenericParamList.cpp diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 0265e94d86357..f40ad741ed6b7 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -385,7 +385,10 @@ DeclAttributes Decl::getSemanticAttrs() const { return getAttrs(); } -void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const { +void Decl::visitAuxiliaryDecls( + AuxiliaryDeclCallback callback, + bool visitFreestandingExpanded +) const { auto &ctx = getASTContext(); auto *mutableThis = const_cast(this); SourceManager &sourceMgr = ctx.SourceMgr; @@ -418,13 +421,15 @@ void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const { } } - else if (auto *med = dyn_cast(mutableThis)) { - if (auto bufferID = evaluateOrDefault( - ctx.evaluator, ExpandMacroExpansionDeclRequest{med}, {})) { - auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); - auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); - for (auto *decl : sourceFile->getTopLevelDecls()) - callback(decl); + if (visitFreestandingExpanded) { + if (auto *med = dyn_cast(mutableThis)) { + if (auto bufferID = evaluateOrDefault( + ctx.evaluator, ExpandMacroExpansionDeclRequest{med}, {})) { + auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); + auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); + for (auto *decl : sourceFile->getTopLevelDecls()) + callback(decl); + } } } @@ -10624,6 +10629,11 @@ MacroDefinition MacroDecl::getDefinition() const { MacroDefinition::forUndefined()); } +void MacroDecl::setDefinition(MacroDefinition definition) { + getASTContext().evaluator.cacheOutput(MacroDefinitionRequest{this}, + std::move(definition)); +} + Optional MacroDecl::getBuiltinKind() const { auto def = getDefinition(); if (def.kind != MacroDefinition::Kind::Builtin) @@ -10640,37 +10650,27 @@ MacroDefinition MacroDefinition::forExpanded( ctx.AllocateCopy(replacements)}; } -MacroExpansionDecl::MacroExpansionDecl( - DeclContext *dc, MacroExpansionInfo *info -) : Decl(DeclKind::MacroExpansion, dc), info(info) { +MacroExpansionDecl::MacroExpansionDecl(DeclContext *dc, + MacroExpansionInfo *info) + : Decl(DeclKind::MacroExpansion, dc), + FreestandingMacroExpansion(FreestandingMacroKind::Decl, info) { Bits.MacroExpansionDecl.Discriminator = InvalidDiscriminator; } -MacroExpansionDecl::MacroExpansionDecl( +MacroExpansionDecl * +MacroExpansionDecl::create( DeclContext *dc, SourceLoc poundLoc, DeclNameRef macro, DeclNameLoc macroLoc, SourceLoc leftAngleLoc, ArrayRef genericArgs, SourceLoc rightAngleLoc, ArgumentList *args -) : Decl(DeclKind::MacroExpansion, dc) { +) { ASTContext &ctx = dc->getASTContext(); - info = new (ctx) MacroExpansionInfo{ + MacroExpansionInfo *info = new (ctx) MacroExpansionInfo{ poundLoc, macro, macroLoc, leftAngleLoc, rightAngleLoc, genericArgs, args ? args : ArgumentList::createImplicit(ctx, {}) }; - Bits.MacroExpansionDecl.Discriminator = InvalidDiscriminator; -} - -SourceRange MacroExpansionDecl::getSourceRange() const { - SourceLoc endLoc; - if (auto argsEndList = info->ArgList->getEndLoc()) - endLoc = argsEndList; - else if (info->RightAngleLoc.isValid()) - endLoc = info->RightAngleLoc; - else - endLoc = info->MacroNameLoc.getEndLoc(); - - return SourceRange(info->SigilLoc, endLoc); + return new (ctx) MacroExpansionDecl(dc, info); } unsigned MacroExpansionDecl::getDiscriminator() const { @@ -10690,8 +10690,9 @@ unsigned MacroExpansionDecl::getDiscriminator() const { return getRawDiscriminator(); } -void MacroExpansionDecl::forEachExpandedExprOrStmt( - ExprOrStmtExpansionCallback callback) const { +void MacroExpansionDecl::forEachExpandedNode( + llvm::function_ref callback +) const { auto mutableThis = const_cast(this); auto bufferID = evaluateOrDefault( getASTContext().evaluator, @@ -10703,8 +10704,7 @@ void MacroExpansionDecl::forEachExpandedExprOrStmt( auto startLoc = sourceMgr.getLocForBufferStart(*bufferID); auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc); for (auto node : sourceFile->getTopLevelItems()) - if (node.is() || node.is()) - callback(node); + callback(node); } NominalTypeDecl * @@ -10814,13 +10814,7 @@ MacroDiscriminatorContext MacroDiscriminatorContext::getParentOf( } MacroDiscriminatorContext -MacroDiscriminatorContext::getParentOf(MacroExpansionExpr *expansion) { - return getParentOf( - expansion->getLoc(), expansion->getDeclContext()); -} - -MacroDiscriminatorContext -MacroDiscriminatorContext::getParentOf(MacroExpansionDecl *expansion) { +MacroDiscriminatorContext::getParentOf(FreestandingMacroExpansion *expansion) { return getParentOf( - expansion->getLoc(), expansion->getDeclContext()); + expansion->getPoundLoc(), expansion->getDeclContext()); } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 8c83ed6f1030d..4ecfffea89eaf 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2680,34 +2680,20 @@ TypeJoinExpr::forBranchesOfSingleValueStmtExpr(ASTContext &ctx, Type joinType, return createImpl(ctx, joinType.getPointer(), /*elements*/ {}, arena, SVE); } -MacroExpansionExpr::MacroExpansionExpr( +MacroExpansionExpr *MacroExpansionExpr::create( DeclContext *dc, SourceLoc sigilLoc, DeclNameRef macroName, DeclNameLoc macroNameLoc, SourceLoc leftAngleLoc, ArrayRef genericArgs, SourceLoc rightAngleLoc, ArgumentList *argList, MacroRoles roles, bool isImplicit, Type ty -) : Expr(ExprKind::MacroExpansion, isImplicit, ty), DC(dc), - Rewritten(nullptr), Roles(roles), SubstituteDecl(nullptr) { +) { ASTContext &ctx = dc->getASTContext(); - info = new (ctx) MacroExpansionInfo{ + MacroExpansionInfo *info = new (ctx) MacroExpansionInfo{ sigilLoc, macroName, macroNameLoc, leftAngleLoc, rightAngleLoc, genericArgs, argList ? argList : ArgumentList::createImplicit(ctx, {}) }; - - Bits.MacroExpansionExpr.Discriminator = InvalidDiscriminator; -} - -SourceRange MacroExpansionExpr::getSourceRange() const { - SourceLoc endLoc; - if (info->ArgList && !info->ArgList->isImplicit()) - endLoc = info->ArgList->getEndLoc(); - else if (info->RightAngleLoc.isValid()) - endLoc = info->RightAngleLoc; - else - endLoc = info->MacroNameLoc.getEndLoc(); - - return SourceRange(info->SigilLoc, endLoc); + return new (ctx) MacroExpansionExpr(dc, info, roles, isImplicit, ty); } unsigned MacroExpansionExpr::getDiscriminator() const { @@ -2731,7 +2717,8 @@ MacroExpansionDecl *MacroExpansionExpr::createSubstituteDecl() { auto dc = DC; if (auto *tlcd = dyn_cast_or_null(dc->getAsDecl())) dc = tlcd->getDeclContext(); - SubstituteDecl = new (DC->getASTContext()) MacroExpansionDecl(dc, info); + SubstituteDecl = + new (DC->getASTContext()) MacroExpansionDecl(dc, getExpansionInfo()); return SubstituteDecl; } diff --git a/lib/AST/FreestandingMacroExpansion.cpp b/lib/AST/FreestandingMacroExpansion.cpp new file mode 100644 index 0000000000000..ad368a5177410 --- /dev/null +++ b/lib/AST/FreestandingMacroExpansion.cpp @@ -0,0 +1,56 @@ +//===--- FreestandingMacroExpansion.cpp -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/FreestandingMacroExpansion.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" + +using namespace swift; + +SourceRange MacroExpansionInfo::getSourceRange() const { + SourceLoc endLoc; + if (ArgList && !ArgList->isImplicit()) + endLoc = ArgList->getEndLoc(); + else if (RightAngleLoc.isValid()) + endLoc = RightAngleLoc; + else + endLoc = MacroNameLoc.getEndLoc(); + + return SourceRange(SigilLoc, endLoc); +} + +#define FORWARD_VARIANT(NAME) \ + switch (getFreestandingMacroKind()) { \ + case FreestandingMacroKind::Expr: \ + return cast(this)->NAME(); \ + case FreestandingMacroKind::Decl: \ + return cast(this)->NAME(); \ + } + +DeclContext *FreestandingMacroExpansion::getDeclContext() const { + FORWARD_VARIANT(getDeclContext); +} +SourceRange FreestandingMacroExpansion::getSourceRange() const { + FORWARD_VARIANT(getSourceRange); +} +unsigned FreestandingMacroExpansion::getDiscriminator() const { + FORWARD_VARIANT(getDiscriminator); +} + +ASTNode FreestandingMacroExpansion::getASTNode() { + switch (getFreestandingMacroKind()) { + case FreestandingMacroKind::Expr: + return cast(this); + case FreestandingMacroKind::Decl: + return cast(this); + } +} diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index 6d24777a11c87..747faf50f5170 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -133,6 +133,20 @@ void PrettyStackTraceAnyFunctionRef::print(llvm::raw_ostream &out) const { } } +void PrettyStackTraceFreestandingMacroExpansion::print( + llvm::raw_ostream &out) const { + out << "While " << Action << ' '; + auto &Context = Expansion->getDeclContext()->getASTContext(); + switch (Expansion->getFreestandingMacroKind()) { + case FreestandingMacroKind::Expr: + printExprDescription(out, cast(Expansion), Context); + break; + case FreestandingMacroKind::Decl: + printDeclDescription(out, cast(Expansion), Context); + break; + } +} + void PrettyStackTraceExpr::print(llvm::raw_ostream &out) const { out << "While " << Action << ' '; if (!TheExpr) { @@ -142,7 +156,7 @@ void PrettyStackTraceExpr::print(llvm::raw_ostream &out) const { printExprDescription(out, TheExpr, Context); } -void swift::printExprDescription(llvm::raw_ostream &out, Expr *E, +void swift::printExprDescription(llvm::raw_ostream &out, const Expr *E, const ASTContext &Context, bool addNewline) { out << "expression at "; E->getSourceRange().print(out, Context.SourceMgr); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 426eb501b5efb..0b7666c79bddf 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1681,10 +1681,8 @@ void swift::simple_display( //----------------------------------------------------------------------------// DeclNameRef UnresolvedMacroReference::getMacroName() const { - if (auto *med = pointer.dyn_cast()) - return med->getMacroName(); - if (auto *mee = pointer.dyn_cast()) - return mee->getMacroName(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getMacroName(); if (auto *attr = pointer.dyn_cast()) { auto *identTypeRepr = dyn_cast_or_null(attr->getTypeRepr()); if (!identTypeRepr) @@ -1695,20 +1693,16 @@ DeclNameRef UnresolvedMacroReference::getMacroName() const { } SourceLoc UnresolvedMacroReference::getSigilLoc() const { - if (auto *med = pointer.dyn_cast()) - return med->getPoundLoc(); - if (auto *mee = pointer.dyn_cast()) - return mee->getLoc(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getPoundLoc(); if (auto *attr = pointer.dyn_cast()) return attr->getRangeWithAt().Start; llvm_unreachable("Unhandled case"); } DeclNameLoc UnresolvedMacroReference::getMacroNameLoc() const { - if (auto *med = pointer.dyn_cast()) - return med->getMacroNameLoc(); - if (auto *mee = pointer.dyn_cast()) - return mee->getMacroNameLoc(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getMacroNameLoc(); if (auto *attr = pointer.dyn_cast()) { auto *identTypeRepr = dyn_cast_or_null(attr->getTypeRepr()); if (!identTypeRepr) @@ -1719,10 +1713,8 @@ DeclNameLoc UnresolvedMacroReference::getMacroNameLoc() const { } SourceRange UnresolvedMacroReference::getGenericArgsRange() const { - if (auto *med = pointer.dyn_cast()) - return med->getGenericArgsRange(); - if (auto *mee = pointer.dyn_cast()) - return mee->getGenericArgsRange(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getGenericArgsRange(); if (auto *attr = pointer.dyn_cast()) { auto *typeRepr = attr->getTypeRepr(); @@ -1737,10 +1729,8 @@ SourceRange UnresolvedMacroReference::getGenericArgsRange() const { } ArrayRef UnresolvedMacroReference::getGenericArgs() const { - if (auto *med = pointer.dyn_cast()) - return med->getGenericArgs(); - if (auto *mee = pointer.dyn_cast()) - return mee->getGenericArgs(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getGenericArgs(); if (auto *attr = pointer.dyn_cast()) { auto *typeRepr = attr->getTypeRepr(); @@ -1755,17 +1745,15 @@ ArrayRef UnresolvedMacroReference::getGenericArgs() const { } ArgumentList *UnresolvedMacroReference::getArgs() const { - if (auto *med = pointer.dyn_cast()) - return med->getArgs(); - if (auto *mee = pointer.dyn_cast()) - return mee->getArgs(); + if (auto *expansion = pointer.dyn_cast()) + return expansion->getArgs(); if (auto *attr = pointer.dyn_cast()) return attr->getArgs(); llvm_unreachable("Unhandled case"); } MacroRoles UnresolvedMacroReference::getMacroRoles() const { - if (pointer.is() || pointer.is()) + if (pointer.is()) return getFreestandingMacroRoles(); if (pointer.is()) @@ -1776,10 +1764,8 @@ MacroRoles UnresolvedMacroReference::getMacroRoles() const { void swift::simple_display(llvm::raw_ostream &out, const UnresolvedMacroReference &ref) { - if (ref.getDecl()) - out << "macro-expansion-decl"; - else if (ref.getExpr()) - out << "macro-expansion-expr"; + if (ref.getFreestanding()) + out << "freestanding-macro-expansion"; else if (ref.getAttr()) out << "custom-attr"; } diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 63f0a3ee21a23..ba1135fc3d2c7 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -236,6 +236,22 @@ ASTWalker::PreWalkAction SemaAnnotator::walkToDeclPreProper(Decl *D) { ASTWalker::PostWalkAction SemaAnnotator::walkToDeclPost(Decl *D) { auto Action = walkToDeclPostProper(D); SEWalker.endBalancedASTOrderDeclVisit(D); + + if (Action.Action == PostWalkAction::Stop) + return Action; + + // Walk into peer and conformance expansions if walking expansions + if (shouldWalkMacroArgumentsAndExpansion().second) { + D->visitAuxiliaryDecls([&](Decl *auxDecl) { + if (Action.Action == PostWalkAction::Stop) + return; + + if (auxDecl->walk(*this)) { + Action = Action::Stop(); + } + }, /*visitFreestandingExpanded=*/false); + } + return Action; } diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 776f6a9e54450..6db691f0879ee 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/IDE/Utils.h" +#include "swift/AST/SourceFile.h" #include "swift/Basic/Edit.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" @@ -622,6 +623,102 @@ remove(SourceManager &SM, CharSourceRange Range) { accept(SM, Range, ""); } +/// Given the expanded code for a particular macro, perform whitespace +/// adjustments to make the refactoring more suitable for inline insertion. +static StringRef +adjustMacroExpansionWhitespace(GeneratedSourceInfo::Kind kind, + StringRef expandedCode, + llvm::SmallString<64> &scratch) { + scratch.clear(); + + switch (kind) { + case GeneratedSourceInfo::MemberAttributeMacroExpansion: + // Attributes are added to the beginning, add a space to separate from + // any existing. + scratch += expandedCode; + scratch += " "; + return scratch; + + case GeneratedSourceInfo::MemberMacroExpansion: + case GeneratedSourceInfo::PeerMacroExpansion: + case GeneratedSourceInfo::ConformanceMacroExpansion: + // All added to the end. Note that conformances are always expanded as + // extensions, hence treating them the same as peer. + scratch += "\n\n"; + scratch += expandedCode; + scratch += "\n"; + return scratch; + + case GeneratedSourceInfo::ExpressionMacroExpansion: + case GeneratedSourceInfo::FreestandingDeclMacroExpansion: + case GeneratedSourceInfo::AccessorMacroExpansion: + case GeneratedSourceInfo::ReplacedFunctionBody: + case GeneratedSourceInfo::PrettyPrinted: + return expandedCode; + } +} + +void swift::ide::SourceEditConsumer::acceptMacroExpansionBuffer( + SourceManager &SM, unsigned bufferID, SourceFile *containingSF, + bool adjustExpansion, bool includeBufferName) { + auto generatedInfo = SM.getGeneratedSourceInfo(bufferID); + if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()) + return; + + auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange); + + // If there's no change, drop the edit entirely. + if (generatedInfo->originalSourceRange.getStart() == + generatedInfo->originalSourceRange.getEnd() && + rewrittenBuffer.empty()) + return; + + SmallString<64> scratchBuffer; + if (adjustExpansion) { + rewrittenBuffer = adjustMacroExpansionWhitespace( + generatedInfo->kind, rewrittenBuffer, scratchBuffer); + } + + // `containingFile` is the file of the actual expansion site, where as + // `originalFile` is the possibly enclosing buffer. Concretely: + // ``` + // // m.swift + // @AddMemberAttributes + // struct Foo { + // // --- expanded from @AddMemberAttributes eg. @_someBufferName --- + // @AddedAttribute + // // --- + // let someMember: Int + // } + // ``` + // + // When expanding `AddedAttribute`, the expansion actually applies to the + // original source (`m.swift`) rather than the buffer of the expansion + // site (`@_someBufferName`). Thus, we need to include the path to the + // original source as well. Note that this path could itself be another + // expansion. + auto originalSourceRange = generatedInfo->originalSourceRange; + SourceFile *originalFile = + containingSF->getParentModule()->getSourceFileContainingLocation( + originalSourceRange.getStart()); + StringRef originalPath; + if (originalFile->getBufferID().hasValue() && + containingSF->getBufferID() != originalFile->getBufferID()) { + originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID()); + } + + StringRef bufferName; + if (includeBufferName) { + bufferName = SM.getIdentifierForBuffer(bufferID); + } + + accept(SM, {originalPath, + originalSourceRange, + bufferName, + rewrittenBuffer, + {}}); +} + struct swift::ide::SourceEditJsonConsumer::Implementation { llvm::raw_ostream &OS; std::vector AllEdits; diff --git a/lib/IDETool/CMakeLists.txt b/lib/IDETool/CMakeLists.txt index c7555e0cb3d51..34994928c1459 100644 --- a/lib/IDETool/CMakeLists.txt +++ b/lib/IDETool/CMakeLists.txt @@ -4,6 +4,7 @@ add_swift_host_library(swiftIDETool STATIC CompilerInvocation.cpp IDEInspectionInstance.cpp DependencyChecking.cpp + SyntacticMacroExpansion.cpp ) target_link_libraries(swiftIDETool PRIVATE diff --git a/lib/IDETool/SyntacticMacroExpansion.cpp b/lib/IDETool/SyntacticMacroExpansion.cpp new file mode 100644 index 0000000000000..afcffa672721b --- /dev/null +++ b/lib/IDETool/SyntacticMacroExpansion.cpp @@ -0,0 +1,471 @@ +//===--- SyntacticMacroExpansion.cpp --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDETool/SyntacticMacroExpansion.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/AST/PluginLoader.h" +#include "swift/AST/TypeRepr.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "swift/IDE/Utils.h" +#include "swift/Sema/IDETypeChecking.h" + +using namespace swift; +using namespace ide; + +std::shared_ptr +SyntacticMacroExpansion::getInstance(ArrayRef args, + std::string &error) { + // Create and configure a new instance. + auto instance = std::make_shared(); + + bool failed = instance->setup(SwiftExecutablePath, args, Plugins, error); + if (failed) + return nullptr; + + return instance; +} + +bool SyntacticMacroExpansionInstance::setup( + StringRef SwiftExecutablePath, ArrayRef args, + std::shared_ptr plugins, std::string &error) { + SmallString<256> driverPath(SwiftExecutablePath); + llvm::sys::path::remove_filename(driverPath); + llvm::sys::path::append(driverPath, "swiftc"); + + // Setup CompilerInstance to configure the plugin search path correctly. + bool hadError = driver::getSingleFrontendInvocationFromDriverArguments( + driverPath, args, Diags, + [&](ArrayRef frontendArgs) { + return invocation.parseArgs( + frontendArgs, Diags, /*ConfigurationFileBuffers=*/nullptr, + /*workingDirectory=*/{}, SwiftExecutablePath); + }, + /*ForceNoOutput=*/true); + if (hadError) { + error = "failed to setup compiler invocation"; + return true; + } + + // Setup ASTContext. + Ctx.reset(ASTContext::get( + invocation.getLangOptions(), invocation.getTypeCheckerOptions(), + invocation.getSILOptions(), invocation.getSearchPathOptions(), + invocation.getClangImporterOptions(), invocation.getSymbolGraphOptions(), + SourceMgr, Diags)); + registerParseRequestFunctions(Ctx->evaluator); + registerTypeCheckerRequestFunctions(Ctx->evaluator); + + std::unique_ptr pluginLoader = + std::make_unique(*Ctx, /*DepTracker=*/nullptr); + pluginLoader->setRegistry(plugins.get()); + Ctx->setPluginLoader(std::move(pluginLoader)); + + // Create a module where SourceFiles reside. + Identifier ID = Ctx->getIdentifier(invocation.getModuleName()); + TheModule = ModuleDecl::create(ID, *Ctx); + + return false; +} + +SourceFile * +SyntacticMacroExpansionInstance::getSourceFile(llvm::MemoryBuffer *inputBuf) { + + // If there is a SourceFile with the same name and the content, use it. + // Note that this finds the generated source file that was created in the + // previous expansion requests. + if (auto bufID = + SourceMgr.getIDForBufferIdentifier(inputBuf->getBufferIdentifier())) { + if (inputBuf->getBuffer() == SourceMgr.getEntireTextForBuffer(*bufID)) { + SourceLoc bufLoc = SourceMgr.getLocForBufferStart(*bufID); + if (SourceFile *existing = + TheModule->getSourceFileContainingLocation(bufLoc)) { + return existing; + } + } + } + + // Otherwise, create a new SourceFile. + SourceFile *SF = new (getASTContext()) SourceFile( + *TheModule, SourceFileKind::Main, SourceMgr.addMemBufferCopy(inputBuf)); + SF->setImports({}); + TheModule->addFile(*SF); + + return SF; +} + +MacroDecl *SyntacticMacroExpansionInstance::getSynthesizedMacroDecl( + Identifier name, const MacroExpansionSpecifier &expansion) { + auto &ctx = getASTContext(); + + std::string macroID; + + switch (expansion.macroDefinition.kind) { + case MacroDefinition::Kind::External: { + // '.' + // It's safe to use without 'kind' because 'Expanded' always starts with a + // sigil '#' which can't be valid in a module name. + auto external = expansion.macroDefinition.getExternalMacro(); + macroID += external.moduleName.str(); + macroID += "."; + macroID += external.macroTypeName.str(); + break; + } + case MacroDefinition::Kind::Expanded: { + auto expanded = expansion.macroDefinition.getExpanded(); + macroID += expanded.getExpansionText(); + break; + } + case MacroDefinition::Kind::Builtin: + case MacroDefinition::Kind::Invalid: + case MacroDefinition::Kind::Undefined: + assert(false && "invalid macro definition for syntactic expansion"); + macroID += name.str(); + } + + // Reuse cached MacroDecl of the same name if it's already created. + MacroDecl *macro; + auto found = MacroDecls.find(macroID); + if (found != MacroDecls.end()) { + macro = found->second; + } else { + macro = new (ctx) MacroDecl( + /*macroLoc=*/{}, DeclName(name), /*nameLoc=*/{}, + /*genericParams=*/nullptr, /*parameterList=*/nullptr, + /*arrowLoc=*/{}, /*resultType=*/nullptr, + /*definition=*/nullptr, /*parent=*/TheModule); + macro->setImplicit(); + MacroDecls.insert({macroID, macro}); + } + + // Add missing role attributes to MacroDecl. + MacroRoles roles = expansion.macroRoles; + for (auto attr : macro->getAttrs().getAttributes()) { + roles -= attr->getMacroRole(); + } + for (MacroRole role : getAllMacroRoles()) { + if (!roles.contains(role)) + continue; + + MacroSyntax syntax = getFreestandingMacroRoles().contains(role) + ? MacroSyntax::Freestanding + : MacroSyntax::Attached; + + auto *attr = MacroRoleAttr::create(ctx, /*atLoc=*/{}, /*range=*/{}, syntax, + /*lParenLoc=*/{}, role, /*names=*/{}, + /*rParenLoc=*/{}, /*implicit=*/true); + macro->getAttrs().add(attr); + } + + // Set the macro definition. + macro->setDefinition(expansion.macroDefinition); + + return macro; +} + +/// Create a unique name of the expansion. The result is *appended* to \p out. +static void addExpansionDiscriminator(SmallString<32> &out, + const SourceFile *SF, SourceLoc loc, + Optional supplementalLoc = None, + Optional role = None) { + SourceManager &SM = SF->getASTContext().SourceMgr; + + StableHasher hasher = StableHasher::defaultHasher(); + + // Module name. + hasher.combine(SF->getParentModule()->getName().str()); + hasher.combine(uint8_t{0}); + + // File base name. + // Do not use the full path because we want this hash stable. + hasher.combine(llvm::sys::path::filename(SF->getFilename())); + hasher.combine(uint8_t{0}); + + // Line/column. + auto lineColumn = SM.getLineAndColumnInBuffer(loc); + hasher.combine(lineColumn.first); + hasher.combine(lineColumn.second); + + // Supplemental line/column. + if (supplementalLoc.has_value()) { + auto supLineColumn = SM.getLineAndColumnInBuffer(*supplementalLoc); + hasher.combine(supLineColumn.first); + hasher.combine(supLineColumn.second); + } + + // Macro role. + if (role.has_value()) { + hasher.combine(*role); + } + + Fingerprint hash(std::move(hasher)); + out.append(hash.getRawValue()); +} + +/// Perform expansion of the specified freestanding macro using the 'MacroDecl'. +static std::vector +expandFreestandingMacro(MacroDecl *macro, + FreestandingMacroExpansion *expansion) { + std::vector bufferIDs; + + SmallString<32> discriminator; + discriminator.append("__syntactic_macro_"); + addExpansionDiscriminator(discriminator, + expansion->getDeclContext()->getParentSourceFile(), + expansion->getPoundLoc()); + + expansion->setMacroRef(macro); + + SourceFile *expandedSource = + swift::evaluateFreestandingMacro(expansion, discriminator); + if (expandedSource) + bufferIDs.push_back(*expandedSource->getBufferID()); + + return bufferIDs; +} + +/// Perform expansion of the specified decl and the attribute using the +/// 'MacroDecl'. If the macro has multiple roles, evaluate it for all macro +/// roles. +static std::vector +expandAttachedMacro(MacroDecl *macro, CustomAttr *attr, Decl *attachedDecl) { + + std::vector bufferIDs; + auto evaluate = [&](Decl *target, bool passParent, MacroRole role) { + + SmallString<32> discriminator; + discriminator.append("macro_"); + addExpansionDiscriminator(discriminator, + target->getDeclContext()->getParentSourceFile(), + target->getLoc(), attr->getLocation(), role); + + SourceFile *expandedSource = swift::evaluateAttachedMacro( + macro, target, attr, passParent, role, discriminator); + if (expandedSource) + bufferIDs.push_back(*expandedSource->getBufferID()); + }; + + MacroRoles roles = macro->getMacroRoles(); + if (roles.contains(MacroRole::Accessor)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Accessor); + } + if (roles.contains(MacroRole::MemberAttribute)) { + if (auto *idc = dyn_cast(attachedDecl)) { + for (auto *member : idc->getParsedMembers()) { + // 'VarDecl' in 'IterableDeclContext' are part of 'PatternBindingDecl'. + if (isa(member)) + continue; + evaluate(member, /*passParent=*/true, MacroRole::MemberAttribute); + } + } + } + if (roles.contains(MacroRole::Member)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Member); + } + if (roles.contains(MacroRole::Peer)) { + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Peer); + } + if (roles.contains(MacroRole::Conformance)) { + if (isa(attachedDecl)) + evaluate(attachedDecl, /*passParent=*/false, MacroRole::Conformance); + } + return bufferIDs; +} + +/// Get the name of the custom attribute. This is used to create a dummy +/// MacroDecl. +static Identifier getCustomAttrName(ASTContext &ctx, const CustomAttr *attr) { + TypeRepr *tyR = attr->getTypeRepr(); + if (auto ref = dyn_cast(tyR)) { + return ref->getNameRef().getBaseIdentifier(); + } + + // If the attribute is not an identifier type, create an identifier with its + // textual representation. This is *not* expected to be reachable. + // The only case is like `@Foo?` where the client should not send the + // expansion request on this in the first place. + SmallString<32> name; + llvm::raw_svector_ostream OS(name); + tyR->print(OS); + return ctx.getIdentifier(name); +} + +namespace { + +/// Find macro expansion i.e. '#foo' or '@foo' at the specified source location. +/// If a freestanding expansion (i.e. #foo) is found, the result 'ExpansionNode' +/// only has the node. If an attribute is found, the attribute and the attached +/// decl object is returned. +struct ExpansionNode { + CustomAttr *attribute; + ASTNode node; +}; +class MacroExpansionFinder : public ASTWalker { + SourceManager &SM; + SourceLoc locToResolve; + llvm::Optional result; + + bool rangeContainsLocToResolve(SourceRange Range) const { + return SM.rangeContainsTokenLoc(Range, locToResolve); + } + +public: + MacroExpansionFinder(SourceManager &SM, SourceLoc locToResolve) + : SM(SM), locToResolve(locToResolve) {} + + llvm::Optional getResult() const { return result; } + + MacroWalking getMacroWalkingBehavior() const override { + return MacroWalking::None; + } + + PreWalkAction walkToDeclPre(Decl *D) override { + // Visit all 'VarDecl' because 'getSourceRangeIncludingAttrs()' doesn't + // include its attribute ranges (because attributes are part of PBD.) + if (!isa(D) && + !rangeContainsLocToResolve(D->getSourceRangeIncludingAttrs())) { + return Action::SkipChildren(); + } + + // Check the attributes. + for (DeclAttribute *attr : D->getAttrs()) { + if (auto customAttr = dyn_cast(attr)) { + SourceRange nameRange(customAttr->getRangeWithAt().Start, + customAttr->getTypeExpr()->getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{customAttr, ASTNode(D)}; + return Action::Stop(); + } + } + } + + // Check 'MacroExpansionDecl'. + if (auto med = dyn_cast(D)) { + SourceRange nameRange(med->getExpansionInfo()->SigilLoc, + med->getMacroNameLoc().getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{nullptr, ASTNode(med)}; + return Action::Stop(); + } + } + + return Action::Continue(); + } + + PreWalkResult walkToExprPre(Expr *E) override { + if (!rangeContainsLocToResolve(E->getSourceRange())) { + return Action::SkipChildren(E); + } + + // Check 'MacroExpansionExpr'. + if (auto mee = dyn_cast(E)) { + SourceRange nameRange(mee->getExpansionInfo()->SigilLoc, + mee->getMacroNameLoc().getEndLoc()); + if (rangeContainsLocToResolve(nameRange)) { + result = ExpansionNode{nullptr, ASTNode(mee)}; + return Action::Stop(); + } + } + + return Action::Continue(E); + } + + PreWalkResult walkToStmtPre(Stmt *S) override { + if (!rangeContainsLocToResolve(S->getSourceRange())) { + return Action::SkipChildren(S); + } + return Action::Continue(S); + } + PreWalkResult + walkToArgumentListPre(ArgumentList *AL) override { + if (!rangeContainsLocToResolve(AL->getSourceRange())) { + return Action::SkipChildren(AL); + } + return Action::Continue(AL); + } + PreWalkAction walkToParameterListPre(ParameterList *PL) override { + if (!rangeContainsLocToResolve(PL->getSourceRange())) { + return Action::SkipChildren(); + } + return Action::Continue(); + } + PreWalkAction walkToTypeReprPre(TypeRepr *T) override { + // TypeRepr cannot have macro expansions in it. + return Action::SkipChildren(); + } +}; +} // namespace + +void SyntacticMacroExpansionInstance::expand( + SourceFile *SF, const MacroExpansionSpecifier &expansion, + SourceEditConsumer &consumer) { + + // Find the expansion at 'expantion.offset'. + MacroExpansionFinder expansionFinder( + SourceMgr, + SourceMgr.getLocForOffset(*SF->getBufferID(), expansion.offset)); + SF->walk(expansionFinder); + auto expansionNode = expansionFinder.getResult(); + if (!expansionNode) + return; + + // Expand the macro. + std::vector bufferIDs; + if (auto *attr = expansionNode->attribute) { + // Attached macros. + MacroDecl *macro = getSynthesizedMacroDecl( + getCustomAttrName(getASTContext(), attr), expansion); + auto *attachedTo = expansionNode->node.get(); + bufferIDs = expandAttachedMacro(macro, attr, attachedTo); + + // For an attached macro, remove the custom attribute; it's been fully + // subsumed by its expansions. + SourceRange range = attr->getRangeWithAt(); + auto charRange = Lexer::getCharSourceRangeFromSourceRange(SourceMgr, range); + consumer.remove(SourceMgr, charRange); + } else { + // Freestanding macros. + FreestandingMacroExpansion *freestanding; + auto node = expansionNode->node; + if (node.is()) { + freestanding = cast(node.get()); + } else { + freestanding = cast(node.get()); + } + + MacroDecl *macro = getSynthesizedMacroDecl( + freestanding->getMacroName().getBaseIdentifier(), expansion); + bufferIDs = expandFreestandingMacro(macro, freestanding); + } + + // Send all edits to the consumer. + for (unsigned bufferID : bufferIDs) { + consumer.acceptMacroExpansionBuffer(SourceMgr, bufferID, SF, + /*adjust=*/false, + /*includeBufferName=*/false); + } +} + +void SyntacticMacroExpansionInstance::expandAll( + llvm::MemoryBuffer *inputBuf, ArrayRef expansions, + SourceEditConsumer &consumer) { + + // Create a source file. + SourceFile *SF = getSourceFile(inputBuf); + + for (const auto &expansion : expansions) { + expand(SF, expansion, consumer); + } +} diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f634767af4011..1120a28d22387 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -9940,7 +9940,7 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags, if (!macroNameRef) return status; - auto *med = new (Context) MacroExpansionDecl( + auto *med = MacroExpansionDecl::create( CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc, argList); med->getAttrs() = attributes; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1ce2c3d2c6d49..ab5d8f4ed1606 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3472,12 +3472,11 @@ ParserResult Parser::parseExprMacroExpansion(bool isExprBasic) { return makeParserResult( status, - new (Context) MacroExpansionExpr( + MacroExpansionExpr::create( CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc, argList, - CurDeclContext->isTypeContext() - ? MacroRole::Declaration - : getFreestandingMacroRoles())); + CurDeclContext->isTypeContext() ? MacroRole::Declaration + : getFreestandingMacroRoles())); } /// parseExprCollection - Parse a collection literal expression. diff --git a/lib/Refactoring/Refactoring.cpp b/lib/Refactoring/Refactoring.cpp index 515b96a0e50e7..5cda1ef0ccf58 100644 --- a/lib/Refactoring/Refactoring.cpp +++ b/lib/Refactoring/Refactoring.cpp @@ -8754,40 +8754,6 @@ getMacroExpansionBuffers(SourceManager &sourceMgr, ResolvedCursorInfoPtr Info) { return {}; } -/// Given the expanded code for a particular macro, perform whitespace -/// adjustments to make the refactoring more suitable for inline insertion. -static StringRef adjustMacroExpansionWhitespace( - GeneratedSourceInfo::Kind kind, StringRef expandedCode, - llvm::SmallString<64> &scratch) { - scratch.clear(); - - switch (kind) { - case GeneratedSourceInfo::MemberAttributeMacroExpansion: - // Attributes are added to the beginning, add a space to separate from - // any existing. - scratch += expandedCode; - scratch += " "; - return scratch; - - case GeneratedSourceInfo::MemberMacroExpansion: - case GeneratedSourceInfo::PeerMacroExpansion: - case GeneratedSourceInfo::ConformanceMacroExpansion: - // All added to the end. Note that conformances are always expanded as - // extensions, hence treating them the same as peer. - scratch += "\n\n"; - scratch += expandedCode; - scratch += "\n"; - return scratch; - - case GeneratedSourceInfo::ExpressionMacroExpansion: - case GeneratedSourceInfo::FreestandingDeclMacroExpansion: - case GeneratedSourceInfo::AccessorMacroExpansion: - case GeneratedSourceInfo::ReplacedFunctionBody: - case GeneratedSourceInfo::PrettyPrinted: - return expandedCode; - } -} - static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo, SourceEditConsumer &editConsumer, bool adjustExpansion) { auto bufferIDs = getMacroExpansionBuffers(SM, cursorInfo); @@ -8799,68 +8765,19 @@ static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo, return true; // Send all of the rewritten buffer snippets. - CustomAttr *attachedMacroAttr = nullptr; - SmallString<64> scratchBuffer; for (auto bufferID: bufferIDs) { - auto generatedInfo = SM.getGeneratedSourceInfo(bufferID); - if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()) - continue; - - auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange); - - // If there's no change, drop the edit entirely. - if (generatedInfo->originalSourceRange.getStart() == - generatedInfo->originalSourceRange.getEnd() && - rewrittenBuffer.empty()) - continue; - - if (adjustExpansion) { - rewrittenBuffer = adjustMacroExpansionWhitespace(generatedInfo->kind, rewrittenBuffer, scratchBuffer); - } - - // `containingFile` is the file of the actual expansion site, where as - // `originalFile` is the possibly enclosing buffer. Concretely: - // ``` - // // m.swift - // @AddMemberAttributes - // struct Foo { - // // --- expanded from @AddMemberAttributes eg. @_someBufferName --- - // @AddedAttribute - // // --- - // let someMember: Int - // } - // ``` - // - // When expanding `AddedAttribute`, the expansion actually applies to the - // original source (`m.swift`) rather than the buffer of the expansion - // site (`@_someBufferName`). Thus, we need to include the path to the - // original source as well. Note that this path could itself be another - // expansion. - auto originalSourceRange = generatedInfo->originalSourceRange; - SourceFile *originalFile = - containingSF->getParentModule()->getSourceFileContainingLocation(originalSourceRange.getStart()); - StringRef originalPath; - if (originalFile->getBufferID().hasValue() && - containingSF->getBufferID() != originalFile->getBufferID()) { - originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID()); - } - - editConsumer.accept(SM, {originalPath, - originalSourceRange, - SM.getIdentifierForBuffer(bufferID), - rewrittenBuffer, - {}}); - - if (generatedInfo->attachedMacroCustomAttr && !attachedMacroAttr) - attachedMacroAttr = generatedInfo->attachedMacroCustomAttr; + editConsumer.acceptMacroExpansionBuffer(SM, bufferID, containingSF, + adjustExpansion, /*includeBufferName=*/true); } // For an attached macro, remove the custom attribute; it's been fully // subsumed by its expansions. - if (attachedMacroAttr) { + if (auto attrRef = + cast(cursorInfo)->getCustomAttrRef()) { + const CustomAttr *attachedMacroAttr = attrRef->first; SourceRange range = attachedMacroAttr->getRangeWithAt(); auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range); - editConsumer.accept(SM, charRange, StringRef()); + editConsumer.remove(SM, charRange); } return false; diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 2d2a7e9b97528..8ec1e0de46568 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1621,11 +1621,13 @@ void SILGenFunction::visitVarDecl(VarDecl *D) { } void SILGenFunction::visitMacroExpansionDecl(MacroExpansionDecl *D) { - D->forEachExpandedExprOrStmt([&](ASTNode node) { + D->forEachExpandedNode([&](ASTNode node) { if (auto *expr = node.dyn_cast()) emitIgnoredExpr(expr); else if (auto *stmt = node.dyn_cast()) emitStmt(stmt); + else + visit(node.get()); }); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 529ab0ad23ace..eb34af0307f83 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -6298,11 +6298,13 @@ RValue RValueEmitter::visitMacroExpansionExpr(MacroExpansionExpr *E, } else if (auto *MED = E->getSubstituteDecl()) { Mangle::ASTMangler mangler; - MED->forEachExpandedExprOrStmt([&](ASTNode node) { + MED->forEachExpandedNode([&](ASTNode node) { if (auto *expr = node.dyn_cast()) visit(expr, C); else if (auto *stmt = node.dyn_cast()) SGF.emitStmt(stmt); + else + SGF.visit(node.get()); }); return RValue(); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f64ee2a0a82cb..6b246afdc5071 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2937,7 +2937,7 @@ namespace { auto macro = cast(overload.choice.getDecl()); ConcreteDeclRef macroRef = resolveConcreteDeclRef(macro, locator); - auto expansion = new (ctx) MacroExpansionExpr( + auto *expansion = MacroExpansionExpr::create( dc, expr->getStartLoc(), DeclNameRef(macro->getName()), DeclNameLoc(expr->getLoc()), SourceLoc(), {}, SourceLoc(), nullptr, MacroRole::Expression, /*isImplicit=*/true, expandedType); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index f524ed70dcdd8..febd446ceed92 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1850,7 +1850,7 @@ class DeclChecker : public DeclVisitor { if (!isa(decl->getDeclContext())) { decl->visitAuxiliaryDecls([&](Decl *auxiliaryDecl) { this->visit(auxiliaryDecl); - }); + }, /*visitFreestandingExpanded=*/false); } if (auto *Stats = getASTContext().Stats) @@ -2066,8 +2066,7 @@ class DeclChecker : public DeclVisitor { void visitMacroExpansionDecl(MacroExpansionDecl *MED) { // Assign a discriminator. (void)MED->getDiscriminator(); - // Decls in expansion already visited as auxiliary decls. - MED->forEachExpandedExprOrStmt([&](ASTNode node) { + MED->forEachExpandedNode([&](ASTNode node) { TypeChecker::typeCheckASTNode(node, MED->getDeclContext()); }); } diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 1861cb54d6df0..b607f918842c4 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -24,6 +24,7 @@ #include "swift/AST/CASTBridging.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Expr.h" +#include "swift/AST/FreestandingMacroExpansion.h" #include "swift/AST/MacroDefinition.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/PluginLoader.h" @@ -38,6 +39,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Lexer.h" +#include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "llvm/Config/config.h" @@ -705,24 +707,183 @@ static std::string expandMacroDefinition( return expandedResult; } -Optional -swift::expandMacroExpr(MacroExpansionExpr *mee) { - DeclContext *dc = mee->getDeclContext(); +static GeneratedSourceInfo::Kind getGeneratedSourceInfoKind(MacroRole role) { + switch (role) { + case MacroRole::Expression: + return GeneratedSourceInfo::ExpressionMacroExpansion; + case MacroRole::Declaration: + case MacroRole::CodeItem: + return GeneratedSourceInfo::FreestandingDeclMacroExpansion; + case MacroRole::Accessor: + return GeneratedSourceInfo::AccessorMacroExpansion; + case MacroRole::MemberAttribute: + return GeneratedSourceInfo::MemberAttributeMacroExpansion; + case MacroRole::Member: + return GeneratedSourceInfo::MemberMacroExpansion; + case MacroRole::Peer: + return GeneratedSourceInfo::PeerMacroExpansion; + case MacroRole::Conformance: + return GeneratedSourceInfo::ConformanceMacroExpansion; + } + llvm_unreachable("unhandled MacroRole"); +} + +// If this storage declaration is a variable with an explicit initializer, +// return the range from the `=` to the end of the explicit initializer. +static Optional +getExplicitInitializerRange(AbstractStorageDecl *storage) { + auto var = dyn_cast(storage); + if (!var) + return None; + + auto pattern = var->getParentPatternBinding(); + if (!pattern) + return None; + + unsigned index = pattern->getPatternEntryIndexForVarDecl(var); + SourceLoc equalLoc = pattern->getEqualLoc(index); + SourceRange initRange = pattern->getOriginalInitRange(index); + if (equalLoc.isInvalid() || initRange.End.isInvalid()) + return None; + + return SourceRange(equalLoc, initRange.End); +} + +static CharSourceRange getExpansionInsertionRange(MacroRole role, + ASTNode target, + SourceManager &sourceMgr) { + switch (role) { + case MacroRole::Accessor: { + auto storage = cast(target.get()); + auto bracesRange = storage->getBracesRange(); + + // Compute the location where the accessors will be added. + if (bracesRange.Start.isValid()) { + // We have braces already, so insert them inside the leading '{'. + return CharSourceRange( + Lexer::getLocForEndOfToken(sourceMgr, bracesRange.Start), 0); + } else if (auto initRange = getExplicitInitializerRange(storage)) { + // The accessor had an initializer, so the initializer (including + // the `=`) is replaced by the accessors. + return Lexer::getCharSourceRangeFromSourceRange(sourceMgr, *initRange); + } else { + // The accessors go at the end. + SourceLoc endLoc = storage->getEndLoc(); + if (auto var = dyn_cast(storage)) { + if (auto pattern = var->getParentPattern()) + endLoc = pattern->getEndLoc(); + } + + return CharSourceRange(Lexer::getLocForEndOfToken(sourceMgr, endLoc), 0); + } + } + case MacroRole::MemberAttribute: { + SourceLoc startLoc; + if (auto valueDecl = dyn_cast(target.get())) + startLoc = valueDecl->getAttributeInsertionLoc(/*forModifier=*/false); + else + startLoc = target.getStartLoc(); + + return CharSourceRange(startLoc, 0); + } + case MacroRole::Member: { + // Semantically, we insert members right before the closing brace. + SourceLoc rightBraceLoc; + if (auto nominal = dyn_cast(target.get())) { + rightBraceLoc = nominal->getBraces().End; + } else { + auto ext = cast(target.get()); + rightBraceLoc = ext->getBraces().End; + } + + return CharSourceRange(rightBraceLoc, 0); + } + case MacroRole::Peer: { + SourceLoc afterDeclLoc = + Lexer::getLocForEndOfToken(sourceMgr, target.getEndLoc()); + return CharSourceRange(afterDeclLoc, 0); + break; + } + + case MacroRole::Conformance: { + SourceLoc afterDeclLoc = + Lexer::getLocForEndOfToken(sourceMgr, target.getEndLoc()); + return CharSourceRange(afterDeclLoc, 0); + } + + case MacroRole::Expression: + case MacroRole::Declaration: + case MacroRole::CodeItem: + return Lexer::getCharSourceRangeFromSourceRange(sourceMgr, + target.getSourceRange()); + } + llvm_unreachable("unhandled MacroRole"); +} + +static SourceFile * +createMacroSourceFile(std::unique_ptr buffer, + MacroRole role, ASTNode target, DeclContext *dc, + CustomAttr *attr) { ASTContext &ctx = dc->getASTContext(); SourceManager &sourceMgr = ctx.SourceMgr; - ConcreteDeclRef macroRef = mee->getMacroRef(); - Type expandedType = mee->getType(); + + // Dump macro expansions to standard output, if requested. + if (ctx.LangOpts.DumpMacroExpansions) { + llvm::errs() << buffer->getBufferIdentifier() + << "\n------------------------------\n" + << buffer->getBuffer() + << "\n------------------------------\n"; + } + + CharSourceRange generatedOriginalSourceRange = + getExpansionInsertionRange(role, target, sourceMgr); + GeneratedSourceInfo::Kind generatedSourceKind = + getGeneratedSourceInfoKind(role); + + // Create a new source buffer with the contents of the expanded macro. + unsigned macroBufferID = sourceMgr.addNewSourceBuffer(std::move(buffer)); + auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); + GeneratedSourceInfo sourceInfo{generatedSourceKind, + generatedOriginalSourceRange, + macroBufferRange, + target.getOpaqueValue(), + dc, + attr}; + sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo); + + // Create a source file to hold the macro buffer. This is automatically + // registered with the enclosing module. + auto macroSourceFile = new (ctx) SourceFile( + *dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID, + /*parsingOpts=*/{}, /*isPrimary=*/false); + macroSourceFile->setImports(dc->getParentSourceFile()->getImports()); + return macroSourceFile; +} + +/// Evaluate the given freestanding macro expansion. +static SourceFile * +evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminatorStr = "") { + auto *dc = expansion->getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + SourceLoc loc = expansion->getPoundLoc(); auto moduleDecl = dc->getParentModule(); - auto sourceFile = moduleDecl->getSourceFileContainingLocation(mee->getLoc()); + auto sourceFile = moduleDecl->getSourceFileContainingLocation(loc); if (!sourceFile) - return None; + return nullptr; - MacroDecl *macro = cast(macroRef.getDecl()); + MacroDecl *macro = cast(expansion->getMacroRef().getDecl()); + auto macroRoles = macro->getMacroRoles(); + assert(macroRoles.contains(MacroRole::Expression) || + macroRoles.contains(MacroRole::Declaration) || + macroRoles.contains(MacroRole::CodeItem)); - if (isFromExpansionOfMacro(sourceFile, macro, MacroRole::Expression)) { - ctx.Diags.diagnose(mee->getLoc(), diag::macro_recursive, macro->getName()); - return None; + if (isFromExpansionOfMacro(sourceFile, macro, MacroRole::Expression) || + isFromExpansionOfMacro(sourceFile, macro, MacroRole::Declaration) || + isFromExpansionOfMacro(sourceFile, macro, MacroRole::CodeItem)) { + ctx.Diags.diagnose(loc, diag::macro_recursive, macro->getName()); + return nullptr; } // Evaluate the macro. @@ -730,9 +891,11 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { /// The discriminator used for the macro. LazyValue discriminator([&]() -> std::string { + if (!discriminatorStr.empty()) + return discriminatorStr.str(); #if SWIFT_SWIFT_PARSER Mangle::ASTMangler mangler; - return mangler.mangleMacroExpansion(mee); + return mangler.mangleMacroExpansion(expansion); #else return ""; #endif @@ -743,21 +906,20 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { case MacroDefinition::Kind::Undefined: case MacroDefinition::Kind::Invalid: // Already diagnosed as an error elsewhere. - return None; + return nullptr; case MacroDefinition::Kind::Builtin: { switch (macroDef.getBuiltinKind()) { case BuiltinMacroKind::ExternalMacro: - ctx.Diags.diagnose( - mee->getLoc(), diag::external_macro_outside_macro_definition); - return None; + ctx.Diags.diagnose(loc, diag::external_macro_outside_macro_definition); + return nullptr; } } case MacroDefinition::Kind::Expanded: { // Expand the definition with the given arguments. - auto result = expandMacroDefinition( - macroDef.getExpanded(), macro, mee->getArgs()); + auto result = expandMacroDefinition(macroDef.getExpanded(), macro, + expansion->getArgs()); evaluatedSource = llvm::MemoryBuffer::getMemBufferCopy( result, adjustMacroExpansionBufferName(*discriminator)); break; @@ -766,28 +928,33 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { case MacroDefinition::Kind::External: { // Retrieve the external definition of the macro. auto external = macroDef.getExternalMacro(); - ExternalMacroDefinitionRequest request{ - &ctx, external.moduleName, external.macroTypeName - }; + ExternalMacroDefinitionRequest request{&ctx, external.moduleName, + external.macroTypeName}; auto externalDef = evaluateOrDefault(ctx.evaluator, request, None); if (!externalDef) { - ctx.Diags.diagnose( - mee->getLoc(), diag::external_macro_not_found, - external.moduleName.str(), - external.macroTypeName.str(), - macro->getName() - ); + ctx.Diags.diagnose(loc, diag::external_macro_not_found, + external.moduleName.str(), + external.macroTypeName.str(), macro->getName()); macro->diagnose(diag::decl_declared_here, macro->getName()); - return None; + return nullptr; + } + + // Code item macros require `CodeItemMacros` feature flag. + if (macroRoles.contains(MacroRole::CodeItem) && + !ctx.LangOpts.hasFeature(Feature::CodeItemMacros)) { + ctx.Diags.diagnose(loc, diag::macro_experimental, "code item", + "CodeItemMacros"); + return nullptr; } #if SWIFT_SWIFT_PARSER - PrettyStackTraceExpr debugStack(ctx, "expanding macro", mee); + PrettyStackTraceFreestandingMacroExpansion debugStack( + "expanding freestanding macro", expansion); // Builtin macros are handled via ASTGen. auto *astGenSourceFile = sourceFile->getExportedSourceFile(); if (!astGenSourceFile) - return None; + return nullptr; const char *evaluatedSourceAddress; ptrdiff_t evaluatedSourceLength; @@ -795,51 +962,41 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { &ctx.Diags, externalDef->opaqueHandle, static_cast(externalDef->kind), discriminator->data(), discriminator->size(), astGenSourceFile, - mee->getStartLoc().getOpaquePointerValue(), &evaluatedSourceAddress, - &evaluatedSourceLength); + expansion->getSourceRange().Start.getOpaquePointerValue(), + &evaluatedSourceAddress, &evaluatedSourceLength); if (!evaluatedSourceAddress) - return None; + return nullptr; evaluatedSource = llvm::MemoryBuffer::getMemBufferCopy( {evaluatedSourceAddress, (size_t)evaluatedSourceLength}, adjustMacroExpansionBufferName(*discriminator)); free((void *)evaluatedSourceAddress); break; #else - ctx.Diags.diagnose(mee->getLoc(), diag::macro_unsupported); - return None; + ctx.Diags.diagnose(loc, diag::macro_unsupported); + return nullptr; #endif } } - // Dump macro expansions to standard output, if requested. - if (ctx.LangOpts.DumpMacroExpansions) { - llvm::errs() << evaluatedSource->getBufferIdentifier() << " as " - << expandedType.getString() - << "\n------------------------------\n" - << evaluatedSource->getBuffer() - << "\n------------------------------\n"; - } + return createMacroSourceFile(std::move(evaluatedSource), + isa(expansion) + ? MacroRole::Declaration + : MacroRole::Expression, + expansion->getASTNode(), dc, + /*attr=*/nullptr); +} - // Create a new source buffer with the contents of the expanded macro. - unsigned macroBufferID = - sourceMgr.addNewSourceBuffer(std::move(evaluatedSource)); - auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); - GeneratedSourceInfo sourceInfo{ - GeneratedSourceInfo::ExpressionMacroExpansion, - Lexer::getCharSourceRangeFromSourceRange( - sourceMgr, mee->getSourceRange()), - macroBufferRange, - ASTNode(mee).getOpaqueValue(), - dc - }; - sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo); +Optional swift::expandMacroExpr(MacroExpansionExpr *mee) { + SourceFile *macroSourceFile = ::evaluateFreestandingMacro(mee); + if (!macroSourceFile) + return None; - // Create a source file to hold the macro buffer. This is automatically - // registered with the enclosing module. - auto macroSourceFile = new (ctx) SourceFile( - *dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID, - /*parsingOpts=*/{}, /*isPrimary=*/false); - macroSourceFile->setImports(sourceFile->getImports()); + DeclContext *dc = mee->getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + SourceManager &sourceMgr = ctx.SourceMgr; + + auto macroBufferID = *macroSourceFile->getBufferID(); + auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); // Retrieve the parsed expression from the list of top-level items. auto topLevelItems = macroSourceFile->getTopLevelItems(); @@ -860,6 +1017,8 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { return macroBufferID; } + auto expandedType = mee->getType(); + // Type-check the expanded expression. // FIXME: Would like to pass through type checking options like "discarded" // that are captured by TypeCheckExprOptions. @@ -888,150 +1047,12 @@ swift::expandMacroExpr(MacroExpansionExpr *mee) { /// Expands the given macro expansion declaration. Optional swift::expandFreestandingMacro(MacroExpansionDecl *med) { - auto *dc = med->getDeclContext(); - ASTContext &ctx = dc->getASTContext(); - SourceManager &sourceMgr = ctx.SourceMgr; - - auto moduleDecl = dc->getParentModule(); - auto sourceFile = moduleDecl->getSourceFileContainingLocation(med->getLoc()); - if (!sourceFile) + SourceFile *macroSourceFile = ::evaluateFreestandingMacro(med); + if (!macroSourceFile) return None; MacroDecl *macro = cast(med->getMacroRef().getDecl()); - auto macroRoles = macro->getMacroRoles(); - assert(macroRoles.contains(MacroRole::Declaration) || - macroRoles.contains(MacroRole::CodeItem)); - - if (isFromExpansionOfMacro(sourceFile, macro, MacroRole::Expression) || - isFromExpansionOfMacro(sourceFile, macro, MacroRole::Declaration) || - isFromExpansionOfMacro(sourceFile, macro, MacroRole::CodeItem)) { - med->diagnose(diag::macro_recursive, macro->getName()); - return None; - } - - // Evaluate the macro. - std::unique_ptr evaluatedSource; - - /// The discriminator used for the macro. - LazyValue discriminator([&]() -> std::string { -#if SWIFT_SWIFT_PARSER - Mangle::ASTMangler mangler; - return mangler.mangleMacroExpansion(med); -#else - return ""; -#endif - }); - - auto macroDef = macro->getDefinition(); - switch (macroDef.kind) { - case MacroDefinition::Kind::Undefined: - case MacroDefinition::Kind::Invalid: - // Already diagnosed as an error elsewhere. - return None; - - case MacroDefinition::Kind::Builtin: { - switch (macroDef.getBuiltinKind()) { - case BuiltinMacroKind::ExternalMacro: - // FIXME: Error here. - return None; - } - } - - case MacroDefinition::Kind::Expanded: { - // Expand the definition with the given arguments. - auto result = expandMacroDefinition( - macroDef.getExpanded(), macro, med->getArgs()); - evaluatedSource = llvm::MemoryBuffer::getMemBufferCopy( - result, adjustMacroExpansionBufferName(*discriminator)); - break; - } - - case MacroDefinition::Kind::External: { - // Retrieve the external definition of the macro. - auto external = macroDef.getExternalMacro(); - ExternalMacroDefinitionRequest request{ - &ctx, external.moduleName, external.macroTypeName - }; - auto externalDef = evaluateOrDefault(ctx.evaluator, request, None); - if (!externalDef) { - med->diagnose(diag::external_macro_not_found, - external.moduleName.str(), - external.macroTypeName.str(), - macro->getName() - ); - macro->diagnose(diag::decl_declared_here, macro->getName()); - return None; - } - - // Currently only expression macros are enabled by default. Declaration - // macros need the `FreestandingMacros` feature flag, and code item macros - // need both `FreestandingMacros` and `CodeItemMacros`. - if (!macroRoles.contains(MacroRole::Expression)) { - if (!macroRoles.contains(MacroRole::Declaration) && - !ctx.LangOpts.hasFeature(Feature::CodeItemMacros)) { - med->diagnose(diag::macro_experimental, "code item", "CodeItemMacros"); - return None; - } - } - -#if SWIFT_SWIFT_PARSER - PrettyStackTraceDecl debugStack("expanding declaration macro", med); - - // Builtin macros are handled via ASTGen. - auto *astGenSourceFile = sourceFile->getExportedSourceFile(); - if (!astGenSourceFile) - return None; - - const char *evaluatedSourceAddress; - ptrdiff_t evaluatedSourceLength; - swift_ASTGen_expandFreestandingMacro( - &ctx.Diags, externalDef->opaqueHandle, - static_cast(externalDef->kind), discriminator->data(), - discriminator->size(), astGenSourceFile, - med->getStartLoc().getOpaquePointerValue(), &evaluatedSourceAddress, - &evaluatedSourceLength); - if (!evaluatedSourceAddress) - return None; - evaluatedSource = llvm::MemoryBuffer::getMemBufferCopy( - {evaluatedSourceAddress, (size_t)evaluatedSourceLength}, - adjustMacroExpansionBufferName(*discriminator)); - free((void *)evaluatedSourceAddress); - break; -#else - med->diagnose(diag::macro_unsupported); - return None; -#endif - } - } - - // Dump macro expansions to standard output, if requested. - if (ctx.LangOpts.DumpMacroExpansions) { - llvm::errs() << evaluatedSource->getBufferIdentifier() - << "\n------------------------------\n" - << evaluatedSource->getBuffer() - << "\n------------------------------\n"; - } - - // Create a new source buffer with the contents of the expanded macro. - unsigned macroBufferID = - sourceMgr.addNewSourceBuffer(std::move(evaluatedSource)); - auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); - GeneratedSourceInfo sourceInfo{ - GeneratedSourceInfo::FreestandingDeclMacroExpansion, - Lexer::getCharSourceRangeFromSourceRange( - sourceMgr, med->getSourceRange()), - macroBufferRange, - ASTNode(med).getOpaqueValue(), - dc - }; - sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo); - - // Create a source file to hold the macro buffer. This is automatically - // registered with the enclosing module. - auto macroSourceFile = new (ctx) SourceFile( - *dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID, - /*parsingOpts=*/{}, /*isPrimary=*/false); - macroSourceFile->setImports(sourceFile->getImports()); + DeclContext *dc = med->getDeclContext(); validateMacroExpansion(macroSourceFile, macro, /*attachedTo*/nullptr, @@ -1045,33 +1066,13 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) { if (auto *decl = item.dyn_cast()) decl->setDeclContext(dc); } - return macroBufferID; + return *macroSourceFile->getBufferID(); } -// If this storage declaration is a variable with an explicit initializer, -// return the range from the `=` to the end of the explicit initializer. -static Optional getExplicitInitializerRange( - AbstractStorageDecl *storage) { - auto var = dyn_cast(storage); - if (!var) - return None; - - auto pattern = var->getParentPatternBinding(); - if (!pattern) - return None; - - unsigned index = pattern->getPatternEntryIndexForVarDecl(var); - SourceLoc equalLoc = pattern->getEqualLoc(index); - SourceRange initRange = pattern->getOriginalInitRange(index); - if (equalLoc.isInvalid() || initRange.End.isInvalid()) - return None; - - return SourceRange(equalLoc, initRange.End); -} - -static SourceFile * -evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, - bool passParentContext, MacroRole role) { +static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, + bool passParentContext, MacroRole role, + StringRef discriminatorStr = "") { DeclContext *dc; if (role == MacroRole::Peer) { dc = attachedTo->getDeclContext(); @@ -1083,7 +1084,6 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, } ASTContext &ctx = dc->getASTContext(); - SourceManager &sourceMgr = ctx.SourceMgr; auto moduleDecl = dc->getParentModule(); @@ -1122,6 +1122,8 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, /// The discriminator used for the macro. LazyValue discriminator([&]() -> std::string { + if (!discriminatorStr.empty()) + return discriminatorStr.str(); #if SWIFT_SWIFT_PARSER Mangle::ASTMangler mangler; return mangler.mangleAttachedMacroExpansion(attachedTo, attr, role); @@ -1222,117 +1224,8 @@ evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, CustomAttr *attr, } } - // Dump macro expansions to standard output, if requested. - if (ctx.LangOpts.DumpMacroExpansions) { - llvm::errs() << evaluatedSource->getBufferIdentifier() - << "\n------------------------------\n" - << evaluatedSource->getBuffer() - << "\n------------------------------\n"; - } - - CharSourceRange generatedOriginalSourceRange; - GeneratedSourceInfo::Kind generatedSourceKind; - switch (role) { - case MacroRole::Accessor: { - generatedSourceKind = GeneratedSourceInfo::AccessorMacroExpansion; - - // Compute the location where the accessors will be added. - auto storage = cast(attachedTo); - auto bracesRange = storage->getBracesRange(); - if (bracesRange.Start.isValid()) { - // We have braces already, so insert them inside the leading '{'. - generatedOriginalSourceRange = CharSourceRange( - Lexer::getLocForEndOfToken(sourceMgr, bracesRange.Start), 0); - } else if (auto initRange = getExplicitInitializerRange(storage)) { - // The accessor had an initializer, so the initializer (including - // the `=`) is replaced by the accessors. - generatedOriginalSourceRange = - Lexer::getCharSourceRangeFromSourceRange(sourceMgr, *initRange); - } else { - // The accessors go at the end. - SourceLoc endLoc = storage->getEndLoc(); - if (auto var = dyn_cast(storage)) { - if (auto pattern = var->getParentPattern()) - endLoc = pattern->getEndLoc(); - } - - generatedOriginalSourceRange = CharSourceRange( - Lexer::getLocForEndOfToken(sourceMgr, endLoc), 0); - } - - break; - } - - case MacroRole::MemberAttribute: { - generatedSourceKind = GeneratedSourceInfo::MemberAttributeMacroExpansion; - SourceLoc startLoc; - if (auto valueDecl = dyn_cast(attachedTo)) - startLoc = valueDecl->getAttributeInsertionLoc(/*forModifier=*/false); - else - startLoc = attachedTo->getStartLoc(); - - generatedOriginalSourceRange = CharSourceRange(startLoc, 0); - break; - } - - case MacroRole::Member: { - generatedSourceKind = GeneratedSourceInfo::MemberMacroExpansion; - - // Semantically, we insert members right before the closing brace. - SourceLoc rightBraceLoc; - if (auto nominal = dyn_cast(attachedTo)) { - rightBraceLoc = nominal->getBraces().End; - } else { - auto ext = cast(attachedTo); - rightBraceLoc = ext->getBraces().End; - } - - generatedOriginalSourceRange = CharSourceRange(rightBraceLoc, 0); - break; - } - - case MacroRole::Peer: { - generatedSourceKind = GeneratedSourceInfo::PeerMacroExpansion; - SourceLoc afterDeclLoc = - Lexer::getLocForEndOfToken(sourceMgr, attachedTo->getEndLoc()); - generatedOriginalSourceRange = CharSourceRange(afterDeclLoc, 0); - break; - } - - case MacroRole::Conformance: { - generatedSourceKind = GeneratedSourceInfo::ConformanceMacroExpansion; - SourceLoc afterDeclLoc = - Lexer::getLocForEndOfToken(sourceMgr, attachedTo->getEndLoc()); - generatedOriginalSourceRange = CharSourceRange(afterDeclLoc, 0); - break; - } - - case MacroRole::Expression: - case MacroRole::Declaration: - case MacroRole::CodeItem: - llvm_unreachable("freestanding macro in attached macro evaluation"); - } - - // Create a new source buffer with the contents of the expanded macro. - unsigned macroBufferID = - sourceMgr.addNewSourceBuffer(std::move(evaluatedSource)); - auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); - GeneratedSourceInfo sourceInfo{ - generatedSourceKind, - generatedOriginalSourceRange, - macroBufferRange, - ASTNode(attachedTo).getOpaqueValue(), - dc, - attr - }; - sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo); - - // Create a source file to hold the macro buffer. This is automatically - // registered with the enclosing module. - auto macroSourceFile = new (ctx) SourceFile( - *dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID, - /*parsingOpts=*/{}, /*isPrimary=*/false); - macroSourceFile->setImports(declSourceFile->getImports()); + SourceFile *macroSourceFile = createMacroSourceFile( + std::move(evaluatedSource), role, attachedTo, dc, attr); validateMacroExpansion(macroSourceFile, macro, dyn_cast(attachedTo), role); @@ -1343,9 +1236,9 @@ Optional swift::expandAccessors( AbstractStorageDecl *storage, CustomAttr *attr, MacroDecl *macro ) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, storage, attr, - /*passParentContext*/false, - MacroRole::Accessor); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, storage, attr, + /*passParentContext=*/false, MacroRole::Accessor); if (!macroSourceFile) return None; @@ -1394,9 +1287,9 @@ ArrayRef ExpandAccessorMacros::evaluate( Optional swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, member, attr, - /*passParentContext*/true, - MacroRole::MemberAttribute); + auto macroSourceFile = ::evaluateAttachedMacro(macro, member, attr, + /*passParentContext=*/true, + MacroRole::MemberAttribute); if (!macroSourceFile) return None; @@ -1419,9 +1312,9 @@ swift::expandAttributes(CustomAttr *attr, MacroDecl *macro, Decl *member) { Optional swift::expandMembers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { // Evaluate the macro. - auto macroSourceFile = evaluateAttachedMacro(macro, decl, attr, - /*passParentContext*/false, - MacroRole::Member); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, decl, attr, + /*passParentContext=*/false, MacroRole::Member); if (!macroSourceFile) return None; @@ -1446,9 +1339,9 @@ swift::expandMembers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { Optional swift::expandPeers(CustomAttr *attr, MacroDecl *macro, Decl *decl) { - auto macroSourceFile = evaluateAttachedMacro(macro, decl, attr, - /*passParentContext*/false, - MacroRole::Peer); + auto macroSourceFile = + ::evaluateAttachedMacro(macro, decl, attr, + /*passParentContext=*/false, MacroRole::Peer); if (!macroSourceFile) return None; @@ -1472,10 +1365,9 @@ ExpandConformanceMacros::evaluate(Evaluator &evaluator, Optional swift::expandConformances(CustomAttr *attr, MacroDecl *macro, NominalTypeDecl *nominal) { - auto macroSourceFile = - evaluateAttachedMacro(macro, nominal, attr, - /*passParentContext*/false, - MacroRole::Conformance); + auto macroSourceFile = ::evaluateAttachedMacro(macro, nominal, attr, + /*passParentContext=*/false, + MacroRole::Conformance); if (!macroSourceFile) return None; @@ -1507,11 +1399,8 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator, DeclContext *dc) const { // Macro expressions and declarations have their own stored macro // reference. Use it if it's there. - if (auto *expr = macroRef.getExpr()) { - if (auto ref = expr->getMacroRef()) - return ref; - } else if (auto decl = macroRef.getDecl()) { - if (auto ref = decl->getMacroRef()) + if (auto *expansion = macroRef.getFreestanding()) { + if (auto ref = expansion->getMacroRef()) return ref; } @@ -1530,14 +1419,16 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator, // If we already have a MacroExpansionExpr, use that. Otherwise, // create one. MacroExpansionExpr *macroExpansion; - if (auto *expr = macroRef.getExpr()) { - macroExpansion = expr; - } else if (auto *decl = macroRef.getDecl()) { - macroExpansion = new (ctx) MacroExpansionExpr( - dc, decl->getExpansionInfo(), roles); + if (auto *expansion = macroRef.getFreestanding()) { + if (auto *expr = dyn_cast(expansion)) { + macroExpansion = expr; + } else { + macroExpansion = new (ctx) MacroExpansionExpr( + dc, expansion->getExpansionInfo(), roles); + } } else { SourceRange genericArgsRange = macroRef.getGenericArgsRange(); - macroExpansion = new (ctx) MacroExpansionExpr( + macroExpansion = MacroExpansionExpr::create( dc, macroRef.getSigilLoc(), macroRef.getMacroName(), macroRef.getMacroNameLoc(), genericArgsRange.Start, macroRef.getGenericArgs(), genericArgsRange.End, @@ -1556,12 +1447,26 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator, // reference. If we got a reference, store it there, too. // FIXME: This duplication of state is really unfortunate. if (auto ref = macroExpansion->getMacroRef()) { - if (auto *expr = macroRef.getExpr()) { - expr->setMacroRef(ref); - } else if (auto decl = macroRef.getDecl()) { - decl->setMacroRef(ref); + if (auto *expansion = macroRef.getFreestanding()) { + expansion->setMacroRef(ref); } } return macroExpansion->getMacroRef(); } + +// MARK: for IDE. + +SourceFile *swift::evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, + CustomAttr *attr, + bool passParentContext, MacroRole role, + StringRef discriminator) { + return ::evaluateAttachedMacro(macro, attachedTo, attr, passParentContext, + role, discriminator); +} + +SourceFile * +swift::evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, + StringRef discriminator) { + return ::evaluateFreestandingMacro(expansion, discriminator); +} diff --git a/test/Index/index_macros.swift b/test/Index/index_macros.swift index 08644b8f79307..f20480556e50f 100644 --- a/test/Index/index_macros.swift +++ b/test/Index/index_macros.swift @@ -1,27 +1,284 @@ +// REQUIRES: swift_swift_parser + // RUN: %empty-directory(%t) -// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s -// REQUIRES: OS=macosx +// RUN: split-file --leading-lines %s %t + +// Check that we index code expanded from macros, especially nested references +// (ie. calls within an added function). + +// Create the plugin with various macros for testing +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(IndexMacros) -module-name=IndexMacros %t/IndexMacros.swift -g -no-toolchain-stdlib-rpath + +// Check indexed symbols +// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %t/IndexTest.swift -load-plugin-library %t/%target-library-name(IndexMacros) -parse-as-library 2>&1 | tee %t/test.idx | %FileCheck %s + +//--- IndexTest.swift +@freestanding(expression) +macro freestandingExpr() = #externalMacro(module: "IndexMacros", type: "FreestandingExprMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | freestandingExpr() | [[EXPR_USR:.*]] | Def + +@freestanding(declaration, names: named(TestFree)) +macro freestandingDecl() = #externalMacro(module: "IndexMacros", type: "FreestandingDeclMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | freestandingDecl() | [[DECL_USR:.*]] | Def + +@attached(accessor) +macro Accessor() = #externalMacro(module: "IndexMacros", type: "SomeAccessorMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Accessor() | [[ACCESSOR_USR:.*]] | Def + +@attached(conformance) +macro Conformance() = #externalMacro(module: "IndexMacros", type: "SomeConformanceMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Conformance() | [[CONFORMANCE_USR:.*]] | Def + +@attached(member, names: named(memberFunc)) +macro Member() = #externalMacro(module: "IndexMacros", type: "SomeMemberMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Member() | [[MEMBER_USR:.*]] | Def + +@attached(memberAttribute) +macro MemberAttribute() = #externalMacro(module: "IndexMacros", type: "SomeMemberAttributeMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | MemberAttribute() | [[MEMBER_ATTRIBUTE_USR:.*]] | Def + +@attached(peer, names: named(TestPeer)) +macro Peer() = #externalMacro(module: "IndexMacros", type: "SomePeerMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | Peer() | [[PEER_USR:.*]] | Def + +@attached(peer, names: named(peerMember)) +macro PeerMember() = #externalMacro(module: "IndexMacros", type: "SomePeerMemberMacro") +// CHECK: [[@LINE-1]]:7 | macro/Swift | PeerMember() | [[PEER_MEMBER_USR:.*]] | Def + +protocol TestProto {} +// CHECK: [[@LINE-1]]:10 | protocol/Swift | TestProto | [[PROTO_USR:.*]] | Def + +func accessorLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | accessorLog() | [[ACC_LOG_USR:.*]] | Def +func exprLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | exprLog() | [[EXPR_LOG_USR:.*]] | Def +func freeLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | freeLog() | [[FREE_LOG_USR:.*]] | Def +func memberLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | memberLog() | [[MEMBER_LOG_USR:.*]] | Def +func peerLog() {} +// CHECK: [[@LINE-1]]:6 | function/Swift | peerLog() | [[PEER_LOG_USR:.*]] | Def + +// CHECK: [[@LINE+2]]:8 | struct/Swift | AddOne | [[ADD_ONE_USR:.*]] | Def +@propertyWrapper +struct AddOne { + var value: Int = 1 + var wrappedValue: Int { + get { value } + set { value = newValue + 1 } + } + init(wrappedValue: Int) { + self.wrappedValue = wrappedValue + } +} + +// Creates a `TestFree` struct with `freeFunc` calling `freeLog` +#freestandingDecl +// CHECK: [[@LINE-1]]:2 | macro/Swift | freestandingDecl() | [[DECL_USR]] | Ref +// CHECK: [[@LINE-2]]:1 | struct/Swift | TestFree | [[FREE_STRUCT_USR:.*]] | Def,Impl +// CHECK: [[@LINE-3]]:1 | instance-method/Swift | freeFunc() | [[FREE_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK-NEXT: RelChild | struct/Swift | TestFree | [[FREE_STRUCT_USR]] +// CHECK: [[@LINE-5]]:1 | function/Swift | freeLog() | [[FREE_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | freeFunc() | [[FREE_FUNC_USR]] + +// CHECK: [[@LINE+4]]:40 | macro/Swift | Peer() | [[PEER_USR]] | Ref +// CHECK: [[@LINE+3]]:23 | macro/Swift | MemberAttribute() | [[MEMBER_ATTRIBUTE_USR]] | Ref +// CHECK: [[@LINE+2]]:15 | macro/Swift | Member() | [[MEMBER_USR]] | Ref +// CHECK: [[@LINE+1]]:2 | macro/Swift | Conformance() | [[CONFORMANCE_USR]] | Ref +@Conformance @Member @MemberAttribute @Peer +struct TestAttached { + var attachedMember: Int + + @Accessor + var attachedMemberAccessors: Int +} +// `MemberAttribute` adds `@AddOne` to attachedMember +// CHECK: [[@LINE-8]]:22 | struct/Swift | AddOne | [[ADD_ONE_USR]] | Ref,Impl,RelCont +// CHECK-NEXT: RelCont | instance-property/Swift | attachedMember + +// `Accessor` adds getters/setters to `attachedMemberAccessors` that both call `accessorLog` +// CHECK: [[@LINE-8]]:3 | function/Swift | accessorLog() | [[ACC_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/acc-get/Swift | getter:attachedMemberAccessors +// `Member` adds a new member `memberFunc` that calls `memberLog` +// CHECK: [[@LINE-16]]:14 | instance-method/Swift | memberFunc() | [[MEMBER_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK: [[@LINE-17]]:14 | function/Swift | memberLog() | [[MEMBER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | memberFunc() | [[MEMBER_FUNC_USR]] -@freestanding(expression) macro myLine() -> Int = #externalMacro(module: "MacroDefinition", type: "LineMacro") -@freestanding(expression) macro myFilename() -> T = #externalMacro(module: "MacroDefinition", type: "FileMacro") -@freestanding(expression) macro myStringify(_: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") +// `Peer` adds a new inner type `TestPeer` that contains `peerFunc` with a call to `peerLog` +// CHECK: [[@LINE-21]]:39 | struct/Swift | TestPeer | [[PEER_STRUCT_USR:.*]] | Def,Impl +// CHECK: [[@LINE-22]]:39 | instance-method/Swift | peerFunc() | [[PEER_FUNC_USR:.*]] | Def,Impl,RelChild +// CHECK-NEXT: RelChild | struct/Swift | TestPeer | [[PEER_STRUCT_USR]] +// CHECK: [[@LINE-24]]:39 | function/Swift | peerLog() | [[PEER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | peerFunc() | [[PEER_FUNC_USR]] -func test(x: Int) { - _ = #myLine - let _: String = #myFilename - _ = #myStringify(x + x) +// `Conformance` adds `TestProto` as a conformance on an extension of `TestAttached` +// CHECK: [[@LINE-28]]:1 | extension/ext-struct/Swift | TestAttached | {{.*}} | Def,Impl +// CHECK: [[@LINE-29]]:1 | protocol/Swift | TestProto | [[PROTO_USR]] | Ref,Impl,RelBase +// CHECK-NEXT: RelBase | extension/ext-struct/Swift | TestAttached + +// CHECK: [[@LINE+1]]:8 | struct/Swift | Outer | [[OUTER_USR:.*]] | Def +struct Outer { + // CHECK: [[@LINE+1]]:4 | macro/Swift | PeerMember() | [[PEER_MEMBER_USR]] | Ref + @PeerMember + var anyMember: Int + // `PeerMember` adds a new `peerMember` + // CHECK: [[@LINE-3]]:3 | instance-property/Swift | peerMember | {{.*}} | Def,Impl,RelChild + // CHECK-NEXT: RelChild | struct/Swift | Outer | [[OUTER_USR]] + + // CHECK: [[@LINE+2]]:17 | macro/Swift | Member() | [[MEMBER_USR]] | Ref + // CHECK: [[@LINE+1]]:4 | macro/Swift | Conformance() | [[CONFORMANCE_USR]] | Ref + @Conformance @Member + struct TestInner {} +} +// `Member` adds a new member `memberFunc` that calls `memberLog` +// CHECK: [[@LINE-4]]:16 | instance-method/Swift | memberFunc() | [[INNER_FUNC_USR:.*]] | Def,Impl +// CHECK-NEXT: RelChild | struct/Swift | TestInner +// CHECK: [[@LINE-6]]:16 | function/Swift | memberLog() | [[MEMBER_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont +// CHECK-NEXT: RelCall,RelCont | instance-method/Swift | memberFunc() | [[INNER_FUNC_USR]] + +// `Conformance` adds `TestProto` as a conformance on an extension of `TestInner` +// CHECK: [[@LINE-10]]:3 | extension/ext-struct/Swift | TestInner | {{.*}} | Def,Impl +// CHECK: [[@LINE-11]]:3 | protocol/Swift | TestProto | [[PROTO_USR]] | Ref,Impl,RelBase +// CHECK-NEXT: RelBase | extension/ext-struct/Swift | TestInner + +func testExpr() { + #freestandingExpr + // CHECK: [[@LINE-1]]:3 | function/Swift | exprLog() | [[EXPR_LOG_USR]] | Ref,Call,Impl,RelCall,RelCont + // CHECK-NEXT: RelCall,RelCont | function/Swift | testExpr() } -// CHECK: 6:33 | macro/Swift | myLine() | s:14swift_ide_test6myLineSiycfm | Def | rel: 0 -// CHECK: 6:45 | struct/Swift | Int | s:Si | Ref | rel: 0 -// CHECK: 7:33 | macro/Swift | myFilename() | s:14swift_ide_test10myFilenamexycs26ExpressibleByStringLiteralRzlufm | Def | rel: 0 -// CHECK: 7:47 | protocol/Swift | ExpressibleByStringLiteral | s:s26ExpressibleByStringLiteralP | Ref | rel: 0 -// CHECK: 8:33 | macro/Swift | myStringify(_:) | s:14swift_ide_test11myStringifyyx_SStxclufm | Def | rel: 0 +//--- IndexMacros.swift +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct FreestandingExprMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return "exprLog()" + } +} + +public struct FreestandingDeclMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [""" + struct TestFree { + func freeFunc() { + freeLog() + } + } + """] + } +} + +public struct SomeAccessorMacro: AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + return [ + """ + get { + accessorLog() + return 1 + } + """, + """ + set { + accessorLog() + } + """, + ] + } +} -// CHECK: 11:8 | macro/Swift | myLine() | s:14swift_ide_test6myLineSiycfm | Ref,RelCont | rel: 1 -// CHECK: 12:20 | macro/Swift | myFilename() | s:14swift_ide_test10myFilenamexycs26ExpressibleByStringLiteralRzlufm | Ref,RelCont | rel: 1 -// CHECK: 13:8 | macro/Swift | myStringify(_:) | s:14swift_ide_test11myStringifyyx_SStxclufm | Ref,RelCont | rel: 1 -// CHECK: 13:20 | param/Swift | x | s:14swift_ide_test0C01xySi_tFACL_Sivp | Ref,Read,RelCont | rel: 1 -// CHECK: 13:22 | static-method/infix-operator/Swift | +(_:_:) | s:Si1poiyS2i_SitFZ | Ref,Call,RelCall,RelCont | rel: 1 -// CHECK: 13:24 | param/Swift | x | s:14swift_ide_test0C01xySi_tFACL_Sivp | Ref,Read,RelCont | rel: 1 +public struct SomeConformanceMacro: ConformanceMacro { + public static func expansion( + of node: AttributeSyntax, + providingConformancesOf decl: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] { + let protocolName: TypeSyntax = "TestProto" + return [(protocolName, nil)] + } +} + +public struct SomeMemberMacro: MemberMacro { + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + let newFunc: DeclSyntax = + """ + func memberFunc() { + memberLog() + } + """ + return [ + newFunc, + ] + } +} + +public struct SomeMemberAttributeMacro: MemberAttributeMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo parent: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AttributeSyntax] { + guard let varDecl = member.as(VariableDeclSyntax.self), + let binding = varDecl.bindings.first, + let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text, + identifier == "attachedMember" + else { + return [] + } + + return [AttributeSyntax( + attributeName: SimpleTypeIdentifierSyntax( + name: .identifier("AddOne") + ) + )] + } +} + +public struct SomePeerMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [ + """ + struct TestPeer { + func peerFunc() { + peerLog() + } + } + """ + ] + } +} + +public struct SomePeerMemberMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return [ + """ + var peerMember: Int + """ + ] + } +} diff --git a/test/Macros/Inputs/syntax_macro_definitions.swift b/test/Macros/Inputs/syntax_macro_definitions.swift index f95ca9f6ec2b9..7eaa3505105c4 100644 --- a/test/Macros/Inputs/syntax_macro_definitions.swift +++ b/test/Macros/Inputs/syntax_macro_definitions.swift @@ -299,6 +299,25 @@ public struct DefineDeclsWithKnownNamesMacro: DeclarationMacro { } } +public struct VarDeclMacro: CodeItemMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [CodeBlockItemSyntax] { + let name = context.makeUniqueName("fromMacro") + return [ + "let \(name) = 23", + "use(\(name))", + """ + if true { + let \(name) = "string" + use(\(name)) + } + """ + ] + } +} + public struct WarningMacro: ExpressionMacro { public static func expansion( of macro: some FreestandingMacroExpansionSyntax, @@ -1430,23 +1449,22 @@ public struct SimpleCodeItemMacro: CodeItemMacro { ) throws -> [CodeBlockItemSyntax] { [ .init(item: .decl(""" - struct \(context.makeUniqueName("foo")) { var x: Int } """)), .init(item: .stmt(""" - if true { print("from stmt") usedInExpandedStmt() } + """)), + .init(item: .stmt(""" if false { print("impossible") } """)), .init(item: .expr(""" - print("from expr") """)), ] diff --git a/test/Macros/macro_expand_codeitems.swift b/test/Macros/macro_expand_codeitems.swift index db27cc3397bc4..da2dd8e009428 100644 --- a/test/Macros/macro_expand_codeitems.swift +++ b/test/Macros/macro_expand_codeitems.swift @@ -33,3 +33,10 @@ func testFreestandingMacroExpansion() { #codeItems } testFreestandingMacroExpansion() + +@freestanding(codeItem) macro varDecl() = #externalMacro(module: "MacroDefinition", type: "VarDeclMacro") + +func testVarDecl() { + func use(_ t: T) {} + #varDecl() +} diff --git a/test/Macros/top_level_freestanding.swift b/test/Macros/top_level_freestanding.swift index d0decd1d0c220..3fd9c07c1d6bd 100644 --- a/test/Macros/top_level_freestanding.swift +++ b/test/Macros/top_level_freestanding.swift @@ -50,7 +50,7 @@ func lookupGlobalFreestandingExpansion() { #anonymousTypes(public: true) { "hello" } -// CHECK-SIL: sil @$s9MacroUser03$s9A71User33_082AE7CFEFA6960C804A9FE7366EB5A0Ll14anonymousTypesfMf0_4namefMu_C5helloSSyF +// CHECK-SIL: sil @$s9MacroUser03$s9A70User33_082AE7CFEFA6960C804A9FE7366EB5A0Ll14anonymousTypesfMf_4namefMu_C5helloSSyF @main struct Main { diff --git a/test/SourceKit/Macros/macro_basic.swift b/test/SourceKit/Macros/macro_basic.swift index f7c581b0066d8..9109b45f45980 100644 --- a/test/SourceKit/Macros/macro_basic.swift +++ b/test/SourceKit/Macros/macro_basic.swift @@ -131,7 +131,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=57:1 %s -- ${COMPILER_ARGS[@]} -parse-as-library | %FileCheck -check-prefix=EXPAND_MACRO_DECL %s // RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=57:2 %s -- ${COMPILER_ARGS[@]} -parse-as-library | %FileCheck -check-prefix=EXPAND_MACRO_DECL %s // EXPAND_MACRO_DECL: source.edit.kind.active: -// EXPAND_MACRO_DECL-NEXT: 57:1-57:28 (@__swiftmacro_9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_.swift) "class $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu_ { +// EXPAND_MACRO_DECL-NEXT: 57:1-57:28 (@__swiftmacro_9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_.swift) "class $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu_ { // EXPAND_MACRO_DECL-NEXT: func hello() -> String { // EXPAND_MACRO_DECL-NEXT: "hello" // EXPAND_MACRO_DECL-NEXT: } @@ -140,7 +140,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // EXPAND_MACRO_DECL-NEXT: return Self.self // EXPAND_MACRO_DECL-NEXT: } // EXPAND_MACRO_DECL-NEXT: } -// EXPAND_MACRO_DECL-NEXT: enum $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu0_ { +// EXPAND_MACRO_DECL-NEXT: enum $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu0_ { // EXPAND_MACRO_DECL-NEXT: case apple // EXPAND_MACRO_DECL-NEXT: case banana // EXPAND_MACRO_DECL-EMPTY: @@ -148,7 +148,7 @@ macro anonymousTypes(_: () -> String) = #externalMacro(module: "MacroDefinition" // EXPAND_MACRO_DECL-NEXT: "hello" // EXPAND_MACRO_DECL-NEXT: } // EXPAND_MACRO_DECL-NEXT: } -// EXPAND_MACRO_DECL-NEXT: struct $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf0_4namefMu1_: Equatable { +// EXPAND_MACRO_DECL-NEXT: struct $s9MacroUser33_70D4178875715FB9B8B50C58F66F8D53Ll14anonymousTypesfMf_4namefMu1_: Equatable { // EXPAND_MACRO_DECL-NEXT: static func == (lhs: Self, rhs: Self) -> Bool { // EXPAND_MACRO_DECL-NEXT: false // EXPAND_MACRO_DECL-NEXT: } diff --git a/test/SourceKit/Macros/syntactic_expansion.swift b/test/SourceKit/Macros/syntactic_expansion.swift new file mode 100644 index 0000000000000..17d3f7ae8b032 --- /dev/null +++ b/test/SourceKit/Macros/syntactic_expansion.swift @@ -0,0 +1,82 @@ +//--- test.swift +@DelegatedConformance +@wrapAllProperties +struct Generic { + + @myPropertyWrapper + @otherAttr + var value: Int + + func member() {} + var otherVal: Int = 1 + + #bitwidthNumberedStructs("blah") +} + +//--- DelegatedConformance.json +{ + key.macro_roles: [source.lang.swift.macro_role.conformance, source.lang.swift.macro_role.member], + key.modulename: "MacroDefinition", + key.typename: "DelegatedConformanceMacro", +} + +//--- myPropertyWrapper.json +{ + key.macro_roles: [source.lang.swift.macro_role.accessor, source.lang.swift.macro_role.peer], + key.modulename: "MacroDefinition", + key.typename: "PropertyWrapperMacro", +} + +//--- wrapAllProperties.json +{ + key.macro_roles: [source.lang.swift.macro_role.member_attribute], + key.modulename: "MacroDefinition", + key.typename: "WrapAllProperties", +} + +//--- bitwidthNumberedStructs.json +{ + key.macro_roles: [source.lang.swift.macro_role.declaration], + key.modulename: "MacroDefinition", + key.typename: "DefineBitwidthNumberedStructsMacro", +} + +//--- dummy.script +// REQUIRES: swift_swift_parser + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/plugins +// RUN: split-file %s %t + +//##-- Prepare the macro plugin. +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/plugins/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/../../Macros/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath + +// RUN: %sourcekitd-test \ +// RUN: -shell -- echo '### 1' \ +// RUN: == \ +// RUN: -req=syntactic-expandmacro \ +// RUN: -req-opts=1:1:%t/DelegatedConformance.json \ +// RUN: -req-opts=5:3:%t/myPropertyWrapper.json \ +// RUN: -req-opts=2:1:%t/wrapAllProperties.json \ +// RUN: -req-opts=12:3:%t/bitwidthNumberedStructs.json \ +// RUN: %t/test.swift \ +// RUN: -- \ +// RUN: %t/test.swift \ +// RUN: -plugin-path %t/plugins -Xfrontend -dump-macro-expansions \ +// RUN: -module-name TestModule \ +// RUN: == \ +// RUN: -shell -- echo '### 2' \ +// RUN: == \ +// RUN: -req=syntactic-expandmacro \ +// RUN: -req-opts=12:3:%t/bitwidthNumberedStructs.json \ +// RUN: -req-opts=2:1:%t/wrapAllProperties.json \ +// RUN: -req-opts=5:3:%t/myPropertyWrapper.json \ +// RUN: -req-opts=1:1:%t/DelegatedConformance.json \ +// RUN: %t/test.swift \ +// RUN: -- \ +// RUN: %t/test.swift \ +// RUN: -plugin-path %t/plugins -Xfrontend -dump-macro-expansions \ +// RUN: -module-name TestModule \ +// RUN: | tee %t.response + +// RUN: diff -u %s.expected %t.response diff --git a/test/SourceKit/Macros/syntactic_expansion.swift.expected b/test/SourceKit/Macros/syntactic_expansion.swift.expected new file mode 100644 index 0000000000000..90a0ccf5b0f65 --- /dev/null +++ b/test/SourceKit/Macros/syntactic_expansion.swift.expected @@ -0,0 +1,108 @@ +### 1 +source.edit.kind.active: + 1:1-1:22 "" +source.edit.kind.active: + 13:1-13:1 "static func requirement() where Element : P { + Element.requirement() +}" +source.edit.kind.active: + 13:2-13:2 "extension Generic : P where Element: P {}" +source.edit.kind.active: + 5:3-5:21 "" +source.edit.kind.active: + 7:17-7:17 "{ + get { + _value.wrappedValue + } + + set { + _value.wrappedValue = newValue + } +}" +source.edit.kind.active: + 7:12-7:12 "var _value: MyWrapperThingy" +source.edit.kind.active: + 2:1-2:19 "" +source.edit.kind.active: + 7:3-7:3 "@Wrapper" +source.edit.kind.active: + 10:3-10:3 "@Wrapper" +source.edit.kind.active: + 12:3-12:35 "struct blah8 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu0_() { + } +} +struct blah16 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu1_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu2_() { + } +} +struct blah32 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu3_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu4_() { + } +} +struct blah64 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu5_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu6_() { + } +}" +### 2 +source.edit.kind.active: + 12:3-12:35 "struct blah8 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu0_() { + } +} +struct blah16 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu1_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu2_() { + } +} +struct blah32 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu3_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu4_() { + } +} +struct blah64 { + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu5_() { + } + func __syntactic_macro_a01f07de3e9ee8585adf794ccd6dec8c6methodfMu6_() { + } +}" +source.edit.kind.active: + 2:1-2:19 "" +source.edit.kind.active: + 7:3-7:3 "@Wrapper" +source.edit.kind.active: + 10:3-10:3 "@Wrapper" +source.edit.kind.active: + 5:3-5:21 "" +source.edit.kind.active: + 7:17-7:17 "{ + get { + _value.wrappedValue + } + + set { + _value.wrappedValue = newValue + } +}" +source.edit.kind.active: + 7:12-7:12 "var _value: MyWrapperThingy" +source.edit.kind.active: + 1:1-1:22 "" +source.edit.kind.active: + 13:1-13:1 "static func requirement() where Element : P { + Element.requirement() +}" +source.edit.kind.active: + 13:2-13:2 "extension Generic : P where Element: P {}" diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 5dccd87dd59a0..4203393ca53b2 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -220,6 +220,64 @@ struct RawCharSourceRange { unsigned Length; }; +enum class MacroRole : uint8_t { + // This should align with 'swift::MacroRole'. + Expression = 0x01, + Declaration = 0x02, + Accessor = 0x04, + MemberAttribute = 0x08, + Member = 0x10, + Peer = 0x20, + Conformance = 0x40, + CodeItem = 0x80, +}; +using MacroRoles = swift::OptionSet; + +struct MacroExpansionInfo { + // See swift::ExternalMacroReference. + struct ExternalMacroReference { + std::string moduleName; + std::string typeName; + + ExternalMacroReference(StringRef moduleName, StringRef typeName) + : moduleName(moduleName), typeName(typeName){}; + }; + // See swift::ExpandedMacroDefinition. + struct ExpandedMacroDefinition { + // 'Replacement.range' references some part of code in 'expansionText'. + // 'expansionText' will be replaced by the 'parameterIndex'-th argument of + // the macro. + struct Replacement { + RawCharSourceRange range; + unsigned parameterIndex; + Replacement(RawCharSourceRange range, unsigned parameterIndex) + : range(range), parameterIndex(parameterIndex) {} + }; + std::string expansionText; + std::vector replacements; + + ExpandedMacroDefinition(StringRef expansionText) + : expansionText(expansionText), replacements(){}; + }; + + // Offset of the macro expansion syntax (i.e. attribute or #) from + // the start of the source file. + unsigned offset; + + // Macro roles. + MacroRoles roles; + + // Tagged union of macro definition. + std::variant macroDefinition; + + MacroExpansionInfo(unsigned offset, MacroRoles roles, + ExternalMacroReference macroRef) + : offset(offset), roles(roles), macroDefinition(macroRef) {} + MacroExpansionInfo(unsigned offset, MacroRoles roles, + ExpandedMacroDefinition definition) + : offset(offset), roles(roles), macroDefinition(definition) {} +}; + /// Stores information about a given buffer, including its name and, if /// generated, its source text and original location. struct BufferInfo { @@ -1128,6 +1186,11 @@ class LangSupport { ConformingMethodListConsumer &Consumer, Optional vfsOptions) = 0; + virtual void expandMacroSyntactically(llvm::MemoryBuffer *inputBuf, + ArrayRef args, + ArrayRef expansions, + CategorizedEditsReceiver receiver) = 0; + virtual void performCompile(StringRef Name, ArrayRef Args, Optional vfsOptions, diff --git a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt index 38725d140d215..31f596e5a0019 100644 --- a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt +++ b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt @@ -11,6 +11,7 @@ add_sourcekit_library(SourceKitSwiftLang SwiftLangSupport.cpp SwiftMangling.cpp SwiftSourceDocInfo.cpp + SwiftSyntacticMacroExpansion.cpp SwiftTypeContextInfo.cpp LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index a77254e38d6ce..6f8b019d2db92 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -29,6 +29,7 @@ #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/Utils.h" #include "swift/IDETool/IDEInspectionInstance.h" +#include "swift/IDETool/SyntacticMacroExpansion.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" @@ -303,6 +304,9 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) // By default, just use the in-memory cache. CCCache->inMemory = std::make_unique(); + SyntacticMacroExpansions = + std::make_shared(SwiftExecutablePath, Plugins); + // Provide a default file system provider. setFileSystemProvider("in-memory-vfs", std::make_unique()); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 5fd15b589bd20..ad4a8a7fe5769 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -55,6 +55,7 @@ namespace ide { class IDEInspectionInstance; class OnDiskCodeCompletionCache; class SourceEditConsumer; + class SyntacticMacroExpansion; enum class CodeCompletionDeclKind : uint8_t; enum class SyntaxNodeKind : uint8_t; enum class SyntaxStructureKind : uint8_t; @@ -366,6 +367,7 @@ class SwiftLangSupport : public LangSupport { llvm::StringMap> FileSystemProviders; std::shared_ptr IDEInspectionInst; std::shared_ptr CompileManager; + std::shared_ptr SyntacticMacroExpansions; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -756,6 +758,11 @@ class SwiftLangSupport : public LangSupport { ConformingMethodListConsumer &Consumer, Optional vfsOptions) override; + void expandMacroSyntactically(llvm::MemoryBuffer *inputBuf, + ArrayRef args, + ArrayRef expansions, + CategorizedEditsReceiver receiver) override; + void performCompile(StringRef Name, ArrayRef Args, Optional vfsOptions, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp new file mode 100644 index 0000000000000..170ca176bc7f0 --- /dev/null +++ b/tools/SourceKit/lib/SwiftLang/SwiftSyntacticMacroExpansion.cpp @@ -0,0 +1,94 @@ +//===--- SwiftSyntaxMacro.cpp ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "SwiftLangSupport.h" +#include "swift/AST/MacroDefinition.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/TypeContextInfo.h" +#include "swift/IDETool/SyntacticMacroExpansion.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Decl.h" + +using namespace SourceKit; +using namespace swift; +using namespace ide; + +void SwiftLangSupport::expandMacroSyntactically( + llvm::MemoryBuffer *inputBuf, ArrayRef args, + ArrayRef reqExpansions, + CategorizedEditsReceiver receiver) { + + std::string error; + auto instance = SyntacticMacroExpansions->getInstance(args, error); + if (!instance) { + return receiver( + RequestResult>::fromError(error)); + } + auto &ctx = instance->getASTContext(); + + // Convert 'SourceKit::MacroExpansionInfo' to 'ide::MacroExpansionSpecifier'. + SmallVector expansions; + for (auto &req : reqExpansions) { + unsigned offset = req.offset; + + swift::MacroRoles macroRoles; + if (req.roles.contains(SourceKit::MacroRole::Expression)) + macroRoles |= swift::MacroRole::Expression; + if (req.roles.contains(SourceKit::MacroRole::Declaration)) + macroRoles |= swift::MacroRole::Declaration; + if (req.roles.contains(SourceKit::MacroRole::CodeItem)) + macroRoles |= swift::MacroRole::CodeItem; + if (req.roles.contains(SourceKit::MacroRole::Accessor)) + macroRoles |= swift::MacroRole::Accessor; + if (req.roles.contains(SourceKit::MacroRole::MemberAttribute)) + macroRoles |= swift::MacroRole::MemberAttribute; + if (req.roles.contains(SourceKit::MacroRole::Member)) + macroRoles |= swift::MacroRole::Member; + if (req.roles.contains(SourceKit::MacroRole::Peer)) + macroRoles |= swift::MacroRole::Peer; + if (req.roles.contains(SourceKit::MacroRole::Conformance)) + macroRoles |= swift::MacroRole::Conformance; + + MacroDefinition definition = [&] { + if (auto *expanded = + std::get_if( + &req.macroDefinition)) { + SmallVector replacements; + for (auto &reqReplacement : expanded->replacements) { + replacements.push_back( + {/*startOffset=*/reqReplacement.range.Offset, + /*endOffset=*/reqReplacement.range.Offset + + reqReplacement.range.Length, + /*parameterIndex=*/reqReplacement.parameterIndex}); + } + return MacroDefinition::forExpanded(ctx, expanded->expansionText, + replacements); + } else if (auto *externalRef = + std::get_if( + &req.macroDefinition)) { + return MacroDefinition::forExternal( + ctx.getIdentifier(externalRef->moduleName), + ctx.getIdentifier(externalRef->typeName)); + } else { + return MacroDefinition::forUndefined(); + } + }(); + + expansions.push_back({offset, macroRoles, definition}); + } + + RequestRefactoringEditConsumer consumer(receiver); + instance->expandAll(inputBuf, expansions, consumer); + // consumer automatically send the results on destruction. +} diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index e92982d18ea0a..d6f97c5d446ef 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -153,6 +153,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("diags", SourceKitRequest::Diagnostics) .Case("compile", SourceKitRequest::Compile) .Case("compile.close", SourceKitRequest::CompileClose) + .Case("syntactic-expandmacro", SourceKitRequest::SyntacticMacroExpansion) #define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND) #include "swift/Refactoring/RefactoringKinds.def" .Default(SourceKitRequest::None); @@ -203,6 +204,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { << "- collect-type\n" << "- global-config\n" << "- dependency-updated\n" + << "- syntactic-expandmacro\n" #define SEMANTIC_REFACTORING(KIND, NAME, ID) << "- refactoring." #ID "\n" #include "swift/Refactoring/RefactoringKinds.def" "\n"; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index d8b9868dc28c9..ace443a58fa86 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -70,6 +70,7 @@ enum class SourceKitRequest { Diagnostics, Compile, CompileClose, + SyntacticMacroExpansion, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, #include "swift/Refactoring/RefactoringKinds.def" }; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 2db2dc7206c92..84f4ae8482940 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -416,6 +416,46 @@ static bool readPopularAPIList(StringRef filename, return false; } +/// Read '-req-opts' for syntactic macro expansion request and apply it to 'req' +/// object. +/// The format of the argument is '-req-opts={line}:{column}:{path}' +/// where {path} is a path to a JSON file that has macro roles and definition. +/// {line} and {column} is resolved to 'offset' using \p inputBuf . +static bool setSyntacticMacroExpansions(sourcekitd_object_t req, + TestOptions &opts, + llvm::MemoryBuffer *inputBuf) { + SmallVector expansions; + for (std::string &opt : opts.RequestOptions) { + SmallVector args; + StringRef(opt).split(args, ":"); + unsigned line, column; + + if (args.size() != 3 || args[0].getAsInteger(10, line) || + args[1].getAsInteger(10, column)) { + llvm::errs() << "-req-opts should be {line}:{column}:{json-path}"; + return true; + } + unsigned offset = resolveFromLineCol(line, column, inputBuf); + + auto Buffer = getBufferForFilename(args[2], opts.VFSFiles)->getBuffer(); + char *Err = nullptr; + auto expansion = sourcekitd_request_create_from_yaml(Buffer.data(), &Err); + if (!expansion) { + assert(Err); + llvm::errs() << Err; + free(Err); + return true; + } + sourcekitd_request_dictionary_set_int64(expansion, KeyOffset, + int64_t(offset)); + expansions.push_back(expansion); + } + sourcekitd_request_dictionary_set_value( + req, KeyExpansions, + sourcekitd_request_array_create(expansions.data(), expansions.size())); + return false; +} + namespace { class PrintingTimer { std::string desc; @@ -1107,6 +1147,12 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCompileClose); break; + + case SourceKitRequest::SyntacticMacroExpansion: + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, + RequestSyntacticMacroExpansion); + setSyntacticMacroExpansions(Req, Opts, SourceBuf.get()); + break; } if (!Opts.SourceFile.empty()) { @@ -1521,6 +1567,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, #define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: #include "swift/Refactoring/RefactoringKinds.def" case SourceKitRequest::SyntacticRename: + case SourceKitRequest::SyntacticMacroExpansion: printSyntacticRenameEdits(Info, llvm::outs()); break; case SourceKitRequest::FindRenameRanges: diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index 94686abdf8567..d0ad86c6ed704 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -1824,6 +1824,134 @@ handleRequestDiagnostics(const RequestDict &Req, }); } +/// Expand macros in the specified source file syntactically. +/// +/// Request would look like: +/// { +/// key.compilerargs: [] +/// key.sourcefile: +/// key.sourcetext: (optional) +/// key.expansions: [...] +/// } +/// 'compilerargs' is used for plugin search paths. +/// 'expansion specifier' is +/// { +/// key.offset: +/// key.modulename: +/// key.typename: +/// key.macro_roles: [...] +/// } +/// +/// Sends the results as a 'CategorizedEdits'. +/// Note that, unlike refactoring, each edit doesn't have 'key.buffer_name'. +/// FIXME: Support nested expansion. +static void handleRequestSyntacticMacroExpansion( + const RequestDict &req, SourceKitCancellationToken cancellationToken, + ResponseReceiver rec) { + + Optional vfsOptions = getVFSOptions(req); + std::unique_ptr inputBuf = + getInputBufForRequestOrEmitError(req, vfsOptions, rec); + if (!inputBuf) + return; + + SmallVector args; + if (getCompilerArgumentsForRequestOrEmitError(req, args, rec)) + return; + + // key.expansions: [ + // { key.offset: 42, + // key.macro_roles: [source.lang.swift.macrorole.conformance, + // source.lang.swift.macrorole.member], + // key.modulename: "MyMacroImpl", + // key.typename: "StringifyMacro"}, + // { key.offset: 132, + // key.sourceText: "foo(bar, baz)", + // key.macro_roles: [source.lang.swift.macrorole.conformance, + // source.lang.swift.macrorole.member], + // key.expandedmacro_replacements: [ + // {key.offset: 4, key.length: 3, key.argindex: 0}, + // {key.offset: 9, key.length: 3, key.argindex: 1}]} + // ] + std::vector expansions; + bool failed = req.dictionaryArrayApply(KeyExpansions, [&](RequestDict dict) { + // offset. + int64_t offset; + dict.getInt64(KeyOffset, offset, false); + + // macro roles. + SmallVector macroRoleUIDs; + if (dict.getUIDArray(KeyMacroRoles, macroRoleUIDs, false)) { + rec(createErrorRequestInvalid( + "missing 'key.macro_roles' for expansion specifier")); + return true; + } + MacroRoles macroRoles; + for (auto uid : macroRoleUIDs) { + if (uid == KindMacroRoleExpression) + macroRoles |= MacroRole::Expression; + if (uid == KindMacroRoleDeclaration) + macroRoles |= MacroRole::Declaration; + if (uid == KindMacroRoleCodeItem) + macroRoles |= MacroRole::CodeItem; + if (uid == KindMacroRoleAccessor) + macroRoles |= MacroRole::Accessor; + if (uid == KindMacroRoleMemberAttribute) + macroRoles |= MacroRole::MemberAttribute; + if (uid == KindMacroRoleMember) + macroRoles |= MacroRole::Member; + if (uid == KindMacroRolePeer) + macroRoles |= MacroRole::Peer; + if (uid == KindMacroRoleConformance) + macroRoles |= MacroRole::Conformance; + } + + // definition. + if (auto moduleName = dict.getString(KeyModuleName)) { + auto typeName = dict.getString(KeyTypeName); + if (!typeName) { + rec(createErrorRequestInvalid( + "missing 'key.typename' for external macro definition")); + return true; + } + MacroExpansionInfo::ExternalMacroReference definition(moduleName->str(), + typeName->str()); + expansions.emplace_back(offset, macroRoles, definition); + } else if (auto expandedText = dict.getString(KeySourceText)) { + MacroExpansionInfo::ExpandedMacroDefinition definition(*expandedText); + bool failed = dict.dictionaryArrayApply( + KeyExpandedMacroReplacements, [&](RequestDict dict) { + int64_t offset, length, paramIndex; + bool failed = false; + failed |= dict.getInt64(KeyOffset, offset, false); + failed |= dict.getInt64(KeyLength, length, false); + failed |= dict.getInt64(KeyArgIndex, paramIndex, false); + if (failed) { + rec(createErrorRequestInvalid( + "macro replacement should have key.offset, key.length, and " + "key.argindex")); + return true; + } + definition.replacements.emplace_back( + RawCharSourceRange{unsigned(offset), unsigned(length)}, + paramIndex); + return false; + }); + if (failed) + return true; + expansions.emplace_back(offset, macroRoles, definition); + } + return false; + }); + if (failed) + return; + + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); + Lang.expandMacroSyntactically( + inputBuf.get(), args, expansions, + [&](const auto &Result) { rec(createCategorizedEditsResponse(Result)); }); +} + void handleRequestImpl(sourcekitd_object_t ReqObj, SourceKitCancellationToken CancellationToken, ResponseReceiver Rec) { @@ -1927,6 +2055,8 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, HANDLE_REQUEST(RequestRelatedIdents, handleRequestRelatedIdents) HANDLE_REQUEST(RequestActiveRegions, handleRequestActiveRegions) HANDLE_REQUEST(RequestDiagnostics, handleRequestDiagnostics) + HANDLE_REQUEST(RequestSyntacticMacroExpansion, + handleRequestSyntacticMacroExpansion) { SmallString<64> ErrBuf; diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 4bd5a9f40a534..a660222a15665 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -209,6 +209,9 @@ def __init__(self, internal_name, external_name): KEY('IsSynthesized', 'key.is_synthesized'), KEY('BufferName', 'key.buffer_name'), KEY('BarriersEnabled', 'key.barriers_enabled'), + KEY('Expansions', 'key.expansions'), + KEY('MacroRoles', 'key.macro_roles'), + KEY('ExpandedMacroReplacements', 'key.expanded_macro_replacements'), ] @@ -274,6 +277,8 @@ def __init__(self, internal_name, external_name): REQUEST('Compile', 'source.request.compile'), REQUEST('CompileClose', 'source.request.compile.close'), REQUEST('EnableRequestBarriers', 'source.request.enable_request_barriers'), + REQUEST('SyntacticMacroExpansion', + 'source.request.syntactic_macro_expansion'), ] @@ -491,4 +496,12 @@ def __init__(self, internal_name, external_name): KIND('StatInstructionCount', 'source.statistic.instruction-count'), KIND('Swift', 'source.lang.swift'), KIND('ObjC', 'source.lang.objc'), + KIND('MacroRoleExpression', 'source.lang.swift.macro_role.expression'), + KIND('MacroRoleDeclaration', 'source.lang.swift.macro_role.declaration'), + KIND('MacroRoleCodeItem', 'source.lang.swift.macro_role.codeitem'), + KIND('MacroRoleAccessor', 'source.lang.swift.macro_role.accessor'), + KIND('MacroRoleMemberAttribute', 'source.lang.swift.macro_role.member_attribute'), + KIND('MacroRoleMember', 'source.lang.swift.macro_role.member'), + KIND('MacroRolePeer', 'source.lang.swift.macro_role.peer'), + KIND('MacroRoleConformance', 'source.lang.swift.macro_role.conformance'), ]