Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;

using Microsoft.PowerShell;
using System.Management.Automation.Security;
Expand All @@ -17,7 +18,7 @@

namespace System.Management.Automation.Language
{
internal sealed class SemanticChecks : AstVisitor2, IAstPostVisitHandler
internal sealed partial class SemanticChecks : AstVisitor2, IAstPostVisitHandler
{
private readonly Parser _parser;

Expand Down Expand Up @@ -1319,20 +1320,50 @@ public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionA

public override AstVisitAction VisitUsingStatement(UsingStatementAst usingStatementAst)
{
bool usingKindSupported = usingStatementAst.UsingStatementKind == UsingStatementKind.Namespace ||
usingStatementAst.UsingStatementKind == UsingStatementKind.Assembly ||
usingStatementAst.UsingStatementKind == UsingStatementKind.Module;
if (!usingKindSupported ||
usingStatementAst.Alias != null)
UsingStatementKind kind = usingStatementAst.UsingStatementKind;
bool usingKindSupported = kind is UsingStatementKind.Namespace or UsingStatementKind.Assembly or UsingStatementKind.Module;
if (!usingKindSupported || usingStatementAst.Alias != null)
{
_parser.ReportError(usingStatementAst.Extent,
_parser.ReportError(
usingStatementAst.Extent,
nameof(ParserStrings.UsingStatementNotSupported),
ParserStrings.UsingStatementNotSupported);
}

if (kind is UsingStatementKind.Namespace)
{
Regex nsPattern = NamespacePattern();
if (!nsPattern.IsMatch(usingStatementAst.Name.Value))
{
_parser.ReportError(
usingStatementAst.Name.Extent,
nameof(ParserStrings.InvalidNamespaceValue),
ParserStrings.InvalidNamespaceValue);
}
}

return AstVisitAction.Continue;
}

/// <summary>
/// This regular expression is for validating if a namespace string is valid.
///
/// In C#, a legit namespace is defined as `identifier ('.' identifier)*` [see https://learn.microsoft.com/dotnet/csharp/language-reference/language-specification/namespaces#143-namespace-declarations].
/// And `identifier` is defined in https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/identifier-names#naming-rules, summarized below:
/// - Identifiers must start with a letter or underscore (_).
/// - Identifiers can contain
/// * Unicode letter characters (categories: Lu, Ll, Lt, Lm, Lo or Nl);
/// * decimal digit characters (category: Nd);
/// * Unicode connecting characters (category: Pc);
/// * Unicode combining characters (categories: Mn, Mc);
/// * Unicode formatting characters (category: Cf).
///
/// For details about how Unicode categories are represented in regular expression, see the "Unicode Categories" section in the following article:
/// - https://www.regular-expressions.info/unicode.html
/// </summary>
[GeneratedRegex(@"^[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}\p{Pc}\p{Mn}\p{Mc}\p{Cf}_]*(?:\.[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}\p{Pc}\p{Mn}\p{Mc}\p{Cf}_]*)*$")]
private static partial Regex NamespacePattern();

public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
{
//
Expand Down
3 changes: 3 additions & 0 deletions src/System.Management.Automation/resources/ParserStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,9 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent
<data name="UsingStatementNotSupported" xml:space="preserve">
<value>This syntax of the 'using' statement is not supported.</value>
</data>
<data name="InvalidNamespaceValue" xml:space="preserve">
<value>The specified namespace in the 'using' statement contains invalid characters.</value>
</data>
<data name="InformationStream" xml:space="preserve">
<value>information stream</value>
</data>
Expand Down
5 changes: 4 additions & 1 deletion test/powershell/Language/Parser/UsingNamespace.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ Describe "Using Namespace" -Tags "CI" {

ShouldBeParseError "1; using namespace System" UsingMustBeAtStartOfScript 3
ShouldBeParseError "using namespace Foo = System" UsingStatementNotSupported 0
ShouldBeParseError "using namespace ''" InvalidNamespaceValue 16
ShouldBeParseError "using namespace [System]" InvalidNamespaceValue 16
ShouldBeParseError "using namespace ',System'" InvalidNamespaceValue 16
ShouldBeParseError "using namespace ']System'" InvalidNamespaceValue 16
# TODO: add diagnostic (low pri)
# ShouldBeParseError "using namespace System; using namespace System" UsingNamespaceAlreadySpecified 24
}