rekkonnect / genericsanalyzer Goto Github PK
View Code? Open in Web Editor NEWA Roslyn analyzer for C# for generic types and functions
License: MIT License
A Roslyn analyzer for C# for generic types and functions
License: MIT License
Currently, it is speculated that no diagnostics will be emitted in the best case, with the worst being that the analyzer will crash attempting to report one on a nonexistent token.
An example case of this error occuring:
void Function
<
[ProhibitedTypes(typeof(int))]
T
>
(T v)
{
}
// usage
Function(5);
The error should be reported on the function's name, since there is no type to explicitly show it on.
This would only require the creation of a new attribute, InheritTypeConstraintsAttribute
, which has the following design specifications:
InheritTypeConstraintsAttribute(params string[] typeParameterNames)
.Example:
class ObjectConverter
<
[PermittedTypes(typeof(string), typeof(int))]
[OnlyPermitSpecifiedTypes]
T1,
[InheritTypeConstraints(nameof(T1))] // for functions this is illegal as of now
T2
>
{
}
Suggested by @KoziLord
The feature would implement a way for the user to specify the constriants in a more natural way through a variation of C#'s syntax for generic constraints that would be parsed and compiled. Syntax errors will be reported as diagnostic errors within the string.
The diagnostics reported for syntax errors within the scripting code will have to make use of a brand new rule prefix, GAS
. This is so that it can follow the respective C# error rule ID system, without reporting actual CS
errors.
Here is an example of an early concept of the implemenation:
[TypeConstraintScript(
@"
IEquatable<int>, IEnumerable<string>, new();
(class; unmanaged), IEquatable<long>, IEnumerable<char>
")]
In that example, the ,
denotes an AND, ;
denotes an OR, and parentheses work as better proioritizing the expression within them. AND has a higher priority than OR, meaning that the first line is a constraint, and the second line is another constraint, requiring that one of the two constraints be met.
More specifically, the type argument must meet one of the following criteria in this case:
IEquatable<int>
, IEnumerable<string>
, and have a new()
constructor, ORIEquatable<long>
, IEnumerable<char>
Passes:
struct S0 : IEquatable<int>, IEnumerable<string>
{
// implement interfaces
}
Fails:
struct S1 : IEquatable<long>, IEnumerable<char>
{
private string s;
// implement interfaces
}
TODO
Upon the solution's creation, the 5 initial projects (all but GenericsAnalyzer.Core
) were included in a GenericsAnalyzer
directory, instead of being in the solution's root directory. For consistency (and to avoid some notable VS bugs when it comes to project dependencies), it would be good if they were all migrated to the root solution directory.
Title currently sucks but the meaning is conveyed in some way or another
This feature enables the end user to prevent usage of a type with type arguments that may match some criteria, with regards to other type arguments. For example, the currently considered possibilities are:
T1 != T2
)
sizeof(T1) == a * sizeof(T2)
)
class
, struct
, unmanaged
)
class
, the struct
, or the unmanaged
constraintnotnull
, new()
(could be adjusted by the time shapes arrive)TODO
The attribute should be named ProhibitNotPermittedTypes
or OnlyPermitSpecifiedTypes
.
That feature would also come with the following design specifications:
Currently, the permitted type argument analyzer alone could use the following diagnostics:
Partial interfaces are not properly supported in the type constraint profile feature.
The issues are tracked in #50
For diagnostics emitted in attribute argument syntax nodes that are typeof
expressions, the error lies on the types themselves, not the fact that a typeof
expression was used. Therefore, semantically it would make more sense to only highlight the type that was used instead of the whole expression.
Another benefit is that it reduces clutter by having smaller chunks of code being highlighted with red squiggles.
Smaller type names will be harder to distinguish an error on. The main problem would be those with too short names, of up to about 3 letters which seem to be common enough. In a dense document, along with the syntax that generic type arguments come with, users could have a harder time distinguishing where the error is at without resorting to having the IDE navigate them.
Current:
[Attribute(__typeof(int*)__, __typeof(void)__)]
Proposed:
[Attribute(typeof(__int*__), typeof(__void__)]
The feature revolving around presence of InheritBaseTypeUsageConstraintsAttribute
in a type parameter does not properly evaluate the usage of the marked type parameter in the inherited types' type arguments.
In fact, what this would do is inherit the type constraints system from type parameters in the base type whose names matched the marked one's. This means that in the following example:
class A
<
T,
U,
V,
W
>
class B
<
[InheritBaseTypeUsageConstraints]
U,
[InheritBaseTypeUsageConstraints]
V,
[InheritBaseTypeUsageConstraints]
T
>
: A<U, V, T, V>
when instead the following should happen:
This bug is being fixed in #33; the test case for GA0001 has been updated to properly reflect this fix
The attribute, named InheritBaseTypeUsageConstraintsAttribute
should automatically inherit type constraints from the specific type's usage in base types.
For example:
class A
<
[PermittedTypes(typeof(int), typeof(short))]
[OnlyPermitSpecifiedTypes]
T
>
{ }
class B
<
[InheritBaseTypeUsageConstraints]
T
> : A<T>
{ }
The type constraint system for B.T should only permit usage of the types int
and short
, just like A.T's.
This only applies to types that may use their declared type arguments when inheriting other types.
Suggested by @ZacharyPatten
This feature is about having specific constraints about requiring certain members, including fields, properties, functions, events, and so on.
The available constraint information would be:
For properties or events:
For functions:
Such constraints can also be generalized on type constraint profiles.
This feature introduces 6 new attributes that can be assigned to both interfaces (type constraint profiles) and directly on the type parameters themselves.
Each of those attributes declares that a required member of the specified member kind be declared with the aforementioned properties.
public class RequiredFieldTypeConstraintAttribute { }
public class RequiredPropertyTypeConstraintAttribute { }
public class RequiredEventTypeConstraintAttribute { }
public class RequiredMethodTypeConstraintAttribute { }
public class RequiredConstructorTypeConstraintAttribute { }
The only unsupported feature of attributed declaration is generic type constraint clauses. This is intentionally unsupported due to the complexity it adds to both the analyzer, and the end user that may wish to apply such constraints. Instead, templated declaration is preferred.
The aforementioned attributes may also be used in declaring the required member from an already explicitly declared member, acting as a template. In other words, a member found in a (preferably sealed) instance class type can act as a template for a required member declaration. Its accessibility, name, return type, etc. will all be taken into account. Instance class types are preferred for this case so that accessibility does not require an extra declaration attribute. An additional analyzer-internal identifier can be also declared on the member through the RequiredMemberTypeConstraintTemplateAttribute
attribute.
Currently, the docs/usage.md
file only covers the basics of the analyzer, which are not sufficient through the most recent changes of adding new features. The usage docs should be changed accordingly:
docs/guides
which will contain such docs about usage of the analyzerusage.md
should be split into 4 separate documents, or chapters, that will cover the single most basic featureAdd a Reason
property in the type constraint attributes. This should probably only be allowed for prohibition attributes.
The feature would display the reason behind prohibiting usage of the specified type (or type argument) in emitted GA0001 and GA0017 diagnostics. It would be accompanied by the source of the prohibition.
There is a great portion of developers that use Rider, so this analyzer should also be available to that IDE.
Currently, the tests for GA0008 are ignored because they will fail due to some issue with the resulting document's syntax node trivia.
The cause for this problem is unknown.
There is the case of using both the base type parameter inheritance and a locally declared type parameter inheritance attribute, in which case conflicting rules cannot be prioritized. This should yield an error:
The project is currently on .NET Standard 2.0. This dependency was used with creating the VSIX in mind. Ever since its deprecation, there is no real reason to not use .NET 5.0, since analyzers using that version are supported by the latest VS versions.
For the time being, this is only tested on whether, within the same project, the types are correctly identified and marked. It is not known if it works when the type is from a dependency to another project or a package.
This will definitely require some unit tests dedicated for that.
Idea from JiiLib
All of the above filters can be exclusive or excluded. An exclusive filter denotes that no other type is permitted (equivalent to "only"), whereas an excluded filter denotes that the specified type category is prohibited (equivalent to "not").
TODO
TypeConstraintProfileAttribute
, as in the example:[TypeConstraintProfile]
interface IExampleProfile { }
Type constraint profile interfaces can be inherited. Inheriting automatically inherits the base profiles' constraints.
Profile usage can be achieved through the InheritProfileTypeConstraintsAttribute
:
[InheritProfileTypeConstraints(typeof(IExampleProfile))]
T
Using the profile automatically inherits the profile's type constraints. Multiple profiles may be used simultaneously. Conflicts emit GA0022.
Profile groups may restrict usage of multiple profiles that belong to the same group. Profile group interfaces may be declared like this:
[TypeConstraintProfileGroup]
interface IExampleProfileGroup { }
A profile may belong to multiple groups. Assigning profiles to groups happens in the attribute constructor:
[TypeConstraintProfile(typeof(IExampleProfileGroup), typeof(IExampleProfileGroup1))]
interface IExampleProfile { }
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.