Closed LaurentDardenne closed 3 years ago
Une autre construction dérivé, mais une autre exception : New-FLowChartGraph: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
$sb={
$grade = 92
Switch ($Grade)
{
{$grade -ge 90} { "Grade A";Break}
# {$grade -ge 80} { "Grade B";Break}
# {$grade -ge 70} { "Grade C";Break}
# {$grade -ge 60} { "Grade D";Break}
# default { "Grade F" }
}
}
$Result=Find-FLowChartNodes -ScriptBlock $Sb
New-FLowChartGraph $Result > c:\temp\testIfWithSwitch.graph
C:\Tools\Graphviz\bin\dot -Tpng c:\temp\testIfWithSwitch.graph -o c:\temp\testIfWithSwitch.png
ii c:\temp\testIfWithSwitch.png
La suivante aboutie, mais la représentation est fausse il me semble:
$sb={
$grade = 92
Switch ($Grade)
{
{$grade -ge 90} { "Grade A";Break}
# {$grade -ge 80} { "Grade B";Break}
# {$grade -ge 70} { "Grade C";Break}
# {$grade -ge 60} { "Grade D";Break}
default { "Grade F" }
}
writelog "suite" # !!!!!!!! n'est pas considéré dans le graph !!!!!!!!!!
}
del c:\temp\testIfWithSwitch.graphn, c:\temp\testIfWithSwitch.png -ea ignore
$Result=Find-FLowChartNodes -ScriptBlock $Sb
New-FLowChartGraph $Result > c:\temp\testIfWithSwitch.graph
C:\Tools\Graphviz\bin\dot -Tpng c:\temp\testIfWithSwitch.graph -o c:\temp\testIfWithSwitch.png
ii c:\temp\testIfWithSwitch.png
Super merci pour les exemple de code qui pose problème! je sais que j'ai le problème dans un autre cas, je vais me pencher la dessus
Merci beaucoup pour les issues @LaurentDardenne !
Normalement c'est fixé ! j'ai testé les 3 cas que tu m as présenté et tout semble OK.
Ce cas n'est pas géré et déclenche la même exception :
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"} #;Break}
elseif ($grade -ge 70) { "Grade C"}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
break
}
del c:\temp\testWithIF2.graph, c:\temp\testWithIF2.png -ea Ignore
Find-FLowChartNodes -ScriptBlock $Sb|New-FLowChartGraph
#New-FLowChartGraph : La référence d'objet n'est pas définie à une instance d'un objet.
$stacktrace
<#
à FlowChartCore.Graph.BreakBuilder.CreateSpecialEdge()
à FlowChartCore.Graph.BreakBuilder..ctor(BreakNode breaknode)
à FlowChartCore.BreakNode.GenerateGraph(Boolean recursive)
à FlowChartCore.ElseIfNode.GenerateGraph(Boolean recursive)
à FlowChartCore.IfNode.GenerateGraph(Boolean recursive)
à FlowChartCore.Cmdlets.NewFlowChartGraph.ProcessRecord()
à System.Management.Automation.CommandProcessor.ProcessRecord()
#>
La présence du break, peut importe sa position, déclenche l'exception.
En passant le libellé de fin devrait suivre le nom du paramètre : -Scriptblock ->'end_of_scriptblock ' -path 'end_of_script'
Du coup avec cette version on peut visualiser la comparaison entre les instructions If et Switch :
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"}
elseif ($grade -ge 70) { "Grade C"}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
}
del c:\temp\testWithIF2.graph, c:\temp\testWithIF2.png -ea Ignore
Find-FLowChartNodes -ScriptBlock $Sb|New-FLowChartGraph |Set-Content c:\temp\testIF.graph
C:\Tools\Graphviz\bin\dot -Tpng c:\temp\testIf.graph -o c:\temp\testIf.png
ii c:\temp\testIf.png
$sb={
$grade = 92
Switch ($Grade)
{
{$grade -ge 90} { "Grade A"}
{$grade -ge 80} { "Grade B"}
{$grade -ge 70} { "Grade C"}
{$grade -ge 60} { "Grade D"}
default { "Grade F" }
}
}
$Result=Find-FLowChartNodes -ScriptBlock $Sb
New-FLowChartGraph $Result > c:\temp\testIfWithSwitch.graph
C:\Tools\Graphviz\bin\dot -Tpng c:\temp\testIfWithSwitch.graph -o c:\temp\testIfWithSwitch.png
ii c:\temp\testIfWithSwitch.png
Bien que le rendu soit différent, le 'fonctionnement' est identique.
t as testé le break comme ça pour voir si ça balançait le même message d'erreur:
Switch ($Grade)
{
{$grade -ge 90} { "Grade A";break}
{$grade -ge 80} { "Grade B";break}
{$grade -ge 70} { "Grade C";break}
{$grade -ge 60} { "Grade D";break}
default { "Grade F";break}
}
je ne pense pas avoir gérer ce cas. en gros je me suis basé la dessus: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_break?view=powershell-7#do-not-use-break-outside-of-a-loop-switch-or-trap
je peux éventuellement le gérer si nécessaire, mais en gros ça fait quoi ? ça quitte ? un peu comme exit ou return ?
Salut,
t as testé le break comme ça pour voir si ça balançait le même message d'erreur:
Oui, mais là il faut créer des fichiers de test pour vérifier les régressions, ça va vite avec ce genre d'outil.
Ce cas provoque une exception :
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"}
elseif ($grade -ge 70) { "Grade C";break}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
}
Et celui-ci:
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"} #;Break}
elseif ($grade -ge 70) { "Grade C"}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
break
}
ça quitte ? un peu comme exit ou return ?
Si tu parles bien de l'instruction Break ( j'ai un doute désolé) ?
je peux éventuellement le gérer si nécessaire,
Ici il peut y avoir 5 ou 6 constructions, avec ou sans sens, mais si le langage les permet, on peut les retrouver dans du code à la six-quatre-deux. Ce n'est pas nécessaire, mais il est possible de le rencontrer, C'est un choix à faire.
bon faut que je regarde ces cas .. je crois que je vais écrire des tests ça sera moins relou ...
un fichier contenant n scripblock, et un bon vieux tests pester dans une boucle qui ne doit pas throw
..
concernant le break ...le code de m... bah je vois 2 possibilités,
break
n'est pas au "bon endroit" je le considère comme une instruction qui fait partie d'un code blockbon faut que je regarde ces cas .. je crois que je vais écrire des tests ça sera moins relou ...
:-)
un fichier contenant n scriptblock, et un bon vieux tests pester dans une boucle qui ne doit pas
throw
..
hum, tu peux vite te retrouver avec un paquet de code illisible et pour un cas précis tout exécuter et devoir aller chercher l'info. Et tu as l'approche laborieuse où tu vas passer bcp de temps à écrire des tests.
* la plus simple (je pense) ajouter le code nécessaire qui va générer le graph dans ce genre de cas, à savoir on quitte
Tu veux dire on ajoute un noeud 'Break' ou on quitte l'analyse ?
* la plus complexe, quand le `break` n'est pas au "bon endroit" je le considère comme une instruction qui fait partie d'un code block
Si l'AST indique que la construction d'un break est syntaxiquement correct, il est au 'bon endroit'. Ce serait à PSSA de dire là c'est du code en vrac, ce que n'est pas ton soft. Pour moi ce type de soft prend du code en entrée puis le transforme en graphe, que son rendu soit illisible/incohérent ce n'est pas le sujet. Avec n'importe quoi en entrée on a tjr n'importe quoi en sortie. On ne fait pas de l'alchimie :-)
Ces exemples sont bien évidement du n'importe quoi, mais ton soft ne devrait pas terminer sur une exception. Le rendu peut être faux ou incomplet, à partir de là tu pourras dire "ça je gère pas, ceci est pour la version 7", voir placer un label "t'as qu'a le faire", etc
Je peux prendre un peu de temps pour étudier comment automatiser et structurer ce type de test.
(note to self!) pour les break .. j'ai un fix, mais vu que pour moi ce genre de cas ... n'existait pas .. je n'ai jamais pensé à passer le root
dans le constructeur des keywords break, return, exit ... du coup va falloir que je repasse sur chacun!
le fix ne fonctionne pas, car je ne troue pas l index du noeud, quand le type break est de niveau 0, car le root n'est pas présent .. !
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"}
elseif ($grade -ge 70) { "Grade C";break}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
}
et celui la:
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B"} #;Break}
elseif ($grade -ge 70) { "Grade C"}
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F"}
writeLog "Suite"
break
}
Modif faite pour Breajk. J'ai prépa le terrain pour ce que j'appelle les "keywords" continue, exit, return ... parce t as raison PS ne l interdit pas, j'aurai juste a implémenter les correction de la generation du graph pour ceux la
Les images que tu as intégrées proviennent de la dernière version ? J'ai une exception pour les deux (j'ai recompilé) :/
Eh je crois que g pas merge le dernier que g fait hier soir ...!
Ok, je préfére ça :-)
done! par contres c'est étrange que tu importes la dll dotnet à chaque fois moi j'ai juste besoin d importer la flowchartcore.dll
j'ai testé aussi sur ps5.1 et pareil juste besoin d'importer la flowchartcore.dll
Merci.
par contre c'est étrange que tu importes la dll dotnet à chaque fois
C'est une habitude, je place tout dans un script pour l'initialisation, ensuite je ne crée que le nécessaire. Et une fois une dll dotnet dans le domaine d'application on ne peut plus la décharger son ajout à répétition ne fait rien, comme un module PS ( bien qu'ici on puisse utiliser -Force) Et c'est aussi le code de repro, l'erreur pouvant provenir de mon côté ;-)
Ensuite on peut créer juste un manifeste (psd1), mais le téléchargement via Github ajoute le stream de sécurité à supprimer, là où l'installation d'un module via PSGallery ne le fait pas. Il y a ( avait ?) une gallery de dev sinon utiliser myget. Je l'utilise pour ne pas polluer la gallery avec des POC et autre outil perso.
t as forké, tu peux participer à la fête, et ça sera plus simple que de dll le projet :) t auras juste à rebase ou je sais pas ce que c le terme git ! le psd1 ouais, après ça sera utile le jour ou je publierai sur la gallery
t as forké, tu peux participer à la fête, et ça sera plus simple que de dll le projet :)
Certes, pense à d'autres qui peuvent tester sans devoir creuser le comment du pourquoi.
le psd1 ouais, après ça sera utile le jour ou je publierai sur la gallery
L'avantage de l'utiliser dés le début est que tu valides la chaîne (ton soft et le build) tout au long du développement. C'est le premier truc que je fais. Après tu fais comme tu veux :-)
c vrai :) mais la vu que c du compilé ^^ tu build et boum ! mais ouais je vais créer une issue pour y penser !
d'habitude je fais ça aussi
Autres constructions provoquant le bug :
#OK
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 70) { "Grade C"}
else { "Grade D"}
}
#NOK
$sb={
$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 70) { "Grade C"}
}
#NOK
#if dans une boucle for
$sb={
for ($i = 1; ; ++$i)
{
if ($i * $i -gt 50)
{
dir
}
}
}
it is corrigé ! j'avoue un for sans conditions je savais meme pas que ct possible :)
j'avoue un for sans conditions je savais meme pas que ct possible :)
En direct du doc des spec de PS :
$break7=New-CodeUseCase 'Break use a string value as target' @'
$i = 1
while ($true) # infinite loop
{
if ($i * $i -gt 100)
{
break # break out of current while loop
}
++$i
}
$lab = "go_here"
:go_here
for ($i = 1; ; ++$i)
{
if ($i * $i -gt 50)
{
break $lab # use a string value as target
}
}
:labelA
for ($i = 1; $i -le 2; $i++)
{
:labelB
for ($j = 1; $j -le 2; $j++)
{
:labelC
for ($k = 1; $k -le 3; $k++)
{
if ($true) { break labelA }
}
}
}
'@
Concernant
$sb = {
$lab = "go_here"
:go_here
for ($i = 1; ; ++$i)
{
if ($i * $i -gt 50)
{
break $lab # use a string value as target
}
}
}
ça je l'ai pas ... ! le problème c'est que si on regarde l'AST du break et bien la propriété label est vide ...
PS > $x = Find-FLowChartNodes -ScriptBlock $sb
PS > $break = $x.findnodes({$args[0] -is [FlowChartCore.BreakNode]},$true)
PS > $break.GetAst()
Label Extent Parent
----- ------ ------
break {…
Du coup si le label est vide, j'ai mis une description "Break from Previous Loop" qui donne la definition suivante
digraph "a" {
"01"[label="CodeBlock"];
"01" -> "02";
"02"[label="For "];
"02" -> "0210";
"loop_02"[shape=ellipse,label="Loop"];
"loop_02" -> "02"[label="++$i"];
"loop_02" -> "end_of_script"[label="Loop End"];
"0210"[label="If $i * $i -gt 50",shape=diamond];
"end_0210"[shape=mdiamond,label="End If"];
"0210" -> "021020"[label="True"];
"end_0210" -> "loop_02";
"021020"[label="Break"];
"021020" -> "021021"[style=dotted];
"021020" -> "end_of_script"[label="Break From Previous Loop"];
"021021"[label="CodeBlock"];
"021021" -> "end_0210";
}
J'ai par la même occasion corrigé un bug, sur les label qui était "Break From" au lieu de "Break From TheLabel"
et j'ai ajouté la méthode GetAst
à la classe BreakNode
le problème c'est que si on regarde l'AST du break et bien la propriété label est vide ...
L'information existe mais sur le parent :
$break.GetAst().parent.gettype()
# IsPublic IsSerial Name BaseType
# -------- -------- ---- --------
# True False StatementBlockAst System.Management.Automation.Language.Ast
$break.GetAst().parent|% {$_.statements|% {$_ -as [string]}}
# break
# $lab
En même temps c'est plutôt rare de coder ainsi je pense.
Oui et surtout je vois pas comment faire la corrélation vu que j ai aucune indication de quelle variable je dois chercher
Avec Show-Ast :
Selon les specs il n'y a que trois cas *:
Je regarderais comment on recherche cela, j'ai jamais fait. [edit] A vérifier
Le code utilsé:
$sb = {
$lab = "go_here"
:go_here
for ($i = 1; ; ++$i)
{
if ('a') {dir;break}
if ($i * $i -gt 40)
{
break go_here
}
if ($i * $i -gt 50)
{
#write-host 'before'
break $lab ; write-host 'truc' # use a string value as target
#write-host 'after'
}
}
break
}
ok j'ai un début de piste ...
$lol
etant le fameux break
$lol.GetAst().parent.Find({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst]},$true)
manque plus qu a recup la valeur ...
On pourrait s'arreter la et dire "break from $lab" mais ça pue la défaite ça !!!
bon c'est éventuellement faisable ... Mais, il faudrait lister toute les variables au préalable ...
pour être précis, toute les AssignmentStatementAst
contenant des CommandExpressionAst
, looper dans le resultat qui est une liste, et trouver celle qui correspond ...
mais ça pue la défaite ça !!!
Certains diront que c'est une retraite stratégique :-)
Les specs :
A labeled break need not be resolved in any local scope; the search for a matching label may continue up the calling stack even across script and function-call boundaries.
If no matching label is found, the current command invocation is terminated. The name of the label designated by label-expression need not have a constant value. If label-expression is a unary-expression, it is converted to a string.
Ici la présence du terme scope est une alerte. Un exemple. Et je ne sais même pas si on peut écrire ceci :-)
break $script:Lab
Cela provient peut être d'un outil d'obfuscation (obscurcissement) ?
hier j'étais entrain d'implémenter le cas de la variable, j'ai pas encore fini .. je trouve ça moins facile en c# qu'en PS ... je n'arrive pas à reproduire mon oneliner xD
je trouve ça moins facile en C# qu'en PS
Oui le niveau de détail est plus important. On peut descendre d'un niveau : 'je trouve ça moins facile en C qu'en C#' L'avantage est que l'on a la possibilité de modifier les détails :-)
le cas que j'ai pas gérer ... c'est si tu réaffectes:
break $somevar
et enfait dans ton code tu fais un truc comme ça:
$somvar = "lol"
$somevar = "namaisohSerieux?"
...
break $somevar
``
Autre cas qui déclenche l'exception :
Find-FLowChartNodes
#Find-FLowChartNodes : La référence d'objet n'est pas définie à une instance d'un objet.
ah ouais rigolo ... hum faut que je regarde comment résoudre ça ... !
edit: c'est fixé
PS > Find-FLowChartNodes
Find-FLowChartNodes: Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used
together or an insufficient number of parameters were provided.
Les constructions suivantes déclenchent l'exception sur New-FLowChartGraph :
New-CodeUseCase 'Switch -File' {
$IsXml=$False
#$FileName=(get-process -pid $pid).Path #test de syntaxe
switch -file $FileName
{
#Début de la section XML
"<?xml version=`"1.0`" encoding=`"ibm850`"?>" {$IsXml=$True;$_;continue}
#Fin de la section XML
"</response>" {$IsXml=$False;$_;break}
default {if ($IsXml)
#On traite les lignes si on se trouve dans la section XML
{$_}
}
}
}
New-CodeUseCase 'Switch Array' {
switch (1,4,-1,3,"Hello",2,1)
{
{$_ -lt 0} { Continue }
{$_ -isnot [Int32]} { Break }
{$_ % 2} {
"$_ is Odd"
}
{-not ($_ % 2)} {
"$_ is Even"
}
}
}
ah c le continue ... ça marche dans un switch ?? ça fait kewa ? ah ok ça passe a la prochaine iteration ...
ah c le continue ... ça marche dans un switch ?? ça fait kewa ?
Cela dépend de la construction, dans un boucle on passe à l'itération suivante, ici comme on a un tableau en entrée d'un switch on passe à la valeur suivante ( ici le comportement est identique à celui dans une boucle) :
switch (1,4,-1,3,"Hello",2,1)
{
{$_ -lt 0} { write-warning "continue : $_"; Continue }
{$_ -isnot [Int32]} { write-warning "break : $_ ";Break }
{$_ % 2} {
write-warning "suite : $_ ";"$_ is Odd"
}
{-not ($_ % 2)} {
write-warning "suite : $_ ";"$_ is Even"
}
}
Mais l'exemple de code Switch -File n'est pas courante on filtre dans une boucle car switch -file lit tout le fichier $filename:
function ParseXml([String] $FileName)
{ #Récupération des lignes de la déclaration XML issu de MsdnMan
#L'extraction XML contient quelques lignes parasites.
$IsXml=$False
switch -file $FileName
{
#Début de la section XML
"<?xml version=`"1.0`" encoding=`"ibm850`"?>" {$IsXml=$True;$_;continue}
#Fin de la section XML
"</response>" {$IsXml=$False;$_;break}
default {if ($IsXml)
#On traite les lignes si on se trouve dans la section XML
{$_}
}
}
}
ok good !
Du coup j'ai aussi corriger un petit truc, je viens de découvrire que les flags sont des énums, du coup quand c'eétait un switch "classique", cela affichait switch none
Il semble qu'il y ait une régression sur le foreach :
[-] FlowChartCode .When there is no violation.Foreach enhanced. 4ms (3ms|0ms)
Expected no exception to be thrown, but an exception "Object reference not set to an instance of an object." was thrown
étrange! j'ai rouler les tests hier soir et tout fonctionnait ... ! je check ça !
j'ai testé "manuellemen" en copiant le scriptblock du foreach enahanced,
$a = {
[CmdletBinding()]
[OutputType('FunctionPosition')]
param(
[Parameter(Position = 0, Mandatory,
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath')]
[System.String[]]
$Path
)
process {
try {
$filesToProcess = if ($_ -is [System.IO.FileSystemInfo]) {
Write-Verbose "From pipeline"
$_
} else {
Write-Verbose "From parameter, $Path"
Get-Item -Path $Path
}
$parser = [System.Management.Automation.Language.Parser]
Write-Verbose "lets start the foreach loop on `$filesToProcess with $($filesToProcess.count) as count"
foreach ($item in $filesToProcess) {
Write-Verbose "$item"
if ($item.PSIsContainer -or
$item.Extension -notin @('.ps1', '.psm1')) {
continue
}
$tokens = $errors = $null
$parser::ParseFile($item.FullName, ([REF]$tokens),
([REF]$errors)) | Out-Null
if ($errors) {
$msg = "File '{0}' has {1} parser errors." -f $item.FullName,
$errors.Count
Write-Warning $msg
}
:tokenLoop foreach ($token in $tokens) {
if ($token.Kind -ne 'Function') {
continue
}
$position = $token.Extent.StartLineNumber
do {
if (-not $foreach.MoveNext()) {
break tokenLoop
}
$token = $foreach.Current
} until ($token.Kind -in @('Generic', 'Identifier'))
$functionPosition = [pscustomobject]@{
Name = $token.Text
LineNumber = $position
Path = $item.FullName
}
Add-Member -InputObject $functionPosition `
-TypeName FunctionPosition -PassThru
}
}
}
catch {
throw
}
}
}
Find-FlowChartNodes -ScriptBlock $a
Et je n'ai pas d'exception ... ! whyyyyy ???
Et je n'ai pas d'exception ... ! whyyyyy ???
La dernière fois, cette situation était due à une initialisation qui n'était pas faite. Une régression dans les sources :-/ ?
Ok j'ai trouvé je vais corrigé... !
[edit] il ne s'agissait pas d'une regression ... juste que je ne savais pas que throw
sans rien ça existait ... du coup j'essayai de setter ne propriété pipeline
sur le noeud throw, alors que bah dans l'AST c'était completement vide...
c'est corrigé, pusher en dev, et soon sur master
Probléme à priori avec la présence du return dans ces constructions :
$sb={if (! $data) { return }}
$sb={
Function Lock-File{
#Verrouille un fichier à des fins de tests
param([string] $Path)
New-Object System.IO.FileStream($Path,
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None)
} #Lock-File
return
&{
try {
$Filename="C:\Temp\t1.txt"
$TestLockFile= Lock-File $FileName
Test-IncludeFile $PsionicIncludeFiles
} finally {
$TestLockFile.Close()
}
}
}
Même exception que pour le poste d'origine.
ok c fixé !) j'ai du virer la branche dev .. je m y retrouvais plus et j avais trop de conflits xD si j'ai le time demain ... et normalement j'ai le temps vu que j'attends que des gens veuillent bien se mettre à travailler ... BREF .. je vais regarder les recommandations que tu as faits sur le renommage des cmlets ! Merci @LaurentDardenne en tout cas :)
ok c fixé !)
Merci. J'avais testé sur 7000 fichiers, je vérifierais de nouveau.
C est dans la branche feature-dev pour l instant! Si tu as besoin je merge dans master
7000 fichier!!!! Et alors il n y avait pas trop d erreur??
7000 fichiers!!!! Et alors il n y avait pas trop d erreur??
Avec PS v7.1 non, sauf celles du return et bcp d'erreur de syntaxe dans les fichiers. Pour les constructions de base c'est rassurant.
Tu peux clore je pense.
The following code based on a test of the switch statement raises an exception :
$stacktrace: