Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interceptors open issues for C# 13 #7835

Merged
merged 24 commits into from
May 14, 2024
Merged

Interceptors open issues for C# 13 #7835

merged 24 commits into from
May 14, 2024

Conversation

RikkiGibson
Copy link
Contributor

Related to #7009

@RikkiGibson RikkiGibson requested a review from a team as a code owner January 10, 2024 23:43
proposals/interceptors-issues-2024-01.md Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved

Having looked at several strategies to address here, including adjusting when the build system passes `/pathmap` to the compiler, it seems the most straightforward way is to permit relative paths. This is a strategy which is already proven out by `#line` directives.

When a relative path appears in an `[InterceptsLocation]`, we will resolve it relative to the path of the containing file.
Copy link
Member

Choose a reason for hiding this comment

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

Need to add a note that we will normalize / and / during this process that way paths are portable across operating system builds. Effectively the resolution for relative paths here will match what happens in #line today. What works there will work in interceptors

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since source generators are already creating the #line directives with relative paths, even before they were run as Roslyn extensions, I'm wondering how they are doing that. Perhaps this is something generators already get wrong regularly, but isn't noticed as much, as the only impact is on debugging in a different environment than we generated the code in, rather than completely failing to build.

proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
- it's in the expression of expression statement `Call();`
- it's in the receiver of invocation expression `Call()`.

This sequence could be encoded somehow and stuffed into the attribute. This would make it so that edits in other method bodies etc. will not necessarily require regenerating the interceptor. However, it's not clear how source generators, or the generator driver, could make use of this information in order to reduce the frequency of rerunning generators. Also, public API would likely be needed for this in order for generators to create the expected InterceptsLocationAttribute argument.
Copy link
Member

Choose a reason for hiding this comment

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

Another potential is we could both

  1. Largely take the recommendations here about using relative file paths
  2. Declare that the identifier passed to InterceptsLocation is opaque

Essentially tell customers

The value passed into InterceptsLocation is opaque. The current implementation is an encoding of relative paths + line column but that could change at any time. Conforming generators should not depend on this encoding. Instead all arguments to InterceptsLocation should be generated by calling GetInterceptsLocation(syntaxNode, hintName).

That would require using the API I suggested above but taking a SyntaxNode (thus has line / column + path) instead of SyntaxTree.

I'm not saying we should do this but it is an item to consider.

proposals/interceptors-issues-2024-01.md Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved
proposals/interceptors-issues-2024-01.md Outdated Show resolved Hide resolved

public static class CSharpExtensions
{
+ public static IMethodSymbol? GetInterceptorMethod(this SemanticModel model, InvocationExpressionSyntax invocation, CancellationToken cancellationToken = default);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some feedback I got includes:

  1. What about IOperation? Do we have to get the .Syntax out of the operation node and cast it? Or can we just add a property to IInvocationOperation?
  2. What about future additional member kinds? Will we add additional public API for additional kinds?

}
```

`GetFilePath()` will return the absolute file path which the generated file *would be written to* if `$(EmitCompilerGeneratedFiles)` is true in the project. Additionally, the syntax tree for the file added by a subsequent call to `AddSource` using the same `hintName` will have a `FilePath` which exactly matches the result of `GetFilePath()`.
Copy link
Member

Choose a reason for hiding this comment

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

This is assuming that as a generator author my AddSource calls look like this:

context.AddSource(context.GetFilePath("GeneratedRouteHandlers.g.cs"), mySourceText)

If so, can we make it implicit so that I can just write:

context.AddSource("GeneratedRouteHandlers.g.cs", mySourceText)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes my intent here matches what you're saying. AddSource will always take a hintName, not an absolute path. Also, it's feeling unclear to me now if GetFilePath() API is worth it, if we also have to add GetInterceptsFilePath() or similar API anyway.


For method invocations like `receiver.M(args)`, we use the location of the `M` token to intercept the call. Perhaps for property accesses like `receiver.P` we can use location of the `P` token. For constructors should use the location of the `new` token.

For member usages such as user-defined implicit conversions which do not have any specific corresponding syntax, we expect that interception will never be possible.
Copy link
Member

Choose a reason for hiding this comment

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

Should a section on dotnet/roslyn#68218 be included somewhere in the doc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, and it will also be useful to get a sense of how urgent it is to get it done in C# 13.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW, it feels like the real kicker here is not just that we want generics support (through something more sophisticated like a unification approach), but that we actually want the interceptor to be able to have a more specific signature than the original method. e.g. in this case where in order for type parameters to propagate through the system, the interceptor parameter needs to be Func<TypeParam> even though the original parameter type is Delegate.

@RikkiGibson RikkiGibson marked this pull request as draft January 25, 2024 21:43
@RikkiGibson
Copy link
Contributor Author

@jaredpar I consider this ready to merge, please give a review and/or assign reviewers.

I'd like to send a follow up PR which essentially migrates the speclet from the Roslyn repo (linked in this document), into this same document, and renames it to match the other proposal docs (e.g. renaming it to interceptors.md, including the speclet content first, then as a subsequent section include these open questions.)

@RikkiGibson RikkiGibson merged commit e1e6c41 into main May 14, 2024
1 check passed
@RikkiGibson RikkiGibson deleted the interceptors-2024-01 branch May 14, 2024 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants