Analyse interprocédurale : détecter les déréférences nil pour éviter le crash de votre code
La nouvelle version GoLand 2025.2 introduit un puissant ensemble de nouvelles fonctionnalités et d’améliorations conçues pour vous aider à écrire un code Go plus sûr et plus fiable. Pour une description détaillée des toutes les mises à jour, veuillez consulter les notes de publication.
Cet article est consacré à l’une des nouvelles fonctionnalités les plus significatives de GoLand : l’analyse de code interprocédurale pour détecter les déréférencements des pointeurs nil
. En vous aidant à détecter des bugs subtils, souvent difficiles à identifier lors des processus de révision et de test du code, cette amélioration rend votre code de production plus stable et plus facile à maintenir.
L’équipe GoLand a concentré ses efforts sur l’augmentation de la puissance et de l’intelligence du processus d’analyse statique, afin d’améliorer votre expérience de développement et de vous éviter les moments de panique frustrants lors de l’exécution. Si vous souhaitez essayer cette fonctionnalité dans votre IDE, vous pouvez cloner le projet suivant depuis GitHub.
Déréférencement des pointeurs nil
en Go
L’une des principales difficultés du langage de programmation Go, à laquelle presque tous les développeurs Go ont confrontés au moins une fois, réside dans le déréférencement des pointeurs nil
. Malgré la simplicité de Go et son typage statique fort, nil
demeure une source de bugs subtils et souvent critiques.
L’impact d’un déréférencement nil
peut être grave, surtout dans les environnements de production. Un seul déréférencement inattendu peut cause le crash d’un service et mettre une API ou un processus hors service avec peu de signes avant-coureurs voire aucun avertissement.
Avec Go, des problèmes encore plus subtils peuvent se produire. Par exemple, l’écriture dans un canal nil
, par exemple, peut provoquer le blocage permanent d’une goroutine, ce qui peut générer d’autres blocages et des pannes système en cascade. Tenter d’accéder aux champs sur un pointeur nil
non initialisé entraînera une panique immédiate. Ces types d’erreurs sont faciles à manquer et difficiles à détecter après le déploiement.
Certains problèmes de déréférencement nil
peuvent être interceptés au moyen d’un processus rigoureux de révision ou de test, mais cela n’est pas toujours suffisant. En cas de développement rapides ou de grande base de code, les bugs subtils liés à nil
peuvent facilement passer inaperçus. Dans l’idéal, ces problèmes devraient être détectés automatiquement et aussi tôt que possible lors de l’écriture du code.
C’est ici que l’analyse statique du code entre en jeu. GoLand inclut déjà une inspection des déréférencements nil
intégrée qui effectue une analyse intraprocédurale locale. Elle fonctionne bien dans de nombreux cas courants, en détectant lorsqu’un pointeur nil
peut se trouver au niveau d’une fonction.
Toutefois, l’analyse actuelle ne fonctionne que dans les fonctions individuelles. Elle ne suit pas les mouvements des valeurs entre les fonctions et peut donc passer à côté de problèmes impliquant de multiples appels. Ces cas plus complexes sont en réalité communs dans le code Go de production et sont souvent les plus dangereux. Pour les intercepter, nous avons implémenté quelque chose de plus puissant : l’analyse de code interprocédurale.
Analyse de code interprocédurale
L’analyse interprocédurale, également appelée analyse globale, vous permet de comprendre comment les valeurs se déplacent entre les appels de fonctions. Elle va au-delà du niveau de la fonction pour suivre les données entre différents fichiers et paquets. Par contraste, une analyse locale vérifie seulement que ce qui se passe dans une seule fonction. Les problèmes locaux sont souvent faciles à détecter en examinant une seule fonction. Les problèmes globaux sont plus difficiles à identifier, car la source d’un problème, comme une valeur nil
, peut être éloignée de l’endroit où l’erreur se produit. C’est pourquoi l’analyse interprocédurale est aussi utile pour détecter les problèmes de déréférencement nil
.
Suivre le flux : comprendre les déréférencements nil
Prenons maintenant un exemple. Ce code semble plutôt simple à première vue. Nous créons un utilisateur au moyen d’un constructeur, puis nous imprimons ses champs. Mais l’analyse nous donne un avertissement : user.Age
risque de causer un déréférencement nil
.

Essayons d’effectuer une recherche manuellement. Pour comprendre ce qui se passe, nous devons examiner comment la fonction NewUser
est implémentée. Elle est définie dans un autre fichier appelé model.go
.

Ce constructeur semble un peu bizarre : NewUser
renvoie nil
en cas d’erreur, mais dans main
, nous utilisons le résultat sans vérification. Cela génère un déréférencement nil
potentiel.
Pour corriger cela, nous pouvons réécrire NewUser
pour renvoyer à la fois un résultat et une erreur – le style Go le plus idiomatique.

Le code est maintenant plus sûr. Nous recherchons les erreurs potentielles avant l’accès à user
, ce qui élimine le risque de déréférencement nil
. Bien que ce code semble correct, nous voyons toujours le même avertissement.
Pour comprendre ce qui se passe, nous allons creuser un peu plus et regarder de près l’implémentation de CreateUser
.

C’est ici que nous trouvons la deuxième cause du problème.
Dans la fonction CreateUser
contient un cas dans lequel le code renvoie nil
à la fois pour user
et pour error
.

Il s’agit d’un problème assez courant en traitement des erreurs. Le renvoi de nil
sans une erreur laisse penser que tout est correct, alors qu’en réalité le résultat n’est pas valide. L’appelant vérifie simplement l’erreur, voit qu’elle est nil
, puis essaie d’utiliser le résultat. Dans notre exemple, cela entraîne un crash lorsque le code accède à user.Age
.
Nous pouvons corriger cela en renvoyant une erreur réelle lorsque l’entrée n’est pas valide :

Avec cette modification, le code est maintenant correct et l’inspection ne signale plus de déréférencement nil.
La recherche manuelle de ce type de problème peut être lente et frustrante, notamment dans les gros projets. L’emplacement auquel une valeur nil
est créée peut être éloigné de l’endroit où elle cause un problème.
C’est pourquoi GoLand met en évidence ces problèmes directement dans l’éditeur dès qu’ils sont détectés. Pour ces avertissements, nous avons une action contextuelle dédiée : Explain potential nil dereference. Cette action ouvre la fenêtre d’outils Data Flow Analysis, dans laquelle vous obtenez une explication étape par étape de la manière dont la valeur nil
circule dans le code et des endroits où elle est finalement utilisée. Cela permet de comprendre bien plus facilement quel est le problème et comment le corriger sans avoir à parcourir l’ensemble de la base de code.
Lorsque nil
passe inaperçu : identifier les arguments et les récepteurs non sûrs
Notre analyse ne se contente pas de suivre les valeurs return
. Il peut également faire un raisonnement sur la nillabilité des paramètres en comprenant si une fonction attend un argument non nil ou si elle peut accepter nil
en toute sécurité, ce qui est particulièrement utile pour identifier les cas dans lesquels une valeur nil
est transmise de façon non intentionnelle à une fonction qui ne la traite pas correctement.
Prenons un autre exemple :

Ici, nous appelons la méthode Copy
sur un user
. En même temps, nous transmettons nil
en tant que contexte, en supposant qu’il n’y a pas de risque à faire cela.
Mais l’inspection renvoie un avertissement : l’argument de contexte pourrait causer un déréférencement nil
lors du passage d’une valeur nil
comme contexte. Vérifions l’implémentation de la méthode Copy
:

Dans ce code, la méthode accède à ctx.isDebugEnabled
sans vérifier si ctx
est nil
. Si ctx
est nil
, le programme paniquera lors de l’exécution.
Pour corriger cela, nous pouvons rendre le paramètre ctx
« nil-safe »en ajoutant une vérification nil
explicite avant d’accéder à ses champs.

Avec cette modification, le code devient sûr et l’avertissement au niveau du site d’appel disparaît.

Toutefois, cela n’est pas le seul problème. L’analyse signale également un déréférencement nil
potentiel en relation avec la variable user
.
Pour comprendre pourquoi, nous pouvons utiliser l’action Explain potential nil dereference.
La fonction process
permet à user
d’être nil
et nous le transmettons à Copy
sans vérification.
Dans la méthode Copy
, le récepteur u
est utilisé avant d’être vérifié. Plus spécifiquement, u
est transmise à la fonction logUserEvent
, où un déréférencement se produit lors de l’accès au champ u.Name
. Par conséquent, si la variable user
de la fonction process
est nil
, un déréférencement nil
se produira.
Ces exemples démontrent que les problèmes de déréférencement nil
sont souvent subtils et peuvent facilement passer inaperçus. Même si le code semble propre et idiomatique, supposer qu’il n’y a aucun problème peut conduire à n crash lors de l’exécution. Retrouver la cause première manuellement peut s’avérer étonnamment compliqué, surtout lorsque l’origine de la valeur nil
est loin de l’endroit où elle est utilisée, séparée du déréférencement par plusieurs appels de fonctions, fichiers ou paquets.
C’est ici que l’analyse interprocédurale se révèle utile. Elle suit la façon dont les valeurs nil
circulent au fil des appels de fonction. Au lieu de devoir deviner où le problème a commencé, vous pouvez voir clairement le chemin complet, de l’origine au point de déréférencement.
La documentation rapide montre désormais des informations sur la nillabilité
L’analyse de nillabilité dans GoLand ne consiste pas seulement à mettre en évidence des problèmes dans l’éditeur. Comme vous l’avez déjà vu, notre analyse permet de déterminer si une fonction peut renvoyer nil
et s’il est sûr de transmettre nil
comme argument à un paramètre particulier. Comme l’analyse comprend comment les fonctions sont censées se comporter, nous avons décidé de rendre ces informations facilement accessibles. C’est pourquoi nous avons intégré des informations sur la nillabilité directement dans la fenêtre contextuelle de documentation rapide.
Revenons à notre premier exemple, avant l’application des correctifs. Si nous plaçons le curseur sur la fonction NewUser
et déclenchons la documentation rapide, cela affiche la section Nilability info, qui indique le risque de nillabilité des paramètres de la fonction et de la valeur return
. Dans cet exemple, la fonction peut renvoyer un résultat nil
et la fenêtre contextuelle de documentation rapide nous l’indique clairement.

La même fonctionnalité existe pour les paramètres et les récepteurs. Dans le deuxième exemple, également avant d’appliquer les correctifs, la section Nilability info montre que le récepteur u
et le paramètre ctx
de la fonction sont censés ne pas être nil.

Ce petit ajout fait une grande différence. Une recherche rapide permet d’avoir un aperçu des détails importants, ce qui peut vous aider à écrire un code plus sûr et à réduire les risques de déréférencements nil
imprévus. Toutefois, gardez à l’esprit que tous les cas ne sont pas couverts par l’analyse et qu’il faut toujours vérifier le code soigneusement.
Limitations et contraintes
La première version de cette analyse est volontairement simple et prudente. Elle n’essaye pas de détecter tous les déréférencements nil
possibles. Nous nous sommes intentionnellement focalisés sur les cas les plus courants et les plus importants, avec pour objectif de maintenir le nombre de faux positifs au minimum. Nous allons poursuivre l’amélioration de l’analyse au fil du temps, en ajoutant de nouveaux cas avec précaution. Notre objectif est de détecter rapidement autant de problèmes que possible sans que cela ne vienne parasiter votre activité.
Éviter la panique, assurer la sécurité
L’analyse de code interprocédurale facilite considérablement l’interception et la correction en amont des problèmes de déréférencement du pointeur nil
. En suivant les valeurs nil
entre les fonctions, les fichiers et les paquets, cette analyse permet de mieux comprendre les causes premières des bugs potentiels avant la phase de production, afin de réduire les temps d’arrêt et de prévenir les incidents coûteux.
Nous avons hâte de poursuivre l’amélioration et l’enrichissement de ces fonctionnalités dans les futures mises à jour. Restez à l’écoute, et comme toujours, vos retours d’expérience et avis sont les bienvenus !
L’Équipe GoLand
Auteur de l’article original en anglais :