Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

RikkiGibson
Copy link
Member

Closes #80006

note: this is currently in a majorly hacky state. Working out the API surface that we want from an SDK source package, by coping the impl and modifying.

Also includes changes from #80410 until we merge that

return builder.ToImmutable();
}

public static void FindDirectives(
Copy link
Member Author

Choose a reason for hiding this comment

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

@jjonescz wanted you to see the helper I extracted. This is what I have so far, for what the analyzer would call in order to get the diagnostics from "SDK layer".

// TODO2: no Path.GetRelativePath on netstandard
directiveText = PathUtilities.GetRelativePath(sourceDirectory, fullFilePath);
}
else if (!File.Exists(resolvedProjectPath))
Copy link
Member Author

Choose a reason for hiding this comment

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

In an ordinary analyzer, checking if files exist on disk is not ok, since nothing will trigger the analyzer to re-run if the disk state changes. I am unsure if this is also a significant problem for an analyzer which we only want to run in the editor. @jasonmalinowski for a spot check.

But, I don't know what could substitute for this. I don't think it's realistic to pass all the '*.csproj's' paths which could be used as additional files or something like that.

Copy link
Member

Choose a reason for hiding this comment

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

Note that I'm extracting this IO access into a separate "evaluation" phase in dotnet/sdk#51108. So the IDE could in theory choose to skip this, but that doesn't sound very nice, as then the IDE wouldn't get the relevant #:project diagnostics.

Copy link
Member Author

Choose a reason for hiding this comment

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

Once I get through some other feedback I will try this out in some scenarios where we also mess with the file system.

e.g. if we have a bad #:project ./path/to/project.csproj, and then we go create the project at that location on disk, what is the experience. Does just tabbing over to the editor make the error go away? Do you have to make edits? close/reopen the file? etc. As well as the opposite, deleting a csproj which is already present.

The thing is, creating and deleting csproj's is an infrequent enough event that, I feel like this analyzer can have a little file I/O, as a treat. Especially because the behavior doesn't depend on file contents

#pragma warning disable IDE0002 // Name can be simplified
var diagnosticBag = FileBasedPrograms.DiagnosticBag.Collect(out var diagnosticsBuilder);
#pragma warning restore IDE0002
AppDirectiveHelpers.FindDirectives(
Copy link
Member Author

Choose a reason for hiding this comment

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

@jjonescz call site for the newly extracted helper.

{
public const string DiagnosticId = "FileBasedPrograms";

private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
Copy link
Member

@jjonescz jjonescz Oct 8, 2025

Choose a reason for hiding this comment

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

I'm getting this warning when I open this in VSCode (and didn't see it reported by CI, so just FYI):

The symbol 'DiagnosticDescriptor.DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string?, string?, params string[])' is banned in this project: Analyzers should extend 'AbstractBuiltInCodeStyleDiagnosticAnalyzer' or 'AbstractCodeQualityDiagnosticAnalyzer' instead

<value>Property accessor can be simplified</value>
</data>

<!-- TODO2: should this live here? Or sdk source package gives us some other resource file? -->
Copy link
Member

Choose a reason for hiding this comment

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

I guess source packages can contain any sources, including resx and xlf, and if these strings need to be used inside SDK too, I don't see other option than having these in the package.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll try creating a new resource file next to AppDirectiveHelpers for short term, and hopefully it is something we can just move to the source package when ready.

There is no real overhead to creating an additional resource file vs reusing existing one, I think?


// TODO: should SimpleDiagnostics have IDs? message args? TextSpan?
// It feels unreasonable for users to suppress these.
// When these are present, the user cannot build/run, period.
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, currently we only have errors. We might want to add warnings in the future, but don't know about any candidates right now.

Copy link
Member

Choose a reason for hiding this comment

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

Future candidate is #r support. We've talked about enabling it for ease of conversion/pasting from CSX but marking it as a warning.

// When these are present, the user cannot build/run, period.
Diagnostic createDiagnostic(SyntaxTree syntaxTree, SimpleDiagnostic simpleDiagnostic)
{
const string FileBasedPrograms = nameof(FileBasedPrograms);
Copy link
Member

Choose a reason for hiding this comment

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

This looks unused.

internal static class AppDirectiveHelpers
{

#pragma warning disable RSEXPERIMENTAL003 // 'SyntaxTokenParser' is experimental
Copy link
Member

Choose a reason for hiding this comment

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

I think SyntaxTokenParser is not experimental anymore (but in the sdk we don't have that new version of roslyn yet)

var message = trivia.GetStructure() is IgnoredDirectiveTriviaSyntax { Content: { RawKind: (int)SyntaxKind.StringLiteralToken } content }
? content.Text.AsSpan().Trim()
: "";
// TODO: original impl was using more recent span-oriented APIs. Optimal sharing may be tricky.
Copy link
Member

Choose a reason for hiding this comment

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

Hm, that's sad that we need to target netstandard2.0, but it's probably fine, I don't expect the directive parsing to be perf critical (I just like to use modern APIs).

We also likely should ensure the source package is built against netstandard2.0 inside the sdk repo to verify the compatibility early.

// TODO2: no Path.GetRelativePath on netstandard
directiveText = PathUtilities.GetRelativePath(sourceDirectory, fullFilePath);
}
else if (!File.Exists(resolvedProjectPath))
Copy link
Member

Choose a reason for hiding this comment

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

Note that I'm extracting this IO access into a separate "evaluation" phase in dotnet/sdk#51108. So the IDE could in theory choose to skip this, but that doesn't sound very nice, as then the IDE wouldn't get the relevant #:project diagnostics.

// See the LICENSE file in the project root for more information.

// Largely copied from https://github.com/dotnet/sdk/blob/ce691a53dad7f2b6057c85c1e7fd7800dfe0788f/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs#L1410
// TODO: we'd like to extract a source package which makes this copy of the source unnecessary.
Copy link
Member

@jjonescz jjonescz Oct 8, 2025

Choose a reason for hiding this comment

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

This looks good to me overall. Do you intend to create the source package yourself in the sdk, or should I go do and prioritize that?

Btw, I think this source package will be useful also if we move some of the virtual project building logic to msbuild as we discussed. And I plan to look at that next.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll try creating a source package this week, once next round of feedback here is dealt with, and if I get stumped by something I will reach out for help.

I think it is not too difficult to build a source package locally, and adjust my local Roslyn enlistment to consume the package, which would allow determining that the package is working for the intended purpose.

Copy link
Member Author

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

Submitting comments

<value>Property accessor can be simplified</value>
</data>

<!-- TODO2: should this live here? Or sdk source package gives us some other resource file? -->
Copy link
Member Author

Choose a reason for hiding this comment

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

I'll try creating a new resource file next to AppDirectiveHelpers for short term, and hopefully it is something we can just move to the source package when ready.

There is no real overhead to creating an additional resource file vs reusing existing one, I think?

SourceFile sourceFile,
SyntaxTriviaList triviaList,
DiagnosticBag diagnostics,
ImmutableArray<CSharpDirective>.Builder builder)
Copy link
Member Author

Choose a reason for hiding this comment

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

note that analyzer has no need for this part. Possibly I will try to adjust factoring so that we don't need to create this, maybe make the builder nullable or something.

// TODO2: no Path.GetRelativePath on netstandard
directiveText = PathUtilities.GetRelativePath(sourceDirectory, fullFilePath);
}
else if (!File.Exists(resolvedProjectPath))
Copy link
Member Author

Choose a reason for hiding this comment

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

Once I get through some other feedback I will try this out in some scenarios where we also mess with the file system.

e.g. if we have a bad #:project ./path/to/project.csproj, and then we go create the project at that location on disk, what is the experience. Does just tabbing over to the editor make the error go away? Do you have to make edits? close/reopen the file? etc. As well as the opposite, deleting a csproj which is already present.

The thing is, creating and deleting csproj's is an infrequent enough event that, I feel like this analyzer can have a little file I/O, as a treat. Especially because the behavior doesn't depend on file contents

@RikkiGibson RikkiGibson force-pushed the fbp-live-directive-diagnostics branch from 6233d9b to e774d7a Compare October 10, 2025 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Live file-based program directive diagnostics

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.