代码合同错误警告

时间:2016-01-05 03:35:03

标签: c# code-contracts

我最近在一个非常大的项目中添加了代码合同。经过几百次警告后,添加断言来安抚检查器,我留下一些警告似乎显然是不正确的!这可能是我可以做的最简单的例子(完整代码是here,如果您认为细节可能很重要):

protected Thing DoStuff(A a)
{
    Contract.Requires(a != null);
    //CodeContracts: Consider adding the postcondition Contract.Ensures(Contract.Result<Thing>() == null); to provide extra-documentation to the library clients

    D outResult;
    var result = DoSomething(a, out outResult);
    if (result == null)
        return null;

    return new Thing(outResult, result);
}

这个建议显然是错误的(我有单元测试从这个函数返回非空值来证明它)!唯一可行的方法是&#34; DoSomething&#34;也总是返回null,但它没有提出任何建议。

编辑:我通过完全重写DoSomething方法以不使用out结果来修复上述问题(而是返回包含outResult和result的元组)。但是我还有其他的错误警告我想解决,可能也有相同的根本原因。

所以这真的是两个问题:

  1. 做错了什么或遗漏了明显的东西?
  2. 假设CC只是错误我将来可以做些什么来缓解这类问题 - 至少隐藏警告!

1 个答案:

答案 0 :(得分:4)

Re:Contract.Ensures返回值

在您的MVCE和生产代码中,遵循&#39;举证责任&#39;在上游,问题几乎肯定是使用被调用的SelectScript扩展方法(或MVCE中的DoSomething),其中静态分析器推断(可能不正确)扩展方法总是返回null,因此在调用方法SelectSingle中,将始终选择第一个分支(也返回null),因此建议使用null的后置条件。

我无法找到SelectScript的代码,但是在VS 2013更新4 / CC 1.7.11202.10上,我只能通过显式返回SelectScript中的空来重复“合同”警告。启用&#34;推断确保&#34;静态检查选项,或者通过明确地将Contract.Ensures(Contract.Result<ScriptReference>() == null);添加到SelectScript,例如确保推断:

public static ScriptReference SelectScript(
    this IEnumerable<KeyValuePair<float, KeyValuePair<string, string>[]>> tags,
    Func<double> random,
    Func<KeyValuePair<string, string>[], Type[], ScriptReference> finder,
    out KeyValuePair<string, string>[] selectedTags,
    Type t)
{
    selectedTags = null;
    return null;
}

在调用方法SelectSingle中生成相同的警告:

  

CodeContracts:考虑添加后置条件Contract.Ensures(Contract.Result()== null);为库客户提供额外的文档

但是,对我来说,分析器似乎确实正确地推断出下面的代码有分支,它们都返回null和非null,并且不建议调用者使用前置条件:

    public static ScriptReference SelectScript(
        this IEnumerable<KeyValuePair<float, KeyValuePair<string, string>[]>> tags,
        Func<double> random,
        Func<KeyValuePair<string, string>[], Type[], ScriptReference> finder,
        out KeyValuePair<string, string>[] selectedTags,
        Type t)
    {
        Contract.Requires(random != null);

        selectedTags = null;
        return (random() > 0.5)
            ? null
            : new ScriptReference();
    }

Re:Contract.Ensures on out value

出于兴趣,还可以使用out将合同添加到Contract.ValueAtReturn参数中 - reference,第2.2.3节第8页。

e.g。如果您仍然在 out 参数上收到警告,则可以使用grek40的想法通过将其添加到SelectScript来抑制来电者中的警告:

 Contract.Ensures(Contract.ValueAtReturn(out selectedTags) == null ||
                  Contract.ValueAtReturn(out selectedTags) != null);