-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Validate generic arguments in using static
directives
#30737
Validate generic arguments in using static
directives
#30737
Conversation
public void UsingStaticGenericConstraint() | ||
{ | ||
var code = @" | ||
using static Test<System.String>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the diagnostic we produce will squiggle all of Test<System.String>
instead of just System.String
. This is maybe not perfect (why not squiggle the particular type argument that's wrong?) but shouldn't impact usability much.
var typeSymbol = (TypeSymbol)@using.NamespaceOrType; | ||
var corLibrary = typeSymbol.ContainingAssembly.CorLibrary; | ||
var conversions = new TypeConversions(corLibrary); | ||
typeSymbol.CheckAllConstraints(conversions, @using.UsingDirective.Name.Location, semanticDiagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there's an NRE in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like UsingDirective can be null in scripting scenarios? It seems like a location is always necessary in order to provide diagnostics. Not sure what to do here when @using.UsingDirective
is null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like UsingDirective can be null in scripting scenarios? Do I need to supply an alternate location in such cases?
We usually use NoLocation.Singleton
in situations when there is no specific syntax to refer to.
In reply to: 227996503 [](ancestors = 227996503)
foreach (var @using in Usings) | ||
{ | ||
// Check if `using static` directives meet constraints. | ||
if (@using.NamespaceOrType.IsType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth explicitly checking if this is a static using? Today yes when NamespaceOrType.IsType
is true then it's a using static
. That's possibly something that is not going to be true in the future though. Think it might benefit from a more explicit check. Possibly add a property to NamespaceOrTypeUsingDirective
called IsStatic
that does this under the hood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth explicitly checking if this is a static using?
It feels like it shouldn't matter whether the using is static or not, the constraints should be satisfied anyway.
In reply to: 227993769 [](ancestors = 227993769)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not certain, but I'm leaning toward no because it seems like any conceivable feature that involves referencing a type in a using directive should still have the compiler check constraints on that type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
if (@using.NamespaceOrType.IsType) | ||
{ | ||
var typeSymbol = (TypeSymbol)@using.NamespaceOrType; | ||
var corLibrary = typeSymbol.ContainingAssembly.CorLibrary; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var corLibrary = typeSymbol.ContainingAssembly.CorLibrary; [](start = 20, length = 58)
I think it is more appropriate to request CorLibrary from _compilation.SourceAssembly
rather than from the assembly that defines the type. The type name in the using was resolved in context of the _compilation
. We will also be able to share a single TypeConversions
instance to check all types. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 2fb9e18
// (2,14): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test<T>' | ||
// using static Test<System.String>; | ||
Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Test<System.String>").WithArguments("Test<T>", "T", "string").WithLocation(2, 14)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} [](start = 8, length = 1)
Consider testing constraint errors in containing types and nested types as well.
using static A<A<int>[]>.B;
class A<T> where T : class
{
internal static class B { }
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 53b307e
{ | ||
var typeSymbol = (TypeSymbol)@using.NamespaceOrType; | ||
var corLibrary = _compilation.SourceAssembly.CorLibrary; | ||
var conversions = new TypeConversions(corLibrary); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to recreate conversions
for every type or can it be re-used here? Believe it can be re-used as it's the same for every iteration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. There's no reason to believe it will change per-using.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that basically the same conversions object is created in order to check constraints on using aliases. Any benefit in trying to lazy-load it on the compilation or something and reuse it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that basically the same conversions object is created in order to check constraints on using aliases. Any benefit in trying to lazy-load it on the compilation or something and reuse it?
It is probably safe to use Conversions cached on a compilation in all these places
In reply to: 228274243 [](ancestors = 228274243)
@@ -2070,8 +2070,11 @@ AbstractClass Test() | |||
}"; | |||
var comp = CreateCompilation(text); | |||
comp.VerifyDiagnostics( | |||
// (2,14): error CS0311: The type 'object' cannot be used as type parameter 'T' in the generic type or method 'Crash<T>'. There is no implicit reference conversion from 'object' to 'CrashTest.Crash<object>.AbstractClass'. | |||
// using static CrashTest.Crash<object>; | |||
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "CrashTest.Crash<object>").WithArguments("CrashTest.Crash<T>", "CrashTest.Crash<object>.AbstractClass", "T", "object").WithLocation(2, 14), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test contains a using static which violates a generic constraint. Before, we emitted a diagnostic on the usage of a class contained within the type imported via using static
, but it seems correct to add a diagnostic to the using static
directive itself here too.
Determinism test is broken. Weird.
|
88e9cca
to
e723ad8
Compare
CI is all good. Please review @AlekseyTs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (iteration 8)
…out-if-error-message * origin/master: (1712 commits) User-defined operator checks should ignore differences in tuple member names (dotnet#30774) Attempted fix for correctly reporting error CS1069 when using implicit namespaces (dotnet#30244) Invert the binding order of InitializerExpressions and CreationExpressions (dotnet#30805) Use Arcade bootstrapping scripts (dotnet#30498) Ensure that the compilers produce double.NaN values in IEEE canonical form. (dotnet#30587) Remove properties set in BeforeCommonTargets.targets Fix publishing of dependent projects Contributing page: reference Unix build instructions Delete 0 Propagate values from AbstractProject to VisualStudioProjectCreationInfo Fix publishing nuget info of dev16.0.x-vs-deps branch Revert "Add a SetProperty API for CPS to passing msbuild properties" Validate generic arguments in `using static` directives (dotnet#30737) Correct 15.9 publish data Enable test. Do not inject attribute types into .Net modules. Add a SetProperty API for CPS to passing msbuild properties Revert "add beta2 suffix to dev16 branch" Fix references Remove commit sha from package versions ...
Resolves #30726
Looks like there is a separate Validate() phase in Imports which takes place after binding, probably because attempting certain validations during binding can result in a stack overflow.
This PR adds to the Imports.Validate method to validate constraints on
using static
. It was already validating constraints on using aliases which explains what we were seeing before @AlekseyTs.