2010-12-04 4 views
2

J'ai passé quelques heures sur cette question ... voici mon code:Contrats - analyse statique mal repérage Condition à remplir

public static IEnumerable<T> Generate<T>(this Func<T> generator) where T : class 
{ 
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null); 
    Contract.Ensures(Contract.ForAll<T>(Contract.Result<IEnumerable<T>>(), item => item != null)); 

    if (generator == null) 
    yield break; 

    T t; 
    while ((t = generator()) != null) 
    yield return t; 
} 


public static IEnumerable<string> ReadLines(this TextReader reader) 
{ 
    Contract.Requires(reader != null); 
    Contract.Ensures(Contract.Result<IEnumerable<string>>() != null); 
    Contract.Ensures(Contract.ForAll<string>(Contract.Result<IEnumerable<string>>(), s => s != null)); 

    return Generate(() => reader.ReadLine()); 
} 

Pour une raison quelconque, la dernière ligne (l'invocation de générer()) est marqué comme "ensures unproven: Contract.ForAll<string>(Contract.Result<IEnumerable<string>>(), s => s != null)". Je ne vois pas comment cela peut être le cas - Generate garantit qu'il ne renverra pas d'éléments NULL, et l'analyseur ne s'en plaindra pas. Qu'est-ce que je fais mal? [Edit] Hmm ... tout d'un coup, je reçois un avertissement sur la fonction Générer - même chose, l'assurance n'est pas prouvée. Je pourrais jurer que cela ne se passait pas avant, mais en tout cas - maintenant le problème a bougé. Comment puis-je persuader l'analyseur qu'il n'y a aucun moyen de renvoyer des éléments Null? Ou, alternativement, où ai-je tort et la fonction peut retourner des éléments null? [Modifier] Blech ... this article dit "les conditions de publication (c.-à-d. Contrat.Assurance) ne sont pas supportées par le vérificateur statique" (dans les itérateurs) ... quelqu'un peut-il confirmer cela?

+0

pinailler: les gens dans le domaine utilisent le mot « correct » pour la capacité de l'analyseur à ne jamais diagnostiquer comme vraie une propriété qui ne l'est pas. J'ai momentanément pensé que cette question était très intéressante. Dans le contexte d'une vraie propriété diagnostiquée comme non prouvée, je recommanderais plutôt d'utiliser "imprécisément". –

+0

Je suis d'avis que les faux négatifs sont aussi faux que les faux positifs :) Dans les deux cas, je ne peux pas faire mon travail à cause de cela. –

Répondre

1

yield return ne fonctionnera pas actuellement avec Contract.Ensures en raison d'interactions entre le compilateur et ccrewrite.

mieux que vous pouvez faire pour le moment est d'écrire une méthode privée sans contrat, puis Assume les contrats nécessaires à une méthode publique:

private static IEnumerable<T> RealGenerate<T>(this Func<T> generator) where T : class 
{ 

    if (generator == null) 
    yield break; 

    T t; 
    while ((t = generator()) != null) 
    yield return t; 
} 

public static IEnumerable<T> Generate<T>(this Func<T> generator) where T: class 
{ 
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null); 
    Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<T>>(), item => item != null)); 

    var result = RealGenerate(generator); 
    Contract.Assume(result != null); 
    Contract.Assume(Contract.ForAll(result, item => item != null)); 
    return result; 
} 
+1

Hmm ... Je dois faire quelque chose de mal, ça ne marche toujours pas. La ligne avec "résultat de retour" a les gribouillis avec la légende "... assure non prouvé ...". –

+0

Désolé, je n'ai pas testé le code car je n'avais pas le temps - mais j'avais déjà utilisé ce modèle auparavant. L'analyse du conteneur (c'est-à-dire "ForAll" et "Exists") est également "en cours de développement", ce qui pourrait ne pas fonctionner pour le moment. En fait, si vous changez 'IEnumerable ' à' T [] 'ou' List 'cela fonctionne ... Je commence à penser que c'est quelque chose à voir avec la nature paresseuse de' IEnumerable 'ou quelque chose comme le fait que certains énumérables ne peuvent être énumérés qu'une seule fois ... cela peut valoir la peine de le demander sur les forums de contrats de code. – porges

+0

Oui ... J'avais besoin d'être paresseux, pour des raisons multiples (comme je lis d'un fichier texte, et je ne veux pas que tout le fichier soit en mémoire - je veux traiter chaque ligne séparément) –

Questions connexes