Как использовать SemanticModel для проверки того, была ли протестирована переменная?

Я работаю над расширением Roslyn для предупреждения о незащищенном .Value доступе к Nullable<T> значениям.

Это обеспечивает следующее поведение:

Предупреждение: Nullable 'x' должен быть проверен на нулевые значения перед доступом к свойству 'Value'

Это расширение уже вроде как работает, но код для проверки того, является ли доступ «безопасным», является своего рода взломом. Теперь я просто просматриваю синтаксическое дерево в поисках if заявления.

Этот подход довольно уродлив и выдает кучу недопустимых предупреждений.

Вот несколько примеров случаев, когда доступ x.Value должен быть безопасным:

int y;
int? x = foo();

y = x != null ? x.Value : 42;

if (x > 4)
  y = x.Value;

if (x != null && someExpr) // With && only one branch needs to be a test
  y = x.Value;

if (x == 3 || x == 4) // With || both branches must be a test
  y = x.Value;

if (x == null) return; // Exit method body before access .Value
y = x.Value;

Есть ли способ использовать SemanticModel для правильного написания этого теста?

Одна из вещей, о которых я думаю, это сделать абстрактную интерпретацию над синтаксическим деревом. Но это кажется большой работой, и я надеюсь, что полноценный абстрактный интерпретатор не нужен.

Я не совсем уверен, как анализ мертвого кода реализован в Roslyn, но, похоже, это как-то связано с этим.


person Tom Lokhorst    schedule 05.05.2014    source источник
comment
Я думаю, что в вашем примере с примечанием есть ошибка: если bar == true и x == null, произойдет сбой. || Я думаю, что оператор должен быть симметричным с точки зрения безопасности (хотя левая сторона может подразумевать безопасность с правой стороны).   -  person Jason Malinowski    schedule 06.05.2014
comment
Это должно быть &&, а не ||   -  person SLaks    schedule 06.05.2014
comment
И то, о чем вы просите, называется анализом достижимости.   -  person SLaks    schedule 06.05.2014
comment
Реализация Roslyn является внутренней, и ее можно найти в источнике. .roslyn.codeplex.com/#Microsoft.CodeAnalysis.CSharp/   -  person SLaks    schedule 06.05.2014
comment
@JasonMalinowski Верно, это была ошибка. Я исправил пример.   -  person Tom Lokhorst    schedule 06.05.2014
comment
@SLaks: реализация представляет собой общедоступный API, вы можете просто вызвать SemanticModel.AnalyzeDataFlow. Тому нужно что-то покрепче.   -  person Jason Malinowski    schedule 06.05.2014


Ответы (2)


Я еще не играл с Рослин. Но, будучи пользователем ReSharper, который также имеет именно эту функцию, я могу сделать следующее утверждение:

По моим наблюдениям, ReSharper смотрит только на последнее недавнее использование соответствующего символа (типа Nullable‹>). Это итеративное доказательство. Если переменная использовалась (очевидно) успешно, она безопасна для постоянного использования. Затем проверка предыдущего использования подтверждает, что это снова безопасный доступ. Таким образом: проверьте предыдущий доступ либо на другой предыдущий доступ, либо на сравнение восстановления с нулевым значением, чтобы убедиться, что он безопасен.

if( x != null)
     x.access

Пример:

var z = x.Value; // unsafe because it is first and un-tested
var y = x.Value; // safe because it is not first and therefore dependent on first access
person Robetto    schedule 03.06.2014
comment
Мне нравится ваша идея (или ReSharpers ;-)) но разве это не больше оптимизация, чем реальное решение проблемы? Вам все равно придется доказать, что первый доступ к переменной является безопасным или нулевой проверкой (либо явной с помощью != null или HasValue, либо неявной путем сравнения, ненулевого присваивания и т. д.). - person andyp; 03.06.2014
comment
Это, вероятно, заканчивается рекурсивной проблемой. Так сказать: докажите первый доступ и дальнейшие безопасны. - person Robetto; 30.06.2014

Для решения этой проблемы требуется анализ потока, которого Roslyn еще не делает.

person Neal Gafter    schedule 05.06.2014