Skip to content

Conversation

@RikkiGibson
Copy link
Member

@RikkiGibson RikkiGibson commented Nov 21, 2025

Follow up to #81252

  • Removes unexpected errors for #: in miscellaneous files.
  • Fixes a completion crash for #:project $$ in the ctrl+N case. The file has a non-absolute path and we shouldn't try to use its containing directory as a base for path lookup.

Usually just the combination of: document is not part of an ordinary project, and document contains this directive, would make it be treated as an ordinary file. But there is also the case of virtual documents (not present on disk), containing these directives, e.g. when you hit ctrl+N in VS Code, set language mode to C# and start typing. Virtual documents are never treated as file-based programs, they are essentially only treated as miscellaneous files. But the user might be intending for this to be a file-based program, once they have saved it to disk. So reporting an error in this case is much more a nuisance than a benefit.

Copy link
Member

@dibarbet dibarbet left a comment

Choose a reason for hiding this comment

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

ide side lgtm

@jcouv
Copy link
Member

jcouv commented Nov 21, 2025

Did we forget to create a test plan and label for file-based apps feature? #Closed

/// In this mode, ignored directives <c>#:</c> are allowed.
/// </remarks>
internal bool FileBasedProgram => Features.ContainsKey("FileBasedProgram");
internal bool AllowIgnoredDirectives => Features.ContainsKey("FileBasedProgram") || Features.ContainsKey("MiscellaneousFile");
Copy link
Member

Choose a reason for hiding this comment

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

Should those feature flags documented anywhere?
Not directly related to the PR: Aren't feature flags typically used for temporary or not fully supported scenarios? Should we consider exposing a proper API on parse options for file-based apps?

Copy link
Member Author

Choose a reason for hiding this comment

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

Perhaps, but, I will note that explicitly including these feature names in ordinary projects and similar to result in unspecified behavior. These are generally supposed to be only included by the tooling.

It’s possible there is a more preferred way of “tagging” compilations in the desired way here than just using special feature names. Happy to have that discussion in more depth.

if (isActive)
{
if (!lexer.Options.FileBasedProgram)
if (!lexer.Options.AllowIgnoredDirectives)
Copy link
Member

@jcouv jcouv Nov 21, 2025

Choose a reason for hiding this comment

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

Sorry for my lack of context, but I'm surprised that the parsing of ignored directives is conditional in the first place. The spec only says we're adding directives to the language and that they are ignored: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-14.0/ignored-directives.md #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

AFAIK, this only controls whether an error is reported on usage.

Copy link
Member

Choose a reason for hiding this comment

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

Not blocking this PR: should we update the spec or remove the condition? In other words, did we intentionally fork the language?

Copy link
Member Author

Choose a reason for hiding this comment

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

The spec says:

Compilers are also free to report errors if these directives are used in unsupported scenarios

It's possible we want to push some of those rules into the language, but it's not clear to me. I think the mechanism that decides whether the scenario is supported would also have to be defined in the language spec.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, reporting an error on #: directives in normal project-based apps is intentional. That is because those directives are ignored for project-based apps and users might be confused if they were ignored silently. This is only a tooling feature though, i.e., roslyn implements it; sdk tells it the kind of the program (file-based vs project-based) via passing the feature flag. It is not a language feature since language has no way to interpret those directives.

Copy link
Member

Choose a reason for hiding this comment

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

That is because those directives are ignored for project-based apps and users might be confused if they were ignored silently.

That behavior makes sense.
Still, the net result is that we have forked the language, but I don't recall that was discussed as part of the language design. The spec only says we add these to the grammar and they are ignored, but our implementation diverges (errors in some cases). We're using a feature flag for a long-living feature, which smells.

I'll start an email thread.


var markup = $"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="Test1" Features="FileBasedProgram=true">
Copy link
Member

@jcouv jcouv Nov 21, 2025

Choose a reason for hiding this comment

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

Features="FileBasedProgram=true"

Did this test fail before the changes in this PR?
The reason I'm asking is because I see logic elsewhere that adds the "MiscellaneousFile" feature and therefore it's not clear whether this test should have the "FileBasedProgram" feature flag. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it passed. It’s reasonable for some of these tests to use FileBasedProgram feature flag, because we want to see how the editor features behave on file-based programs.

However I do think the actual applications of FileBasedProgram versus MiscellaneousFile feature names requires some discussion.

Copy link
Member

Choose a reason for hiding this comment

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

From the description of the bug being fixed, I expected a test like this one but without FileBasedProgram and with MiscellaneousFile. It used to fail, but now it would pass. Did I understand right?

Copy link
Member Author

@RikkiGibson RikkiGibson Dec 10, 2025

Choose a reason for hiding this comment

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

Resolving by deleting MiscellaneousFile flag from the compiler layer. (i.e. reverting all compiler changes in this PR.)

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

Done with review pass (commit 1) for compiler. Only glanced at IDE code

@jcouv jcouv self-assigned this Nov 21, 2025
@jjonescz jjonescz added the Feature - Run File #: and #! directives and file-based C# programs label Nov 21, 2025
@jjonescz
Copy link
Member

jjonescz commented Nov 24, 2025

Did we forget to create a test plan and label for file-based apps feature?

We have a label: Feature - Run File #: and #! directives and file-based C# programs
I don't think we have a test plan; IIRC that might have been intentional since the relevant language feature "ignored directives" was merged in a single PR. #Closed

else
{
// Any non-script misc file should not complain about usage of '#:' ignored directives.
parseOptions = parseOptions.WithFeatures([.. parseOptions.Features, new("FileBasedProgram", "true")]);
Copy link
Member Author

Choose a reason for hiding this comment

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

This change makes it so if there are parse options, then we are either going to treat this as a script, or we'll pass the FileBasedProgram flag (to avoid reporting possibly unwanted errors for #:). Not neither, not both.

// FileBasedProgram feature flag is not passed, so an error is expected on '#:'.
var primordialSyntaxTree = await primordialDocument.GetRequiredSyntaxTreeAsync(CancellationToken.None);
primordialSyntaxTree.GetDiagnostics(CancellationToken.None).Verify(
// script.csx(1,2): error CS9298: '#:' directives can be only used in file-based programs ('-features:FileBasedProgram')"
Copy link
Member Author

@RikkiGibson RikkiGibson Dec 10, 2025

Choose a reason for hiding this comment

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

We wouldn't want to report this error for a misc file like Untitled-1, which is likely to be ordinary .cs code. But I think it's good to report it when we know it is a .csx script.

@RikkiGibson RikkiGibson enabled auto-merge (squash) December 10, 2025 22:28
@RikkiGibson RikkiGibson merged commit 46a48b8 into dotnet:main Dec 10, 2025
25 of 26 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Dec 10, 2025
@RikkiGibson RikkiGibson deleted the fbp-directives-non-file-documents branch December 10, 2025 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE Feature - Run File #: and #! directives and file-based C# programs VSCode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants