From fc43f5457540c3dfa31317bc3cb460076f744f3e Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 8 Aug 2019 11:58:54 -0300 Subject: [PATCH 1/2] deprecate workflow-related keywords --- .../CommandCompletion/CompletionCompleters.cs | 2 +- .../engine/parser/Parser.cs | 127 +++++------------- .../engine/parser/SemanticChecks.cs | 36 ++--- .../engine/parser/ast.cs | 115 ++++++---------- .../engine/parser/token.cs | 27 ++-- .../engine/parser/tokenizer.cs | 4 +- .../remoting/commands/PSRemotingCmdlet.cs | 2 +- .../engine/runtime/ScriptBlockToPowerShell.cs | 14 +- .../resources/ParserStrings.resx | 12 +- .../Language/Parser/Parsing.Tests.ps1 | 13 ++ .../engine/Api/TypeInference.Tests.ps1 | 3 +- 11 files changed, 127 insertions(+), 228 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 2119e513259..b637a1a139f 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -207,7 +207,7 @@ List ExecuteGetCommandCommand(bool useModulePrefix) } private static readonly HashSet s_keywordsToExcludeFromAddingAmpersand - = new HashSet(StringComparer.OrdinalIgnoreCase) { TokenKind.InlineScript.ToString(), TokenKind.Configuration.ToString() }; + = new HashSet(StringComparer.OrdinalIgnoreCase) { TokenKind.Configuration.ToString() }; internal static CompletionResult GetCommandNameCompletionResult(string name, object command, bool addAmpersandIfNecessary, string quote) { string syntax = name, listItem = name; diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index b48637cd108..3e89866492f 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2013,7 +2013,6 @@ private StatementAst StatementRule() break; case TokenKind.Function: case TokenKind.Filter: - case TokenKind.Workflow: statement = FunctionDeclarationRule(token); break; case TokenKind.Return: @@ -2040,17 +2039,25 @@ private StatementAst StatementRule() case TokenKind.Data: statement = DataStatementRule(token); break; - case TokenKind.Parallel: - case TokenKind.Sequence: - statement = BlockStatementRule(token); - break; case TokenKind.Configuration: statement = ConfigurationStatementRule(attributes != null ? attributes.OfType() : null, token); break; + case TokenKind.Workflow: + case TokenKind.Parallel: + case TokenKind.Sequence: + case TokenKind.InlineScript: + ReportError( + token.Extent, + nameof(ParserStrings.DeprecatedKeywordNotAllowed), + ParserStrings.DeprecatedKeywordNotAllowed, + token.Kind.Text()); + statement = new ErrorStatementAst(token.Extent); + break; case TokenKind.From: case TokenKind.Define: case TokenKind.Var: - ReportError(token.Extent, + ReportError( + token.Extent, nameof(ParserStrings.ReservedKeywordNotAllowed), ParserStrings.ReservedKeywordNotAllowed, token.Kind.Text()); @@ -2100,7 +2107,8 @@ private StatementAst StatementRule() case TokenKind.Using: statement = UsingStatementRule(token); // Report an error - usings must appear before anything else in the script, but parse it anyway - ReportError(statement.Extent, + ReportError( + statement.Extent, nameof(ParserStrings.UsingMustBeAtStartOfScript), ParserStrings.UsingMustBeAtStartOfScript); break; @@ -2320,45 +2328,6 @@ private StatementAst BlockStatementRule(Token kindToken) return new BlockStatementAst(ExtentOf(kindToken, body), kindToken, body); } - /// - /// Handle the InlineScript syntax in the script workflow. - /// - /// - /// - /// - /// true -- InlineScript parsing successful - /// false -- InlineScript parsing unsuccessful - /// - private bool InlineScriptRule(Token inlineScriptToken, List elements) - { - // G Command - // G InlineScript scriptblock-expression - - Diagnostics.Assert(elements != null && elements.Count == 0, "The CommandElement list should be empty"); - var commandName = new StringConstantExpressionAst(inlineScriptToken.Extent, inlineScriptToken.Text, StringConstantType.BareWord); - inlineScriptToken.TokenFlags |= TokenFlags.CommandName; - elements.Add(commandName); - - SkipNewlines(); - Token lCurly = NextToken(); - - if (lCurly.Kind != TokenKind.LCurly) - { - // ErrorRecovery: If there is no opening curly, assume it hasn't been entered yet and don't consume anything. - - UngetToken(lCurly); - ReportIncompleteInput(After(inlineScriptToken), - nameof(ParserStrings.MissingStatementAfterKeyword), - ParserStrings.MissingStatementAfterKeyword, - inlineScriptToken.Text); - return false; - } - - var expr = ScriptBlockExpressionRule(lCurly); - elements.Add(expr); - return true; - } - private StatementAst IfStatementRule(Token ifToken) { // G if-statement: @@ -5222,7 +5191,7 @@ private StatementAst MethodDeclarationRule(Token functionNameToken, string class SetTokenizerMode(TokenizerMode.Command); ScriptBlockAst scriptBlock = ScriptBlockRule(lCurly, false, baseCtorCallStatement); var result = new FunctionDefinitionAst(ExtentOf(functionNameToken, scriptBlock), - /*isFilter:*/false, /*isWorkflow:*/false, functionNameToken, parameters, scriptBlock); + /*isFilter:*/false, functionNameToken, parameters, scriptBlock); return result; } @@ -5237,7 +5206,6 @@ private StatementAst FunctionDeclarationRule(Token functionToken) // G function-statement: // G 'function' new-lines:opt function-name function-parameter-declaration:opt '{' script-block '}' // G 'filter' new-lines:opt function-name function-parameter-declaration:opt '{' script-block '}' - // G 'workflow' new-lines:opt function-name function-parameter-declaration:opt '{' script-block '}' // G // G function-name: // G command-argument @@ -5309,26 +5277,12 @@ private StatementAst FunctionDeclarationRule(Token functionToken) } bool isFilter = functionToken.Kind == TokenKind.Filter; - bool isWorkflow = functionToken.Kind == TokenKind.Workflow; - - bool oldTokenizerWorkflowContext = _tokenizer.InWorkflowContext; - try - { - _tokenizer.InWorkflowContext = isWorkflow; - ScriptBlockAst scriptBlock = ScriptBlockRule(lCurly, isFilter); - var functionName = (functionNameToken.Kind == TokenKind.Generic) - ? ((StringToken)functionNameToken).Value - : functionNameToken.Text; + ScriptBlockAst scriptBlock = ScriptBlockRule(lCurly, isFilter); - FunctionDefinitionAst result = new FunctionDefinitionAst(ExtentOf(functionToken, scriptBlock), - isFilter, isWorkflow, functionNameToken, parameters, scriptBlock); - return result; - } - finally - { - _tokenizer.InWorkflowContext = oldTokenizerWorkflowContext; - } + FunctionDefinitionAst result = new FunctionDefinitionAst(ExtentOf(functionToken, scriptBlock), + isFilter, functionNameToken, parameters, scriptBlock); + return result; } private List FunctionParameterDeclarationRule(out IScriptExtent endErrorStatement, out Token rParen) @@ -6344,42 +6298,31 @@ internal Ast CommandRule(bool forDynamicKeyword) break; default: - if (token.Kind == TokenKind.InlineScript && context == CommandArgumentContext.CommandName) - { - scanning = InlineScriptRule(token, elements); - Diagnostics.Assert(elements.Count >= 1, "We should at least have the command name: inlinescript"); - endExtent = elements.Last().Extent; + var ast = GetCommandArgument(context, token); - if (!scanning) { continue; } - } - else + // If this is the special verbatim argument syntax, look for the next element + StringToken argumentToken = token as StringToken; + if ((argumentToken != null) && string.Equals(argumentToken.Value, VERBATIM_ARGUMENT, StringComparison.OrdinalIgnoreCase)) { - var ast = GetCommandArgument(context, token); + elements.Add(ast); + endExtent = ast.Extent; - // If this is the special verbatim argument syntax, look for the next element - StringToken argumentToken = token as StringToken; - if ((argumentToken != null) && string.Equals(argumentToken.Value, VERBATIM_ARGUMENT, StringComparison.OrdinalIgnoreCase)) + var verbatimToken = GetVerbatimCommandArgumentToken(); + if (verbatimToken != null) { + foundVerbatimArgument = true; + scanning = false; + ast = new StringConstantExpressionAst(verbatimToken.Extent, verbatimToken.Value, StringConstantType.BareWord); elements.Add(ast); endExtent = ast.Extent; - - var verbatimToken = GetVerbatimCommandArgumentToken(); - if (verbatimToken != null) - { - foundVerbatimArgument = true; - scanning = false; - ast = new StringConstantExpressionAst(verbatimToken.Extent, verbatimToken.Value, StringConstantType.BareWord); - elements.Add(ast); - endExtent = ast.Extent; - } - - break; } - endExtent = ast.Extent; - elements.Add(ast); + break; } + endExtent = ast.Extent; + elements.Add(ast); + break; } diff --git a/src/System.Management.Automation/engine/parser/SemanticChecks.cs b/src/System.Management.Automation/engine/parser/SemanticChecks.cs index 54c211e6043..149591c3869 100644 --- a/src/System.Management.Automation/engine/parser/SemanticChecks.cs +++ b/src/System.Management.Automation/engine/parser/SemanticChecks.cs @@ -443,13 +443,6 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun CheckForDuplicateParameters(functionDefinitionAst.Parameters); } - if (functionDefinitionAst.IsWorkflow) - { - _parser.ReportError(functionDefinitionAst.Extent, - nameof(ParserStrings.WorkflowNotSupportedInPowerShellCore), - ParserStrings.WorkflowNotSupportedInPowerShellCore); - } - return AstVisitAction.Continue; } @@ -458,13 +451,9 @@ public override AstVisitAction VisitSwitchStatement(SwitchStatementAst switchSta // Parallel flag not allowed if ((switchStatementAst.Flags & SwitchFlags.Parallel) == SwitchFlags.Parallel) { - bool reportError = !switchStatementAst.IsInWorkflow(); - if (reportError) - { - _parser.ReportError(switchStatementAst.Extent, - nameof(ParserStrings.ParallelNotSupported), - ParserStrings.ParallelNotSupported); - } + _parser.ReportError(switchStatementAst.Extent, + nameof(ParserStrings.ParallelNotSupported), + ParserStrings.ParallelNotSupported); } return AstVisitAction.Continue; @@ -494,13 +483,9 @@ public override AstVisitAction VisitForEachStatement(ForEachStatementAst forEach // Parallel flag not allowed if ((forEachStatementAst.Flags & ForEachFlags.Parallel) == ForEachFlags.Parallel) { - bool reportError = !forEachStatementAst.IsInWorkflow(); - if (reportError) - { - _parser.ReportError(forEachStatementAst.Extent, - nameof(ParserStrings.ParallelNotSupported), - ParserStrings.ParallelNotSupported); - } + _parser.ReportError(forEachStatementAst.Extent, + nameof(ParserStrings.ParallelNotSupported), + ParserStrings.ParallelNotSupported); } // Throttle limit must be combined with Parallel flag @@ -1134,11 +1119,6 @@ public override AstVisitAction VisitAttributedExpression(AttributedExpressionAst public override AstVisitAction VisitBlockStatement(BlockStatementAst blockStatementAst) { - if (blockStatementAst.IsInWorkflow()) - { - return AstVisitAction.Continue; - } - _parser.ReportError(blockStatementAst.Kind.Extent, nameof(ParserStrings.UnexpectedKeyword), ParserStrings.UnexpectedKeyword, @@ -2354,8 +2334,8 @@ public override AstVisitAction VisitBlockStatement(BlockStatementAst blockStatem { // Keyword blocks are not allowed ReportError(blockStatementAst, - nameof(ParserStrings.ParallelAndSequenceBlockNotSupportedInDataSection), - ParserStrings.ParallelAndSequenceBlockNotSupportedInDataSection); + nameof(ParserStrings.BlockStatementNotSupportedInDataSection), + ParserStrings.BlockStatementNotSupportedInDataSection); return AstVisitAction.Continue; } diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs index 434b5a6b5e6..db2e95120a7 100644 --- a/src/System.Management.Automation/engine/parser/ast.cs +++ b/src/System.Management.Automation/engine/parser/ast.cs @@ -288,42 +288,6 @@ internal void ClearParent() internal static PSTypeName[] EmptyPSTypeNameArray = Array.Empty(); - internal bool IsInWorkflow() - { - // Scan up the AST's parents, looking for a script block that is either - // a workflow, or has a job definition attribute. - // Stop scanning when we encounter a FunctionDefinitionAst - Ast current = this; - bool stopScanning = false; - - while (current != null && !stopScanning) - { - ScriptBlockAst scriptBlock = current as ScriptBlockAst; - if (scriptBlock != null) - { - // See if this uses the workflow keyword - FunctionDefinitionAst functionDefinition = scriptBlock.Parent as FunctionDefinitionAst; - if ((functionDefinition != null)) - { - stopScanning = true; - if (functionDefinition.IsWorkflow) { return true; } - } - } - - CommandAst commandAst = current as CommandAst; - if (commandAst != null && - string.Equals(TokenKind.InlineScript.Text(), commandAst.GetCommandName(), StringComparison.OrdinalIgnoreCase) && - this != commandAst) - { - return false; - } - - current = current.Parent; - } - - return false; - } - internal bool HasSuspiciousContent { get; set; } #region Search Ancestor Ast @@ -894,7 +858,7 @@ public ScriptBlockAst(IScriptExtent extent, /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// /// If or is null. /// @@ -913,7 +877,7 @@ public ScriptBlockAst(IScriptExtent extent, List usingStateme /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// /// If or is null. /// @@ -932,7 +896,7 @@ public ScriptBlockAst(IScriptExtent extent, ParamBlockAst paramBlock, StatementB /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// True if the script block is a configuration. /// /// If or is null. @@ -953,7 +917,7 @@ public ScriptBlockAst(IScriptExtent extent, ParamBlockAst paramBlock, StatementB /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// True if the script block is a configuration. /// /// If or is null. @@ -974,7 +938,7 @@ public ScriptBlockAst(IScriptExtent extent, IEnumerable using /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// True if the script block is a configuration. /// /// If or is null. @@ -996,7 +960,7 @@ public ScriptBlockAst(IScriptExtent extent, IEnumerable attributes /// The statements that go in the end block if is false, or the /// process block if is true. /// - /// True if the script block is a filter, false if it is a function or workflow. + /// True if the script block is a filter, false if it is a function. /// True if the script block is a configuration. /// /// If or is null. @@ -3556,13 +3520,12 @@ public Tuple GetWithInputHandlingForInvokeCommandWithUsingExpres public class FunctionDefinitionAst : StatementAst, IParameterMetadataProvider { /// - /// Construct a function definition. + /// Initializes a new instance of the class. /// /// /// The extent of the function definition, starting with the function or filter keyword, ending at the closing curly. /// /// True if the filter keyword was used. - /// True if the workflow keyword was used. /// The name of the function. /// /// The parameters specified after the function name. This does not include parameters specified with a param statement. @@ -3572,12 +3535,15 @@ public class FunctionDefinitionAst : StatementAst, IParameterMetadataProvider /// If , , or is null, or /// if is an empty string. /// - public FunctionDefinitionAst(IScriptExtent extent, - bool isFilter, - bool isWorkflow, - string name, - IEnumerable parameters, - ScriptBlockAst body) + /// + /// This class represents a function definition in PowerShell. + /// + public FunctionDefinitionAst( + IScriptExtent extent, + bool isFilter, + string name, + IEnumerable parameters, + ScriptBlockAst body) : base(extent) { if (string.IsNullOrEmpty(name)) @@ -3590,13 +3556,7 @@ public FunctionDefinitionAst(IScriptExtent extent, throw PSTraceSource.NewArgumentNullException("body"); } - if (isFilter && isWorkflow) - { - throw PSTraceSource.NewArgumentException("isFilter"); - } - this.IsFilter = isFilter; - this.IsWorkflow = isWorkflow; this.Name = name; if (parameters != null && parameters.Any()) @@ -3609,18 +3569,18 @@ public FunctionDefinitionAst(IScriptExtent extent, SetParent(body); } - internal FunctionDefinitionAst(IScriptExtent extent, - bool isFilter, - bool isWorkflow, - Token functionNameToken, - IEnumerable parameters, - ScriptBlockAst body) - : this(extent, - isFilter, - isWorkflow, - (functionNameToken.Kind == TokenKind.Generic) ? ((StringToken)functionNameToken).Value : functionNameToken.Text, - parameters, - body) + internal FunctionDefinitionAst( + IScriptExtent extent, + bool isFilter, + Token functionNameToken, + IEnumerable parameters, + ScriptBlockAst body) + : this( + extent, + isFilter, + (functionNameToken.Kind == TokenKind.Generic) ? ((StringToken)functionNameToken).Value : functionNameToken.Text, + parameters, + body) { NameExtent = functionNameToken.Extent; } @@ -3631,8 +3591,13 @@ internal FunctionDefinitionAst(IScriptExtent extent, public bool IsFilter { get; private set; } /// - /// If true, the workflow keyword was used. + /// Gets a value indicating whether or not the function is actually a workflow. /// + /// + /// This property has been deprecated. It can be removed once PowerShellGet has + /// been updated to check the PowerShell version before looking at the IsWorkflow + /// property. + /// public bool IsWorkflow { get; private set; } /// @@ -3707,7 +3672,7 @@ public override Ast Copy() var newParameters = CopyElements(this.Parameters); var newBody = CopyElement(this.Body); - return new FunctionDefinitionAst(this.Extent, this.IsFilter, this.IsWorkflow, this.Name, newParameters, newBody) { NameExtent = this.NameExtent }; + return new FunctionDefinitionAst(this.Extent, this.IsFilter, this.Name, newParameters, newBody) { NameExtent = this.NameExtent }; } internal string GetParamTextFromParameterList(Tuple, string> usingVariablesTuple = null) @@ -7304,8 +7269,8 @@ internal override AstVisitAction InternalVisit(AstVisitor visitor) } /// - /// The ast that represents a scriptblock with a keyword name. This is normally allowed only for script workflow. - /// e.g. parallel { ... } or sequence { ... }. + /// The ast that represents a scriptblock with a keyword name. + /// e.g. The parallel and sequence block statements that were deprecated along with workflow in PowerShell 7. /// public class BlockStatementAst : StatementAst { @@ -7323,7 +7288,7 @@ public BlockStatementAst(IScriptExtent extent, Token kind, StatementBlockAst bod throw PSTraceSource.NewArgumentNullException(kind == null ? "kind" : "body"); } - if (kind.Kind != TokenKind.Sequence && kind.Kind != TokenKind.Parallel) + if (!tokenKindsThatSupportBlockStatements.Contains(kind.Kind)) { throw PSTraceSource.NewArgumentException("kind"); } @@ -7333,6 +7298,10 @@ public BlockStatementAst(IScriptExtent extent, Token kind, StatementBlockAst bod SetParent(body); } + // This should remain empty until block statements are needed in PowerShell. The only tokens that supported + // them in the past were deprecated along with workflow in PowerShell 7. + private static SortedSet tokenKindsThatSupportBlockStatements = new SortedSet(); + /// /// The scriptblockexpression that has a keyword applied to it. This property is nerver null. /// diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs index def6b8d0dc1..0351167af1c 100644 --- a/src/System.Management.Automation/engine/parser/token.cs +++ b/src/System.Management.Automation/engine/parser/token.cs @@ -516,16 +516,16 @@ public enum TokenKind /// The 'while' keyword. While = 150, - /// The 'workflow' keyword. + /// The 'workflow' keyword. This keyword was part of workflow functionality that was deprecated in PowerShell 7. Workflow = 151, - /// The 'parallel' keyword. + /// The 'parallel' keyword. This keyword was part of workflow functionality that was deprecated in PowerShell 7. Parallel = 152, - /// The 'sequence' keyword. + /// The 'sequence' keyword. This keyword was part of workflow functionality that was deprecated in PowerShell 7. Sequence = 153, - /// The 'InlineScript' keyword + /// The 'InlineScript' keyword. This keyword was part of workflow functionality that was deprecated in PowerShell 7. InlineScript = 154, /// The "configuration" keyword @@ -720,6 +720,11 @@ public enum TokenFlags /// The token is a statement but does not support attributes. /// StatementDoesntSupportAttributes = 0x01000000, + + /// + /// The token has been deprecated. + /// + Deprecated = 0x10000000, } /// @@ -903,10 +908,10 @@ public static class TokenTraits /* Using */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, /* Var */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, /* While */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, - /* Workflow */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, - /* Parallel */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, - /* Sequence */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, - /* InlineScript */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes, + /* Workflow */ TokenFlags.Keyword | TokenFlags.Deprecated, + /* Parallel */ TokenFlags.Keyword | TokenFlags.Deprecated, + /* Sequence */ TokenFlags.Keyword | TokenFlags.Deprecated, + /* InlineScript */ TokenFlags.Keyword | TokenFlags.Deprecated, /* Configuration */ TokenFlags.Keyword, /* */ TokenFlags.Keyword, /* Public */ TokenFlags.Keyword, @@ -1133,10 +1138,10 @@ static TokenTraits() // Some random assertions to make sure the enum and the traits are in sync Diagnostics.Assert(GetTraits(TokenKind.Begin) == (TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName), "Table out of sync with enum - flags Begin"); - Diagnostics.Assert(GetTraits(TokenKind.Workflow) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes), + Diagnostics.Assert(GetTraits(TokenKind.Workflow) == (TokenFlags.Keyword | TokenFlags.Deprecated), "Table out of sync with enum - flags Workflow"); - Diagnostics.Assert(GetTraits(TokenKind.Sequence) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes), - "Table out of sync with enum - flags Sequence"); + Diagnostics.Assert(GetTraits(TokenKind.Configuration) == TokenFlags.Keyword, + "Table out of sync with enum - flags Configuration"); Diagnostics.Assert(GetTraits(TokenKind.Shr) == (TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold), "Table out of sync with enum - flags Shr"); Diagnostics.Assert(s_tokenText[(int)TokenKind.Shr].Equals("-shr", StringComparison.OrdinalIgnoreCase), diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs index 328417a8eae..6f839bd189e 100644 --- a/src/System.Management.Automation/engine/parser/tokenizer.cs +++ b/src/System.Management.Automation/engine/parser/tokenizer.cs @@ -722,7 +722,6 @@ internal Tokenizer(Parser parser) internal TokenizerMode Mode { get; set; } internal bool AllowSignedNumbers { get; set; } internal bool WantSimpleName { get; set; } - internal bool InWorkflowContext { get; set; } internal List TokenList { get; set; } internal Token FirstToken { get; private set; } @@ -4392,8 +4391,7 @@ private Token ScanIdentifier(char firstChar) sb = null; if (s_keywordTable.TryGetValue(ident, out tokenKind)) { - if (tokenKind != TokenKind.InlineScript || InWorkflowContext) - return NewToken(tokenKind); + return NewToken(tokenKind); } if (DynamicKeyword.ContainsKeyword(ident) && !DynamicKeyword.IsHiddenKeyword(ident)) diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index b12680865f4..9009f3d59f5 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -2411,7 +2411,7 @@ private List GetUsingVariables(ScriptBlock localScriptBlo throw new ArgumentNullException("localScriptBlock", "Caller needs to make sure the parameter value is not null"); } - var allUsingExprs = UsingExpressionAstSearcher.FindAllUsingExpressionExceptForWorkflow(localScriptBlock.Ast); + var allUsingExprs = UsingExpressionAstSearcher.FindAllUsingExpression(localScriptBlock.Ast); return allUsingExprs.Select(usingExpr => UsingExpressionAst.ExtractUsingVariable((UsingExpressionAst)usingExpr)).ToList(); } diff --git a/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs b/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs index 3a26e8eec91..79d0c20117d 100644 --- a/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs +++ b/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs @@ -173,7 +173,7 @@ internal static void ThrowError(ScriptBlockToPowerShellNotSupportedException ex, internal class UsingExpressionAstSearcher : AstSearcher { - internal static IEnumerable FindAllUsingExpressionExceptForWorkflow(Ast ast) + internal static IEnumerable FindAllUsingExpression(Ast ast) { Diagnostics.Assert(ast != null, "caller to verify arguments"); @@ -189,12 +189,6 @@ private UsingExpressionAstSearcher(Func callback, bool stopOnFirst, b public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst ast) { - // Skip the workflow. We are not interested in the UsingExpressions in a workflow - if (ast.IsWorkflow) - { - return AstVisitAction.SkipChildren; - } - return CheckScriptBlock(ast); } } @@ -346,7 +340,7 @@ private static Tuple, object[]> GetUsingValues(Ast bo { Diagnostics.Assert(context != null || variables != null, "can't retrieve variables with no context and no variables"); - var usingAsts = UsingExpressionAstSearcher.FindAllUsingExpressionExceptForWorkflow(body).ToList(); + var usingAsts = UsingExpressionAstSearcher.FindAllUsingExpression(body).ToList(); var usingValueArray = new object[usingAsts.Count]; var usingValueMap = new Dictionary(usingAsts.Count); HashSet usingVarNames = (variables != null && filterNonUsingVariables) ? new HashSet() : null; @@ -458,10 +452,6 @@ private static Tuple, object[]> GetUsingValues(Ast bo /// /// Check if the given UsingExpression is in a different scope from the previous UsingExpression that we analyzed. /// - /// - /// Note that the value of is retrieved by calling 'UsingExpressionAstSearcher.FindAllUsingExpressionExceptForWorkflow'. - /// So is guaranteed not inside a workflow. - /// /// The UsingExpression to analyze. /// The top level Ast, should be either ScriptBlockAst or FunctionDefinitionAst. /// The ScriptBlockAst that represents the scope of the previously analyzed UsingExpressions. diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index adaa86dbcb7..4da710fe646 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -469,6 +469,9 @@ The correct form is: foreach ($a in $b) {...} The '{0}' keyword is not supported in this version of the language. + + The '{0}' keyword has been deprecated and is no longer supported. + Missing expression after '{0}' in loop. @@ -870,7 +873,7 @@ The correct form is: foreach ($a in $b) {...} Expression is not allowed in a Using expression. - A Using variable cannot be retrieved. A Using variable can be used only with Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is used with Invoke-Command, the Using variable is valid only if the script block is invoked on a remote computer. + A Using variable cannot be retrieved. A Using variable can be used only with Invoke-Command or Start-Job. When it is used with Invoke-Command, the Using variable is valid only if the script block is invoked on a remote computer. Variable reference is not valid. The variable name is missing. @@ -911,8 +914,8 @@ The correct form is: foreach ($a in $b) {...} Missing statement body after keyword '{0}'. - - Parallel and sequence blocks are not allowed in restricted language mode or a Data section. + + Block statements are not allowed in restricted language mode or a Data section. Unexpected keyword '{0}'. @@ -1202,9 +1205,6 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Exception occurred when post-parsing dynamic keyword '{0}' with details '{1}'. - - Workflow is not supported in PowerShell 6+. - Meta Configuration resource {0} is not allowed in the regular configuration. Use meta configuration resources in a configuration with [DscLocalConfigurationManager()] attribute. diff --git a/test/powershell/Language/Parser/Parsing.Tests.ps1 b/test/powershell/Language/Parser/Parsing.Tests.ps1 index afb15081124..96b899f0fd4 100644 --- a/test/powershell/Language/Parser/Parsing.Tests.ps1 +++ b/test/powershell/Language/Parser/Parsing.Tests.ps1 @@ -2,6 +2,19 @@ # Licensed under the MIT License. set-strictmode -v 2 +Describe 'reserved keyword parsing' -Tags 'CI' { + ShouldBeParseError 'from' ReservedKeywordNotAllowed 1 -CheckColumnNumber + ShouldBeParseError 'define' ReservedKeywordNotAllowed 1 -CheckColumnNumber + ShouldBeParseError 'var' ReservedKeywordNotAllowed 1 -CheckColumnNumber +} + +Describe 'deprecated keyword parsing' -Tags 'CI' { + ShouldBeParseError 'workflow' DeprecatedKeywordNotAllowed 1 -CheckColumnNumber + ShouldBeParseError 'parallel' DeprecatedKeywordNotAllowed 1 -CheckColumnNumber + ShouldBeParseError 'sequence' DeprecatedKeywordNotAllowed 1 -CheckColumnNumber + ShouldBeParseError 'inlinescript' DeprecatedKeywordNotAllowed 1 -CheckColumnNumber +} + Describe 'for statement parsing' -Tags "CI" { ShouldBeParseError 'for' MissingOpenParenthesisAfterKeyword 4 -CheckColumnNumber ShouldBeParseError 'for(' MissingEndParenthesisAfterStatement 5 -CheckColumnNumber diff --git a/test/powershell/engine/Api/TypeInference.Tests.ps1 b/test/powershell/engine/Api/TypeInference.Tests.ps1 index c31cff4de33..48819d4957d 100644 --- a/test/powershell/engine/Api/TypeInference.Tests.ps1 +++ b/test/powershell/engine/Api/TypeInference.Tests.ps1 @@ -591,7 +591,8 @@ Describe "Type inference Tests" -tags "CI" { $res.Name | Should -Be 'System.Int32' } - It "Infers type from block statement" { + # This test should be skipped until block statements are brought back into PowerShell + It 'Infers type from block statement' -Skip:$true { $errors = $null $tokens = $null $ast = [Language.Parser]::ParseInput("parallel {1}", [ref] $tokens, [ref] $errors) From 0edfc912c190e96db51d756a5809f7ca8355c49f Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 8 Aug 2019 12:07:57 -0300 Subject: [PATCH 2/2] update IsWorkflow flag --- src/System.Management.Automation/engine/parser/ast.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs index db2e95120a7..50e6ca6eec6 100644 --- a/src/System.Management.Automation/engine/parser/ast.cs +++ b/src/System.Management.Automation/engine/parser/ast.cs @@ -3598,7 +3598,7 @@ internal FunctionDefinitionAst( /// been updated to check the PowerShell version before looking at the IsWorkflow /// property. /// - public bool IsWorkflow { get; private set; } + public bool IsWorkflow { get { return false; } } /// /// The name of the function or filter. This property is never null or empty.