From fee57e1cdccff39514044366d4b5f1d5a1c264ee Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 10 Dec 2019 15:41:43 -0800 Subject: [PATCH] Make approved features non-experimental (#11303) # Conflicts: # src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs --- .../commands/utility/Get-Error.cs | 1 - .../host/msh/UpdatesNotification.cs | 2 +- .../ExperimentalFeature.cs | 23 ----- .../engine/InitialSessionState.cs | 4 +- .../engine/InternalCommands.cs | 4 - .../engine/Modules/AnalysisCache.cs | 14 --- .../engine/Modules/ImportModuleCommand.cs | 15 ++-- .../engine/Modules/ModuleCmdletBase.cs | 64 +++----------- .../engine/parser/Compiler.cs | 4 +- .../engine/parser/Parser.cs | 84 +----------------- .../engine/parser/tokenizer.cs | 21 ++--- .../remoting/commands/newrunspacecommand.cs | 3 +- .../Operators/NullConditional.Tests.ps1 | 33 ++----- .../Operators/PipelineChainOperator.Tests.ps1 | 15 ---- .../Operators/TernaryOperator.Tests.ps1 | 80 ++++++++--------- .../Language/Parser/Parsing.Tests.ps1 | 86 +++++-------------- .../ConstrainedLanguageRestriction.Tests.ps1 | 17 ---- .../Foreach-Object-Parallel.Tests.ps1 | 69 +-------------- .../Get-Error.Tests.ps1 | 15 ---- .../engine/Formatting/ErrorView.Tests.ps1 | 6 +- test/tools/TestMetadata.json | 4 +- 21 files changed, 107 insertions(+), 457 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Get-Error.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Get-Error.cs index b85554bea4..2b0d9083ed 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Get-Error.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Get-Error.cs @@ -11,7 +11,6 @@ namespace Microsoft.PowerShell.Commands /// /// Class for Get-Error implementation. /// - [Experimental("Microsoft.PowerShell.Utility.PSGetError", ExperimentAction.Show)] [Cmdlet(VerbsCommon.Get, "Error", HelpUri = "https://docs.microsoft.com/powershell/module/microsoft.powershell.utility/get-error?view=powershell-7&WT.mc_id=ps-gethelp", DefaultParameterSetName = NewestParameterSetName)] diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs index 9f00f1b464..ff7b952ed3 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs @@ -57,7 +57,7 @@ namespace Microsoft.PowerShell static UpdatesNotification() { s_notificationType = GetNotificationType(); - CanNotifyUpdates = s_notificationType != NotificationType.Off && ExperimentalFeature.IsEnabled("PSUpdatesNotification"); + CanNotifyUpdates = s_notificationType != NotificationType.Off; if (CanNotifyUpdates) { diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index a260956bcb..1cc27d6212 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -109,24 +109,6 @@ namespace System.Management.Automation new ExperimentalFeature( name: "PSCommandNotFoundSuggestion", description: "Recommend potential commands based on fuzzy search on a CommandNotFoundException"), - new ExperimentalFeature( - name: "PSForEachObjectParallel", - description: "New parameter set for ForEach-Object to run script blocks in parallel"), - new ExperimentalFeature( - name: "PSTernaryOperator", - description: "Support the ternary operator in PowerShell language"), - new ExperimentalFeature( - name: "PSErrorView", - description: "New formatting for ErrorRecord"), - new ExperimentalFeature( - name: "PSUpdatesNotification", - description: "Print notification message when new releases are available"), - new ExperimentalFeature( - name: "PSCoalescingOperators", - description: "Support the null coalescing operator and null coalescing assignment operator in PowerShell language"), - new ExperimentalFeature( - name: "PSPipelineChainOperators", - description: "Allow use of && and || as operators between pipeline invocations"), #if UNIX new ExperimentalFeature( name: "PSUnixFileStat", @@ -135,11 +117,6 @@ namespace System.Management.Automation new ExperimentalFeature( name: "PSNullConditionalOperators", description: "Support the null conditional member access operators in PowerShell language"), -#if !UNIX - new ExperimentalFeature( - name: "PSWindowsPowerShellCompatibility", - description: "Load non-PSCore-compartible modules into Windows PowerShell over PS Remoting") -#endif }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index d0f402cefc..06ebd01d3a 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4426,7 +4426,7 @@ end { internal const ActionPreference DefaultWarningPreference = ActionPreference.Continue; internal const ActionPreference DefaultInformationPreference = ActionPreference.SilentlyContinue; - internal const ErrorView DefaultErrorView = ErrorView.NormalView; + internal const ErrorView DefaultErrorView = ErrorView.ConciseView; internal const bool DefaultWhatIfPreference = false; internal const ConfirmImpact DefaultConfirmPreference = ConfirmImpact.High; @@ -4501,7 +4501,7 @@ end { new ArgumentTypeConverterAttribute(typeof(ActionPreference))), new SessionStateVariableEntry( SpecialVariables.ErrorView, - ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.ConciseView : DefaultErrorView, + DefaultErrorView, RunspaceInit.ErrorViewDescription, ScopedItemOptions.None, new ArgumentTypeConverterAttribute(typeof(ErrorView))), diff --git a/src/System.Management.Automation/engine/InternalCommands.cs b/src/System.Management.Automation/engine/InternalCommands.cs index 770b7e2131..406a9b6176 100644 --- a/src/System.Management.Automation/engine/InternalCommands.cs +++ b/src/System.Management.Automation/engine/InternalCommands.cs @@ -234,7 +234,6 @@ namespace Microsoft.PowerShell.Commands /// /// Gets or sets a script block to run in parallel for each pipeline object. /// - [Experimental("PSForEachObjectParallel", ExperimentAction.Show)] [Parameter(Mandatory = true, ParameterSetName = ForEachObjectCommand.ParallelParameterSet)] public ScriptBlock Parallel { get; set; } @@ -242,7 +241,6 @@ namespace Microsoft.PowerShell.Commands /// Gets or sets the maximum number of concurrently running scriptblocks on separate threads. /// The default number is 5. /// - [Experimental("PSForEachObjectParallel", ExperimentAction.Show)] [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)] [ValidateRange(1, Int32.MaxValue)] public int ThrottleLimit { get; set; } = 5; @@ -251,7 +249,6 @@ namespace Microsoft.PowerShell.Commands /// Gets or sets a timeout time in seconds, after which the parallel running scripts will be stopped /// The default value is 0, indicating no timeout. /// - [Experimental("PSForEachObjectParallel", ExperimentAction.Show)] [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)] [ValidateRange(0, (Int32.MaxValue / 1000))] public int TimeoutSeconds { get; set; } @@ -260,7 +257,6 @@ namespace Microsoft.PowerShell.Commands /// Gets or sets a flag that returns a job object immediately for the parallel operation, instead of returning after /// all foreach processing is completed. /// - [Experimental("PSForEachObjectParallel", ExperimentAction.Show)] [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)] public SwitchParameter AsJob { get; set; } diff --git a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs index 6a88107e7a..5c996e67b6 100644 --- a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs +++ b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs @@ -103,12 +103,6 @@ namespace System.Management.Automation var moduleManifestProperties = PsUtils.GetModuleManifestProperties(modulePath, PsUtils.FastModuleManifestAnalysisPropertyNames); if (moduleManifestProperties != null) { - if (!ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && ModuleIsEditionIncompatible(modulePath, moduleManifestProperties)) - { - ModuleIntrinsics.Tracer.WriteLine($"Module lies on the Windows System32 legacy module path and is incompatible with current PowerShell edition, skipping module: {modulePath}"); - return null; - } - Version version; if (ModuleUtils.IsModuleInVersionSubdirectory(modulePath, out version)) { @@ -491,14 +485,6 @@ namespace System.Management.Automation { ModuleIntrinsics.Tracer.WriteLine("Requested caching for {0}", module.Name); - // Don't cache incompatible modules on the system32 module path even if loaded with - // -SkipEditionCheck, since it will break subsequent sessions. - if (!ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && !module.IsConsideredEditionCompatible) - { - ModuleIntrinsics.Tracer.WriteLine($"Module '{module.Name}' not edition compatible and not cached."); - return; - } - DateTime lastWriteTime; ModuleCacheEntry moduleCacheEntry; GetModuleEntryFromCache(module.Path, out lastWriteTime, out moduleCacheEntry); diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index 12db5ca33f..c55bbfba13 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -53,7 +53,6 @@ namespace Microsoft.PowerShell.Commands private const string ParameterSet_FQName_ViaPsrpSession = "FullyQualifiedNameAndPSSession"; private const string ParameterSet_ViaWinCompat = "WinCompat"; private const string ParameterSet_FQName_ViaWinCompat = "FullyQualifiedNameAndWinCompat"; - /// /// This parameter specifies whether to import to the current session state @@ -85,7 +84,7 @@ namespace Microsoft.PowerShell.Commands [Parameter(ParameterSetName = ParameterSet_Name, Mandatory = true, ValueFromPipeline = true, Position = 0)] [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)] [Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = true, ValueFromPipeline = true, Position = 0)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)] + [Parameter(ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)] [ValidateTrustedData] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] public string[] Name { set; get; } = Array.Empty(); @@ -95,7 +94,7 @@ namespace Microsoft.PowerShell.Commands /// [Parameter(ParameterSetName = ParameterSet_FQName, Mandatory = true, ValueFromPipeline = true, Position = 0)] [Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)] + [Parameter(ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)] [ValidateTrustedData] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] public ModuleSpecification[] FullyQualifiedName { get; set; } @@ -275,7 +274,7 @@ namespace Microsoft.PowerShell.Commands [Parameter(ParameterSetName = ParameterSet_Name)] [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)] [Parameter(ParameterSetName = ParameterSet_ViaCimSession)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)] + [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)] [Alias("Version")] public Version MinimumVersion { @@ -290,7 +289,7 @@ namespace Microsoft.PowerShell.Commands [Parameter(ParameterSetName = ParameterSet_Name)] [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)] [Parameter(ParameterSetName = ParameterSet_ViaCimSession)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)] + [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)] public string MaximumVersion { get @@ -320,7 +319,7 @@ namespace Microsoft.PowerShell.Commands [Parameter(ParameterSetName = ParameterSet_Name)] [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)] [Parameter(ParameterSetName = ParameterSet_ViaCimSession)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)] + [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)] public Version RequiredVersion { get { return BaseRequiredVersion; } @@ -425,8 +424,8 @@ namespace Microsoft.PowerShell.Commands /// This parameter causes a module to be loaded into Windows PowerShell. /// This is mutually exclusive with SkipEditionCheck parameter. /// - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true)] - [Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true)] + [Parameter(ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true)] + [Parameter(ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true)] [Alias("UseWinPS")] public SwitchParameter UseWindowsPowerShell { get; set; } diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index 7fa4ec6a57..d77dcfeb06 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -293,7 +293,7 @@ namespace Microsoft.PowerShell.Commands /// Session name for WindowsPS compat remoting session. /// internal const string WindowsPowerShellCompatRemotingSessionName = "WinPSCompatSession"; - + /// /// Synchronization object for creation/cleanup of WindowsPS compat remoting session. /// @@ -2363,62 +2363,18 @@ namespace Microsoft.PowerShell.Commands bool isConsideredCompatible = ModuleUtils.IsPSEditionCompatible(moduleManifestPath, inferredCompatiblePSEditions); if (!BaseSkipEditionCheck && !isConsideredCompatible) { - if (ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility")) + if (importingModule) { - if (importingModule) - { - IList moduleProxies = ImportModulesUsingWinCompat(new string [] {moduleManifestPath}, null, new ImportModuleOptions()); + IList moduleProxies = ImportModulesUsingWinCompat(new string [] {moduleManifestPath}, null, new ImportModuleOptions()); - // we are loading by a single ManifestPath so expect max of 1 - if(moduleProxies.Count > 0) - { - return moduleProxies[0]; - } - else - { - return null; - } + // we are loading by a single ManifestPath so expect max of 1 + if(moduleProxies.Count > 0) + { + return moduleProxies[0]; } - } - else - { - containedErrors = true; - - if (writingErrors) + else { - message = StringUtil.Format( - Modules.PSEditionNotSupported, - moduleManifestPath, - PSVersionInfo.PSEdition, - string.Join(',', inferredCompatiblePSEditions)); - - InvalidOperationException ioe = new InvalidOperationException(message); - - ErrorRecord er = new ErrorRecord( - ioe, - nameof(Modules) + "_" + nameof(Modules.PSEditionNotSupported), - ErrorCategory.ResourceUnavailable, - moduleManifestPath); - - WriteError(er); - } - - if (bailOnFirstError) - { - // If we're trying to load the module, return null so that caches - // are not polluted - if (importingModule) - { - return null; - } - - // If we return null with Get-Module, a fake module info will be created. Since - // we want to suppress output of the module, we need to do that here. - return new PSModuleInfo(moduleManifestPath, context: null, sessionState: null) - { - HadErrorsLoading = true, - IsConsideredEditionCompatible = false, - }; + return null; } } } @@ -4954,7 +4910,7 @@ namespace Microsoft.PowerShell.Commands } } - if (ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && module.IsWindowsPowerShellCompatModule && (System.Threading.Interlocked.Decrement(ref s_WindowsPowerShellCompatUsageCounter) == 0)) + if (module.IsWindowsPowerShellCompatModule && (System.Threading.Interlocked.Decrement(ref s_WindowsPowerShellCompatUsageCounter) == 0)) { CleanupWindowsPowerShellCompatResources(); } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 06b7ce313a..cfc1e8727d 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -955,7 +955,7 @@ namespace System.Management.Automation.Language case TokenKind.MultiplyEquals: et = ExpressionType.Multiply; break; case TokenKind.DivideEquals: et = ExpressionType.Divide; break; case TokenKind.RemainderEquals: et = ExpressionType.Modulo; break; - case TokenKind.QuestionQuestionEquals when ExperimentalFeature.IsEnabled("PSCoalescingOperators"): et = ExpressionType.Coalesce; break; + case TokenKind.QuestionQuestionEquals: et = ExpressionType.Coalesce; break; } var exprs = new List(); @@ -5896,7 +5896,7 @@ namespace System.Management.Automation.Language lhs.Cast(typeof(object)), rhs.Cast(typeof(object)), ExpressionCache.Constant(false)); - case TokenKind.QuestionQuestion when ExperimentalFeature.IsEnabled("PSCoalescingOperators"): + case TokenKind.QuestionQuestion: return Coalesce(lhs, rhs); } diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index c4ddf84c3f..149c971127 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -35,8 +35,6 @@ namespace System.Management.Automation.Language /// public sealed class Parser { - private static readonly bool s_pipelineChainsEnabled = ExperimentalFeature.IsEnabled("PSPipelineChainOperators"); - private readonly Tokenizer _tokenizer; internal Token _ungotToken; private bool _disableCommaOperator; @@ -5715,11 +5713,6 @@ namespace System.Management.Automation.Language // If this feature is not enabled, // just look for pipelines as before. - if (!s_pipelineChainsEnabled) - { - return PipelineRule(allowBackground: true); - } - RuntimeHelpers.EnsureSufficientExecutionStack(); // First look for assignment, since PipelineRule once handled that and this supercedes that. @@ -5950,7 +5943,6 @@ namespace System.Management.Automation.Language while (scanning) { CommandBaseAst commandAst; - Token assignToken = null; if (expr == null) { @@ -5960,21 +5952,6 @@ namespace System.Management.Automation.Language { SetTokenizerMode(TokenizerMode.Expression); expr = ExpressionRule(); - - // When pipeline chains are not enabled, we must process assignment. - // We are looking for = , and have seen . - // Now looking for '=' - if (!s_pipelineChainsEnabled && expr != null) - { - // We peek here because we are in expression mode, - // otherwise =0 will get scanned as a single token. - Token token = PeekToken(); - if (token.Kind.HasTrait(TokenFlags.AssignmentOperator)) - { - SkipToken(); - assignToken = token; - } - } } finally { @@ -5993,35 +5970,6 @@ namespace System.Management.Automation.Language ParserStrings.ExpressionsMustBeFirstInPipeline); } - // To process assignment when pipeline chains are not enabled, - // we have seen ' = ' and now expect a statement - if (!s_pipelineChainsEnabled && assignToken != null) - { - SkipNewlines(); - StatementAst statement = StatementRule(); - - if (statement == null) - { - // ErrorRecovery: - // We are likely at EOF, since almost anything else should result in a pipeline, - // so just keep parsing - IScriptExtent errorExtent = After(assignToken); - ReportIncompleteInput( - errorExtent, - nameof(ParserStrings.ExpectedValueExpression), - ParserStrings.ExpectedValueExpression, - assignToken.Kind.Text()); - statement = new ErrorStatementAst(errorExtent); - } - - return new AssignmentStatementAst( - ExtentOf(expr, statement), - expr, - assignToken.Kind, - statement, - assignToken.Extent); - } - RedirectionAst[] redirections = null; var redirectionToken = PeekToken() as RedirectionToken; RedirectionAst lastRedirection = null; @@ -6098,31 +6046,11 @@ namespace System.Management.Automation.Language case TokenKind.AndAnd: case TokenKind.OrOr: - if (s_pipelineChainsEnabled) - { - scanning = false; - continue; - } - - // Report that &&/|| are unsupported - SkipToken(); - SkipNewlines(); - ReportError( - nextToken.Extent, - nameof(ParserStrings.InvalidEndOfLine), - ParserStrings.InvalidEndOfLine, - nextToken.Text); - - if (PeekToken().Kind == TokenKind.EndOfInput) - { - scanning = false; - } - - break; + scanning = false; + continue; case TokenKind.Ampersand: - // When pipeline chains are not enabled, pipelines always handle backgrounding - if (s_pipelineChainsEnabled && !allowBackground) + if (!allowBackground) { // Handled by invoking rule scanning = false; @@ -6720,12 +6648,6 @@ namespace System.Management.Automation.Language // G ternary-expression: // G binary-expression '?' new-lines:opt ternary-expression new-lines:opt ':' new-lines:opt ternary-expression - // TODO: remove this if-block when making 'ternary operator' an official feature. - if (!ExperimentalFeature.IsEnabled("PSTernaryOperator")) - { - return BinaryExpressionRule(); - } - RuntimeHelpers.EnsureSufficientExecutionStack(); var oldTokenizerMode = _tokenizer.Mode; try diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs index 44a931dc9f..52b28d8529 100644 --- a/src/System.Management.Automation/engine/parser/tokenizer.cs +++ b/src/System.Management.Automation/engine/parser/tokenizer.cs @@ -727,7 +727,7 @@ namespace System.Management.Automation.Language internal bool ForceEndNumberOnTernaryOpChars { get { return _forceEndNumberOnTernaryOpChars; } - set { _forceEndNumberOnTernaryOpChars = value && ExperimentalFeature.IsEnabled("PSTernaryOperator"); } + set { _forceEndNumberOnTernaryOpChars = value; } } internal bool WantSimpleName { get; set; } @@ -5015,23 +5015,20 @@ namespace System.Management.Automation.Language return this.NewToken(TokenKind.Colon); case '?' when InExpressionMode(): - if (ExperimentalFeature.IsEnabled("PSCoalescingOperators")) + c1 = PeekChar(); + + if (c1 == '?') { + SkipChar(); c1 = PeekChar(); - if (c1 == '?') + if (c1 == '=') { SkipChar(); - c1 = PeekChar(); - - if (c1 == '=') - { - SkipChar(); - return this.NewToken(TokenKind.QuestionQuestionEquals); - } - - return this.NewToken(TokenKind.QuestionQuestion); + return this.NewToken(TokenKind.QuestionQuestionEquals); } + + return this.NewToken(TokenKind.QuestionQuestion); } return this.NewToken(TokenKind.QuestionMark); diff --git a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs index ec6b6582f3..1d1c6e5533 100644 --- a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs @@ -161,11 +161,10 @@ namespace Microsoft.PowerShell.Commands [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = NewPSSessionCommand.VMNameParameterSet)] public string ConfigurationName { get; set; } - + /// /// Gets or sets parameter value that creates connection to a Windows PowerShell process. /// - [Experimental("PSWindowsPowerShellCompatibility", ExperimentAction.Show)] [Parameter(Mandatory = true, ParameterSetName = NewPSSessionCommand.UseWindowsPowerShellParameterSet)] public SwitchParameter UseWindowsPowerShell { get; set; } diff --git a/test/powershell/Language/Operators/NullConditional.Tests.ps1 b/test/powershell/Language/Operators/NullConditional.Tests.ps1 index 6d4464c86a..97dbc32e1a 100644 --- a/test/powershell/Language/Operators/NullConditional.Tests.ps1 +++ b/test/powershell/Language/Operators/NullConditional.Tests.ps1 @@ -3,30 +3,15 @@ Describe 'NullCoalesceOperations' -Tags 'CI' { BeforeAll { - - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators') - - if ($skipTest) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - } else { - $someGuid = New-Guid - $typesTests = @( - @{ name = 'string'; valueToSet = 'hello' } - @{ name = 'dotnetType'; valueToSet = $someGuid } - @{ name = 'byte'; valueToSet = [byte]0x94 } - @{ name = 'intArray'; valueToSet = 1..2 } - @{ name = 'stringArray'; valueToSet = 'a'..'c' } - @{ name = 'emptyArray'; valueToSet = @(1, 2, 3) } - ) - } - } - - AfterAll { - if ($skipTest) { - $global:PSDefaultParameterValues = $originalDefaultParameterValues - } + $someGuid = New-Guid + $typesTests = @( + @{ name = 'string'; valueToSet = 'hello' } + @{ name = 'dotnetType'; valueToSet = $someGuid } + @{ name = 'byte'; valueToSet = [byte]0x94 } + @{ name = 'intArray'; valueToSet = 1..2 } + @{ name = 'stringArray'; valueToSet = 'a'..'c' } + @{ name = 'emptyArray'; valueToSet = @(1, 2, 3) } + ) } Context "Null conditional assignment operator ??=" { diff --git a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 index f1d81add97..1447837bc3 100644 --- a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 @@ -3,15 +3,6 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { BeforeAll { - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSPipelineChainOperators') - if ($skipTest) - { - Write-Verbose "Test Suite Skipped: These tests require the PSPipelineChainOperators experimental feature to be enabled" -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - return - } - function Test-SuccessfulCommand { Write-Output "SUCCESS" @@ -135,12 +126,6 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { ) } - AfterAll { - if ($skipTest) { - $PSDefaultParameterValues = $originalDefaultParameterValues - } - } - It "Gets the correct output with statement ''" -TestCases $simpleTestCases { param($Statement, $Output) diff --git a/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 b/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 index ce48c98c6f..99b219a6e6 100644 --- a/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 @@ -3,54 +3,46 @@ Describe "Using of ternary operator" -Tags CI { BeforeAll { - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSTernaryOperator') - if ($skipTest) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSTernaryOperator' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - } - else { - $testCases = @( - ## Condition: variable and constant expressions - @{ Script = { $true ? 1 : 2 }; ExpectedValue = 1 } - @{ Script = { $true? ?1 :2 }; ExpectedValue = 2 } - @{ Script = { ${true}?1:2 }; ExpectedValue = 1 } - @{ Script = { 1 ? 1kb : 0xf }; ExpectedValue = 1kb } - @{ Script = { 0 ?1kb:0xf }; ExpectedValue = 15 } - @{ Script = { 's' ?1kb:0xf }; ExpectedValue = 1kb } - @{ Script = { $null ?1kb:0xf }; ExpectedValue = 15 } - @{ Script = { '' ?1kb:0xf }; ExpectedValue = 15 } + $testCases = @( + ## Condition: variable and constant expressions + @{ Script = { $true ? 1 : 2 }; ExpectedValue = 1 } + @{ Script = { $true? ?1 :2 }; ExpectedValue = 2 } + @{ Script = { ${true}?1:2 }; ExpectedValue = 1 } + @{ Script = { 1 ? 1kb : 0xf }; ExpectedValue = 1kb } + @{ Script = { 0 ?1kb:0xf }; ExpectedValue = 15 } + @{ Script = { 's' ?1kb:0xf }; ExpectedValue = 1kb } + @{ Script = { $null ?1kb:0xf }; ExpectedValue = 15 } + @{ Script = { '' ?1kb:0xf }; ExpectedValue = 15 } - ## Condition: other primary expressions - @{ Script = { 1,2,3,4 ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { @(1,2,3,4) ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { @{name = 'name'} ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { @{name = 'name'}.name ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { @{name = 'name'}.Contains('name') ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { (Test-Path Env:\NonExist) ? 'true' : 'false' }; ExpectedValue = 'false' } - @{ Script = { (Test-Path Env:\PSModulePath) ? 'true' : 'false' }; ExpectedValue = 'true' } - @{ Script = { $($p = Get-Process -Id $PID; $p.Name -eq 'pwsh') ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { ($a = 1) ? 2 : 3 }; ExpectedValue = 2 } - @{ Script = { $($a = 1) ? 2 : 3 }; ExpectedValue = 3 } - @{ Script = { (Write-Warning -Message warning -WarningAction SilentlyContinue) ? 1 : 2 }; ExpectedValue = 2 } - @{ Script = { (Write-Error -Message error -ErrorAction SilentlyContinue) ? 1 : 2 }; ExpectedValue = 2 } + ## Condition: other primary expressions + @{ Script = { 1,2,3,4 ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { @(1,2,3,4) ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { @{name = 'name'} ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { @{name = 'name'}.name ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { @{name = 'name'}.Contains('name') ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { (Test-Path Env:\NonExist) ? 'true' : 'false' }; ExpectedValue = 'false' } + @{ Script = { (Test-Path Env:\PSModulePath) ? 'true' : 'false' }; ExpectedValue = 'true' } + @{ Script = { $($p = Get-Process -Id $PID; $p.Name -eq 'pwsh') ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { ($a = 1) ? 2 : 3 }; ExpectedValue = 2 } + @{ Script = { $($a = 1) ? 2 : 3 }; ExpectedValue = 3 } + @{ Script = { (Write-Warning -Message warning -WarningAction SilentlyContinue) ? 1 : 2 }; ExpectedValue = 2 } + @{ Script = { (Write-Error -Message error -ErrorAction SilentlyContinue) ? 1 : 2 }; ExpectedValue = 2 } - ## Condition: unary and binary expression expressions - @{ Script = { -not $IsCoreCLR ? 'Desktop' : 'Core' }; ExpectedValue = 'Core' } - @{ Script = { $PSEdition -eq 'Core' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { $IsCoreCLR -and (Get-Process -Id $PID).Name -eq 'pwsh' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { $IsCoreCLR -and 'pwsh' -match 'p.*h' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - @{ Script = { 1,2,3 -contains 2 ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + ## Condition: unary and binary expression expressions + @{ Script = { -not $IsCoreCLR ? 'Desktop' : 'Core' }; ExpectedValue = 'Core' } + @{ Script = { $PSEdition -eq 'Core' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { $IsCoreCLR -and (Get-Process -Id $PID).Name -eq 'pwsh' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { $IsCoreCLR -and 'pwsh' -match 'p.*h' ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } + @{ Script = { 1,2,3 -contains 2 ? 'Core' : 'Desktop' }; ExpectedValue = 'Core' } - ## Nested ternary expressions - @{ Script = { $IsCoreCLR ? $false ? 'nested-if-true' : 'nested-if-false' : 'if-false' }; ExpectedValue = 'nested-if-false' } - @{ Script = { $IsCoreCLR ? $false ? 'nested-if-true' : $true ? 'nested-nested-if-true' : 'nested-nested-if-false' : 'if-false' }; ExpectedValue = 'nested-nested-if-true' } + ## Nested ternary expressions + @{ Script = { $IsCoreCLR ? $false ? 'nested-if-true' : 'nested-if-false' : 'if-false' }; ExpectedValue = 'nested-if-false' } + @{ Script = { $IsCoreCLR ? $false ? 'nested-if-true' : $true ? 'nested-nested-if-true' : 'nested-nested-if-false' : 'if-false' }; ExpectedValue = 'nested-nested-if-true' } - ## Binary operator has higher precedence order than ternary - @{ Script = { !$IsCoreCLR ? 'Core' : 'Desktop' -eq 'Core' }; ExpectedValue = !$IsCoreCLR ? 'Core' : ('Desktop' -eq 'Core') } - @{ Script = { ($IsCoreCLR ? 'Core' : 'Desktop') -eq 'Core' }; ExpectedValue = $true } - ) - } + ## Binary operator has higher precedence order than ternary + @{ Script = { !$IsCoreCLR ? 'Core' : 'Desktop' -eq 'Core' }; ExpectedValue = !$IsCoreCLR ? 'Core' : ('Desktop' -eq 'Core') } + @{ Script = { ($IsCoreCLR ? 'Core' : 'Desktop') -eq 'Core' }; ExpectedValue = $true } + ) } AfterAll { diff --git a/test/powershell/Language/Parser/Parsing.Tests.ps1 b/test/powershell/Language/Parser/Parsing.Tests.ps1 index 7754eded97..870df2c256 100644 --- a/test/powershell/Language/Parser/Parsing.Tests.ps1 +++ b/test/powershell/Language/Parser/Parsing.Tests.ps1 @@ -263,21 +263,6 @@ Describe 'assignment statement parsing' -Tags "CI" { } Describe 'null coalescing assignment statement parsing' -Tag 'CI' { - BeforeAll { - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators') - if ($skipTest) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - } - } - - AfterAll { - if ($skipTest) { - $global:PSDefaultParameterValues = $originalDefaultParameterValues - } - } - ShouldBeParseError '1 ??= 1' InvalidLeftHandSide 0 ShouldBeParseError '@() ??= 1' InvalidLeftHandSide 0 ShouldBeParseError '@{} ??= 1' InvalidLeftHandSide 0 @@ -287,21 +272,6 @@ Describe 'null coalescing assignment statement parsing' -Tag 'CI' { } Describe 'null coalescing statement parsing' -Tag "CI" { - BeforeAll { - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSCoalescingOperators') - if ($skipTest) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCoalescingOperators' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - } - } - - AfterAll { - if ($skipTest) { - $global:PSDefaultParameterValues = $originalDefaultParameterValues - } - } - ShouldBeParseError '$x??=' ExpectedValueExpression 5 ShouldBeParseError '$x ??Get-Thing' ExpectedValueExpression,UnexpectedToken 5,5 ShouldBeParseError '$??=$false' ExpectedValueExpression,InvalidLeftHandSide 3,0 @@ -393,42 +363,28 @@ Describe 'Unicode escape sequence parsing' -Tag "CI" { Describe "Ternary Operator parsing" -Tags CI { BeforeAll { - $skipTest = -not $EnabledExperimentalFeatures.Contains('PSTernaryOperator') - if ($skipTest) { - Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSTernaryOperator' to be enabled." -Verbose - $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = $true - } - else { - $testCases_basic = @( - @{ Script = '$true?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } - @{ Script = '$false?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } - @{ Script = '$:abc'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } - @{ Script = '$env:abc'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } - @{ Script = '$env:123'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } - @{ Script = 'a?2:2'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - @{ Script = '1?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - @{ Script = 'a?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - @{ Script = 'a?b'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - @{ Script = '1?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - @{ Script = '?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } - ) + $testCases_basic = @( + @{ Script = '$true?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } + @{ Script = '$false?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } + @{ Script = '$:abc'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } + @{ Script = '$env:abc'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } + @{ Script = '$env:123'; TokenKind = [System.Management.Automation.Language.TokenKind]::Variable; } + @{ Script = 'a?2:2'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + @{ Script = '1?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + @{ Script = 'a?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + @{ Script = 'a?b'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + @{ Script = '1?'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + @{ Script = '?2:3'; TokenKind = [System.Management.Automation.Language.TokenKind]::Generic; } + ) - $testCases_incomplete = @( - @{ Script = '$true ?'; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } - @{ Script = '$true ? 3'; ErrorId = "MissingColonInTernaryExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } - @{ Script = '$true ? 3 :'; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.TernaryExpressionAst] } - @{ Script = "`$true`t?"; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } - @{ Script = "`$true`t?`t3"; ErrorId = "MissingColonInTernaryExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } - @{ Script = "`$true`t?`t3`t:"; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.TernaryExpressionAst] } - ) - } - } - - AfterAll { - if ($skipTest) { - $global:PSDefaultParameterValues = $originalDefaultParameterValues - } + $testCases_incomplete = @( + @{ Script = '$true ?'; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } + @{ Script = '$true ? 3'; ErrorId = "MissingColonInTernaryExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } + @{ Script = '$true ? 3 :'; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.TernaryExpressionAst] } + @{ Script = "`$true`t?"; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } + @{ Script = "`$true`t?`t3"; ErrorId = "MissingColonInTernaryExpression"; AstType = [System.Management.Automation.Language.ErrorExpressionAst] } + @{ Script = "`$true`t?`t3`t:"; ErrorId = "ExpectedValueExpression"; AstType = [System.Management.Automation.Language.TernaryExpressionAst] } + ) } It "Question-mark and colon parsed correctly in