Я думаю, вы можете сделать это, используя пользовательские атрибуты и анализ кода roslyn. Набросаю решение. Это должно по крайней мере решить первый вариант использования, когда вы инициализируете литерал.
Сначала вам понадобится настраиваемый атрибут, который применяется к вашей структуре, чтобы анализ кода мог знать допустимый диапазон:
[AttributeUsage(System.AttributeTargets.Struct)]
public class MinMaxSizeAttribute : Attribute
{
public int MinVal { get; set;}
public int MaxVal { get; set;}
public MinMaxSizeAttribute()
{
}
}
Здесь вы сохраняете минимальное и максимальное значение в атрибуте. Таким образом, вы можете использовать это позже при анализе исходного кода.
Теперь примените этот атрибут к объявлению структуры:
[MinMaxSize(MinVal = 0, MaxVal = 100)]
public struct Foo
{
//members and implicit conversion operators go here
}
Теперь информация о типе для структуры Foo
содержит диапазон значений. Следующее, что вам нужно, это DiagnosticAnalyzer
для анализа вашего кода.
public class MyAnalyzer : DiagnosticAnalyzer
{
internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor("CS00042",
"Value not allowed here",
@"Type {0} does not allow Values in this range",
"type checker",
DiagnosticSeverity.Error,
isEnabledByDefault: true, description: "Value to big");
public MyAnalyzer()
{
}
#region implemented abstract members of DiagnosticAnalyzer
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSyntaxTree, SyntaxKind.SimpleAssignmentExpression);
}
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
#endregion
private static void AnalyzeSyntaxTree(SyntaxNodeAnalysisContext context)
{
}
}
Это голый скелет для участия в анализе кода. Анализатор регистрируется для анализа заданий:
context.RegisterSyntaxNodeAction(AnalyzeSyntaxTree, SyntaxKind.SimpleAssignmentExpression);
Для объявлений переменных вам нужно будет зарегистрироваться для другого SyntaxKind
, но для простоты я буду придерживаться одного здесь.
Давайте посмотрим на логику анализа:
private static void AnalyzeSyntaxTree(SyntaxNodeAnalysisContext context)
{
if (context.Node.IsKind(SyntaxKind.SimpleAssignmentExpression))
{
var assign = (AssignmentExpressionSyntax)context.Node;
var leftType = context.SemanticModel.GetTypeInfo(assign.Left).GetType();
var attr = leftType.GetCustomAttributes(typeof(MinMaxSizeAttribute), false).OfType<MinMaxSizeAttribute>().FirstOrDefault();
if (attr != null && assign.Right.IsKind(SyntaxKind.NumericLiteralExpression))
{
var numLitteral = (LiteralExpressionSyntax)assign.Right;
var t = numLitteral.Token;
if (t.Value.GetType().Equals(typeof(int)))
{
var intVal = (int)t.Value;
if (intVal > attr.MaxVal || intVal < attr.MaxVal)
{
Diagnostic.Create(Rule, assign.GetLocation(), leftType.Name);
}
}
}
}
}
Что делает анализатор, так это проверяет, имеет ли тип слева связанный с ним MinMaxSize
, и если да, то он проверяет, является ли правая сторона литералом. Когда это литерал, он пытается получить целочисленное значение и сравнивает его с MinVal
и MaxVal
, связанными с типом. Если значения превышают этот диапазон, он сообщит об ошибке диагностики.
Обратите внимание, что весь этот код в основном не тестировался. Он компилируется и проходит некоторые базовые тесты. Но это предназначено только для иллюстрации возможного решения. Дополнительную информацию см. в документах Rsolyn.
Второй случай, который вы хотите рассмотреть, является более сложным, поскольку вам нужно будет применить анализ потока данных, чтобы получить значение x
.
person
Kolja
schedule
14.11.2015