Mise à jour du profil à vie dans Visual Studio 2019 Preview 2

Le profil de durée de vie des directives C ++ Core , qui fait partie des directives C ++ Core , vise à détecter les problèmes de durée de vie, comme les pointeurs et les références pendantes, dans le code C ++. Il utilise les informations de type déjà présentes dans la source ainsi que quelques contrats simples entre les fonctions pour détecter les défauts au moment de la compilation avec une annotation minimale.





Original dans le blog

Ce sont les contrats de base que le profil attend du code:


  1. N'utilisez pas de pointeur potentiellement suspendu.
  2. Ne passez pas un pointeur potentiellement suspendu à une autre fonction.
  3. Ne renvoyez aucun pointeur potentiellement suspendu à partir d'une fonction.

Pour plus d'informations sur l'historique et les objectifs du profil, consultez le blog de Herb Sutter sur la version 1.0 .


Nouveautés de Visual Studio 2019 Preview 2


Dans l'aperçu 2, nous avons fourni une version d'aperçu du vérificateur de profil à vie qui implémente la version publiée du profil à vie . Ce vérificateur fait partie des vérificateurs de base C ++ dans Visual Studio.


  • Prise en charge des itérateurs, des vues de chaîne et des étendues.
  • Meilleure détection des types de propriétaire et de pointeur personnalisés, ce qui permet aux types personnalisés qui se comportent comme des conteneurs, des pointeurs propriétaires ou des pointeurs non propriétaires de participer à l'analyse.
  • Les règles par défaut sensibles au type pour les conditions de pré et de post d'appel de fonction aident à réduire les faux positifs et à améliorer la précision.
  • Meilleur support pour les types d'agrégats.
  • Amélioration générale de l'exactitude et des performances.
  • Une analyse nullptr simple.

Activation des règles du vérificateur de profil à vie


Les règles du vérificateur ne sont pas activées par défaut. Si vous souhaitez essayer les nouvelles règles, vous devrez mettre à jour le jeu de règles d'analyse de code sélectionné pour votre projet. Vous pouvez soit sélectionner les «Règles de durée de vie de C ++ Core Check» - qui n'activent que les règles de profil de durée de vie - ou vous pouvez modifier votre ensemble de règles existant pour activer les avertissements 26486 à 26489.


Capture d'écran de la page de propriétés d'analyse de code qui montre l'ensemble de règles C ++ Core Check Lifetime Rules sélectionné.

Capture d'écran de la page de propriétés d'analyse de code qui montre l'ensemble de règles C ++ Core Check Lifetime Rules sélectionné.


Des avertissements apparaissent dans la liste des erreurs lorsque l'analyse de code est exécutée (Analyser> Exécuter l'analyse de code), ou si vous avez activé l'analyse de code en arrière-plan, des erreurs de durée de vie s'affichent dans l'éditeur avec des gribouillis verts.


Capture d'écran montrant un avertissement du vérificateur de profil à vie avec un gribouillis vert dans le code source.

Capture d'écran montrant un avertissement du vérificateur de profil à vie avec un gribouillis vert dans le code source.


Des exemples


Pointeur pendant


L'exemple le plus simple - en utilisant un pointeur suspendu - est le meilleur endroit pour commencer. Ici, px pointe vers x , puis x laisse la portée, laissant px pendant. Lorsque px est utilisé, un avertissement est émis.


 void simple_test() { int* px; { int x = 0; px = &x; } *px = 1; // error, dangling pointer to 'x' } 

Pointeur de sortie pendant


Le retour de pointeurs pendants n'est pas non plus autorisé. Dans ce cas, le paramètre ppx est présumé être un paramètre de sortie. Dans ce cas, il est défini pour pointer sur x qui sort du champ d'application à la fin de la fonction. Cela laisse *ppx pendant.


 void out_parameter(int x, int** ppx) // *ppx points to 'x' which is invalid { *ppx = &x; } 

Vue des chaînes pendantes


Les deux derniers exemples étaient évidents, mais des instances temporaires peuvent introduire des bogues subtils. Pouvez-vous trouver le bogue dans le code suivant?


 std::string get_string(); void dangling_string_view() { std::string_view sv = get_string(); auto c = sv.at(0); } 

Dans ce cas, la vue de chaîne sv est construite avec l'instance de chaîne temporaire renvoyée par get_string() . La chaîne temporaire est ensuite détruite, ce qui laisse la vue de chaîne référençant un objet non valide.


Itérateur suspendu


Un autre problème de durée de vie difficile à détecter se produit lors de l'utilisation d'un itérateur invalidé dans un conteneur. Dans le cas ci-dessous, l'appel à push_back peut amener le vecteur à réallouer son stockage sous-jacent, ce qui invalide l'itérateur.


 void dangling_iterator() { std::vector<int> v = { 1, 2, 3 }; auto it = v.begin(); *it = 0; // ok, iterator is valid v.push_back(4); *it = 0; // error, using an invalid iterator } 

Une chose à noter à propos de cet exemple est qu'il n'y a pas de gestion spéciale pour 'std :: vector :: push_back'. Ce comportement sort des règles de profil par défaut. Une règle classe les conteneurs en tant que «propriétaire». Ensuite, lorsqu'une méthode non const est appelée sur le propriétaire, sa mémoire possédée est supposée invalidée et les itérateurs qui pointent vers la mémoire possédée sont également considérés comme invalides.


Propriétaire modifié


Le profil est normatif dans son orientation. Il s'attend à ce que ce code utilise idiomatiquement le système de types lors de la définition des paramètres de fonction. Dans cet exemple suivant, std::unique_ptr , un type 'Owner', est passé à une autre fonction par référence non const. Selon les règles du profil, les propriétaires transmis par référence non constante sont supposés être modifiés par l'appelé.


 void use_unique_ptr(std::unique_ptr<int>& upRef); void assumes_modification() { auto unique = std::make_unique<int>(0); // Line A auto ptr = unique.get(); *ptr = 10; // ok, ptr is valid use_unique_ptr(unique); *ptr = 10; // error, dangling pointer to the memory held by 'unique' at Line A } 

Dans cet exemple, nous obtenons un pointeur brut, ptr , vers la mémoire appartenant à unique . Ensuite, unique est passé à la fonction use_unique_ptr par référence non const. Puisqu'il s'agit d'une utilisation non-constante d' unique où la fonction pourrait faire n'importe quoi, l'analyse suppose que l' unique 'est invalidé d'une manière ou d'une autre (par exemple, unique_ptr :: reset), ce qui ferait ptr .


Plus d'exemples


Il existe de nombreux autres cas que l'analyse peut détecter. Essayez-le dans Visual Studio sur votre propre code et voyez ce que vous trouvez. Consultez également le blog de Herb pour plus d'exemples et, si vous êtes curieux, lisez le document Profil de la vie.


Problèmes connus


L'implémentation actuelle ne prend pas totalement en charge l'analyse telle que décrite dans l'article sur le profil de durée de vie. Voici les grandes catégories qui ne sont pas implémentées dans cette version.


  • Annotations - L'article présente des annotations (c'est-à-dire [[gsl::lifetime-const]] ) qui ne sont pas prises en charge. Cela signifie pratiquement que si les règles d'analyse par défaut ne fonctionnent pas pour votre code, vous ne pouvez pas faire grand-chose d'autre que supprimer les faux positifs.
  • Exceptions - Les chemins de gestion des exceptions, y compris le contenu des blocs catch , ne sont pas actuellement analysés.
  • Règles par défaut pour les types STL - Au lieu d'une annotation à vie, le document recommande que pour les rares fonctions membres du conteneur STL où nous voulons remplacer les valeurs par défaut, nous les traitions comme si elles étaient annotées. Par exemple, une surcharge de std::vector::at n'est pas const car elle peut renvoyer une référence non const - cependant nous savons que l'appeler est lifetime-const car il n'invalide pas la mémoire du vecteur. Nous n'avons pas terminé le travail pour faire cette annotation implicite de tous les types de conteneurs STL.
  • Captures Lambda - Si une variable de pile est capturée par référence dans un lambda, nous ne détectons pas actuellement si le lambda quitte la portée de la variable capturée.

     auto lambda_test() { int x; auto captures_x = [&x] { return x; }; return captures_x; // returns a dangling reference to 'x' } 

Conclure


Essayez le vérificateur de profil à vie dans Visual Studio 2019 Preview 2. Nous espérons qu'il vous aidera à identifier les problèmes de durée de vie dans vos projets. Si vous trouvez des faux positifs ou des faux négatifs, veuillez les signaler afin que nous puissions hiérarchiser les scénarios qui sont importants pour vous. Si vous avez des suggestions ou des problèmes avec cette vérification - ou toute fonctionnalité de Visual Studio - signalez un problème ou publiez sur la communauté des développeurs et faites-le nous savoir. Nous sommes également sur Twitter à @VisualC .

Source: https://habr.com/ru/post/fr437660/


All Articles