Skip to content

Commit ab899c9

Browse files
jjonesczMiYanni
andauthored
Consolidate file-level directive separators (#49309)
Co-authored-by: Michael Yanni <[email protected]>
1 parent 5e3306a commit ab899c9

18 files changed

+189
-108
lines changed

documentation/general/dotnet-run-file.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,15 @@ Other directives result in an error, reserving them for future use.
153153

154154
```cs
155155
#:sdk Microsoft.NET.Sdk.Web
156-
#:property TargetFramework net11.0
157-
#:property LangVersion preview
156+
#:property TargetFramework=net11.0
157+
#:property LangVersion=preview
158158
159159
```
160160

161-
The value must be separated from the name of the directive by white space (`@` is additionally allowed separator for the package directive)
161+
The value must be separated from the kind (`package`/`sdk`/`property`) of the directive by whitespace
162162
and any leading and trailing white space is not considered part of the value.
163-
Any value can optionally have two parts separated by a space (more whitespace characters could be allowed in the future).
163+
Any value can optionally have two parts separated by `@` in case of `package`/`sdk` or `=` in case of `property`
164+
and whitespace is trimmed from the two parts around the separator.
164165
The value of the first `#:sdk` is injected into `<Project Sdk="{0}">` with the separator (if any) replaced with `/`,
165166
and the subsequent `#:sdk` directive values are split by the separator and injected as `<Sdk Name="{0}" Version="{1}" />` elements (or without the `Version` attribute if there is no separator).
166167
It is an error if the first part (name) is empty (the version is allowed to be empty, but that results in empty `Version=""`).

src/Cli/dotnet/Commands/CliCommandStrings.resx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,9 +1506,13 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
15061506
<comment>{0} is the file path and line number. {1} is an inner exception message.</comment>
15071507
</data>
15081508
<data name="PropertyDirectiveMissingParts" xml:space="preserve">
1509-
<value>The property directive needs to have two parts separated by a space like 'PropertyName PropertyValue': {0}</value>
1509+
<value>The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue': {0}</value>
15101510
<comment>{0} is the file path and line number.</comment>
15111511
</data>
1512+
<data name="InvalidDirectiveName" xml:space="preserve">
1513+
<value>The directive at '{2}' should contain a name without special characters and an optional version separated by '{1}' like '#:{0} Abc{1}Xyz'.</value>
1514+
<comment>{0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. {2} is the file path and line number.</comment>
1515+
</data>
15121516
<data name="CannotConvertDirective" xml:space="preserve">
15131517
<value>Some directives cannot be converted: the first error is at {0}. Run the file to see all compilation errors. Specify '--force' to convert anyway.</value>
15141518
<comment>{Locked="--force"}. {0} is the file path and line number.</comment>

src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,9 @@ internal static partial class Patterns
896896
{
897897
[GeneratedRegex("""\s+""")]
898898
public static partial Regex Whitespace { get; }
899+
900+
[GeneratedRegex("""[\s@=/]""")]
901+
public static partial Regex DisallowedNameCharacters { get; }
899902
}
900903

901904
/// <summary>
@@ -935,25 +938,29 @@ private CSharpDirective() { }
935938
}
936939
}
937940

938-
private static (string, string?)? ParseOptionalTwoParts(ImmutableArray<SimpleDiagnostic>.Builder? errors, SourceFile sourceFile, TextSpan span, string directiveKind, string directiveText, SearchValues<char>? separators = null)
941+
private static (string, string?)? ParseOptionalTwoParts(ImmutableArray<SimpleDiagnostic>.Builder? errors, SourceFile sourceFile, TextSpan span, string directiveKind, string directiveText, char separator)
939942
{
940-
var i = separators != null
941-
? directiveText.AsSpan().IndexOfAny(separators)
942-
: directiveText.IndexOf(' ', StringComparison.Ordinal);
943-
var firstPart = i < 0 ? directiveText : directiveText[..i];
943+
var i = directiveText.IndexOf(separator, StringComparison.Ordinal);
944+
var firstPart = (i < 0 ? directiveText : directiveText.AsSpan(..i)).TrimEnd();
944945

945-
if (string.IsNullOrWhiteSpace(firstPart))
946+
if (firstPart.IsWhiteSpace())
946947
{
947948
return ReportError<(string, string?)?>(errors, sourceFile, span, string.Format(CliCommandStrings.MissingDirectiveName, directiveKind, sourceFile.GetLocationString(span)));
948949
}
949950

951+
// If the name contains characters that resemble separators, report an error to avoid any confusion.
952+
if (Patterns.DisallowedNameCharacters.IsMatch(firstPart))
953+
{
954+
return ReportError<(string, string?)?>(errors, sourceFile, span, string.Format(CliCommandStrings.InvalidDirectiveName, directiveKind, separator, sourceFile.GetLocationString(span)));
955+
}
956+
950957
var secondPart = i < 0 ? [] : directiveText.AsSpan((i + 1)..).TrimStart();
951958
if (i < 0 || secondPart.IsWhiteSpace())
952959
{
953-
return (firstPart, null);
960+
return (firstPart.ToString(), null);
954961
}
955962

956-
return (firstPart, secondPart.ToString());
963+
return (firstPart.ToString(), secondPart.ToString());
957964
}
958965

959966
/// <summary>
@@ -977,7 +984,7 @@ private Sdk() { }
977984

978985
public static new Sdk? Parse(ImmutableArray<SimpleDiagnostic>.Builder? errors, SourceFile sourceFile, TextSpan span, string directiveKind, string directiveText)
979986
{
980-
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText) is not var (sdkName, sdkVersion))
987+
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText, separator: '@') is not var (sdkName, sdkVersion))
981988
{
982989
return null;
983990
}
@@ -1007,7 +1014,7 @@ private Property() { }
10071014

10081015
public static new Property? Parse(ImmutableArray<SimpleDiagnostic>.Builder? errors, SourceFile sourceFile, TextSpan span, string directiveKind, string directiveText)
10091016
{
1010-
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText) is not var (propertyName, propertyValue))
1017+
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText, separator: '=') is not var (propertyName, propertyValue))
10111018
{
10121019
return null;
10131020
}
@@ -1040,15 +1047,13 @@ private Property() { }
10401047
/// </summary>
10411048
public sealed class Package : Named
10421049
{
1043-
private static readonly SearchValues<char> s_separators = SearchValues.Create(' ', '@');
1044-
10451050
private Package() { }
10461051

10471052
public string? Version { get; init; }
10481053

10491054
public static new Package? Parse(ImmutableArray<SimpleDiagnostic>.Builder? errors, SourceFile sourceFile, TextSpan span, string directiveKind, string directiveText)
10501055
{
1051-
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText, s_separators) is not var (packageName, packageVersion))
1056+
if (ParseOptionalTwoParts(errors, sourceFile, span, directiveKind, directiveText, separator: '@') is not var (packageName, packageVersion))
10521057
{
10531058
return null;
10541059
}

src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf

Lines changed: 8 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)