diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.cbl b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.cbl new file mode 100644 index 000000000..96e7ed9e6 --- /dev/null +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.cbl @@ -0,0 +1,17 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. TCOMFL06. + PROCEDURE DIVISION. + main. + perform a + goback + . + a. + perform b + . + b. + perform c + . + c. + perform a + . + END PROGRAM TCOMFL06. \ No newline at end of file diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.diag new file mode 100644 index 000000000..f77827768 --- /dev/null +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.diag @@ -0,0 +1,11 @@ +--- Diagnostics --- +Line 5[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM a', recursive instruction is ' perform b' at line 9. +Line 5[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM a', recursive instruction is ' perform c' at line 12. +Line 5[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM a', recursive instruction is ' perform a' at line 15. +Line 6[12,17] <37, Warning, General> - Warning: Unreachable code detected +Line 9[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM b', recursive instruction is ' perform c' at line 12. +Line 9[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM b', recursive instruction is ' perform a' at line 15. +Line 9[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM b', recursive instruction is ' perform b' at line 9. +Line 12[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM c', recursive instruction is ' perform a' at line 15. +Line 12[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM c', recursive instruction is ' perform b' at line 9. +Line 12[12,20] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM c', recursive instruction is ' perform c' at line 12. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.dot b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.dot new file mode 100644 index 000000000..fdbe69782 --- /dev/null +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/CyclicPerformRecursive.dot @@ -0,0 +1,51 @@ +digraph Cfg { +compound=true; +node [ +shape = "record" +] + +edge [ +arrowtail = "empty" +] +Block0 [ +label = "{START|}" +] +Block1 [ +label = "{MAIN. Block1|}" +] +Block2 [ +label = "{Block2| perform a\l}" +] +Block14 [ +label = "{A. Block14|}" +] +Block15 [ +label = "{Block15| perform b\l}" +] +Block25 [ +label = "{B. Block25|}" +] +Block18 [ +label = "{Block18| perform c\l}" +] +Block27 [ +label = "{C. Block27|}" +] +Block21 [ +label = "{Block21| perform a\l}" +] +Block29 [ +label = "{A. Block29|}" +] +Block0 -> Block1 +Block1 -> Block2 +Block2 -> Block14 +Block14 -> Block15 +Block15 -> Block25 +Block25 -> Block18 +Block18 -> Block27 +Block27 -> Block21 +Block21 -> Block29 +Block29 -> Block15 + +} diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc3Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc3Recursive0.diag index 37f27f57b..ddf8d787a 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc3Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc3Recursive0.diag @@ -1,3 +1,14 @@ --- Diagnostics --- -Line 28[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 16. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3' at line 20. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 28. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 39. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec' at line 28. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 16. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3' at line 20. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 39. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 39. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec' at line 28. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 16. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3' at line 20. Line 29[15,57] <37, Warning, General> - Warning: Unreachable code detected diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc4Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc4Recursive0.diag index 16afa8351..dcbfbbdc3 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc4Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/ExtendedPerformProc4Recursive0.diag @@ -1,5 +1,30 @@ --- Diagnostics --- -Line 31[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 17. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3' at line 21. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec4' at line 23. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 31. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 42. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec2' at line 42. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec' at line 31. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec2' at line 17. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3' at line 21. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4' at line 23. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec' at line 31. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 17. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3' at line 21. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec4' at line 23. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 42. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 42. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec' at line 31. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 17. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3' at line 21. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec4' at line 23. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. Line 32[15,57] <37, Warning, General> - Warning: Unreachable code detected -Line 57[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5'. -Line 57[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n'. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformIdentity.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformIdentity.diag index 0449f56a9..e1480353d 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformIdentity.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformIdentity.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 6[12,23] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM main', recursive instruction is ' PERFORM main'. +Line 6[12,23] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM main', recursive instruction is ' PERFORM main' at line 6. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc1Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc1Recursive0.diag index 7a1c41c0c..a38256d6f 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc1Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc1Recursive0.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 16[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc2Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc2Recursive0.diag index c5163b4b7..1fd8269b0 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc2Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc2Recursive0.diag @@ -1,2 +1,5 @@ --- Diagnostics --- -Line 27[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 16. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 27. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec' at line 27. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc3Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc3Recursive0.diag index 18ede9f54..ac7b2673f 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc3Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc3Recursive0.diag @@ -1,2 +1,13 @@ --- Diagnostics --- -Line 28[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 16. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3' at line 20. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 28. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 39. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec' at line 28. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 16. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3' at line 20. +Line 16[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 39. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 39. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec' at line 28. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 16. +Line 20[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3' at line 20. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc4Recursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc4Recursive0.diag index 6953076dc..6c6dbd69f 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc4Recursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProc4Recursive0.diag @@ -1,4 +1,29 @@ --- Diagnostics --- -Line 31[15,29] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2'. -Line 57[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5'. -Line 57[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n'. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 17. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3' at line 21. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec4' at line 23. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec' at line 31. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec2' at line 42. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 8[12,26] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec2' at line 42. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec' at line 31. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec2' at line 17. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec3' at line 21. +Line 9[12,27] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec4', recursive instruction is ' perform pararec4' at line 23. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec' at line 31. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 17. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3' at line 21. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec4' at line 23. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec2' at line 42. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 17[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec2', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 42. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec' at line 31. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec2' at line 17. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3' at line 21. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec4' at line 23. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec3 test after varying n from 1 by 1 until n' at line 53. +Line 21[15,30] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec3', recursive instruction is ' perform pararec4 varying n from 1 by 1 until n > 5' at line 57. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive0.diag index 0189177ad..45bf3057b 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive0.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 16[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec varying n from 1 by 1 until n > 5'. +Line 8[12,71] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec varying n from 1 by 1 until n > 5' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive1.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive1.diag index c0376cafa..ba456c070 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive1.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive1.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 16[15,72] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec test after varying n from 1 by 1 until n>5'. +Line 8[12,60] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec test after varying n from 1 by 1 until n>5' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive2.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive2.diag index c0376cafa..75a9713ea 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive2.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeAfterRecursive2.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 16[15,72] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec test after varying n from 1 by 1 until n>5'. +Line 8[12,71] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec test after varying n from 1 by 1 until n>5' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeRecursive0.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeRecursive0.diag index 0189177ad..0b0c0aa4f 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeRecursive0.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/PerformProcIterativeRecursive0.diag @@ -1,2 +1,2 @@ --- Diagnostics --- -Line 16[15,64] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec varying n from 1 by 1 until n > 5'. +Line 8[12,60] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM pararec', recursive instruction is ' perform pararec varying n from 1 by 1 until n > 5' at line 16. diff --git a/TypeCobol.Analysis.Test/BasicCfgInstrs/SG102A.diag b/TypeCobol.Analysis.Test/BasicCfgInstrs/SG102A.diag index aa0cf7dc4..06af16610 100644 --- a/TypeCobol.Analysis.Test/BasicCfgInstrs/SG102A.diag +++ b/TypeCobol.Analysis.Test/BasicCfgInstrs/SG102A.diag @@ -15,10 +15,6 @@ Line 242[13,46] <37, Warning, General> - Warning: "end-if" is missing Line 250[12,37] <37, Warning, General> - Warning: "end-if" is missing Line 266[12,43] <37, Warning, General> - Warning: "end-if" is missing Line 267[12,42] <37, Warning, General> - Warning: "end-if" is missing -Line 269[41,66] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM WRITE-LINE', recursive instruction is ' PERFORM WRT-LN'. -Line 269[41,66] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM WRITE-LINE', recursive instruction is ' PERFORM WRT-LN 2 TIMES'. -Line 269[41,66] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM WRITE-LINE', recursive instruction is ' PERFORM WRT-LN'. -Line 269[41,66] <37, Warning, General> - Warning: A recursive loop has been encountered while analyzing 'PERFORM WRITE-LINE', recursive instruction is ' PERFORM WRT-LN'. Line 276[12,43] <37, Warning, General> - Warning: "end-if" is missing Line 277[12,38] <37, Warning, General> - Warning: "end-if" is missing Line 293[12,16] <37, Warning, General> - Warning: ALTER should not be used diff --git a/TypeCobol.Analysis.Test/BasicControlFlowInstructionTest.cs b/TypeCobol.Analysis.Test/BasicControlFlowInstructionTest.cs index 948b64199..b83f08f7c 100644 --- a/TypeCobol.Analysis.Test/BasicControlFlowInstructionTest.cs +++ b/TypeCobol.Analysis.Test/BasicControlFlowInstructionTest.cs @@ -723,5 +723,12 @@ public void ExtendedPerformProcIterativeAfterRecursive2() const string baseName = "PerformProcIterativeAfterRecursive2"; TestTemplate(inputFileName: baseName, expectedDiagnosticsFileName: baseName, mode: CfgBuildingMode.Extended); } + + [TestMethod] + public void CyclicPerformRecursive() + { + const string baseName = "CyclicPerformRecursive"; + TestTemplate(inputFileName: baseName, expectedDiagnosticsFileName: baseName, mode: CfgBuildingMode.Extended); + } } } diff --git a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.BasicBlockForNodeGroup.cs b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.BasicBlockForNodeGroup.cs index 33d588ffc..f3445407e 100644 --- a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.BasicBlockForNodeGroup.cs +++ b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.BasicBlockForNodeGroup.cs @@ -63,17 +63,6 @@ internal bool IsAfterIterativeGroup set; } - /// - /// To detect recursivity on PERFORM Procedure calls. - /// This is a bit array of GroupIndex encountered during the workflow - /// call of other PERFORM. - /// - internal BitArray RecursivityGroupSet - { - get; - set; - } - /// /// Constructor. /// diff --git a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformCallRelation.cs b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformCallRelation.cs new file mode 100644 index 000000000..1a2e4afe2 --- /dev/null +++ b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformCallRelation.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TypeCobol.Compiler.Nodes; + +namespace TypeCobol.Analysis.Cfg +{ + public partial class ControlFlowGraphBuilder + { + /// + /// Represent the perform call chain of a PerformProcedure caller + /// + private class PerformCallChain + { + /// + /// Source Perform Procedure instruction + /// + internal PerformProcedure PerformCaller; + /// + /// List of PerformProcedure (Nodes) that form the call chain from the 'PerformCaller' + /// + internal List PerformCallees; + /// + /// All target procedures + /// + internal HashSet Procedures; + + /// + /// Constructor + /// + /// + internal PerformCallChain(PerformProcedure performCaller) + { + this.PerformCaller = performCaller; + this.PerformCallees = new List(); + this.Procedures = new HashSet(); + } + /// + /// Constructor + /// + /// + /// + /// + internal PerformCallChain(PerformProcedure performCaller, List performCallees, HashSet procedures) + { + this.PerformCaller = performCaller; + this.PerformCallees = performCallees; + this.Procedures = procedures; + } + } + + /// + /// Representation of Perform Call Relation. + /// It is a dictionary which gives for each Procedure its Perform Call chain. + /// + private class PerformCallRelation : Dictionary + { + /// + /// The Relation's domain + /// + internal HashSet Domain; + + internal PerformCallRelation() + { + Domain = new HashSet(); + } + /// + /// Add a PERFORM CALL relation : Caller => Callee (Procedure) + /// Example of code: + /// main. + /// perform a + /// goback + /// . + /// a. + /// perform b + /// . + /// The relation is: a => b + /// + /// PERFORM procedure caller, source instruction: in the example (perform a) + /// Caller procedure: in the example 'a'/// + /// The PERFORM procedure called, target instruction: in the example (perform b) + /// The called procedure: in the example 'b' + internal void AddToRelation(PerformProcedure performCaller, Procedure caller, PerformProcedure performCallee, Procedure callee) + { + if (!TryGetValue(caller, out var performCallChain)) + { + performCallChain = new PerformCallChain(performCaller); + Add(caller, performCallChain); + } + performCallChain.Procedures.Add(callee); + if (!performCallChain.PerformCallees.Contains(performCallee)) + { + performCallChain.PerformCallees.Add(performCallee); + } + Domain.Add(caller); + Domain.Add(callee); + } + + /// + /// Compute the transitive closure of the PERFORM CALL relation. + /// We browse the graph represented using the Call Relation, then we found during the visit cycles. + /// In the same time we compute the call chain. + /// + internal void TransitiveClosure() + { + // The dfs stack of all procedures in active consideration. + Stack dfsStack = new Stack(); + // The dictionary dfsMark is used for three purposes + // 1) To record unmarked procedures (dfsMark[p] == 0). + // 2) To associate a positive number value with procedures that are active consideration (0 < dfsMark[p] < Infnity). + // 3) To mark Infinity Procedures whose cycles have already been found + Dictionary dfsMark = new Dictionary(); + + // For each procedure in the relation's domain + foreach (var procedure in Domain) + { // For each procedure not yet visted, treat it. + var proc_visitMark = Visited(procedure); + if (proc_visitMark == 0) + dfs(procedure); + } + + // Depth First Search Traversal. + void dfs(Procedure proc_a) + { + dfsStack.Push(proc_a); + int depth = dfsStack.Count; + dfsMark[proc_a] = depth; + var proca_callChain = GetPerformCallChain(proc_a); + if (proca_callChain.Procedures != null) + { //For all procedure b called by a + foreach (var proc_b in proca_callChain.Procedures.ToArray()) + { + int procb_VisitMark = Visited(proc_b); + if (procb_VisitMark == 0) + { + dfs(proc_b);//Procedure not visited yet + procb_VisitMark = Visited(proc_b); ; + } + int proca_VisitMark = Visited(proc_a); + dfsMark[proc_a] = Math.Min(proca_VisitMark, procb_VisitMark); // N[b] < N[a] : we find a cycle + var procb_callChain = GetPerformCallChain(proc_b); + // proca_callChain = proca_callChain U procb_callChain + // We update the call chain + UpdatePerformCallChain(procb_callChain, proca_callChain); + } + } + var proca_visitMark = Visited(proc_a); + if (proca_visitMark == depth) + { // 'a' is an entry point of a calculated component, that is to say the start procedure of a cycle. + // because if na != d then 'a' has been included in another cycle whose root is not 'a'. + do + { + dfsMark[dfsStack.Peek()] = Int32.MaxValue; // The Node(The procedure) is terminated + if (TryGetValue(dfsStack.Peek(), out var top)) + { + UpdatePerformCallChain(proca_callChain, top); + } + else + { + Add(dfsStack.Peek(), new PerformCallChain( + proca_callChain.PerformCaller, new List(proca_callChain.PerformCallees), new HashSet(proca_callChain.Procedures))); + } + } while (dfsStack.Count > 0 && dfsStack.Pop() != proc_a); + } + } + // Get the visited mark of the procedure + int Visited(Procedure procedure) + { + if (dfsMark.TryGetValue(procedure, out var visitMark)) + return visitMark; + return 0; + } + // Get the PerformCallChain value of the procedure a + PerformCallChain GetPerformCallChain(Procedure procedure) + { + if (TryGetValue(procedure, out var callChain)) + return callChain; + else + return new PerformCallChain(null, new List(), new HashSet()); + } + // Update PerformCallChain value of procedure 'to' when prodecure 'from' call + // procedure 'to' this the transitive aspect: all procedures called by 'from' are + // also called by 'to' + void UpdatePerformCallChain(PerformCallChain from, PerformCallChain to) + { + foreach (var perform in from.PerformCallees) + { + if (!to.PerformCallees.Contains(perform)) + { + to.PerformCallees.Add(perform); + } + } + foreach (var procedure in from.Procedures) + { + to.Procedures.Add(procedure); + } + } + } + } + } +} + diff --git a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformTarget.cs b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformTarget.cs new file mode 100644 index 000000000..0f366f136 --- /dev/null +++ b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.PerformTarget.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using TypeCobol.Compiler.CodeElements; +using TypeCobol.Compiler.Nodes; +using SectionNode = TypeCobol.Compiler.Nodes.Section; + +namespace TypeCobol.Analysis.Cfg +{ + public partial class ControlFlowGraphBuilder + { + /// + /// PerformTarget class to capture target sentences and procedures from a PERFORM instruction. + /// + private class PerformTarget + { + /// + /// All target sentences + /// + internal List Sentences { get; set; } + /// + /// All target procedures. + /// + internal List Procedures { get; set; } + + public PerformTarget(List sentences, List procedures) + { + this.Sentences = sentences; + this.Procedures = procedures; + } + } + + /// + /// Cache of PerformTarget instances already calculated for a Perform procedure + /// + private Dictionary _performTargetCache; + private PerformTarget GetPerformTarget(PerformProcedure p, SectionNode sectionNode) + { + if (_performTargetCache == null) + _performTargetCache = new Dictionary(); + if (_performTargetCache.TryGetValue(p, out var target)) + { + return target; + } + _performTargetCache[p] = null;//By default + SymbolReference procedureReference = p.CodeElement.Procedure; + SymbolReference throughProcedureReference = p.CodeElement.ThroughProcedure; + + Node procedureNode = ResolveProcedure(p, sectionNode, procedureReference); + if (procedureNode == null) + { + return null; + } + + Procedure procedure = _nodeToProcedure[procedureNode]; + List sentences = new List(); + List procedures = new List(); + if (throughProcedureReference != null) + { + Node throughProcedureNode = ResolveProcedure(p, sectionNode, throughProcedureReference); + if (throughProcedureNode == null) + { + return null; + } + + Procedure throughProcedure = _nodeToProcedure[throughProcedureNode]; + if (procedure.Number > throughProcedure.Number) + { + // the second procedure name is declared before the first one. + this.Cfg.AddWrongOrderPerformThru(p, procedureNode, throughProcedureNode); + return null; + } + + //Accumulate sentences located between the two procedures + int currentProcedureNumber = procedure.Number; + while (currentProcedureNumber <= throughProcedure.Number) + { + var currentProcedure = this.CurrentProgramCfgBuilder.AllProcedures[currentProcedureNumber]; + currentProcedure.AccumulateSentencesThrough(sentences, throughProcedure, out var lastProcedure); + currentProcedureNumber = lastProcedure.Number + 1; + procedures.Add(currentProcedure); + } + } + else + { + procedures.Add(procedure); + sentences.AddRange(procedure); + } + target = new PerformTarget(sentences, procedures); + _performTargetCache[p] = target; + return target; + } + } +} diff --git a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.Sentence.cs b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.Sentence.cs index ede69526f..7b703c33c 100644 --- a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.Sentence.cs +++ b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.Sentence.cs @@ -17,19 +17,26 @@ private class Sentence : ProcedureDivisionRegion /// public int? FirstBlockIndex { get; } + /// + /// The associated procedure + /// + internal readonly Procedure Procedure; + /// /// Constructor. /// /// Order number of appearance of the sentence. /// First block of the sentence. /// Index of the first block the global SuccessorEdges list. + /// the procedure to which the sentence belongs. /// Pass null if the first block is a root block and consequently has no index in successors list. - public Sentence(int number, BasicBlockForNode firstBlock, int? firstBlockIndex) + public Sentence(int number, BasicBlockForNode firstBlock, int? firstBlockIndex, Procedure procedure) : base(number) { _blocks = new LinkedList(); _blocks.AddLast(firstBlock); FirstBlockIndex = firstBlockIndex; + this.Procedure = procedure; } /// diff --git a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.cs b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.cs index fa7929feb..750ba5539 100644 --- a/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.cs +++ b/TypeCobol.Analysis/Cfg/ControlFlowGraphBuilder.cs @@ -634,18 +634,15 @@ public override void Exit(Node node) /// /// Link this sentence to the current section or paragraph if any. /// - /// The sentence to link. + /// The sentence to link. private void LinkBlockSentenceToCurrentSectionParagraph(Sentence sentence) { - var currentProcedure = (Procedure) this.CurrentProgramCfgBuilder.CurrentParagraph ?? - this.CurrentProgramCfgBuilder.CurrentSection; - - if (currentProcedure != null) + if (sentence.Procedure != null) { - currentProcedure.AddSentence(sentence); + sentence.Procedure.AddSentence(sentence); //Give to this block the name of its paragraph/section as tag. - sentence.FirstBlock.Tag = currentProcedure.Name; + sentence.FirstBlock.Tag = sentence.Procedure.Name; } } @@ -666,7 +663,11 @@ private void StartBlockSentence() this.CurrentProgramCfgBuilder.CurrentBasicBlock.SuccessorEdges.Add(firstBlockIndex.Value); this.CurrentProgramCfgBuilder.Cfg.SuccessorEdges.Add(firstBlock); } - Sentence sentence = new Sentence(number, firstBlock, firstBlockIndex); + + Procedure currentProcedure = (Procedure)this.CurrentProgramCfgBuilder.CurrentParagraph ?? + this.CurrentProgramCfgBuilder.CurrentSection; + + Sentence sentence = new Sentence(number, firstBlock, firstBlockIndex, currentProcedure); this.CurrentProgramCfgBuilder.AllSentences.Add(sentence); this.CurrentProgramCfgBuilder.CurrentSentence = sentence; @@ -910,9 +911,8 @@ private void ResolvePendingGOTOs() case StatementType.GotoSimpleStatement: { GotoSimpleStatement simpleGoto = (GotoSimpleStatement)@goto.CodeElement; - HashSet alteredSymbolRefs = null; //Check if we have altered GOTOs to take in account. - if (PendingAlteredGOTOS != null && PendingAlteredGOTOS.TryGetValue(@goto, out alteredSymbolRefs)) + if (PendingAlteredGOTOS != null && PendingAlteredGOTOS.TryGetValue(@goto, out HashSet alteredSymbolRefs)) { int i = 0; target = new SymbolReference[alteredSymbolRefs.Count + 1]; @@ -986,67 +986,83 @@ T GetUnique(IList list) where T : Node #endregion + /// + /// Add a Direct Call Perform to the Call Perform Relation. + /// For a PERFORM THRU all intermediate procedures are added to the relation. + /// Example of code: + /// main. + /// perform a + /// goback + /// . + /// a. + /// perform b + /// . + /// The relation is: a => b + /// + /// PERFORM procedure caller, source instruction: in the example (perform a) + /// The Procedure of the performCaller: in the example 'a' + /// The PERFORM procedure called, target instruction: in the example (perform b) + /// Call Perform Relation + private void AddDirectCallPerformRelation(PerformProcedure performCaller, Procedure caller, PerformProcedure performCallee, + PerformCallRelation performCallRelation) + { + var item = PendingPERFORMProcedures.FirstOrDefault(i => i.Item1 == performCallee); + if (item == null) + return; + PerformTarget performTarget = GetPerformTarget(performCallee, item.Item2); + if (performTarget == null) + return; + foreach (Procedure calleeProcedure in performTarget.Procedures) + { + performCallRelation.AddToRelation(performCaller, caller, performCallee, calleeProcedure); + } + } + + /// + /// Report Recursive Perform Call diagnostics + /// + /// Call Perform Relation + private void ReportRecursivePerformCall(PerformCallRelation performCallRelation) + { + // First Compute the transitive closure of the relation. + performCallRelation.TransitiveClosure(); + // Report all cyclic procedure. + foreach (var item in performCallRelation) + { + if (item.Value.Procedures.Contains(item.Key)) + { + this.Cfg.AddRecursivePerform(item.Value.PerformCaller, item.Value.PerformCallees); + } + } + } + /// /// Resolve a pending PERFORM procedure /// /// A Tuple made of the PerformProcedure node, the section in which the PERFORM appears /// and the Basic Block Group associated to the Perform Procedure. /// List of new PERFORMs cloned during the resolve process. - private void ResolvePendingPERFORMProcedure(Tuple perform, List> clonedPerforms) + /// The Perform Call relation. + private void ResolvePendingPERFORMProcedure(Tuple perform, List> clonedPerforms, + PerformCallRelation performCallRelation) { PerformProcedure p = perform.Item1; SectionNode sectionNode = perform.Item2; BasicBlockForNodeGroup group = perform.Item3; - SymbolReference procedureReference = p.CodeElement.Procedure; - SymbolReference throughProcedureReference = p.CodeElement.ThroughProcedure; - - Node procedureNode = ResolveProcedure(p, sectionNode, procedureReference); - if (procedureNode == null) + PerformTarget performTarget = GetPerformTarget(p, sectionNode); + if (performTarget == null) return; - - Procedure procedure = _nodeToProcedure[procedureNode]; var clonedBlocksIndexMap = new Dictionary(); - if (throughProcedureReference != null) - { - Node throughProcedureNode = ResolveProcedure(p, sectionNode, throughProcedureReference); - if (throughProcedureNode == null) - return; - - Procedure throughProcedure = _nodeToProcedure[throughProcedureNode]; - if (procedure.Number > throughProcedure.Number) - { - // the second procedure name is declared before the first one. - this.Cfg.AddWrongOrderPerformThru(p, procedureNode, throughProcedureNode); - return; - } - - //Accumulate sentences located between the two procedures - List sentences = new List(); - int currentProcedureNumber = procedure.Number; - while (currentProcedureNumber <= throughProcedure.Number) - { - var currentProcedure = this.CurrentProgramCfgBuilder.AllProcedures[currentProcedureNumber]; - currentProcedure.AccumulateSentencesThrough(sentences, throughProcedure, out var lastProcedure); - currentProcedureNumber = lastProcedure.Number + 1; - } - - //Store sentences into the group - StoreSentences(sentences); - } - else - { - //Store all sentences from the procedure into the group - StoreSentences(procedure); - } - + StoreSentences(); //And finally relocate the Graph. RelocateBasicBlockForNodeGroupGraph(p, group, clonedBlocksIndexMap); - void StoreSentences(IEnumerable sentences) + void StoreSentences() { - foreach (var sentence in sentences) + foreach (var sentence in performTarget.Sentences) { + var currentProcedure = sentence.Procedure; foreach (var block in sentence.Blocks) { System.Diagnostics.Debug.Assert(!clonedBlocksIndexMap.ContainsKey(block.Index)); @@ -1056,16 +1072,7 @@ void StoreSentences(IEnumerable sentences) if (block is BasicBlockForNodeGroup group0) { isPerform = true; //To avoid a second dynamic cast - - //Is there a recursion in the graph ? - if (group.RecursivityGroupSet.Get(group0.GroupIndex) && !group0.HasFlag(BasicBlock.Flags.Recursive)) - { - //Flag group and store recursive perform - group0.SetFlag(BasicBlock.Flags.Recursive, true); - Node offendingInstruction = group0.Instructions.Last.Value; - System.Diagnostics.Debug.Assert(offendingInstruction != null); - this.Cfg.AddRecursivePerform(p, offendingInstruction); - } + AddDirectCallPerformRelation(p, currentProcedure, (PerformProcedure)group0.Instructions.Last.Value, performCallRelation); var clonedGroup0 = clonedPerforms .Select(t => t.Item3) @@ -1102,9 +1109,6 @@ void StoreSentences(IEnumerable sentences) clonedGroup.Group = new LinkedList>(); clonedGroup.TerminalBlocks = null; - group.RecursivityGroupSet.Set(clonedGroup.GroupIndex, true); - clonedGroup.RecursivityGroupSet = new BitArray(group.RecursivityGroupSet); - var originalPerform = this.CurrentProgramCfgBuilder.PendingPERFORMProcedures .Single(t => t.Item3 == block); clonedPerforms.Add(new Tuple(originalPerform.Item1, originalPerform.Item2, clonedGroup)); @@ -1174,14 +1178,12 @@ private void ResolvePendingPERFORMProcedures() { if (this.CurrentProgramCfgBuilder.PendingPERFORMProcedures != null) { + PerformCallRelation performCallRelation = new PerformCallRelation(); var clonedPerforms = new List>(); - //First pass: resolve targets of PERFORMs, some new groups may be created during this foreach (var item in this.CurrentProgramCfgBuilder.PendingPERFORMProcedures) { - item.Item3.RecursivityGroupSet = new BitArray(GroupCounter + 1); - item.Item3.RecursivityGroupSet.Set(item.Item3.GroupIndex, true); - ResolvePendingPERFORMProcedure(item, clonedPerforms); + ResolvePendingPERFORMProcedure(item, clonedPerforms, performCallRelation); } //Second pass: resolve cloned groups created during first pass @@ -1189,22 +1191,23 @@ private void ResolvePendingPERFORMProcedures() { //We are using a regular for instead of foreach because new groups may also be created during this second pass. //New groups/performs to resolve are added at tail and all are processed during this second pass - ResolvePendingPERFORMProcedure(clonedPerforms[i], clonedPerforms); + ResolvePendingPERFORMProcedure(clonedPerforms[i], clonedPerforms, performCallRelation); } + // Report Recursive PERFORM call + ReportRecursivePerformCall(performCallRelation); + //Index groups by their GroupIndex, keep only the final instance of each group after all resolve have been done var groupOrder = new Dictionary(); foreach (var item in this.CurrentProgramCfgBuilder.PendingPERFORMProcedures) { BasicBlockForNodeGroup group = item.Item3; groupOrder[group.GroupIndex] = group; - group.RecursivityGroupSet = null; } foreach (var item in clonedPerforms) { BasicBlockForNodeGroup group = item.Item3; groupOrder[group.GroupIndex] = group; - group.RecursivityGroupSet = null; } //Extend groups according to the current building mode @@ -1279,7 +1282,6 @@ private void ComputeBasicBlockGroupTerminalBlocks(BasicBlockForNodeGroup group) if (group.Group.Count > 0 && group.TerminalBlocks == null) { LinkedListNode> first = group.Group.First; - MultiBranchContext ctx = new MultiBranchContext(null); List> terminals = new List>(); this.CurrentProgramCfgBuilder.Cfg.GetTerminalSuccessorEdges(first.Value, terminals); group.TerminalBlocks = terminals; @@ -1443,7 +1445,7 @@ private void CreateOptionalDiagnostics() System.Diagnostics.Debug.Assert(offendingInstruction.CodeElement != null); string offendingStatement = offendingInstruction.CodeElement.SourceText; Diagnostic d = new Diagnostic(_compilerOptions.CheckRecursivePerforms.GetMessageCode(), perform.CodeElement.Position(), - string.Format(Resource.RecursiveBlockOnPerformProcedure, performTarget, offendingStatement)); + string.Format(Resource.RecursiveBlockOnPerformProcedure, performTarget, offendingStatement, offendingInstruction.CodeElement.Line)); AddDiagnostic(d); } } @@ -2207,7 +2209,7 @@ public virtual void LeavePerformLoop(Perform perform) this.CurrentProgramCfgBuilder.Cfg.SuccessorEdges.Add(nextBlock); - int transBlockIndex = -1; + int transBlockIndex; if (IsIterative(perform.CodeElement)) { //For an Iterative perform, body transition is the perform instruction //the next block is a transition for the perform. diff --git a/TypeCobol.Analysis/Graph/ControlFlowGraph.cs b/TypeCobol.Analysis/Graph/ControlFlowGraph.cs index e38b7f3c7..47b060755 100644 --- a/TypeCobol.Analysis/Graph/ControlFlowGraph.cs +++ b/TypeCobol.Analysis/Graph/ControlFlowGraph.cs @@ -225,22 +225,14 @@ internal void AddWrongOrderPerformThru(PerformProcedure performThru, N procedure /// Register a recursive jump for a perform statement. /// /// Perform node - /// Recursive jump node - internal void AddRecursivePerform(PerformProcedure perform, N recursiveJump) + /// Recursive jump node + internal void AddRecursivePerform(PerformProcedure perform, List recursiveCallChain) { if (RecursivePerforms == null) { RecursivePerforms = new Dictionary>(); } - - if (RecursivePerforms.TryGetValue(perform, out var nodes)) - { - nodes.Add(recursiveJump); - } - else - { - RecursivePerforms.Add(perform, new List() { recursiveJump }); - } + RecursivePerforms.Add(perform, recursiveCallChain); } /// diff --git a/TypeCobol.Analysis/Resource.Designer.cs b/TypeCobol.Analysis/Resource.Designer.cs index 885a89a33..8b89e992b 100644 --- a/TypeCobol.Analysis/Resource.Designer.cs +++ b/TypeCobol.Analysis/Resource.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// Ce code a été généré par un outil. +// Version du runtime :4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si +// le code est régénéré. // //------------------------------------------------------------------------------ @@ -13,13 +13,13 @@ namespace TypeCobol.Analysis { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder + // à l'aide d'un outil, tel que ResGen ou Visual Studio. + // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen + // avec l'option /str ou régénérez votre projet VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resource { @@ -33,7 +33,7 @@ internal Resource() { } /// - /// Returns the cached ResourceManager instance used by this class. + /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal Resource() { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// Remplace la propriété CurrentUICulture du thread actuel pour toutes + /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal Resource() { } /// - /// Looks up a localized string similar to Bad ALTER instruction: could not locate corresponding altered GO TO.. + /// Recherche une chaîne localisée semblable à Bad ALTER instruction: could not locate corresponding altered GO TO.. /// internal static string BadAlterIntrWithNoSiblingGotoInstr { get { @@ -70,7 +70,7 @@ internal static string BadAlterIntrWithNoSiblingGotoInstr { } /// - /// Looks up a localized string similar to PERFORM '{0}' THRU '{1}': '{1}' is declared before '{0}'.. + /// Recherche une chaîne localisée semblable à PERFORM '{0}' THRU '{1}': '{1}' is declared before '{0}'.. /// internal static string BadPerformProcedureThru { get { @@ -79,7 +79,7 @@ internal static string BadPerformProcedureThru { } /// - /// Looks up a localized string similar to Statement '{0}' located at line {1}, column {2} prevents this PERFORM statement to reach its exit.. + /// Recherche une chaîne localisée semblable à Statement '{0}' located at line {1}, column {2} prevents this PERFORM statement to reach its exit.. /// internal static string BasicBlockGroupGoesBeyondTheLimit { get { @@ -88,7 +88,7 @@ internal static string BasicBlockGroupGoesBeyondTheLimit { } /// - /// Looks up a localized string similar to A recursive loop has been encountered while analyzing 'PERFORM {0}', recursive instruction is '{1}'.. + /// Recherche une chaîne localisée semblable à A recursive loop has been encountered while analyzing 'PERFORM {0}', recursive instruction is '{1}' at line {2}.. /// internal static string RecursiveBlockOnPerformProcedure { get { diff --git a/TypeCobol.Analysis/Resource.resx b/TypeCobol.Analysis/Resource.resx index 1cdc2c18a..b06686800 100644 --- a/TypeCobol.Analysis/Resource.resx +++ b/TypeCobol.Analysis/Resource.resx @@ -127,6 +127,6 @@ Statement '{0}' located at line {1}, column {2} prevents this PERFORM statement to reach its exit. - A recursive loop has been encountered while analyzing 'PERFORM {0}', recursive instruction is '{1}'. + A recursive loop has been encountered while analyzing 'PERFORM {0}', recursive instruction is '{1}' at line {2}. \ No newline at end of file diff --git a/TypeCobol.Analysis/TypeCobol.Analysis.csproj b/TypeCobol.Analysis/TypeCobol.Analysis.csproj index fd58633f1..f1826ccef 100644 --- a/TypeCobol.Analysis/TypeCobol.Analysis.csproj +++ b/TypeCobol.Analysis/TypeCobol.Analysis.csproj @@ -34,6 +34,7 @@ + @@ -68,6 +69,7 @@ +