diff --git a/include/swift/AST/DefaultArgumentKind.h b/include/swift/AST/DefaultArgumentKind.h index a60b9101e931a..f687bb4426eac 100644 --- a/include/swift/AST/DefaultArgumentKind.h +++ b/include/swift/AST/DefaultArgumentKind.h @@ -38,6 +38,8 @@ enum class DefaultArgumentKind : uint8_t { Inherited, /// The #file default argument, which is expanded at the call site. File, + /// The #filePath default argument, which is expanded at the call site. + FilePath, /// The #line default argument, which is expanded at the call site. Line, /// The #column default argument, which is expanded at the call site. diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 78cbaddb9c771..9fcddb0c066a8 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1040,7 +1040,7 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { class MagicIdentifierLiteralExpr : public LiteralExpr { public: enum Kind : unsigned { - File, Line, Column, Function, DSOHandle + File, FilePath, Line, Column, Function, DSOHandle }; private: SourceLoc Loc; @@ -1067,6 +1067,7 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { bool isString() const { switch (getKind()) { case File: + case FilePath: case Function: return true; case Line: diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 934da2d6e4945..8d24bd41d831b 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -94,6 +94,10 @@ namespace swift { /// when using RequireExplicitAvailability. std::string RequireExplicitAvailabilityTarget; + /// If false, '#file' evaluates to the full path rather than a + /// human-readable string. + bool EnableConcisePoundFile = false; + /// /// Support for alternate usage modes /// diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index e9799c7130123..c6d9718a52f39 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -454,6 +454,11 @@ def enable_experimental_differentiable_programming : Flag<["-"], "enable-experim Flags<[FrontendOption]>, HelpText<"Enable experimental differentiable programming features">; +def enable_experimental_concise_pound_file : Flag<["-"], + "enable-experimental-concise-pound-file">, + Flags<[FrontendOption]>, + HelpText<"Enable experimental concise '#file' identifier and '#filePath' alternative">; + // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, Flags<[FrontendOption]>, diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index d918e3406fe6d..bec37a1a045e4 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -300,6 +300,7 @@ static StringRef getDefaultArgumentKindString(DefaultArgumentKind value) { case DefaultArgumentKind::Column: return "#column"; case DefaultArgumentKind::DSOHandle: return "#dsohandle"; case DefaultArgumentKind::File: return "#file"; + case DefaultArgumentKind::FilePath: return "#filePath"; case DefaultArgumentKind::Function: return "#function"; case DefaultArgumentKind::Inherited: return "inherited"; case DefaultArgumentKind::Line: return "#line"; @@ -316,6 +317,7 @@ static StringRef getMagicIdentifierLiteralExprKindString(MagicIdentifierLiteralExpr::Kind value) { switch (value) { case MagicIdentifierLiteralExpr::File: return "#file"; + case MagicIdentifierLiteralExpr::FilePath: return "#filePath"; case MagicIdentifierLiteralExpr::Function: return "#function"; case MagicIdentifierLiteralExpr::Line: return "#line"; case MagicIdentifierLiteralExpr::Column: return "#column"; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c6e634f724552..95c8bc0942d90 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6083,6 +6083,7 @@ bool ParamDecl::hasDefaultExpr() const { return false; case DefaultArgumentKind::Normal: case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Column: case DefaultArgumentKind::Function: @@ -6105,6 +6106,7 @@ bool ParamDecl::hasCallerSideDefaultExpr() const { case DefaultArgumentKind::Normal: return false; case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Column: case DefaultArgumentKind::Function: @@ -6414,6 +6416,7 @@ ParamDecl::getDefaultValueStringRepresentation( } case DefaultArgumentKind::Inherited: return "super"; case DefaultArgumentKind::File: return "#file"; + case DefaultArgumentKind::FilePath: return "#filePath"; case DefaultArgumentKind::Line: return "#line"; case DefaultArgumentKind::Column: return "#column"; case DefaultArgumentKind::Function: return "#function"; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index a07d30dc7cebb..945ae5e4586d2 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -238,6 +238,8 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_enable_astscope_lookup); inputArgs.AddLastArg(arguments, options::OPT_disable_astscope_lookup); inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup); + inputArgs.AddLastArg(arguments, + options::OPT_enable_experimental_concise_pound_file); // Pass on any build config options inputArgs.AddAllArgs(arguments, options::OPT_D); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 879f670e1fe50..7c3188f0dc168 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -453,6 +453,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.OptimizationRemarkMissedPattern = generateOptimizationRemarkRegex(Diags, Args, A); + Opts.EnableConcisePoundFile = + Args.hasArg(OPT_enable_experimental_concise_pound_file); + llvm::Triple Target = Opts.Target; StringRef TargetArg; if (const Arg *A = Args.getLastArg(OPT_target)) { diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 28dfe3598b4a3..355326f5b8a1c 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -2244,6 +2244,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return !includeDefaultArgs; case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Column: case DefaultArgumentKind::Function: @@ -3653,6 +3654,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionLiteralKind::StringLiteral, "String"); addFromProto("#file", CodeCompletionKeywordKind::pound_file, CodeCompletionLiteralKind::StringLiteral, "String"); + if (Ctx.LangOpts.EnableConcisePoundFile) { + addFromProto("#filePath", CodeCompletionKeywordKind::pound_file, + CodeCompletionLiteralKind::StringLiteral, "String"); + } addFromProto("#line", CodeCompletionKeywordKind::pound_line, CodeCompletionLiteralKind::IntegerLiteral, "Int"); addFromProto("#column", CodeCompletionKeywordKind::pound_column, diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d8ce5c140fc64..58592287d021b 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1014,6 +1014,8 @@ getMagicIdentifierLiteralKind(tok Kind) { case tok::kw___FILE__: case tok::pound_file: return MagicIdentifierLiteralExpr::Kind::File; + case tok::pound_filePath: + return MagicIdentifierLiteralExpr::Kind::FilePath; case tok::kw___FUNCTION__: case tok::pound_function: return MagicIdentifierLiteralExpr::Kind::Function; @@ -1446,6 +1448,15 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { .fixItReplace(Tok.getLoc(), replacement); LLVM_FALLTHROUGH; } + + case tok::pound_filePath: + // Check twice because of fallthrough--this is ugly but temporary. + if (Tok.is(tok::pound_filePath) && !Context.LangOpts.EnableConcisePoundFile) + diagnose(Tok.getLoc(), diag::unknown_pound_expr, "filePath"); + // Continue since we actually do know how to handle it. This avoids extra + // diagnostics. + LLVM_FALLTHROUGH; + case tok::pound_column: case tok::pound_file: case tok::pound_function: @@ -1455,6 +1466,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { switch (Tok.getKind()) { case tok::pound_column: SKind = SyntaxKind::PoundColumnExpr; break; case tok::pound_file: SKind = SyntaxKind::PoundFileExpr; break; + case tok::pound_filePath: SKind = SyntaxKind::PoundFilePathExpr; break; case tok::pound_function: SKind = SyntaxKind::PoundFunctionExpr; break; // FIXME: #line was renamed to #sourceLocation case tok::pound_line: SKind = SyntaxKind::PoundLineExpr; break; diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index d0cd5536ee1ce..89a70cf3d5082 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -48,6 +48,8 @@ static DefaultArgumentKind getDefaultArgKind(Expr *init) { return DefaultArgumentKind::Column; case MagicIdentifierLiteralExpr::File: return DefaultArgumentKind::File; + case MagicIdentifierLiteralExpr::FilePath: + return DefaultArgumentKind::FilePath; case MagicIdentifierLiteralExpr::Line: return DefaultArgumentKind::Line; case MagicIdentifierLiteralExpr::Function: diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index c61711255a40c..64a337a26d021 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1090,6 +1090,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, case DefaultArgumentKind::Inherited: case DefaultArgumentKind::Column: case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Function: case DefaultArgumentKind::DSOHandle: diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index ac8ba2912ecfa..44d001c10f1bf 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4876,6 +4876,27 @@ getMagicFunctionString(SILGenFunction &SGF) { return SGF.MagicFunctionString; } +static StringRef +getMagicFilePathString(SILGenFunction &SGF, SourceLoc loc) { + if (!loc.isValid()) + return ""; + + return SGF.getASTContext().SourceMgr.getDisplayNameForLoc(loc); +} + +static std::string +getConciseMagicFileString(SILGenFunction &SGF, SourceLoc loc) { + if (!loc.isValid()) + return ""; + + auto path = getMagicFilePathString(SGF, loc); + auto value = llvm::sys::path::filename(path).str(); + value += " ("; + value += SGF.getModule().getSwiftModule()->getNameStr(); + value += ")"; + return value; +} + /// Emit an application of the given allocating initializer. RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, ConcreteDeclRef init, @@ -5131,9 +5152,18 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { auto magicLiteral = cast(literal); switch (magicLiteral->getKind()) { case MagicIdentifierLiteralExpr::File: { - std::string value; - if (loc.isValid()) - value = ctx.SourceMgr.getDisplayNameForLoc(loc); + std::string value = getASTContext().LangOpts.EnableConcisePoundFile + ? getConciseMagicFileString(*this, loc) + : getMagicFilePathString(*this, loc).str(); + builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, + magicLiteral->getStringEncoding()); + builtinInit = magicLiteral->getBuiltinInitializer(); + init = magicLiteral->getInitializer(); + break; + } + + case MagicIdentifierLiteralExpr::FilePath: { + StringRef value = getMagicFilePathString(*this, loc); builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, magicLiteral->getStringEncoding()); builtinInit = magicLiteral->getBuiltinInitializer(); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 99cde2d3e0e6a..83711be6d8ae9 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3681,6 +3681,7 @@ RValue RValueEmitter:: visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C) { switch (E->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: case MagicIdentifierLiteralExpr::Line: case MagicIdentifierLiteralExpr::Column: diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f411bc6105fe2..2efc2f5d60dc0 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2157,6 +2157,7 @@ namespace { Expr *visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) { switch (expr->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: return handleStringLiteralExpr(expr); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 2ff2bee03e517..c7d45a660a49d 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1243,6 +1243,7 @@ namespace { switch (expr->getKind()) { case MagicIdentifierLiteralExpr::Column: case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: case MagicIdentifierLiteralExpr::Line: return visitLiteralExpr(expr); diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 363734188e12f..699f31c96e278 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -730,6 +730,11 @@ static Expr *synthesizeCallerSideDefault(const ParamDecl *param, MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::File, loc, /*implicit=*/true); + case DefaultArgumentKind::FilePath: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::FilePath, loc, + /*implicit=*/true); + case DefaultArgumentKind::Line: return new (ctx) MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::Line, loc, diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 61d70b4f17e2a..7695a180a8378 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1531,6 +1531,7 @@ static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) { case ExprKind::MagicIdentifierLiteral: switch (cast(LE)->getKind()) { case MagicIdentifierLiteralExpr::Kind::File: return "#file"; + case MagicIdentifierLiteralExpr::Kind::FilePath: return "#filePath"; case MagicIdentifierLiteralExpr::Kind::Line: return "#line"; case MagicIdentifierLiteralExpr::Kind::Column: return "#column"; case MagicIdentifierLiteralExpr::Kind::Function: return "#function"; diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 46fe5d847ef81..21ee5fca83e43 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -131,6 +131,7 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(ASTContext &Context, Expr *expr) { if (auto E = dyn_cast(expr)) { switch (E->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: return TypeChecker::getProtocol( Context, expr->getLoc(), diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e9272b23dc0eb..e6f211a22a2c7 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -207,6 +207,8 @@ getActualDefaultArgKind(uint8_t raw) { return swift::DefaultArgumentKind::Column; case serialization::DefaultArgumentKind::File: return swift::DefaultArgumentKind::File; + case serialization::DefaultArgumentKind::FilePath: + return swift::DefaultArgumentKind::FilePath; case serialization::DefaultArgumentKind::Line: return swift::DefaultArgumentKind::Line; case serialization::DefaultArgumentKind::Function: diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9176636a29cc6..27636695f690c 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 526; // @_dynamicReplacement adjustments +const uint16_t SWIFTMODULE_VERSION_MINOR = 527; // #filePath /// A standard hash seed used for all string hashes in a serialized module. /// @@ -437,6 +437,7 @@ enum class DefaultArgumentKind : uint8_t { None = 0, Normal, File, + FilePath, Line, Column, Function, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index c2d07ba6d5c08..837fbabc0d0ed 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1065,6 +1065,7 @@ static uint8_t getRawStableDefaultArgumentKind(swift::DefaultArgumentKind kind) CASE(Inherited) CASE(Column) CASE(File) + CASE(FilePath) CASE(Line) CASE(Function) CASE(DSOHandle) diff --git a/test/SILGen/magic_identifier_file.swift b/test/SILGen/magic_identifier_file.swift new file mode 100644 index 0000000000000..f2cfeecfc73d4 --- /dev/null +++ b/test/SILGen/magic_identifier_file.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-emit-silgen -module-name Foo %s | %FileCheck --check-prefixes=BOTH,ABSOLUTE %s +// RUN: %target-swift-emit-silgen -enable-experimental-concise-pound-file -DNEEDS_CONCISE -module-name Foo %s | %FileCheck --check-prefixes=BOTH,CONCISE %s + +// FIXME: Once this feature becomes non-experimental, we should update existing +// tests and delete this file. + +func directUse() { +// BOTH-LABEL: sil {{.*}} @$s3Foo9directUseyyF + print(#file) +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} + +func indirectUse() { +// BOTH-LABEL: sil {{.*}} @$s3Foo11indirectUseyyF + fatalError() +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} diff --git a/test/SILGen/magic_identifier_filepath.swift b/test/SILGen/magic_identifier_filepath.swift new file mode 100644 index 0000000000000..3a407b0da9f0a --- /dev/null +++ b/test/SILGen/magic_identifier_filepath.swift @@ -0,0 +1,26 @@ +// Check that we generate the right code with the flag. +// RUN: %target-swift-emit-silgen -enable-experimental-concise-pound-file -module-name Foo %s | %FileCheck %s + +// Check that we give errors for use of #filePath if concise #file isn't enabled. +// FIXME: Drop if we stop rejecting this. +// RUN: %target-typecheck-verify-swift -module-name Foo %s + +// FIXME: Once this feature becomes non-experimental, we should duplicate +// existing #file tests and delete this file. + +func directUse() { + print(#filePath) // expected-error {{use of unknown directive '#filePath'}} + +// CHECK-LABEL: sil {{.*}} @$s3Foo9directUseyyF +// CHECK: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_filepath.swift" +} + +func indirectUse() { + functionWithFilePathDefaultArgument() + +// CHECK-LABEL: sil {{.*}} @$s3Foo11indirectUseyyF +// CHECK: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_filepath.swift" +} + +func functionWithFilePathDefaultArgument(file: String = #filePath) {} +// expected-error@-1 {{use of unknown directive '#filePath'}} diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index 208194499cbe8..8f9835970008f 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -130,6 +130,12 @@ Child('PoundFile', kind='PoundFileToken'), ]), + # A #filePath expression. + Node('PoundFilePathExpr', kind='Expr', + children=[ + Child('PoundFilePath', kind='PoundFilePathToken'), + ]), + # A #function expression. Node('PoundFunctionExpr', kind='Expr', children=[ diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 748f089bd1d36..04df2153cd083 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -241,6 +241,7 @@ 'DifferentiationParam': 237, 'DifferentiableAttributeFuncSpecifier': 238, 'FunctionDeclName': 239, + 'PoundFilePathExpr': 240, } diff --git a/utils/gyb_syntax_support/Token.py b/utils/gyb_syntax_support/Token.py index 3e116471bcaa4..aaacc4fcfeb33 100644 --- a/utils/gyb_syntax_support/Token.py +++ b/utils/gyb_syntax_support/Token.py @@ -265,6 +265,8 @@ def macro_name(self): serialization_code=73), PoundKeyword('PoundFile', 'file', text='#file', serialization_code=68), + PoundKeyword('PoundFilePath', 'filePath', text='#filePath', + serialization_code=121), PoundKeyword('PoundColumn', 'column', text='#column', serialization_code=70), PoundKeyword('PoundFunction', 'function', text='#function',