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 @@
+