Ответ 1
Да, я думаю, что Roslyn и его проблемы с кодом - это именно тот инструмент. С их помощью вы можете анализировать код во время ввода и создавать ошибки (или предупреждения), которые отображаются как другие ошибки в Visual Studio.
Я попытался создать такую проблему с кодом:
[ExportSyntaxNodeCodeIssueProvider("PropertyExpressionCodeIssue", LanguageNames.CSharp, typeof(InvocationExpressionSyntax))]
class PropertyExpressionCodeIssueProvider : ICodeIssueProvider
{
[ImportingConstructor]
public PropertyExpressionCodeIssueProvider()
{}
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken)
{
var invocation = (InvocationExpressionSyntax)node;
var semanticModel = document.GetSemanticModel(cancellationToken);
var semanticInfo = semanticModel.GetSemanticInfo(invocation, cancellationToken);
var methodSymbol = (MethodSymbol)semanticInfo.Symbol;
if (methodSymbol == null)
yield break;
var attributes = methodSymbol.GetAttributes();
if (!attributes.Any(a => a.AttributeClass.Name == "PropertyExpressionAttribute"))
yield break;
var arguments = invocation.ArgumentList.Arguments;
foreach (var argument in arguments)
{
var lambdaExpression = argument.Expression as SimpleLambdaExpressionSyntax;
if (lambdaExpression == null)
continue;
var parameter = lambdaExpression.Parameter;
var memberAccess = lambdaExpression.Body as MemberAccessExpressionSyntax;
if (memberAccess != null)
{
var objectIdentifierSyntax = memberAccess.Expression as IdentifierNameSyntax;
if (objectIdentifierSyntax != null
&& objectIdentifierSyntax.PlainName == parameter.Identifier.ValueText
&& semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol is PropertySymbol)
continue;
}
yield return
new CodeIssue(
CodeIssue.Severity.Error, argument.Span,
string.Format("Has to be simple property access of '{0}'", parameter.Identifier.ValueText));
}
}
#region Unimplemented ICodeIssueProvider members
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxToken token, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxTrivia trivia, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
#endregion
}
Использование будет выглядеть следующим образом:
[AttributeUsage(AttributeTargets.Method)]
class PropertyExpressionAttribute : Attribute
{ }
…
[PropertyExpression]
static void Foo<T>(Expression<Func<SomeType, T>> expr)
{ }
…
Foo(x => x.P); // OK
Foo(x => x.M()); // error
Foo(x => 42); // error
В приведенном выше коде есть несколько проблем:
- Он полностью неоптимизирован.
- Возможно, потребуется еще немного проверки ошибок.
- Он не работает. По крайней мере, в текущем CTP. Выражение
semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol
рядом с концом всегда возвращаетnull
. Это связано с тем, что семантика деревьев выражений находится в не реализованных в настоящее время функциях.