Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 2 additions & 1 deletion documentation/general/dotnet-run-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ We do not limit these directives to appear only in entry point files because it
- which also makes it possible to share it independently or symlink it to multiple script folders,
- and it's similar to `global using`s which users usually put into a single file but don't have to.

We disallow duplicate `#:` directives to allow us design some deduplication mechanism in the future.
We disallow duplicate `#:` directives (except `#:project` and `#:ref`) to allow us to design some deduplication mechanism in the future.
Specifically, directives are considered duplicate if their type and name (case insensitive) are equal.
`#:project` and `#:ref` duplicates are allowed because MSBuild handles deduplication of project references naturally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe clarify that multiple <ProjectReference> directives are allowed? The current wording says that indirectly, but it could be more explicit about it.

Later with deduplication, separate "self-contained" utilities could reference overlapping sets of packages
even if they end up in the same compilation.
For example, properties could be concatenated via `;`, more specific package versions could override less specific ones.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,19 @@ public static void FindLeadingDirectives(

if (CSharpDirective.Parse(context) is { } directive)
{
// If the directive is already present, report an error.
if (deduplicated.TryGetValue(directive, out var existingDirective))
// Duplicate #:project and #:ref directives are allowed (we let MSBuild handle that).
if (directive is not (CSharpDirective.Project or CSharpDirective.Ref))
{
var typeAndName = $"#:{existingDirective.GetType().Name.ToLowerInvariant()} {existingDirective.Name}";
context.ReportError(directive.Info.Span, string.Format(FileBasedProgramsResources.DuplicateDirective, typeAndName));
}
else
{
deduplicated.Add(directive, directive);
// If the directive is already present, report an error.
if (deduplicated.TryGetValue(directive, out var existingDirective))
{
var typeAndName = $"#:{existingDirective.GetType().Name.ToLowerInvariant()} {existingDirective.Name}";
context.ReportError(directive.Info.Span, string.Format(FileBasedProgramsResources.DuplicateDirective, typeAndName));
}
else
{
deduplicated.Add(directive, directive);
}
}

builder?.Add(directive);
Expand Down
14 changes: 6 additions & 8 deletions test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,7 @@ public void ProjectReference_Duplicate(string? subdir)
</Project>
""");

// Duplicate #:project directives are allowed (MSBuild can handle that).
File.WriteAllText(filePath, """
#:project dir/
#:project dir/
Expand All @@ -3516,16 +3517,15 @@ public void ProjectReference_Duplicate(string? subdir)
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
.Should().Fail()
.And.HaveStdErrContaining(DirectiveError(filePath, 2, FileBasedProgramsResources.DuplicateDirective, "#:project dir/"));
.Should().Pass()
.And.HaveStdOut("Hello");

File.WriteAllText(filePath, """
#:project dir/
#:project dir/proj1.csproj
Console.WriteLine("Hello");
""");

// https://github.com/dotnet/sdk/issues/51139: we should detect the duplicate project reference
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
Expand All @@ -3538,7 +3538,6 @@ public void ProjectReference_Duplicate(string? subdir)
Console.WriteLine("Hello");
""");

// https://github.com/dotnet/sdk/issues/51139: we should detect the duplicate project reference
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
Expand Down Expand Up @@ -3805,6 +3804,7 @@ public static class Greeter
}
""");

// Duplicate #:ref directives are allowed (MSBuild handles deduplication).
File.WriteAllText(filePath, """
#:ref lib.cs
#:ref lib.cs
Expand All @@ -3814,16 +3814,15 @@ public static class Greeter
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
.Should().Fail()
.And.HaveStdErrContaining(DirectiveError(filePath, 2, FileBasedProgramsResources.DuplicateDirective, "#:ref lib.cs"));
.Should().Pass()
.And.HaveStdOut("Hello!");

File.WriteAllText(filePath, """
#:ref lib.cs
#:ref ./lib.cs
Console.WriteLine(MyLib.Greeter.Greet());
""");

// https://github.com/dotnet/sdk/issues/51139: we should detect the duplicate ref
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
Expand All @@ -3836,7 +3835,6 @@ public static class Greeter
Console.WriteLine(MyLib.Greeter.Greet());
""");

// https://github.com/dotnet/sdk/issues/51139: we should detect the duplicate ref
new DotnetCommand(Log, "run", relativeFilePath)
.WithWorkingDirectory(testInstance.Path)
.Execute()
Expand Down
Loading