[Proposal] String Interceptors #9108
Unanswered
kg
asked this question in
Language Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
String Interceptors
Summary
String Interceptors are a C# compiler feature that allows substituting a method call - to the interceptor - for a string literal (including interpolated strings) at compile time. This substitution occurs by having the interceptor declare the source locations of the string literals that it intercepts. This provides a limited facility to change what existing string literals evaluate to by adding new code to a compilation (e.g. in a source generator).
Motivation
At present C# and the .NET BCL provide excellent tooling for high-performance string formatting, but none of that tooling is applicable when you wish to localize a formatted string. The process of taking code full of
$""
literals and preparing it for localization is complex and error-prone, in part due to the need to replace named expansions like$"Good afternoon, {firstName} {lastName}"
with"Good afternoon, {0} {1}"
, destroying useful semantic information. For non-interpolated strings the process of localizing them is still an extremely manual one.String interceptors allow the creation of automated localization tooling in the vein of industry-standard tools like gettext with minimally intrusive changes to the compiler and language, leveraging existing investments across the ecosystem into interception, source generators, CompositeFormat, and resx files. Use of string interceptors allows building a high-performance solution that works for most string literals and integrates with existing localization pipelines.
Detailed design
InterceptsStringAtLocationAttribute
A method indicates that it is a string interceptor by adding one or more
[InterceptsStringAtLocation]
attribute(s). These attributes refer to the source locations of the string literals that it intercepts.[InterceptsStringAtLocation]
attributes included in source are emitted to the resulting assembly, just like other custom attributes.File-local declarations of this type (
file class InterceptsStringAtLocationAttribute
) are valid and usages are recognized by the compiler when they are within the same file and compilation. A generator which needs to declare this attribute should use a file-local declaration to ensure it doesn't conflict with other generators that need to do the same thing.Interceptable strings
Any string literal in the following categories (subject to future expansion) can be intercepted:
Attempting to intercept a string literal not included in the above set of categories produces a compile-time error.
Attempting to intercept a string literal in the definition of a compile-time constant i.e.
const s = "hello"
produces a compile-time error.Attempting to intercept a string literal within an attribute's argument list i.e.
[AttributeName("literal")]
produces a compile-time error.Attempting to intercept a non-string-literal expression of any type - string or otherwise - as if it were a string literal produces a compile-time error.
Location encoding
The attribute's arguments and their encoding are identical to those in the Interceptors specification.
Arity
String Interceptors cannot be declared in generic types at any level of nesting.
String Interceptors are not permitted to have optional parameters, varargs, params arrays, or default parameter values.
String Interceptors must be either be non-generic, or have a signature exactly matching the types and order of the interpolated expressions within an interpolated string.
For a non-interpolated string, this means they must accept no parameters. For an interpolated string with one interpolated expression of type int, the interceptor must accept one argument of type int, and so on.
If a string interceptor is generic it must accept a number of type parameters exactly matching the number of interpolated expressions within the string literal, and each parameter must be of the corresponding type. i.e. for an interpolated string with three interpolated expressions, the signature must exactly match that shown below:
Allowing string interceptors to be generic makes it possible for them to intercept interpolated strings where the interpolated expressions are of types not accessible at the string interceptor's declaration site.
The return type of the string interceptor is not required to be
System.String
. This enables the interceptor to capture information about an interpolated string for later use.Conflicting interceptors
If more than one string interceptor refers to the same location, it is a compile-time error.
Invalid interception
If an
[InterceptsStringAtLocation]
attribute is found in the compilation which does not refer to the location of a string literal included in the list of categories specified above, it is a compile-time error.Interceptor accessibility
Accessibility rules are identical to those in the Interceptors specification.
Editor experience
String interceptors are treated like a post-compilation step in this design.
User opt-in
To use interceptors, the user project must specify the property
<StringInterceptorsNamespaces>
. This is a list of namespaces which are allowed to contain string interceptors.It's expected that each entry in the StringInterceptorsNamespaces list roughly corresponds to one source generator. Well-behaved components are expected to not insert string interceptors into namespaces they do not own.
Beta Was this translation helpful? Give feedback.
All reactions