diff options
author | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
commit | 7c67bbddb53c364086f66260701bc54a61c9659c (patch) | |
tree | 92ab0d4b91d5e2f6e7a3f9d61ea25089e8a71fe0 /lib/dialyzer/test/small_SUITE_data | |
parent | 97dc5e7f396129222419811c173edc7fa767b0f8 (diff) | |
parent | 3b7a6ffddc819bf305353a593904cea9e932e7dc (diff) | |
download | otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.gz otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.bz2 otp-7c67bbddb53c364086f66260701bc54a61c9659c.zip |
Merge tag 'OTP-19.0' into sverker/19/binary_to_atom-utf8-crash/ERL-474/OTP-14590
Diffstat (limited to 'lib/dialyzer/test/small_SUITE_data')
90 files changed, 3277 insertions, 196 deletions
diff --git a/lib/dialyzer/test/small_SUITE_data/results/abs b/lib/dialyzer/test/small_SUITE_data/results/abs new file mode 100644 index 0000000000..f229a6d036 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/abs @@ -0,0 +1,9 @@ + +abs.erl:12: Function i1/0 has no local return +abs.erl:16: The pattern 'true' can never match the type 'false' +abs.erl:23: Function i2/0 has no local return +abs.erl:27: The pattern 'true' can never match the type 'false' +abs.erl:34: Function i3/0 has no local return +abs.erl:37: The pattern 'true' can never match the type 'false' +abs.erl:45: Function i4/0 has no local return +abs.erl:49: The pattern 'true' can never match the type 'false' diff --git a/lib/dialyzer/test/small_SUITE_data/results/app_call b/lib/dialyzer/test/small_SUITE_data/results/app_call index cc1a63f944..1af649815a 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/app_call +++ b/lib/dialyzer/test/small_SUITE_data/results/app_call @@ -1,3 +1,3 @@ -app_call.erl:6: The call M:'foo'() requires that M is of type atom() | tuple() not 42 +app_call.erl:6: The call M:'foo'() requires that M is of type atom() not 42 app_call.erl:9: The call 'mod':F() requires that F is of type atom() not {'gazonk',[]} diff --git a/lib/dialyzer/test/small_SUITE_data/results/behaviour_info b/lib/dialyzer/test/small_SUITE_data/results/behaviour_info new file mode 100644 index 0000000000..2da4d26acb --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/behaviour_info @@ -0,0 +1,2 @@ + +with_bad_format_status.erl:12: The inferred type for the 1st argument of format_status/2 ('bad_arg') is not a supertype of 'normal' | 'terminate', which is expected type for this argument in the callback of the gen_server behaviour diff --git a/lib/dialyzer/test/small_SUITE_data/results/bif1 b/lib/dialyzer/test/small_SUITE_data/results/bif1 new file mode 100644 index 0000000000..289b6f821f --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/bif1 @@ -0,0 +1,3 @@ + +bif1.erl:13: Function string_chars/0 has no local return +bif1.erl:16: The call string:chars(S::65,10,L2::bif1_adt:s()) contains an opaque term as 3rd argument when terms of different types are expected in these positions diff --git a/lib/dialyzer/test/small_SUITE_data/results/blame_contract_range_suppressed b/lib/dialyzer/test/small_SUITE_data/results/blame_contract_range_suppressed new file mode 100644 index 0000000000..40733434f6 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/blame_contract_range_suppressed @@ -0,0 +1,2 @@ + +blame_contract_range_suppressed.erl:8: Function foo/0 has no local return diff --git a/lib/dialyzer/test/small_SUITE_data/results/comparisons b/lib/dialyzer/test/small_SUITE_data/results/comparisons index 642585d25e..5083d2695a 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/comparisons +++ b/lib/dialyzer/test/small_SUITE_data/results/comparisons @@ -1,50 +1,43 @@ comparisons.erl:100: The pattern 'true' can never match the type 'false' -comparisons.erl:101: The pattern 'true' can never match the type 'false' +comparisons.erl:101: The pattern 'false' can never match the type 'true' comparisons.erl:102: The pattern 'false' can never match the type 'true' -comparisons.erl:103: The pattern 'false' can never match the type 'true' +comparisons.erl:103: The pattern 'true' can never match the type 'false' comparisons.erl:104: The pattern 'true' can never match the type 'false' -comparisons.erl:105: The pattern 'true' can never match the type 'false' -comparisons.erl:107: The pattern 'true' can never match the type 'false' -comparisons.erl:108: The pattern 'true' can never match the type 'false' -comparisons.erl:109: The pattern 'false' can never match the type 'true' -comparisons.erl:110: The pattern 'false' can never match the type 'true' -comparisons.erl:111: The pattern 'true' can never match the type 'false' -comparisons.erl:112: The pattern 'true' can never match the type 'false' -comparisons.erl:113: The pattern 'false' can never match the type 'true' -comparisons.erl:114: The pattern 'false' can never match the type 'true' -comparisons.erl:115: The pattern 'true' can never match the type 'false' -comparisons.erl:116: The pattern 'true' can never match the type 'false' -comparisons.erl:117: The pattern 'false' can never match the type 'true' comparisons.erl:118: The pattern 'false' can never match the type 'true' +comparisons.erl:119: The pattern 'false' can never match the type 'true' +comparisons.erl:120: The pattern 'true' can never match the type 'false' +comparisons.erl:121: The pattern 'true' can never match the type 'false' +comparisons.erl:122: The pattern 'false' can never match the type 'true' comparisons.erl:123: The pattern 'false' can never match the type 'true' -comparisons.erl:124: The pattern 'false' can never match the type 'true' +comparisons.erl:124: The pattern 'true' can never match the type 'false' comparisons.erl:125: The pattern 'true' can never match the type 'false' -comparisons.erl:126: The pattern 'true' can never match the type 'false' +comparisons.erl:126: The pattern 'false' can never match the type 'true' comparisons.erl:127: The pattern 'false' can never match the type 'true' -comparisons.erl:128: The pattern 'false' can never match the type 'true' +comparisons.erl:128: The pattern 'true' can never match the type 'false' comparisons.erl:129: The pattern 'true' can never match the type 'false' -comparisons.erl:130: The pattern 'true' can never match the type 'false' +comparisons.erl:130: The pattern 'false' can never match the type 'true' +comparisons.erl:131: The pattern 'false' can never match the type 'true' comparisons.erl:132: The pattern 'true' can never match the type 'false' comparisons.erl:133: The pattern 'true' can never match the type 'false' comparisons.erl:134: The pattern 'false' can never match the type 'true' comparisons.erl:135: The pattern 'false' can never match the type 'true' comparisons.erl:136: The pattern 'true' can never match the type 'false' comparisons.erl:137: The pattern 'true' can never match the type 'false' -comparisons.erl:138: The pattern 'false' can never match the type 'true' -comparisons.erl:139: The pattern 'false' can never match the type 'true' +comparisons.erl:139: The pattern 'true' can never match the type 'false' comparisons.erl:140: The pattern 'true' can never match the type 'false' -comparisons.erl:141: The pattern 'true' can never match the type 'false' +comparisons.erl:141: The pattern 'false' can never match the type 'true' comparisons.erl:142: The pattern 'false' can never match the type 'true' -comparisons.erl:143: The pattern 'false' can never match the type 'true' +comparisons.erl:143: The pattern 'true' can never match the type 'false' comparisons.erl:144: The pattern 'true' can never match the type 'false' -comparisons.erl:145: The pattern 'true' can never match the type 'false' +comparisons.erl:145: The pattern 'false' can never match the type 'true' comparisons.erl:146: The pattern 'false' can never match the type 'true' -comparisons.erl:147: The pattern 'false' can never match the type 'true' -comparisons.erl:152: The pattern 'false' can never match the type 'true' -comparisons.erl:153: The pattern 'false' can never match the type 'true' -comparisons.erl:154: The pattern 'true' can never match the type 'false' -comparisons.erl:155: The pattern 'true' can never match the type 'false' +comparisons.erl:147: The pattern 'true' can never match the type 'false' +comparisons.erl:148: The pattern 'true' can never match the type 'false' +comparisons.erl:149: The pattern 'false' can never match the type 'true' +comparisons.erl:150: The pattern 'false' can never match the type 'true' +comparisons.erl:155: The pattern 'false' can never match the type 'true' +comparisons.erl:156: The pattern 'false' can never match the type 'true' comparisons.erl:157: The pattern 'true' can never match the type 'false' comparisons.erl:158: The pattern 'true' can never match the type 'false' comparisons.erl:159: The pattern 'false' can never match the type 'true' @@ -59,36 +52,62 @@ comparisons.erl:167: The pattern 'false' can never match the type 'true' comparisons.erl:168: The pattern 'false' can never match the type 'true' comparisons.erl:169: The pattern 'true' can never match the type 'false' comparisons.erl:170: The pattern 'true' can never match the type 'false' -comparisons.erl:171: The pattern 'false' can never match the type 'true' -comparisons.erl:172: The pattern 'false' can never match the type 'true' +comparisons.erl:172: The pattern 'true' can never match the type 'false' comparisons.erl:173: The pattern 'true' can never match the type 'false' -comparisons.erl:174: The pattern 'true' can never match the type 'false' +comparisons.erl:174: The pattern 'false' can never match the type 'true' comparisons.erl:175: The pattern 'false' can never match the type 'true' -comparisons.erl:176: The pattern 'false' can never match the type 'true' +comparisons.erl:176: The pattern 'true' can never match the type 'false' +comparisons.erl:177: The pattern 'true' can never match the type 'false' +comparisons.erl:178: The pattern 'false' can never match the type 'true' +comparisons.erl:179: The pattern 'false' can never match the type 'true' +comparisons.erl:180: The pattern 'true' can never match the type 'false' +comparisons.erl:181: The pattern 'true' can never match the type 'false' +comparisons.erl:182: The pattern 'false' can never match the type 'true' +comparisons.erl:183: The pattern 'false' can never match the type 'true' +comparisons.erl:184: The pattern 'true' can never match the type 'false' +comparisons.erl:185: The pattern 'true' can never match the type 'false' comparisons.erl:186: The pattern 'false' can never match the type 'true' comparisons.erl:187: The pattern 'false' can never match the type 'true' -comparisons.erl:188: The pattern 'true' can never match the type 'false' -comparisons.erl:189: The pattern 'true' can never match the type 'false' -comparisons.erl:190: The pattern 'false' can never match the type 'true' -comparisons.erl:191: The pattern 'false' can never match the type 'true' -comparisons.erl:192: The pattern 'true' can never match the type 'false' -comparisons.erl:193: The pattern 'true' can never match the type 'false' -comparisons.erl:203: The pattern 'false' can never match the type 'true' -comparisons.erl:204: The pattern 'false' can never match the type 'true' +comparisons.erl:192: The pattern 'false' can never match the type 'true' +comparisons.erl:193: The pattern 'false' can never match the type 'true' +comparisons.erl:194: The pattern 'true' can never match the type 'false' +comparisons.erl:195: The pattern 'true' can never match the type 'false' +comparisons.erl:196: The pattern 'false' can never match the type 'true' +comparisons.erl:197: The pattern 'false' can never match the type 'true' +comparisons.erl:198: The pattern 'true' can never match the type 'false' +comparisons.erl:199: The pattern 'true' can never match the type 'false' +comparisons.erl:200: The pattern 'false' can never match the type 'true' +comparisons.erl:201: The pattern 'false' can never match the type 'true' +comparisons.erl:202: The pattern 'true' can never match the type 'false' +comparisons.erl:203: The pattern 'true' can never match the type 'false' comparisons.erl:205: The pattern 'true' can never match the type 'false' comparisons.erl:206: The pattern 'true' can never match the type 'false' -comparisons.erl:208: The pattern 'true' can never match the type 'false' +comparisons.erl:207: The pattern 'false' can never match the type 'true' +comparisons.erl:208: The pattern 'false' can never match the type 'true' comparisons.erl:209: The pattern 'true' can never match the type 'false' -comparisons.erl:210: The pattern 'false' can never match the type 'true' +comparisons.erl:210: The pattern 'true' can never match the type 'false' comparisons.erl:211: The pattern 'false' can never match the type 'true' +comparisons.erl:212: The pattern 'false' can never match the type 'true' +comparisons.erl:213: The pattern 'true' can never match the type 'false' +comparisons.erl:214: The pattern 'true' can never match the type 'false' +comparisons.erl:215: The pattern 'false' can never match the type 'true' +comparisons.erl:216: The pattern 'false' can never match the type 'true' +comparisons.erl:217: The pattern 'true' can never match the type 'false' +comparisons.erl:218: The pattern 'true' can never match the type 'false' +comparisons.erl:219: The pattern 'false' can never match the type 'true' +comparisons.erl:220: The pattern 'false' can never match the type 'true' comparisons.erl:221: The pattern 'true' can never match the type 'false' comparisons.erl:222: The pattern 'true' can never match the type 'false' comparisons.erl:223: The pattern 'false' can never match the type 'true' comparisons.erl:224: The pattern 'false' can never match the type 'true' -comparisons.erl:225: The pattern 'true' can never match the type 'false' -comparisons.erl:226: The pattern 'true' can never match the type 'false' -comparisons.erl:227: The pattern 'false' can never match the type 'true' -comparisons.erl:228: The pattern 'false' can never match the type 'true' +comparisons.erl:229: The pattern 'true' can never match the type 'false' +comparisons.erl:230: The pattern 'true' can never match the type 'false' +comparisons.erl:231: The pattern 'false' can never match the type 'true' +comparisons.erl:232: The pattern 'false' can never match the type 'true' +comparisons.erl:233: The pattern 'false' can never match the type 'true' +comparisons.erl:234: The pattern 'false' can never match the type 'true' +comparisons.erl:235: The pattern 'true' can never match the type 'false' +comparisons.erl:236: The pattern 'true' can never match the type 'false' comparisons.erl:242: The pattern 'false' can never match the type 'true' comparisons.erl:243: The pattern 'false' can never match the type 'true' comparisons.erl:244: The pattern 'true' can never match the type 'false' @@ -97,57 +116,86 @@ comparisons.erl:246: The pattern 'false' can never match the type 'true' comparisons.erl:247: The pattern 'false' can never match the type 'true' comparisons.erl:248: The pattern 'true' can never match the type 'false' comparisons.erl:249: The pattern 'true' can never match the type 'false' -comparisons.erl:251: The pattern 'true' can never match the type 'false' -comparisons.erl:252: The pattern 'true' can never match the type 'false' -comparisons.erl:253: The pattern 'false' can never match the type 'true' -comparisons.erl:254: The pattern 'false' can never match the type 'true' -comparisons.erl:263: The pattern 'false' can never match the type 'true' -comparisons.erl:264: The pattern 'false' can never match the type 'true' +comparisons.erl:259: The pattern 'false' can never match the type 'true' +comparisons.erl:260: The pattern 'false' can never match the type 'true' +comparisons.erl:261: The pattern 'true' can never match the type 'false' +comparisons.erl:262: The pattern 'true' can never match the type 'false' +comparisons.erl:264: The pattern 'true' can never match the type 'false' comparisons.erl:265: The pattern 'true' can never match the type 'false' -comparisons.erl:266: The pattern 'true' can never match the type 'false' -comparisons.erl:268: The pattern 'true' can never match the type 'false' -comparisons.erl:269: The pattern 'true' can never match the type 'false' -comparisons.erl:270: The pattern 'false' can never match the type 'true' -comparisons.erl:271: The pattern 'false' can never match the type 'true' -comparisons.erl:272: The pattern 'true' can never match the type 'false' -comparisons.erl:273: The pattern 'true' can never match the type 'false' -comparisons.erl:274: The pattern 'false' can never match the type 'true' -comparisons.erl:275: The pattern 'false' can never match the type 'true' -comparisons.erl:293: The pattern 'false' can never match the type 'true' -comparisons.erl:294: The pattern 'false' can never match the type 'true' -comparisons.erl:295: The pattern 'true' can never match the type 'false' -comparisons.erl:296: The pattern 'true' can never match the type 'false' -comparisons.erl:311: The pattern 'true' can never match the type 'false' -comparisons.erl:312: The pattern 'true' can never match the type 'false' -comparisons.erl:313: The pattern 'false' can never match the type 'true' -comparisons.erl:314: The pattern 'false' can never match the type 'true' -comparisons.erl:44: The pattern 'false' can never match the type 'true' -comparisons.erl:45: The pattern 'false' can never match the type 'true' -comparisons.erl:46: The pattern 'true' can never match the type 'false' -comparisons.erl:47: The pattern 'true' can never match the type 'false' -comparisons.erl:48: The pattern 'false' can never match the type 'true' -comparisons.erl:49: The pattern 'false' can never match the type 'true' -comparisons.erl:50: The pattern 'true' can never match the type 'false' -comparisons.erl:51: The pattern 'true' can never match the type 'false' +comparisons.erl:266: The pattern 'false' can never match the type 'true' +comparisons.erl:267: The pattern 'false' can never match the type 'true' +comparisons.erl:277: The pattern 'true' can never match the type 'false' +comparisons.erl:278: The pattern 'true' can never match the type 'false' +comparisons.erl:279: The pattern 'false' can never match the type 'true' +comparisons.erl:280: The pattern 'false' can never match the type 'true' +comparisons.erl:281: The pattern 'true' can never match the type 'false' +comparisons.erl:282: The pattern 'true' can never match the type 'false' +comparisons.erl:283: The pattern 'false' can never match the type 'true' +comparisons.erl:284: The pattern 'false' can never match the type 'true' +comparisons.erl:298: The pattern 'false' can never match the type 'true' +comparisons.erl:299: The pattern 'false' can never match the type 'true' +comparisons.erl:300: The pattern 'true' can never match the type 'false' +comparisons.erl:301: The pattern 'true' can never match the type 'false' +comparisons.erl:302: The pattern 'false' can never match the type 'true' +comparisons.erl:303: The pattern 'false' can never match the type 'true' +comparisons.erl:304: The pattern 'true' can never match the type 'false' +comparisons.erl:305: The pattern 'true' can never match the type 'false' +comparisons.erl:307: The pattern 'true' can never match the type 'false' +comparisons.erl:308: The pattern 'true' can never match the type 'false' +comparisons.erl:309: The pattern 'false' can never match the type 'true' +comparisons.erl:310: The pattern 'false' can never match the type 'true' +comparisons.erl:319: The pattern 'false' can never match the type 'true' +comparisons.erl:320: The pattern 'false' can never match the type 'true' +comparisons.erl:321: The pattern 'true' can never match the type 'false' +comparisons.erl:322: The pattern 'true' can never match the type 'false' +comparisons.erl:324: The pattern 'true' can never match the type 'false' +comparisons.erl:325: The pattern 'true' can never match the type 'false' +comparisons.erl:326: The pattern 'false' can never match the type 'true' +comparisons.erl:327: The pattern 'false' can never match the type 'true' +comparisons.erl:328: The pattern 'true' can never match the type 'false' +comparisons.erl:329: The pattern 'true' can never match the type 'false' +comparisons.erl:330: The pattern 'false' can never match the type 'true' +comparisons.erl:331: The pattern 'false' can never match the type 'true' +comparisons.erl:349: The pattern 'false' can never match the type 'true' +comparisons.erl:350: The pattern 'false' can never match the type 'true' +comparisons.erl:351: The pattern 'true' can never match the type 'false' +comparisons.erl:352: The pattern 'true' can never match the type 'false' +comparisons.erl:367: The pattern 'true' can never match the type 'false' +comparisons.erl:368: The pattern 'true' can never match the type 'false' +comparisons.erl:369: The pattern 'false' can never match the type 'true' +comparisons.erl:370: The pattern 'false' can never match the type 'true' comparisons.erl:52: The pattern 'false' can never match the type 'true' comparisons.erl:53: The pattern 'false' can never match the type 'true' comparisons.erl:54: The pattern 'true' can never match the type 'false' comparisons.erl:55: The pattern 'true' can never match the type 'false' +comparisons.erl:56: The pattern 'false' can never match the type 'true' +comparisons.erl:57: The pattern 'false' can never match the type 'true' +comparisons.erl:58: The pattern 'true' can never match the type 'false' +comparisons.erl:59: The pattern 'true' can never match the type 'false' +comparisons.erl:60: The pattern 'false' can never match the type 'true' +comparisons.erl:61: The pattern 'false' can never match the type 'true' +comparisons.erl:62: The pattern 'true' can never match the type 'false' +comparisons.erl:63: The pattern 'true' can never match the type 'false' +comparisons.erl:64: The pattern 'false' can never match the type 'true' +comparisons.erl:65: The pattern 'false' can never match the type 'true' +comparisons.erl:66: The pattern 'true' can never match the type 'false' +comparisons.erl:67: The pattern 'true' can never match the type 'false' +comparisons.erl:68: The pattern 'false' can never match the type 'true' comparisons.erl:69: The pattern 'false' can never match the type 'true' -comparisons.erl:70: The pattern 'false' can never match the type 'true' +comparisons.erl:70: The pattern 'true' can never match the type 'false' comparisons.erl:71: The pattern 'true' can never match the type 'false' -comparisons.erl:72: The pattern 'true' can never match the type 'false' -comparisons.erl:73: The pattern 'false' can never match the type 'true' -comparisons.erl:74: The pattern 'false' can never match the type 'true' -comparisons.erl:75: The pattern 'true' can never match the type 'false' -comparisons.erl:76: The pattern 'true' can never match the type 'false' -comparisons.erl:77: The pattern 'false' can never match the type 'true' -comparisons.erl:78: The pattern 'false' can never match the type 'true' -comparisons.erl:79: The pattern 'true' can never match the type 'false' -comparisons.erl:80: The pattern 'true' can never match the type 'false' +comparisons.erl:85: The pattern 'false' can never match the type 'true' +comparisons.erl:86: The pattern 'false' can never match the type 'true' +comparisons.erl:87: The pattern 'true' can never match the type 'false' +comparisons.erl:88: The pattern 'true' can never match the type 'false' +comparisons.erl:89: The pattern 'false' can never match the type 'true' +comparisons.erl:90: The pattern 'false' can never match the type 'true' +comparisons.erl:91: The pattern 'true' can never match the type 'false' +comparisons.erl:92: The pattern 'true' can never match the type 'false' +comparisons.erl:93: The pattern 'false' can never match the type 'true' comparisons.erl:94: The pattern 'false' can never match the type 'true' -comparisons.erl:95: The pattern 'false' can never match the type 'true' +comparisons.erl:95: The pattern 'true' can never match the type 'false' comparisons.erl:96: The pattern 'true' can never match the type 'false' -comparisons.erl:97: The pattern 'true' can never match the type 'false' +comparisons.erl:97: The pattern 'false' can never match the type 'true' comparisons.erl:98: The pattern 'false' can never match the type 'true' -comparisons.erl:99: The pattern 'false' can never match the type 'true' +comparisons.erl:99: The pattern 'true' can never match the type 'false' diff --git a/lib/dialyzer/test/small_SUITE_data/results/confusing_record_warning b/lib/dialyzer/test/small_SUITE_data/results/confusing_record_warning deleted file mode 100644 index ac3d89b02b..0000000000 --- a/lib/dialyzer/test/small_SUITE_data/results/confusing_record_warning +++ /dev/null @@ -1,3 +0,0 @@ - -confusing_record_warning.erl:18: Function test/1 has no local return -confusing_record_warning.erl:18: Matching of pattern {'r', [_]} tagged with a record name violates the declared type of #r{field::'binary' | 'undefined'} diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes index 173ff3a9f1..d2a3ebb766 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes +++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes @@ -1,31 +1,36 @@ -contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) -contracts_with_subtypes.erl:142: The pattern 1 can never match the type string() -contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()} -contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()} -contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X} -contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X} -contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X} -contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X} -contracts_with_subtypes.erl:202: The pattern 1 can never match the type string() -contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,string()} -contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return -contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed') +contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when Arg :: {'a',A} | {'b',B}, A :: 'a' | {'b',B}, B :: 'b' | {'a',A} +contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when Arg :: {'a',A} | {'b',B}, A :: 'a' | {'b',B}, B :: 'b' | {'a',A} +contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when Arg :: {'a',A} | {'b',B}, A :: 'a' | {'b',B}, B :: 'b' | {'a',A} +contracts_with_subtypes.erl:135: The call contracts_with_subtypes:rec2({'a','b'}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:136: The call contracts_with_subtypes:rec2({'b','a'}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:137: The call contracts_with_subtypes:rec2({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:138: The call contracts_with_subtypes:rec2({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:139: The call contracts_with_subtypes:rec2({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:140: The call contracts_with_subtypes:rec2({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:141: The call contracts_with_subtypes:rec2({'a',{'b',{'a',{'b','a'}}}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:142: The call contracts_with_subtypes:rec2({'b',{'a',{'b',{'a','b'}}}}) breaks the contract (Arg) -> 'ok' when Arg :: ab() +contracts_with_subtypes.erl:175: The pattern 1 can never match the type string() +contracts_with_subtypes.erl:178: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()} +contracts_with_subtypes.erl:180: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()} +contracts_with_subtypes.erl:196: The pattern 'alpha' can never match the type {'ok',_} +contracts_with_subtypes.erl:198: The pattern 42 can never match the type {'ok',_} +contracts_with_subtypes.erl:216: The pattern 'alpha' can never match the type {'ok',_} +contracts_with_subtypes.erl:218: The pattern 42 can never match the type {'ok',_} +contracts_with_subtypes.erl:235: The pattern 1 can never match the type string() +contracts_with_subtypes.erl:238: The pattern {'ok', _} can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:239: The pattern 'alpha' can never match the type {'ok',_,string()} contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something' -contracts_with_subtypes.erl:261: Function factored_ets_new_t/0 has no local return -contracts_with_subtypes.erl:262: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term()) -contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,atom()), is_subtype(Res,atom()) -contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,Arg2), is_subtype(Arg2,atom()), is_subtype(Res,atom()) -contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when is_subtype(Arg2,atom()), is_subtype(Arg1,Arg2), is_subtype(Res,atom()) +contracts_with_subtypes.erl:240: The pattern {'ok', 42} can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:241: The pattern 42 can never match the type {'ok',_,string()} +contracts_with_subtypes.erl:267: Function flat_ets_new_t/0 has no local return +contracts_with_subtypes.erl:268: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when Name :: atom(), Options :: [Option], Option :: 'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed' +contracts_with_subtypes.erl:294: Function factored_ets_new_t/0 has no local return +contracts_with_subtypes.erl:295: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when Name :: atom(), Options :: [Option], Option :: Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks, Type :: type(), Access :: access(), Tweaks :: {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed', Pos :: pos_integer(), HeirData :: term() +contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when Arg1 :: atom(), Res :: atom() +contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when Arg1 :: Arg2, Arg2 :: atom(), Res :: atom() +contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when Arg2 :: atom(), Arg1 :: Arg2, Res :: atom() contracts_with_subtypes.erl:7: Invalid type specification for function contracts_with_subtypes:extract/0. The success typing is () -> 'something' -contracts_with_subtypes.erl:80: The call contracts_with_subtypes:foo4(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) +contracts_with_subtypes.erl:80: The call contracts_with_subtypes:foo4(5) breaks the contract (Type) -> Type when Type :: atom() contracts_with_subtypes.erl:81: The call contracts_with_subtypes:foo5(5) breaks the contract (Type::atom()) -> Type::atom() -contracts_with_subtypes.erl:82: The call contracts_with_subtypes:foo6(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) +contracts_with_subtypes.erl:82: The call contracts_with_subtypes:foo6(5) breaks the contract (Type) -> Type when Type :: atom() diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2 b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2 new file mode 100644 index 0000000000..1a8aeb13d0 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2 @@ -0,0 +1,3 @@ + +contracts_with_subtypes2.erl:18: Function t/0 has no local return +contracts_with_subtypes2.erl:19: The call contracts_with_subtypes2:t({'a',{'b',{'c',{'d',{'e',{'g',3}}}}}}) breaks the contract (Arg) -> 'ok' when Arg :: {'a',A}, A :: {'b',B}, B :: {'c',C}, C :: {'d',D}, D :: {'e',E}, E :: {'f',_} diff --git a/lib/dialyzer/test/small_SUITE_data/results/eep37 b/lib/dialyzer/test/small_SUITE_data/results/eep37 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/eep37 diff --git a/lib/dialyzer/test/small_SUITE_data/results/fun_arity b/lib/dialyzer/test/small_SUITE_data/results/fun_arity new file mode 100644 index 0000000000..cc9db65152 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/fun_arity @@ -0,0 +1,37 @@ + +fun_arity.erl:100: Fun application will fail since _cor1 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:100: Function 'Mfa_0_ko'/1 has no local return +fun_arity.erl:104: Fun application will fail since _cor1 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:104: Function 'Mfa_1_ko'/1 has no local return +fun_arity.erl:111: Fun application will fail since _cor1 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:111: Function mFa_0_ko/1 has no local return +fun_arity.erl:115: Fun application will fail since _cor1 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:115: Function mFa_1_ko/1 has no local return +fun_arity.erl:122: Fun application will fail since _cor2 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:122: Function 'MFa_0_ko'/2 has no local return +fun_arity.erl:126: Fun application will fail since _cor2 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:126: Function 'MFa_1_ko'/2 has no local return +fun_arity.erl:35: Fun application will fail since _cor0 :: fun(() -> 'ok') is not a function of arity 1 +fun_arity.erl:35: Function f_0_ko/0 has no local return +fun_arity.erl:39: Fun application will fail since _cor0 :: fun((_) -> 'ok') is not a function of arity 0 +fun_arity.erl:39: Function f_1_ko/0 has no local return +fun_arity.erl:48: Fun application will fail since _cor0 :: fun(() -> 'ok') is not a function of arity 1 +fun_arity.erl:48: Function fa_0_ko/0 has no local return +fun_arity.erl:53: Fun application will fail since _cor0 :: fun((_) -> 'ok') is not a function of arity 0 +fun_arity.erl:53: Function fa_1_ko/0 has no local return +fun_arity.erl:63: Fun application will fail since _cor0 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:63: Function mfa_0_ko/0 has no local return +fun_arity.erl:68: Fun application will fail since _cor0 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:68: Function mfa_1_ko/0 has no local return +fun_arity.erl:76: Fun application will fail since _cor0 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:76: Function mfa_ne_0_ko/0 has no local return +fun_arity.erl:78: Function mf_ne/0 will never be called +fun_arity.erl:81: Fun application will fail since _cor0 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:81: Function mfa_ne_1_ko/0 has no local return +fun_arity.erl:83: Function mf_ne/1 will never be called +fun_arity.erl:89: Fun application will fail since _cor0 :: fun(() -> any()) is not a function of arity 1 +fun_arity.erl:89: Function mfa_nd_0_ko/0 has no local return +fun_arity.erl:90: Call to missing or unexported function fun_arity:mf_nd/0 +fun_arity.erl:93: Fun application will fail since _cor0 :: fun((_) -> any()) is not a function of arity 0 +fun_arity.erl:93: Function mfa_nd_1_ko/0 has no local return +fun_arity.erl:94: Call to missing or unexported function fun_arity:mf_nd/1 diff --git a/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside new file mode 100644 index 0000000000..3e597ef1bc --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside @@ -0,0 +1,7 @@ + +funs_from_outside.erl:18: The pattern 'error' can never match the type {'ok','nothing' | 'something'} +funs_from_outside.erl:32: Function run2/2 has no local return +funs_from_outside.erl:35: Function testb/3 has no local return +funs_from_outside.erl:41: The pattern 'error' can never match the type {'ok','nothing' | 'something'} +funs_from_outside.erl:78: Function test2/1 has no local return +funs_from_outside.erl:83: The pattern 'error' can never match the type 'ok' diff --git a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy index 7ce440a60d..11b9ecade6 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy +++ b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy @@ -1,4 +1,3 @@ -higher_order_discrepancy.erl:11: The call higher_order_discrepancy:g('foo') will never return since it differs in the 1st argument from the success typing arguments: ('bar') -higher_order_discrepancy.erl:14: Function g/1 has no local return -higher_order_discrepancy.erl:14: The pattern 'bar' can never match the type 'foo' +higher_order_discrepancy.erl:19: Function g/1 has no local return +higher_order_discrepancy.erl:19: The pattern 'bar' can never match the type 'foo' diff --git a/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2 b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2 new file mode 100644 index 0000000000..4565112ea0 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2 @@ -0,0 +1,2 @@ + +scala_user.erl:5: Invalid type specification for function scala_user:is_list/2. The success typing is (maybe_improper_list() | tuple(),_) -> boolean() diff --git a/lib/dialyzer/test/small_SUITE_data/results/literals b/lib/dialyzer/test/small_SUITE_data/results/literals new file mode 100644 index 0000000000..1ee39453a4 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/literals @@ -0,0 +1,17 @@ + +literals.erl:11: Function t1/0 has no local return +literals.erl:12: Record construction #r{id::'a'} violates the declared type of field id::'integer' +literals.erl:14: Function t2/0 has no local return +literals.erl:15: Record construction #r{id::'a'} violates the declared type of field id::'integer' +literals.erl:17: Function t3/0 has no local return +literals.erl:18: Record construction #r{id::'a'} violates the declared type of field id::'integer' +literals.erl:20: Function t4/0 has no local return +literals.erl:21: Record construction #r{id::'a'} violates the declared type of field id::'integer' +literals.erl:23: Function m1/1 has no local return +literals.erl:23: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'} +literals.erl:26: Function m2/1 has no local return +literals.erl:26: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'} +literals.erl:29: Function m3/1 has no local return +literals.erl:29: The pattern {{'r', 'a'}} can never match the type any() +literals.erl:32: Function m4/1 has no local return +literals.erl:32: Matching of pattern {'r', 'a'} tagged with a record name violates the declared type of #r{id::'integer'} diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps1 b/lib/dialyzer/test/small_SUITE_data/results/maps1 new file mode 100644 index 0000000000..f36f7f4926 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/maps1 @@ -0,0 +1,4 @@ + +maps1.erl:43: Function t3/0 has no local return +maps1.erl:44: The call maps1:foo(#{'greger'=>3, #{'arne'=>'anka'}=>45},1) will never return since it differs in the 1st and 2nd argument from the success typing arguments: (#{'beta':=_, _=>_},'b') +maps1.erl:52: The variable Mod can never match since previous clauses completely covered the type #{} diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_difftype b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype new file mode 100644 index 0000000000..3018b888db --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype @@ -0,0 +1,3 @@ + +maps_difftype.erl:10: Function empty_mismatch/1 has no local return +maps_difftype.erl:11: The pattern #{} can never match the type tuple() diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_sum b/lib/dialyzer/test/small_SUITE_data/results/maps_sum new file mode 100644 index 0000000000..bd192bdb93 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/maps_sum @@ -0,0 +1,4 @@ + +maps_sum.erl:15: Invalid type specification for function maps_sum:wrong1/1. The success typing is (map()) -> any() +maps_sum.erl:26: Function wrong2/1 has no local return +maps_sum.erl:27: The call lists:foldl(fun((_,_,_) -> any()),0,Data::any()) will never return since it differs in the 1st argument from the success typing arguments: (fun((_,_) -> any()),any(),[any()]) diff --git a/lib/dialyzer/test/small_SUITE_data/results/my_sofs b/lib/dialyzer/test/small_SUITE_data/results/my_sofs index bc97c08d62..0b933e6cd7 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/my_sofs +++ b/lib/dialyzer/test/small_SUITE_data/results/my_sofs @@ -1,3 +1,3 @@ -my_sofs.erl:34: The pattern {'Set', _, _} can never match the type #'OrdSet'{} -my_sofs.erl:54: The pattern {'Set', _, _} can never match the type #'OrdSet'{} +my_sofs.erl:34: Matching of pattern {'Set', _, _} tagged with a record name violates the declared type of #'OrdSet'{} +my_sofs.erl:54: Matching of pattern {'Set', _, _} tagged with a record name violates the declared type of #'OrdSet'{} diff --git a/lib/dialyzer/test/small_SUITE_data/results/non_existing b/lib/dialyzer/test/small_SUITE_data/results/non_existing index 58da2bfc8b..b0da5998c7 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/non_existing +++ b/lib/dialyzer/test/small_SUITE_data/results/non_existing @@ -1,2 +1,3 @@ +non_existing.erl:12: Call to missing or unexported function lists:non_existing_fun/1 non_existing.erl:9: Call to missing or unexported function lists:non_existing_call/1 diff --git a/lib/dialyzer/test/small_SUITE_data/results/predef b/lib/dialyzer/test/small_SUITE_data/results/predef new file mode 100644 index 0000000000..85e210d6e4 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/predef @@ -0,0 +1,8 @@ + +predef.erl:19: Invalid type specification for function predef:array/1. The success typing is (array:array(_)) -> array:array(_) +predef.erl:24: Invalid type specification for function predef:dict/1. The success typing is (dict:dict(_,_)) -> dict:dict(_,_) +predef.erl:29: Invalid type specification for function predef:digraph/1. The success typing is (digraph:graph()) -> [any()] +predef.erl:39: Invalid type specification for function predef:gb_set/1. The success typing is (gb_sets:set(_)) -> gb_sets:set(_) +predef.erl:44: Invalid type specification for function predef:gb_tree/1. The success typing is (gb_trees:tree(_,_)) -> gb_trees:tree(_,_) +predef.erl:49: Invalid type specification for function predef:queue/1. The success typing is (queue:queue(_)) -> queue:queue(_) +predef.erl:54: Invalid type specification for function predef:set/1. The success typing is (sets:set(_)) -> sets:set(_) diff --git a/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring new file mode 100644 index 0000000000..e148e5cf22 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring @@ -0,0 +1,3 @@ + +pretty_bitstring.erl:7: Function t/0 has no local return +pretty_bitstring.erl:8: The call binary:copy(#{#<1>(8, 1, 'integer', ['unsigned', 'big']), #<2>(8, 1, 'integer', ['unsigned', 'big']), #<3>(3, 1, 'integer', ['unsigned', 'big'])}#,2) breaks the contract (Subject,N) -> binary() when Subject :: binary(), N :: non_neg_integer() diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_construct b/lib/dialyzer/test/small_SUITE_data/results/record_construct index c0110b144f..4c40fec298 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/record_construct +++ b/lib/dialyzer/test/small_SUITE_data/results/record_construct @@ -1,6 +1,6 @@ record_construct.erl:15: Function t_opa/0 has no local return -record_construct.erl:16: Record construction #r_opa{b::gb_set(),c::42,e::'false'} violates the declared type of field c::boolean() +record_construct.erl:16: Record construction #r_opa{b::gb_sets:set(_),c::42,e::'false'} violates the declared type of field c::boolean() record_construct.erl:20: Function t_rem/0 has no local return record_construct.erl:21: Record construction #r_rem{a::'gazonk'} violates the declared type of field a::string() record_construct.erl:6: Function t_loc/0 has no local return diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs b/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs index f00c4b10ff..c971935bbf 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs +++ b/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs @@ -1,3 +1,3 @@ record_creation_diffs.erl:10: Function foo/1 has no local return -record_creation_diffs.erl:11: Record construction #bar{some_list::{'this','is','a','tuple'}} violates the declared type of field some_list::'undefined' | [any()] +record_creation_diffs.erl:11: Record construction #bar{some_list::{'this','is','a','tuple'}} violates the declared type of field some_list::[any()] diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_pat b/lib/dialyzer/test/small_SUITE_data/results/record_pat index 9a3f925e42..8317ea041a 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/record_pat +++ b/lib/dialyzer/test/small_SUITE_data/results/record_pat @@ -1,2 +1,2 @@ -record_pat.erl:14: The pattern {'foo', 'baz'} violates the declared type for #foo{} +record_pat.erl:14: Matching of pattern {'foo', 'baz'} tagged with a record name violates the declared type of #foo{bar::integer()} diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_test b/lib/dialyzer/test/small_SUITE_data/results/record_test index 9715f0dcfb..7060bfa200 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/record_test +++ b/lib/dialyzer/test/small_SUITE_data/results/record_test @@ -1,3 +1,3 @@ -record_test.erl:19: The pattern {'foo', _} can never match the type 'foo' +record_test.erl:19: Matching of pattern {'foo', _} tagged with a record name violates the declared type of 'foo' record_test.erl:21: The variable _ can never match since previous clauses completely covered the type 'foo' diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_update b/lib/dialyzer/test/small_SUITE_data/results/record_update new file mode 100644 index 0000000000..ea52057adf --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/record_update @@ -0,0 +1,2 @@ + +record_update.erl:7: Invalid type specification for function record_update:quux/2. The success typing is (#foo{bar::atom()},atom()) -> #foo{bar::atom()} diff --git a/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning b/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning new file mode 100644 index 0000000000..ea3ac92d96 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning @@ -0,0 +1,3 @@ + +relevant_record_warning.erl:22: Function test/1 has no local return +relevant_record_warning.erl:23: Record construction #r{field::<<_:8>>} violates the declared type of field field::'binary' diff --git a/lib/dialyzer/test/small_SUITE_data/results/request1 b/lib/dialyzer/test/small_SUITE_data/results/request1 new file mode 100644 index 0000000000..0cf4017403 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/request1 @@ -0,0 +1,2 @@ + +request1.erl:8: Expression produces a value of type {'a','b'}, but this value is unmatched diff --git a/lib/dialyzer/test/small_SUITE_data/results/suppress_request b/lib/dialyzer/test/small_SUITE_data/results/suppress_request new file mode 100644 index 0000000000..18e82b7972 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/suppress_request @@ -0,0 +1,6 @@ + +suppress_request.erl:21: Expression produces a value of type {'a','b'}, but this value is unmatched +suppress_request.erl:25: Expression produces a value of type {'a','b'}, but this value is unmatched +suppress_request.erl:35: Function test3_b/0 has no local return +suppress_request.erl:39: Guard test 2 =:= A::fun((none()) -> no_return()) can never succeed +suppress_request.erl:7: Type specification suppress_request:test1('a' | 'b') -> 'ok' is a subtype of the success typing: suppress_request:test1('a' | 'b' | 'c') -> 'ok' diff --git a/lib/dialyzer/test/small_SUITE_data/results/trec b/lib/dialyzer/test/small_SUITE_data/results/trec index 01ccc63761..b95df1e6ef 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/trec +++ b/lib/dialyzer/test/small_SUITE_data/results/trec @@ -1,7 +1,7 @@ -trec.erl:26: Function test/0 has no local return -trec.erl:27: The call trec:mk_foo_loc(42,any()) will never return since it differs in the 1st argument from the success typing arguments: ('undefined',atom()) -trec.erl:29: Function mk_foo_loc/2 has no local return -trec.erl:30: Record construction violates the declared type for #foo{} since variable A cannot be of type atom() -trec.erl:36: Function mk_foo_exp/2 has no local return -trec.erl:37: Record construction violates the declared type for #foo{} since variable A cannot be of type atom() +trec.erl:28: Function test/0 has no local return +trec.erl:29: The call trec:mk_foo_loc(42,any()) will never return since it differs in the 1st argument from the success typing arguments: ('undefined',atom()) +trec.erl:31: Function mk_foo_loc/2 has no local return +trec.erl:32: Record construction violates the declared type for #foo{} since variable A cannot be of type atom() +trec.erl:38: Function mk_foo_exp/2 has no local return +trec.erl:39: Record construction violates the declared type for #foo{} since variable A cannot be of type atom() diff --git a/lib/dialyzer/test/small_SUITE_data/results/undefined b/lib/dialyzer/test/small_SUITE_data/results/undefined new file mode 100644 index 0000000000..9daa8640d3 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/undefined @@ -0,0 +1,2 @@ + +r.erl:16: Record construction #r{a::{'fi'},b::{'a','b'},c::[],d::'undefined',e::[],f::'undefined'} violates the declared type of field b::[any()] and d::[any()] and f::[any()] diff --git a/lib/dialyzer/test/small_SUITE_data/src/abs.erl b/lib/dialyzer/test/small_SUITE_data/src/abs.erl new file mode 100644 index 0000000000..251e24cdfc --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/abs.erl @@ -0,0 +1,71 @@ +-module(abs). + +%% OTP-12948. erlang:abs/1 bug fix. + +-export([t/0]). + +t() -> + Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0], + _ = [catch F() || F <- Fs], + ok. + +i1() -> + A = int(), + I1 = i1(A), + true = I1 < 2, + true = I1 < 1. % can never match + +-spec i1(neg_integer()) -> non_neg_integer(). + +i1(A) when is_integer(A), A < 0 -> + abs(A). + +i2() -> + A = int(), + I2 = i2(A), + true = I2 < 1, + true = I2 < 0. % can never match + +-spec i2(non_neg_integer()) -> non_neg_integer(). + +i2(A) when is_integer(A), A >= 0 -> + abs(A). + +i3() -> + A = int(), + I3 = i3(A), + true = I3 < -1, + true = I3 < 0. % can never match + +-spec i3(integer()) -> non_neg_integer(). + +i3(A) when is_integer(A) -> + abs(A). + +i4() -> + A = int(), + I4 = i4(A), + true = I4 =:= 0 orelse I4 =:= 1, + true = I4 < 0 orelse I4 > 1. % can never match + +-spec i4(integer()) -> number(). + +i4(A) when A =:= -1; A =:= 0; A =:= 1 -> + abs(A). + +f1() -> + F1 = f1(float()), + math:sqrt(F1). + +f1(A) -> + abs(A). + +-spec int() -> integer(). + +int() -> + foo:bar(). + +-spec float() -> float(). + +float() -> + math:sqrt(1.0). diff --git a/lib/dialyzer/test/small_SUITE_data/src/appmon_place.erl b/lib/dialyzer/test/small_SUITE_data/src/appmon_place.erl index 60ffbe818f..ddb97796fb 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/appmon_place.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/appmon_place.erl @@ -1,6 +1,6 @@ %%--------------------------------------------------------------------- %% This is added as a test because it was giving a false positive -%% (function move/4 will nevr be called) due to the strange use of +%% (function move/4 will never be called) due to the strange use of %% self-recursive fun construction in placex/3. %% %% The analysis was getting confused that the foldl call will never diff --git a/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl new file mode 100644 index 0000000000..24591e08fa --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl @@ -0,0 +1,12 @@ +-module(with_bad_format_status). + +-behaviour(gen_server). +-export([handle_call/3,handle_cast/2,handle_info/2, + code_change/3, init/1, terminate/2, format_status/2]). +handle_call(_, _, S) -> {noreply, S}. +handle_cast(_, S) -> {noreply, S}. +handle_info(_, S) -> {noreply, S}. +code_change(_, _, _) -> {error, not_implemented}. +init(_) -> {ok, state}. +terminate(_, _) -> ok. +format_status(bad_arg, _) -> ok. % optional callback diff --git a/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl new file mode 100644 index 0000000000..a56ff63d1d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl @@ -0,0 +1,13 @@ +-module(with_format_status). + +-behaviour(gen_server). +-export([handle_call/3,handle_cast/2,handle_info/2, + code_change/3, init/1, terminate/2, format_status/2]). +-export([handle_call/3,handle_cast/2,handle_info/2]). +handle_call(_, _, S) -> {noreply, S}. +handle_cast(_, S) -> {noreply, S}. +handle_info(_, S) -> {noreply, S}. +code_change(_, _, _) -> {error, not_implemented}. +init(_) -> {ok, state}. +terminate(_, _) -> ok. +format_status(normal, _) -> ok. % optional callback diff --git a/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1.erl b/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1.erl new file mode 100644 index 0000000000..bc746538d3 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1.erl @@ -0,0 +1,16 @@ +-module(bif1). + +%% Other set of warnings due to removed of functions from +%% erl_bif_types. + +-export([ets_rename/0, string_chars/0]). + +ets_rename() -> + A = ets:new(fipp, []), + true = not is_atom(A), + ets:rename(A, fopp). % No warning + +string_chars() -> + L2 = bif1_adt:opaque_string(), + S = $A, + string:chars(S, 10, L2). % Warning diff --git a/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1_adt.erl b/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1_adt.erl new file mode 100644 index 0000000000..01b0bccc68 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/bif1/bif1_adt.erl @@ -0,0 +1,12 @@ +-module(bif1_adt). + +-export([opaque_string/0]). + +-export_type([s/0]). + +-opaque s() :: string(). + +-spec opaque_string() -> s(). + +opaque_string() -> + "string". diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl new file mode 100644 index 0000000000..9ad4810a5e --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl @@ -0,0 +1,529 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%%% A stripped version of erl_parse.yrl. +%%% +%%% A type for the abstract format with *external* types has been added. +%%% The type of the abstract format is not up-to-date, but it does not +%%% matter since the purpose of the type is to stress the conversion +%%% of type forms to erl_type(). + +-module(big_external_type). + +-export([parse_form/1,parse_exprs/1,parse_term/1]). +-export([normalise/1,tokens/1,tokens/2]). +-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). + +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +%% Start of Abstract Format + +-type line() :: erl_anno:line(). + +-export_type([af_record_index/0, af_record_field/1, af_record_name/0, + af_field_name/0, af_function_decl/0]). + +-export_type([af_module/0, af_export/0, af_import/0, af_fa_list/0, + af_compile/0, af_file/0, af_record_decl/0, + af_field_decl/0, af_wild_attribute/0, + af_record_update/1, af_catch/0, af_local_call/0, + af_remote_call/0, af_args/0, af_local_function/0, + af_remote_function/0, af_list_comprehension/0, + af_binary_comprehension/0, af_template/0, + af_qualifier_seq/0, af_qualifier/0, af_generator/0, + af_filter/0, af_block/0, af_if/0, af_case/0, af_try/0, + af_clause_seq/0, af_catch_clause_seq/0, af_receive/0, + af_local_fun/0, af_remote_fun/0, af_fun/0, af_query/0, + af_query_access/0, af_clause/0, + af_catch_clause/0, af_catch_pattern/0, af_catch_class/0, + af_body/0, af_guard_seq/0, af_guard/0, af_guard_test/0, + af_record_access/1, af_guard_call/0, + af_remote_guard_call/0, af_pattern/0, af_literal/0, + af_atom/0, af_lit_atom/1, af_integer/0, af_float/0, + af_string/0, af_match/1, af_variable/0, + af_anon_variable/0, af_tuple/1, af_nil/0, af_cons/1, + af_bin/1, af_binelement/1, af_binelement_size/0, + af_binary_op/1, af_binop/0, af_unary_op/1, af_unop/0]). + +-type abstract_form() :: ?MODULE:af_module() + | ?MODULE:af_export() + | ?MODULE:af_import() + | ?MODULE:af_compile() + | ?MODULE:af_file() + | ?MODULE:af_record_decl() + | ?MODULE:af_wild_attribute() + | ?MODULE:af_function_decl(). + +-type af_module() :: {attribute, line(), module, module()}. + +-type af_export() :: {attribute, line(), export, ?MODULE:af_fa_list()}. + +-type af_import() :: {attribute, line(), import, ?MODULE:af_fa_list()}. + +-type af_fa_list() :: [{function(), arity()}]. + +-type af_compile() :: {attribute, line(), compile, any()}. + +-type af_file() :: {attribute, line(), file, {string(), line()}}. + +-type af_record_decl() :: + {attribute, line(), record, ?MODULE:af_record_name(), [?MODULE:af_field_decl()]}. + +-type af_field_decl() :: {record_field, line(), ?MODULE:af_atom()} + | {record_field, line(), ?MODULE:af_atom(), ?MODULE:abstract_expr()}. + +%% Types and specs, among other things... +-type af_wild_attribute() :: {attribute, line(), ?MODULE:af_atom(), any()}. + +-type af_function_decl() :: + {function, line(), function(), arity(), ?MODULE:af_clause_seq()}. + +-type abstract_expr() :: ?MODULE:af_literal() + | ?MODULE:af_match(?MODULE:abstract_expr()) + | ?MODULE:af_variable() + | ?MODULE:af_tuple(?MODULE:abstract_expr()) + | ?MODULE:af_nil() + | ?MODULE:af_cons(?MODULE:abstract_expr()) + | ?MODULE:af_bin(?MODULE:abstract_expr()) + | ?MODULE:af_binary_op(?MODULE:abstract_expr()) + | ?MODULE:af_unary_op(?MODULE:abstract_expr()) + | ?MODULE:af_record_access(?MODULE:abstract_expr()) + | ?MODULE:af_record_update(?MODULE:abstract_expr()) + | ?MODULE:af_record_index() + | ?MODULE:af_record_field(?MODULE:abstract_expr()) + | ?MODULE:af_catch() + | ?MODULE:af_local_call() + | ?MODULE:af_remote_call() + | ?MODULE:af_list_comprehension() + | ?MODULE:af_binary_comprehension() + | ?MODULE:af_block() + | ?MODULE:af_if() + | ?MODULE:af_case() + | ?MODULE:af_try() + | ?MODULE:af_receive() + | ?MODULE:af_local_fun() + | ?MODULE:af_remote_fun() + | ?MODULE:af_fun() + | ?MODULE:af_query() + | ?MODULE:af_query_access(). + +-type af_record_update(T) :: {record, + line(), + ?MODULE:abstract_expr(), + ?MODULE:af_record_name(), + [?MODULE:af_record_field(T)]}. + +-type af_catch() :: {'catch', line(), ?MODULE:abstract_expr()}. + +-type af_local_call() :: {call, line(), ?MODULE:af_local_function(), ?MODULE:af_args()}. + +-type af_remote_call() :: {call, line(), ?MODULE:af_remote_function(), ?MODULE:af_args()}. + +-type af_args() :: [?MODULE:abstract_expr()]. + +-type af_local_function() :: ?MODULE:abstract_expr(). + +-type af_remote_function() :: + {remote, line(), ?MODULE:abstract_expr(), ?MODULE:abstract_expr()}. + +-type af_list_comprehension() :: + {lc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}. + +-type af_binary_comprehension() :: + {bc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}. + +-type af_template() :: ?MODULE:abstract_expr(). + +-type af_qualifier_seq() :: [?MODULE:af_qualifier()]. + +-type af_qualifier() :: ?MODULE:af_generator() | ?MODULE:af_filter(). + +-type af_generator() :: {generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()} + | {b_generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()}. + +-type af_filter() :: ?MODULE:abstract_expr(). + +-type af_block() :: {block, line(), ?MODULE:af_body()}. + +-type af_if() :: {'if', line(), ?MODULE:af_clause_seq()}. + +-type af_case() :: {'case', line(), ?MODULE:abstract_expr(), ?MODULE:af_clause_seq()}. + +-type af_try() :: {'try', + line(), + ?MODULE:af_body(), + ?MODULE:af_clause_seq(), + ?MODULE:af_catch_clause_seq(), + ?MODULE:af_body()}. + +-type af_clause_seq() :: [?MODULE:af_clause(), ...]. + +-type af_catch_clause_seq() :: [?MODULE:af_clause(), ...]. + +-type af_receive() :: + {'receive', line(), ?MODULE:af_clause_seq()} + | {'receive', line(), ?MODULE:af_clause_seq(), ?MODULE:abstract_expr(), ?MODULE:af_body()}. + +-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}. + +-type af_remote_fun() :: + {'fun', line(), {function, module(), function(), arity()}} + | {'fun', line(), {function, ?MODULE:af_atom(), ?MODULE:af_atom(), ?MODULE:af_integer()}}. + +-type af_fun() :: {'fun', line(), {clauses, ?MODULE:af_clause_seq()}}. + +-type af_query() :: {'query', line(), ?MODULE:af_list_comprehension()}. + +-type af_query_access() :: + {record_field, line(), ?MODULE:abstract_expr(), ?MODULE:af_field_name()}. + +-type abstract_clause() :: ?MODULE:af_clause() | ?MODULE:af_catch_clause(). + +-type af_clause() :: + {clause, line(), [?MODULE:af_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}. + +-type af_catch_clause() :: + {clause, line(), [?MODULE:af_catch_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}. + +-type af_catch_pattern() :: + {?MODULE:af_catch_class(), ?MODULE:af_pattern(), ?MODULE:af_anon_variable()}. + +-type af_catch_class() :: + ?MODULE:af_variable() + | ?MODULE:af_lit_atom(throw) | ?MODULE:af_lit_atom(error) | ?MODULE:af_lit_atom(exit). + +-type af_body() :: [?MODULE:abstract_expr(), ...]. + +-type af_guard_seq() :: [?MODULE:af_guard()]. + +-type af_guard() :: [?MODULE:af_guard_test(), ...]. + +-type af_guard_test() :: ?MODULE:af_literal() + | ?MODULE:af_variable() + | ?MODULE:af_tuple(?MODULE:af_guard_test()) + | ?MODULE:af_nil() + | ?MODULE:af_cons(?MODULE:af_guard_test()) + | ?MODULE:af_bin(?MODULE:af_guard_test()) + | ?MODULE:af_binary_op(?MODULE:af_guard_test()) + | ?MODULE:af_unary_op(?MODULE:af_guard_test()) + | ?MODULE:af_record_access(?MODULE:af_guard_test()) + | ?MODULE:af_record_index() + | ?MODULE:af_record_field(?MODULE:af_guard_test()) + | ?MODULE:af_guard_call() + | ?MODULE:af_remote_guard_call(). + +-type af_record_access(T) :: + {record, line(), ?MODULE:af_record_name(), [?MODULE:af_record_field(T)]}. + +-type af_guard_call() :: {call, line(), function(), [?MODULE:af_guard_test()]}. + +-type af_remote_guard_call() :: + {call, line(), atom(), ?MODULE:af_lit_atom(erlang), [?MODULE:af_guard_test()]}. + +-type af_pattern() :: ?MODULE:af_literal() + | ?MODULE:af_match(?MODULE:af_pattern()) + | ?MODULE:af_variable() + | ?MODULE:af_anon_variable() + | ?MODULE:af_tuple(?MODULE:af_pattern()) + | ?MODULE:af_nil() + | ?MODULE:af_cons(?MODULE:af_pattern()) + | ?MODULE:af_bin(?MODULE:af_pattern()) + | ?MODULE:af_binary_op(?MODULE:af_pattern()) + | ?MODULE:af_unary_op(?MODULE:af_pattern()) + | ?MODULE:af_record_index() + | ?MODULE:af_record_field(?MODULE:af_pattern()). + +-type af_literal() :: ?MODULE:af_atom() | ?MODULE:af_integer() | ?MODULE:af_float() | ?MODULE:af_string(). + +-type af_atom() :: ?MODULE:af_lit_atom(atom()). + +-type af_lit_atom(A) :: {atom, line(), A}. + +-type af_integer() :: {integer, line(), non_neg_integer()}. + +-type af_float() :: {float, line(), float()}. + +-type af_string() :: {string, line(), [byte()]}. + +-type af_match(T) :: {match, line(), T, T}. + +-type af_variable() :: {var, line(), atom()}. + +-type af_anon_variable() :: {var, line(), '_'}. + +-type af_tuple(T) :: {tuple, line(), [T]}. + +-type af_nil() :: {nil, line()}. + +-type af_cons(T) :: {cons, line, T, T}. + +-type af_bin(T) :: {bin, line(), [?MODULE:af_binelement(T)]}. + +-type af_binelement(T) :: {bin_element, + line(), + T, + ?MODULE:af_binelement_size(), + type_specifier_list()}. + +-type af_binelement_size() :: default | ?MODULE:abstract_expr(). + +-type af_binary_op(T) :: {op, line(), T, ?MODULE:af_binop(), T}. + +-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-' + | 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++' + | '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:=' + | '=/='. + +-type af_unary_op(T) :: {op, line(), ?MODULE:af_unop(), T}. + +-type af_unop() :: '+' | '*' | 'bnot' | 'not'. + +%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}. +-type type_specifier_list() :: default | [type_specifier(), ...]. + +-type type_specifier() :: af_type() + | af_signedness() + | af_endianness() + | af_unit(). + +-type af_type() :: integer + | float + | binary + | bytes + | bitstring + | bits + | utf8 + | utf16 + | utf32. + +-type af_signedness() :: signed | unsigned. + +-type af_endianness() :: big | little | native. + +-type af_unit() :: {unit, 1..256}. + +-type af_record_index() :: + {record_index, line(), af_record_name(), af_field_name()}. + +-type af_record_field(T) :: {record_field, line(), af_field_name(), T}. + +-type af_record_name() :: atom(). + +-type af_field_name() :: atom(). + +%% End of Abstract Format + +-type error_description() :: term(). +-type error_info() :: {erl_anno:line(), module(), error_description()}. +-type token() :: {Tag :: atom(), Line :: erl_scan:anno()}. + +%% mkop(Op, Arg) -> {op,Line,Op,Arg}. +%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. + +-define(mkop2(L, OpPos, R), + begin + {Op,Pos} = OpPos, + {op,Pos,Op,L,R} + end). + +-define(mkop1(OpPos, A), + begin + {Op,Pos} = OpPos, + {op,Pos,Op,A} + end). + +%% keep track of line info in tokens +-define(line(Tup), element(2, Tup)). + +%% Entry points compatible to old erl_parse. +%% These really suck and are only here until Calle gets multiple +%% entry points working. + +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). +parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> + parse([{'-',L1},{'spec',L2}|Tokens]); +parse_form([{'-',L1},{atom,L2,callback}|Tokens]) -> + parse([{'-',L1},{'callback',L2}|Tokens]); +parse_form(Tokens) -> + parse(Tokens). + +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). +parse_exprs(Tokens) -> + case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> + {ok,Exprs}; + {error,_} = Err -> Err + end. + +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). +parse_term(Tokens) -> + case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> + try normalise(Expr) of + Term -> {ok,Term} + catch + _:_R -> {error,{?line(Expr),?MODULE,"bad term"}} + end; + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} -> + {error,{?line(E2),?MODULE,"bad term"}}; + {error,_} = Err -> Err + end. + +%% Convert between the abstract form of a term and a term. + +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). +normalise({char,_,C}) -> C; +normalise({integer,_,I}) -> I; +normalise({float,_,F}) -> F; +normalise({atom,_,A}) -> A; +normalise({string,_,S}) -> S; +normalise({nil,_}) -> []; +normalise({bin,_,Fs}) -> + {value, B, _} = + eval_bits:expr_grp(Fs, [], + fun(E, _) -> + {value, normalise(E), []} + end, [], true), + B; +normalise({cons,_,Head,Tail}) -> + [normalise(Head)|normalise(Tail)]; +normalise({tuple,_,Args}) -> + list_to_tuple(normalise_list(Args)); +%% Atom dot-notation, as in 'foo.bar.baz' +%% Special case for unary +/-. +normalise({op,_,'+',{char,_,I}}) -> I; +normalise({op,_,'+',{integer,_,I}}) -> I; +normalise({op,_,'+',{float,_,F}}) -> F; +normalise({op,_,'-',{char,_,I}}) -> -I; %Weird, but compatible! +normalise({op,_,'-',{integer,_,I}}) -> -I; +normalise({op,_,'-',{float,_,F}}) -> -F; +normalise(X) -> erlang:error({badarg, X}). + +normalise_list([H|T]) -> + [normalise(H)|normalise_list(T)]; +normalise_list([]) -> + []. + +%% Generate a list of tokens representing the abstract term. + +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. +tokens(Abs) -> + tokens(Abs, []). + +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. +tokens({char,L,C}, More) -> [{char,L,C}|More]; +tokens({integer,L,N}, More) -> [{integer,L,N}|More]; +tokens({float,L,F}, More) -> [{float,L,F}|More]; +tokens({atom,L,A}, More) -> [{atom,L,A}|More]; +tokens({var,L,V}, More) -> [{var,L,V}|More]; +tokens({string,L,S}, More) -> [{string,L,S}|More]; +tokens({nil,L}, More) -> [{'[',L},{']',L}|More]; +tokens({cons,L,Head,Tail}, More) -> + [{'[',L}|tokens(Head, tokens_tail(Tail, More))]; +tokens({tuple,L,[]}, More) -> + [{'{',L},{'}',L}|More]; +tokens({tuple,L,[E|Es]}, More) -> + [{'{',L}|tokens(E, tokens_tuple(Es, ?line(E), More))]. + +tokens_tail({cons,L,Head,Tail}, More) -> + [{',',L}|tokens(Head, tokens_tail(Tail, More))]; +tokens_tail({nil,L}, More) -> + [{']',L}|More]; +tokens_tail(Other, More) -> + L = ?line(Other), + [{'|',L}|tokens(Other, [{']',L}|More])]. + +tokens_tuple([E|Es], Line, More) -> + [{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))]; +tokens_tuple([], Line, More) -> + [{'}',Line}|More]. + +%% Give the relative precedences of operators. + +inop_prec('=') -> {150,100,100}; +inop_prec('!') -> {150,100,100}; +inop_prec('orelse') -> {160,150,150}; +inop_prec('andalso') -> {200,160,160}; +inop_prec('==') -> {300,200,300}; +inop_prec('/=') -> {300,200,300}; +inop_prec('=<') -> {300,200,300}; +inop_prec('<') -> {300,200,300}; +inop_prec('>=') -> {300,200,300}; +inop_prec('>') -> {300,200,300}; +inop_prec('=:=') -> {300,200,300}; +inop_prec('=/=') -> {300,200,300}; +inop_prec('++') -> {400,300,300}; +inop_prec('--') -> {400,300,300}; +inop_prec('+') -> {400,400,500}; +inop_prec('-') -> {400,400,500}; +inop_prec('bor') -> {400,400,500}; +inop_prec('bxor') -> {400,400,500}; +inop_prec('bsl') -> {400,400,500}; +inop_prec('bsr') -> {400,400,500}; +inop_prec('or') -> {400,400,500}; +inop_prec('xor') -> {400,400,500}; +inop_prec('*') -> {500,500,600}; +inop_prec('/') -> {500,500,600}; +inop_prec('div') -> {500,500,600}; +inop_prec('rem') -> {500,500,600}; +inop_prec('band') -> {500,500,600}; +inop_prec('and') -> {500,500,600}; +inop_prec('#') -> {800,700,800}; +inop_prec(':') -> {900,800,900}; +inop_prec('.') -> {900,900,1000}. + +-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'. + +-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}. + +preop_prec('catch') -> {0,100}; +preop_prec('+') -> {600,700}; +preop_prec('-') -> {600,700}; +preop_prec('bnot') -> {600,700}; +preop_prec('not') -> {600,700}; +preop_prec('#') -> {700,800}. + +-spec func_prec() -> {800,700}. + +func_prec() -> {800,700}. + +-spec max_prec() -> 1000. + +max_prec() -> 1000. + +parse(T) -> + bar:foo(T). diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl new file mode 100644 index 0000000000..fe567ff10d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl @@ -0,0 +1,526 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%%% A stripped version of erl_parse.yrl. +%%% +%%% A type for the abstract format with *local* types has been added. +%%% The type of the abstract format is not up-to-date, but it does not +%%% matter since the purpose of the type is to stress the conversion +%%% of type forms to erl_type(). + +-module(big_local_type). + +-export([parse_form/1,parse_exprs/1,parse_term/1]). +-export([normalise/1,tokens/1,tokens/2]). +-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). + +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +%% Start of Abstract Format + +-type line() :: erl_anno:line(). + +-export_type([af_module/0, af_export/0, af_import/0, af_fa_list/0, + af_compile/0, af_file/0, af_record_decl/0, + af_field_decl/0, af_wild_attribute/0, + af_record_update/1, af_catch/0, af_local_call/0, + af_remote_call/0, af_args/0, af_local_function/0, + af_remote_function/0, af_list_comprehension/0, + af_binary_comprehension/0, af_template/0, + af_qualifier_seq/0, af_qualifier/0, af_generator/0, + af_filter/0, af_block/0, af_if/0, af_case/0, af_try/0, + af_clause_seq/0, af_catch_clause_seq/0, af_receive/0, + af_local_fun/0, af_remote_fun/0, af_fun/0, af_query/0, + af_query_access/0, af_clause/0, + af_catch_clause/0, af_catch_pattern/0, af_catch_class/0, + af_body/0, af_guard_seq/0, af_guard/0, af_guard_test/0, + af_record_access/1, af_guard_call/0, + af_remote_guard_call/0, af_pattern/0, af_literal/0, + af_atom/0, af_lit_atom/1, af_integer/0, af_float/0, + af_string/0, af_match/1, af_variable/0, + af_anon_variable/0, af_tuple/1, af_nil/0, af_cons/1, + af_bin/1, af_binelement/1, af_binelement_size/0, + af_binary_op/1, af_binop/0, af_unary_op/1, af_unop/0]). + +-type abstract_form() :: af_module() + | af_export() + | af_import() + | af_compile() + | af_file() + | af_record_decl() + | af_wild_attribute() + | af_function_decl(). + +-type af_module() :: {attribute, line(), module, module()}. + +-type af_export() :: {attribute, line(), export, af_fa_list()}. + +-type af_import() :: {attribute, line(), import, af_fa_list()}. + +-type af_fa_list() :: [{function(), arity()}]. + +-type af_compile() :: {attribute, line(), compile, any()}. + +-type af_file() :: {attribute, line(), file, {string(), line()}}. + +-type af_record_decl() :: + {attribute, line(), record, af_record_name(), [af_field_decl()]}. + +-type af_field_decl() :: {record_field, line(), af_atom()} + | {record_field, line(), af_atom(), abstract_expr()}. + +%% Types and specs, among other things... +-type af_wild_attribute() :: {attribute, line(), af_atom(), any()}. + +-type af_function_decl() :: + {function, line(), function(), arity(), af_clause_seq()}. + +-type abstract_expr() :: af_literal() + | af_match(abstract_expr()) + | af_variable() + | af_tuple(abstract_expr()) + | af_nil() + | af_cons(abstract_expr()) + | af_bin(abstract_expr()) + | af_binary_op(abstract_expr()) + | af_unary_op(abstract_expr()) + | af_record_access(abstract_expr()) + | af_record_update(abstract_expr()) + | af_record_index() + | af_record_field(abstract_expr()) + | af_catch() + | af_local_call() + | af_remote_call() + | af_list_comprehension() + | af_binary_comprehension() + | af_block() + | af_if() + | af_case() + | af_try() + | af_receive() + | af_local_fun() + | af_remote_fun() + | af_fun() + | af_query() + | af_query_access(). + +-type af_record_update(T) :: {record, + line(), + abstract_expr(), + af_record_name(), + [af_record_field(T)]}. + +-type af_catch() :: {'catch', line(), abstract_expr()}. + +-type af_local_call() :: {call, line(), af_local_function(), af_args()}. + +-type af_remote_call() :: {call, line(), af_remote_function(), af_args()}. + +-type af_args() :: [abstract_expr()]. + +-type af_local_function() :: abstract_expr(). + +-type af_remote_function() :: + {remote, line(), abstract_expr(), abstract_expr()}. + +-type af_list_comprehension() :: + {lc, line(), af_template(), af_qualifier_seq()}. + +-type af_binary_comprehension() :: + {bc, line(), af_template(), af_qualifier_seq()}. + +-type af_template() :: abstract_expr(). + +-type af_qualifier_seq() :: [af_qualifier()]. + +-type af_qualifier() :: af_generator() | af_filter(). + +-type af_generator() :: {generate, line(), af_pattern(), abstract_expr()} + | {b_generate, line(), af_pattern(), abstract_expr()}. + +-type af_filter() :: abstract_expr(). + +-type af_block() :: {block, line(), af_body()}. + +-type af_if() :: {'if', line(), af_clause_seq()}. + +-type af_case() :: {'case', line(), abstract_expr(), af_clause_seq()}. + +-type af_try() :: {'try', + line(), + af_body(), + af_clause_seq(), + af_catch_clause_seq(), + af_body()}. + +-type af_clause_seq() :: [af_clause(), ...]. + +-type af_catch_clause_seq() :: [af_clause(), ...]. + +-type af_receive() :: + {'receive', line(), af_clause_seq()} + | {'receive', line(), af_clause_seq(), abstract_expr(), af_body()}. + +-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}. + +-type af_remote_fun() :: + {'fun', line(), {function, module(), function(), arity()}} + | {'fun', line(), {function, af_atom(), af_atom(), af_integer()}}. + +-type af_fun() :: {'fun', line(), {clauses, af_clause_seq()}}. + +-type af_query() :: {'query', line(), af_list_comprehension()}. + +-type af_query_access() :: + {record_field, line(), abstract_expr(), af_field_name()}. + +-type abstract_clause() :: af_clause() | af_catch_clause(). + +-type af_clause() :: + {clause, line(), [af_pattern()], af_guard_seq(), af_body()}. + +-type af_catch_clause() :: + {clause, line(), [af_catch_pattern()], af_guard_seq(), af_body()}. + +-type af_catch_pattern() :: + {af_catch_class(), af_pattern(), af_anon_variable()}. + +-type af_catch_class() :: + af_variable() + | af_lit_atom(throw) | af_lit_atom(error) | af_lit_atom(exit). + +-type af_body() :: [abstract_expr(), ...]. + +-type af_guard_seq() :: [af_guard()]. + +-type af_guard() :: [af_guard_test(), ...]. + +-type af_guard_test() :: af_literal() + | af_variable() + | af_tuple(af_guard_test()) + | af_nil() + | af_cons(af_guard_test()) + | af_bin(af_guard_test()) + | af_binary_op(af_guard_test()) + | af_unary_op(af_guard_test()) + | af_record_access(af_guard_test()) + | af_record_index() + | af_record_field(af_guard_test()) + | af_guard_call() + | af_remote_guard_call(). + +-type af_record_access(T) :: + {record, line(), af_record_name(), [af_record_field(T)]}. + +-type af_guard_call() :: {call, line(), function(), [af_guard_test()]}. + +-type af_remote_guard_call() :: + {call, line(), atom(), af_lit_atom(erlang), [af_guard_test()]}. + +-type af_pattern() :: af_literal() + | af_match(af_pattern()) + | af_variable() + | af_anon_variable() + | af_tuple(af_pattern()) + | af_nil() + | af_cons(af_pattern()) + | af_bin(af_pattern()) + | af_binary_op(af_pattern()) + | af_unary_op(af_pattern()) + | af_record_index() + | af_record_field(af_pattern()). + +-type af_literal() :: af_atom() | af_integer() | af_float() | af_string(). + +-type af_atom() :: af_lit_atom(atom()). + +-type af_lit_atom(A) :: {atom, line(), A}. + +-type af_integer() :: {integer, line(), non_neg_integer()}. + +-type af_float() :: {float, line(), float()}. + +-type af_string() :: {string, line(), [byte()]}. + +-type af_match(T) :: {match, line(), T, T}. + +-type af_variable() :: {var, line(), atom()}. + +-type af_anon_variable() :: {var, line(), '_'}. + +-type af_tuple(T) :: {tuple, line(), [T]}. + +-type af_nil() :: {nil, line()}. + +-type af_cons(T) :: {cons, line, T, T}. + +-type af_bin(T) :: {bin, line(), [af_binelement(T)]}. + +-type af_binelement(T) :: {bin_element, + line(), + T, + af_binelement_size(), + type_specifier_list()}. + +-type af_binelement_size() :: default | abstract_expr(). + +-type af_binary_op(T) :: {op, line(), T, af_binop(), T}. + +-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-' + | 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++' + | '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:=' + | '=/='. + +-type af_unary_op(T) :: {op, line(), af_unop(), T}. + +-type af_unop() :: '+' | '*' | 'bnot' | 'not'. + +%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}. +-type type_specifier_list() :: default | [type_specifier(), ...]. + +-type type_specifier() :: af_type() + | af_signedness() + | af_endianness() + | af_unit(). + +-type af_type() :: integer + | float + | binary + | bytes + | bitstring + | bits + | utf8 + | utf16 + | utf32. + +-type af_signedness() :: signed | unsigned. + +-type af_endianness() :: big | little | native. + +-type af_unit() :: {unit, 1..256}. + +-type af_record_index() :: + {record_index, line(), af_record_name(), af_field_name()}. + +-type af_record_field(T) :: {record_field, line(), af_field_name(), T}. + +-type af_record_name() :: atom(). + +-type af_field_name() :: atom(). + +%% End of Abstract Format + +-type error_description() :: term(). +-type error_info() :: {erl_anno:line(), module(), error_description()}. +-type token() :: {Tag :: atom(), Line :: erl_anno:line()}. + +%% mkop(Op, Arg) -> {op,Line,Op,Arg}. +%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. + +-define(mkop2(L, OpPos, R), + begin + {Op,Pos} = OpPos, + {op,Pos,Op,L,R} + end). + +-define(mkop1(OpPos, A), + begin + {Op,Pos} = OpPos, + {op,Pos,Op,A} + end). + +%% keep track of line info in tokens +-define(line(Tup), element(2, Tup)). + +%% Entry points compatible to old erl_parse. +%% These really suck and are only here until Calle gets multiple +%% entry points working. + +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). +parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> + parse([{'-',L1},{'spec',L2}|Tokens]); +parse_form([{'-',L1},{atom,L2,callback}|Tokens]) -> + parse([{'-',L1},{'callback',L2}|Tokens]); +parse_form(Tokens) -> + parse(Tokens). + +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). +parse_exprs(Tokens) -> + case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> + {ok,Exprs}; + {error,_} = Err -> Err + end. + +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). +parse_term(Tokens) -> + case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> + try normalise(Expr) of + Term -> {ok,Term} + catch + _:_R -> {error,{?line(Expr),?MODULE,"bad term"}} + end; + {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} -> + {error,{?line(E2),?MODULE,"bad term"}}; + {error,_} = Err -> Err + end. + +%% Convert between the abstract form of a term and a term. + +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). +normalise({char,_,C}) -> C; +normalise({integer,_,I}) -> I; +normalise({float,_,F}) -> F; +normalise({atom,_,A}) -> A; +normalise({string,_,S}) -> S; +normalise({nil,_}) -> []; +normalise({bin,_,Fs}) -> + {value, B, _} = + eval_bits:expr_grp(Fs, [], + fun(E, _) -> + {value, normalise(E), []} + end, [], true), + B; +normalise({cons,_,Head,Tail}) -> + [normalise(Head)|normalise(Tail)]; +normalise({tuple,_,Args}) -> + list_to_tuple(normalise_list(Args)); +%% Atom dot-notation, as in 'foo.bar.baz' +%% Special case for unary +/-. +normalise({op,_,'+',{char,_,I}}) -> I; +normalise({op,_,'+',{integer,_,I}}) -> I; +normalise({op,_,'+',{float,_,F}}) -> F; +normalise({op,_,'-',{char,_,I}}) -> -I; %Weird, but compatible! +normalise({op,_,'-',{integer,_,I}}) -> -I; +normalise({op,_,'-',{float,_,F}}) -> -F; +normalise(X) -> erlang:error({badarg, X}). + +normalise_list([H|T]) -> + [normalise(H)|normalise_list(T)]; +normalise_list([]) -> + []. + +%% Generate a list of tokens representing the abstract term. + +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. +tokens(Abs) -> + tokens(Abs, []). + +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. +tokens({char,L,C}, More) -> [{char,L,C}|More]; +tokens({integer,L,N}, More) -> [{integer,L,N}|More]; +tokens({float,L,F}, More) -> [{float,L,F}|More]; +tokens({atom,L,A}, More) -> [{atom,L,A}|More]; +tokens({var,L,V}, More) -> [{var,L,V}|More]; +tokens({string,L,S}, More) -> [{string,L,S}|More]; +tokens({nil,L}, More) -> [{'[',L},{']',L}|More]; +tokens({cons,L,Head,Tail}, More) -> + [{'[',L}|tokens(Head, tokens_tail(Tail, More))]; +tokens({tuple,L,[]}, More) -> + [{'{',L},{'}',L}|More]; +tokens({tuple,L,[E|Es]}, More) -> + [{'{',L}|tokens(E, tokens_tuple(Es, ?line(E), More))]. + +tokens_tail({cons,L,Head,Tail}, More) -> + [{',',L}|tokens(Head, tokens_tail(Tail, More))]; +tokens_tail({nil,L}, More) -> + [{']',L}|More]; +tokens_tail(Other, More) -> + L = ?line(Other), + [{'|',L}|tokens(Other, [{']',L}|More])]. + +tokens_tuple([E|Es], Line, More) -> + [{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))]; +tokens_tuple([], Line, More) -> + [{'}',Line}|More]. + +%% Give the relative precedences of operators. + +inop_prec('=') -> {150,100,100}; +inop_prec('!') -> {150,100,100}; +inop_prec('orelse') -> {160,150,150}; +inop_prec('andalso') -> {200,160,160}; +inop_prec('==') -> {300,200,300}; +inop_prec('/=') -> {300,200,300}; +inop_prec('=<') -> {300,200,300}; +inop_prec('<') -> {300,200,300}; +inop_prec('>=') -> {300,200,300}; +inop_prec('>') -> {300,200,300}; +inop_prec('=:=') -> {300,200,300}; +inop_prec('=/=') -> {300,200,300}; +inop_prec('++') -> {400,300,300}; +inop_prec('--') -> {400,300,300}; +inop_prec('+') -> {400,400,500}; +inop_prec('-') -> {400,400,500}; +inop_prec('bor') -> {400,400,500}; +inop_prec('bxor') -> {400,400,500}; +inop_prec('bsl') -> {400,400,500}; +inop_prec('bsr') -> {400,400,500}; +inop_prec('or') -> {400,400,500}; +inop_prec('xor') -> {400,400,500}; +inop_prec('*') -> {500,500,600}; +inop_prec('/') -> {500,500,600}; +inop_prec('div') -> {500,500,600}; +inop_prec('rem') -> {500,500,600}; +inop_prec('band') -> {500,500,600}; +inop_prec('and') -> {500,500,600}; +inop_prec('#') -> {800,700,800}; +inop_prec(':') -> {900,800,900}; +inop_prec('.') -> {900,900,1000}. + +-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'. + +-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}. + +preop_prec('catch') -> {0,100}; +preop_prec('+') -> {600,700}; +preop_prec('-') -> {600,700}; +preop_prec('bnot') -> {600,700}; +preop_prec('not') -> {600,700}; +preop_prec('#') -> {700,800}. + +-spec func_prec() -> {800,700}. + +func_prec() -> {800,700}. + +-spec max_prec() -> 1000. + +max_prec() -> 1000. + +parse(T) -> + bar:foo(T). diff --git a/lib/dialyzer/test/small_SUITE_data/src/bin_compr.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_compr.erl deleted file mode 100644 index 8c2497ed21..0000000000 --- a/lib/dialyzer/test/small_SUITE_data/src/bin_compr.erl +++ /dev/null @@ -1,16 +0,0 @@ -%%% -*- erlang-indent-level: 2 -*- -%%%------------------------------------------------------------------------ -%%% File : bin_compr.erl -%%% Purpose : Test case which crashes in dialyzer_dataflow:bind_bin_segs/5. -%%%------------------------------------------------------------------------ - --module(bin_compr). - --export([bc/1]). - -%% The binary comprehension below is stupid: it consumes the whole -%% bitstr in one go and produces a [666] result provided Bits is a -%% bitstr of at least 8 bits. Still, this is a valid Erlang program -%% and dialyzer's analysis should not crash on it. -bc(Bits) -> - [666 || <<_:8/integer, _/bits>> <= Bits]. diff --git a/lib/dialyzer/test/small_SUITE_data/src/blame_contract_range_suppressed.erl b/lib/dialyzer/test/small_SUITE_data/src/blame_contract_range_suppressed.erl new file mode 100644 index 0000000000..8b66d35083 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/blame_contract_range_suppressed.erl @@ -0,0 +1,15 @@ +%%----------------------------------------------------------------------- +%% Like ./blame_contract_range.erl, but warning is suppressed. +%%----------------------------------------------------------------------- +-module(blame_contract_range_suppressed). + +-export([foo/0]). + +foo() -> + bar(b). + +-dialyzer({nowarn_function, bar/1}). + +-spec bar(atom()) -> a. +bar(a) -> a; +bar(b) -> b. diff --git a/lib/dialyzer/test/small_SUITE_data/src/comparisons.erl b/lib/dialyzer/test/small_SUITE_data/src/comparisons.erl index 70e3cb6af4..64927c6c91 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/comparisons.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/comparisons.erl @@ -19,12 +19,20 @@ tuple(X) when is_tuple(X) -> X. list() -> list(?r). list(X) when is_list(X) -> X. +map() -> map(?r). +map(X) when is_map(X) -> #{}. + +bitstring() -> bitstring(?r). +bitstring(X) when is_bitstring(X) -> <<0:63>>. + i() -> integer(). f() -> mfloat(). n() -> case ?r of 1 -> i(); 2 -> f() end. a() -> atom(). t() -> tuple(). l() -> list(). +m() -> map(). +b() -> bitstring(). na() -> case ?r of 1 -> n(); 2 -> a() end. at() -> case ?r of 1 -> t(); 2 -> a() end. tl() -> case ?r of 1 -> t(); 2 -> l() end. @@ -53,6 +61,14 @@ test_i_ll_l() -> case i() < l() of true -> always; false -> never end. test_i_le_l() -> case i() =< l() of true -> always; false -> never end. test_i_gg_l() -> case i() > l() of true -> never; false -> always end. test_i_ge_l() -> case i() >= l() of true -> never; false -> always end. +test_i_ll_m() -> case i() < m() of true -> always; false -> never end. +test_i_le_m() -> case i() =< m() of true -> always; false -> never end. +test_i_gg_m() -> case i() > m() of true -> never; false -> always end. +test_i_ge_m() -> case i() >= m() of true -> never; false -> always end. +test_i_ll_b() -> case i() < b() of true -> always; false -> never end. +test_i_le_b() -> case i() =< b() of true -> always; false -> never end. +test_i_gg_b() -> case i() > b() of true -> never; false -> always end. +test_i_ge_b() -> case i() >= b() of true -> never; false -> always end. test_f_ll_i() -> case f() < i() of true -> maybe; false -> maybe_too end. test_f_le_i() -> case f() =< i() of true -> maybe; false -> maybe_too end. @@ -78,6 +94,14 @@ test_f_ll_l() -> case f() < l() of true -> always; false -> never end. test_f_le_l() -> case f() =< l() of true -> always; false -> never end. test_f_gg_l() -> case f() > l() of true -> never; false -> always end. test_f_ge_l() -> case f() >= l() of true -> never; false -> always end. +test_f_ll_m() -> case f() < m() of true -> always; false -> never end. +test_f_le_m() -> case f() =< m() of true -> always; false -> never end. +test_f_gg_m() -> case f() > m() of true -> never; false -> always end. +test_f_ge_m() -> case f() >= m() of true -> never; false -> always end. +test_f_ll_b() -> case f() < b() of true -> always; false -> never end. +test_f_le_b() -> case f() =< b() of true -> always; false -> never end. +test_f_gg_b() -> case f() > b() of true -> never; false -> always end. +test_f_ge_b() -> case f() >= b() of true -> never; false -> always end. test_n_ll_i() -> case n() < i() of true -> maybe; false -> maybe_too end. test_n_le_i() -> case n() =< i() of true -> maybe; false -> maybe_too end. @@ -103,6 +127,14 @@ test_n_ll_l() -> case n() < l() of true -> always; false -> never end. test_n_le_l() -> case n() =< l() of true -> always; false -> never end. test_n_gg_l() -> case n() > l() of true -> never; false -> always end. test_n_ge_l() -> case n() >= l() of true -> never; false -> always end. +test_n_ll_m() -> case n() < m() of true -> always; false -> never end. +test_n_le_m() -> case n() =< m() of true -> always; false -> never end. +test_n_gg_m() -> case n() > m() of true -> never; false -> always end. +test_n_ge_m() -> case n() >= m() of true -> never; false -> always end. +test_n_ll_b() -> case n() < b() of true -> always; false -> never end. +test_n_le_b() -> case n() =< b() of true -> always; false -> never end. +test_n_gg_b() -> case n() > b() of true -> never; false -> always end. +test_n_ge_b() -> case n() >= b() of true -> never; false -> always end. test_a_ll_i() -> case a() < i() of true -> never; false -> always end. test_a_le_i() -> case a() =< i() of true -> never; false -> always end. @@ -128,6 +160,14 @@ test_a_ll_l() -> case a() < l() of true -> always; false -> never end. test_a_le_l() -> case a() =< l() of true -> always; false -> never end. test_a_gg_l() -> case a() > l() of true -> never; false -> always end. test_a_ge_l() -> case a() >= l() of true -> never; false -> always end. +test_a_ll_m() -> case a() < m() of true -> always; false -> never end. +test_a_le_m() -> case a() =< m() of true -> always; false -> never end. +test_a_gg_m() -> case a() > m() of true -> never; false -> always end. +test_a_ge_m() -> case a() >= m() of true -> never; false -> always end. +test_a_ll_b() -> case a() < b() of true -> always; false -> never end. +test_a_le_b() -> case a() =< b() of true -> always; false -> never end. +test_a_gg_b() -> case a() > b() of true -> never; false -> always end. +test_a_ge_b() -> case a() >= b() of true -> never; false -> always end. test_t_ll_i() -> case t() < i() of true -> never; false -> always end. test_t_le_i() -> case t() =< i() of true -> never; false -> always end. @@ -153,6 +193,14 @@ test_t_ll_l() -> case t() < l() of true -> always; false -> never end. test_t_le_l() -> case t() =< l() of true -> always; false -> never end. test_t_gg_l() -> case t() > l() of true -> never; false -> always end. test_t_ge_l() -> case t() >= l() of true -> never; false -> always end. +test_t_ll_m() -> case t() < m() of true -> always; false -> never end. +test_t_le_m() -> case t() =< m() of true -> always; false -> never end. +test_t_gg_m() -> case t() > m() of true -> never; false -> always end. +test_t_ge_m() -> case t() >= m() of true -> never; false -> always end. +test_t_ll_b() -> case t() < b() of true -> always; false -> never end. +test_t_le_b() -> case t() =< b() of true -> always; false -> never end. +test_t_gg_b() -> case t() > b() of true -> never; false -> always end. +test_t_ge_b() -> case t() >= b() of true -> never; false -> always end. test_l_ll_i() -> case l() < i() of true -> never; false -> always end. test_l_le_i() -> case l() =< i() of true -> never; false -> always end. @@ -178,6 +226,14 @@ test_l_ll_l() -> case l() < l() of true -> maybe; false -> maybe_too end. test_l_le_l() -> case l() =< l() of true -> maybe; false -> maybe_too end. test_l_gg_l() -> case l() > l() of true -> maybe; false -> maybe_too end. test_l_ge_l() -> case l() >= l() of true -> maybe; false -> maybe_too end. +test_l_ll_m() -> case l() < m() of true -> never; false -> always end. +test_l_le_m() -> case l() =< m() of true -> never; false -> always end. +test_l_gg_m() -> case l() > m() of true -> always; false -> never end. +test_l_ge_m() -> case l() >= m() of true -> always; false -> never end. +test_l_ll_b() -> case l() < b() of true -> always; false -> never end. +test_l_le_b() -> case l() =< b() of true -> always; false -> never end. +test_l_gg_b() -> case l() > b() of true -> never; false -> always end. +test_l_ge_b() -> case l() >= b() of true -> never; false -> always end. test_n_ll_na() -> case n() < na() of true -> maybe; false -> maybe_too end. test_n_le_na() -> case n() =< na() of true -> maybe; false -> maybe_too end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/contract3.erl b/lib/dialyzer/test/small_SUITE_data/src/contract3.erl index 5b0bee9694..a6ce91882e 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/contract3.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/contract3.erl @@ -18,16 +18,23 @@ t(X, Y, Z) -> (atom()|list()) -> atom(). t1(X) -> - foo:bar(X). + f(X). -spec t2(atom(), integer()) -> integer(); (atom(), list()) -> atom(). t2(X, Y) -> - foo:bar(X, Y). + g(X, Y). -spec t3(atom(), integer(), list()) -> integer(); (X, integer(), list()) -> X. t3(X, Y, Z) -> X. + +%% dummy functions below + +f(X) -> X. + +g(X, Y) when is_atom(X), is_integer(Y) -> Y; +g(X, Y) when is_atom(X), is_list(Y) -> X. diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl index d72138d509..dbabd904c2 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl @@ -103,15 +103,48 @@ c(babb) -> rec_arg({b, {a, {b, b}}}); c(ababb) -> rec_arg({a, {b, {a, {b, b}}}}); c(babaa) -> rec_arg({b, {a, {b, {a, a}}}}). -w(ab) -> rec_arg({a, b}); -w(ba) -> rec_arg({b, a}); -w(aba) -> rec_arg({a, {b, a}}); -w(bab) -> rec_arg({b, {a, b}}); -w(abab) -> rec_arg({a, {b, {a, b}}}); -w(baba) -> rec_arg({b, {a, {b, a}}}); +w(ab) -> rec_arg({a, b}); % breaks the contract +w(ba) -> rec_arg({b, a}); % breaks the contract +w(aba) -> rec_arg({a, {b, a}}); % no longer breaks the contract +w(bab) -> rec_arg({b, {a, b}}); % breaks the contract +w(abab) -> rec_arg({a, {b, {a, b}}}); % no longer breaks the contract +w(baba) -> rec_arg({b, {a, {b, a}}}); % no longer breaks the contract w(ababa) -> rec_arg({a, {b, {a, {b, a}}}}); w(babab) -> rec_arg({b, {a, {b, {a, b}}}}). +%% For comparison: the same thing with types + +-type ab() :: {a, a()} | {b, b()}. +-type a() :: a | {b, b()}. +-type b() :: b | {a, a()}. + +-spec rec2(Arg) -> ok when + Arg :: ab(). + +rec2(X) -> get(X). + +d(aa) -> rec2({a, a}); +d(bb) -> rec2({b, b}); +d(abb) -> rec2({a, {b, b}}); +d(baa) -> rec2({b, {a, a}}); +d(abaa) -> rec2({a, {b, {a, a}}}); +d(babb) -> rec2({b, {a, {b, b}}}); +d(ababb) -> rec2({a, {b, {a, {b, b}}}}); +d(babaa) -> rec2({b, {a, {b, {a, a}}}}). + +q(ab) -> rec2({a, b}); % breaks the contract +q(ba) -> rec2({b, a}); % breaks the contract +q(aba) -> rec2({a, {b, a}}); % breaks the contract +q(bab) -> rec2({b, {a, b}}); % breaks the contract +q(abab) -> rec2({a, {b, {a, b}}}); % breaks the contract +q(baba) -> rec2({b, {a, {b, a}}}); % breaks the contract +q(ababa) -> rec2({a, {b, {a, {b, a}}}}); % breaks the contract +q(babab) -> rec2({b, {a, {b, {a, b}}}}); % breaks the contract +q(ababab) -> rec2({a, {b, {a, {b, {a, b}}}}}); +q(bababa) -> rec2({b, {a, {b, {a, {b, a}}}}}); +q(abababa) -> rec2({a, {b, {a, {b, {a, {b, a}}}}}}); +q(bababab) -> rec2({b, {a, {b, {a, {b, {a, b}}}}}}). + %=============================================================================== -type dublo(X) :: {X, X}. @@ -143,7 +176,7 @@ st(X) when is_atom(X) -> _Other -> ok end; alpha -> bad; - {ok, 42} -> bad; + {ok, 42} -> ok; 42 -> bad end. @@ -161,7 +194,7 @@ dt(X) when is_atom(X) -> err2 -> ok; {ok, X} -> ok; alpha -> bad; - {ok, 42} -> bad; + {ok, 42} -> ok; 42 -> bad end. @@ -181,7 +214,7 @@ dt2(X) when is_atom(X) -> err2 -> ok; {ok, X} -> ok; alpha -> bad; - {ok, 42} -> bad; + {ok, 42} -> ok; 42 -> bad end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl new file mode 100644 index 0000000000..d2f945b284 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl @@ -0,0 +1,40 @@ +-module(contracts_with_subtypes2). + +-compile(export_all). + +-behaviour(supervisor). + +-spec t(Arg) -> ok when + Arg :: {a, A}, + A :: {b, B}, + B :: {c, C}, + C :: {d, D}, + D :: {e, E}, + E :: {f, _}. + +t(X) -> + get(X). + +t() -> + t({a, {b, {c, {d, {e, {g, 3}}}}}}). % breaks the contract + +%% This one should possibly result in warnings about unused variables. +-spec l() -> ok when + X :: Y, + Y :: X. + +l() -> + ok. + +%% This is the example from seq12547 (ticket OTP-11798). +%% There used to be a warning. + +-spec init(term()) -> Result when + Result :: {ok, {{supervisor:strategy(), + non_neg_integer(), + pos_integer()}, + [supervisor:child_spec()]}} + | ignore. + +init(_) -> + foo:bar(). diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl new file mode 100644 index 0000000000..f362a06bca --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl @@ -0,0 +1,6 @@ +-type host() :: nonempty_string(). +-type path() :: nonempty_string(). +-type url() :: binary(). + +% The host portion of a url, if available. +-type url_host() :: host() | none. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl new file mode 100644 index 0000000000..8cab65fc9c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl @@ -0,0 +1,148 @@ + +-define(SECOND, 1000). +-define(MINUTE, (60 * ?SECOND)). +-define(HOUR, (60 * ?MINUTE)). +-define(DAY, (24 * ?HOUR)). +-define(MB, (1024 * 1024)). + +% Maximum length of tag/blob prefix +-define(NAME_MAX, 511). + +% How long ddfs node startup can take. The most time-consuming part +% is the scanning of the tag objects in the node's DDFS volumes. +-define(NODE_STARTUP, (1 * ?MINUTE)). + +% How long to wait on the master for replies from nodes. +-define(NODE_TIMEOUT, (10 * ?SECOND)). + +% How long to wait for a reply from an operation coordinated by the +% master that accesses nodes. This value should be larger than +% NODE_TIMEOUT. +-define(NODEOP_TIMEOUT, (1 * ?MINUTE)). + +% The minimum amount of free space a node must have, to be considered +% a primary candidate host for a new blob. +-define(MIN_FREE_SPACE, (1024 * ?MB)). + +% The maximum number of active HTTP connections on a system (this +% applies separately for GET and PUT operations). +-define(HTTP_MAX_ACTIVE, 3). + +% The maximum number of waiting HTTP connections to queue up on a busy system. +-define(HTTP_QUEUE_LENGTH, 100). + +% The maximum number of simultaneous HTTP connections. Note that +% HTTP_MAX_CONNS * 2 * 2 + 32 < Maximum number of file descriptors, where +% 2 = Get and put, 2 = two FDs required for each connection (connection +% itself + a file it accesses), 32 = a guess how many extra fds is needed. +-define(HTTP_MAX_CONNS, 128). + +% How long to keep a PUT request in queue if the system is busy. +-define(PUT_WAIT_TIMEOUT, (1 * ?MINUTE)). + +% How long to keep a GET request in queue if the system is busy. +-define(GET_WAIT_TIMEOUT, (1 * ?MINUTE)). + +% An unused loaded tag expires in TAG_EXPIRES milliseconds. Note that +% if TAG_EXPIRES is not smaller than GC_INTERVAL, tags will never +% expire from the memory cache and will always take up memory. +-define(TAG_EXPIRES, (10 * ?HOUR)). + +% How often the master's cache of all known tag names is refreshed. +% This refresh is only needed to purge deleted tags eventually from +% the tag cache. It doesn't harm to have a long interval. +-define(TAG_CACHE_INTERVAL, (10 * ?MINUTE)). + +% How soon a tag object initialized in memory expires if it's content +% cannot be fetched from the cluster. +-define(TAG_EXPIRES_ONERROR, (1 * ?SECOND)). + +% How often a DDFS node should refresh its tag cache from disk. +-define(FIND_TAGS_INTERVAL, ?DAY). + +% How often buffered (delayed) updates to a tag need to be +% flushed. Tradeoff: The longer the interval, the more updates are +% bundled in a single commit. On the other hand, in the worst case +% the requester has to wait for the full interval before getting a +% reply. A long interval also increases the likelihood that the server +% crashes before the commit has finished successfully, making requests +% more unreliable. +-define(DELAYED_FLUSH_INTERVAL, (1 * ?SECOND)). + +% How long to wait between garbage collection runs. +-define(GC_INTERVAL, ?DAY). + +% Max duration for a GC run. This should be smaller than +% min(ORPHANED_{BLOB,TAG}_EXPIRES). +-define(GC_MAX_DURATION, (3 * ?DAY)). + +% How long to wait after startup for cluster to stabilize before +% starting the first GC run. +-define(GC_DEFAULT_INITIAL_WAIT, (5 * ?MINUTE)). + +% The longest potential interval between messages in the GC protocol; +% used to ensure GC makes forward progress. This can be set to the +% estimated time to traverse all the volumes on a DDFS node. +-define(GC_PROGRESS_INTERVAL, (30 * ?MINUTE)). + +% Number of extra replicas (i.e. lost replicas recovered during GC) to +% allow before deleting extra replicas. +-define(NUM_EXTRA_REPLICAS, 1). + +% Permissions for files backing blobs and tags. +-define(FILE_MODE, 8#00400). + +% How often to check available disk space in ddfs_node. +-define(DISKSPACE_INTERVAL, (10 * ?SECOND)). + +% The maximum size of payloads of HTTP requests to the /ddfs/tag/ +% prefix. +-define(MAX_TAG_BODY_SIZE, (512 * ?MB)). + +% Tag attribute names and values have a limited size, and there +% can be only a limited number of them. +-define(MAX_TAG_ATTRIB_NAME_SIZE, 1024). +-define(MAX_TAG_ATTRIB_VALUE_SIZE, 1024). +-define(MAX_NUM_TAG_ATTRIBS, 1000). + +% How long HTTP requests that perform tag updates should wait to +% finish (a long time). +-define(TAG_UPDATE_TIMEOUT, ?DAY). + +% Timeout for re-replicating a single blob over HTTP PUT. This +% depends on the largest blobs hosted by DDFS, and the speed of the +% cluster network. +-define(GC_PUT_TIMEOUT, (180 * ?MINUTE)). + +% Delete !partial files after this many milliseconds. +-define(PARTIAL_EXPIRES, ?DAY). + +% When orphaned blob can be deleted. This should be large enough that +% you can upload all the new blobs of a tag and perform the tag update +% within this time. +-define(ORPHANED_BLOB_EXPIRES, (5 * ?DAY)). + +% When orphaned tag can be deleted. +-define(ORPHANED_TAG_EXPIRES, (5 * ?DAY)). + +% How long a tag has to stay on the deleted list before +% we can permanently forget it, after all known instances +% of the tag object have been removed. This quarantine period +% ensures that a node that was temporarily unavailable +% and reactivates can't resurrect deleted tags. You +% must ensure that all temporarily inactive nodes +% are reactivated (or cleaned) within the ?DELETED_TAG_EXPIRES +% time frame. +% +% This value _must_ be larger than the other time-related DDFS +% parameters listed in this file. In particular, it must be larger +% than ORPHANED_TAG_EXPIRES. +-define(DELETED_TAG_EXPIRES, (30 * ?DAY)). + +% How many times a tag operation should be retried before aborting. +-define(MAX_TAG_OP_RETRIES, 3). + +% How long to wait before timing out a tag retrieval. This should be +% large enough to read a large tag object off the disk and send it +% over the network. +-define(GET_TAG_TIMEOUT, (5 * ?MINUTE)). diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl new file mode 100644 index 0000000000..e43ec23fe1 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl @@ -0,0 +1,9 @@ +-type volume_name() :: nonempty_string(). + +% Diskinfo is {FreeSpace, UsedSpace}. +-type diskinfo() :: {non_neg_integer(), non_neg_integer()}. +-type volume() :: {diskinfo(), volume_name()}. + +-type object_type() :: 'blob' | 'tag'. +-type object_name() :: binary(). +-type taginfo() :: {erlang:timestamp(), volume_name()}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl new file mode 100644 index 0000000000..dc43f7586b --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl @@ -0,0 +1,17 @@ +-type local_object() :: {object_name(), node()}. +-type phase() :: 'start' | 'build_map' | 'map_wait' | 'gc' + | 'rr_blobs' | 'rr_blobs_wait' | 'rr_tags'. +-type protocol_msg() :: {'check_blob', object_name()} | 'start_gc' | 'end_rr'. + +-type blob_update() :: {object_name(), 'filter' | [url()]}. + +-type check_blob_result() :: 'false' | {'true', volume_name()}. + +% GC statistics + +% {Files, Bytes} +-type gc_stat() :: {non_neg_integer(), non_neg_integer()}. +% {Kept, Deleted} +-type obj_stats() :: {gc_stat(), gc_stat()}. +% {Tags, Blobs}. +-type gc_run_stats() :: {obj_stats(), obj_stats()}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl new file mode 100644 index 0000000000..2be2773dc5 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl @@ -0,0 +1,531 @@ +-module(ddfs_master). +-behaviour(gen_server). + +-export([start_link/0]). +-export([get_tags/1, get_tags/3, + get_nodeinfo/1, + get_read_nodes/0, + get_hosted_tags/1, + gc_blacklist/0, gc_blacklist/1, + gc_stats/0, + choose_write_nodes/3, + new_blob/4, new_blob/5, + safe_gc_blacklist/0, safe_gc_blacklist/1, + refresh_tag_cache/0, + tag_notify/2, + tag_operation/2, tag_operation/3, + update_gc_stats/1, + update_nodes/1 + ]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + +-define(WEB_PORT, 8011). + +-compile(nowarn_deprecated_type). + +-include("common_types.hrl"). +-include("gs_util.hrl"). +-include("config.hrl"). +-include("ddfs.hrl"). +-include("ddfs_tag.hrl"). +-include("ddfs_gc.hrl"). + +-type node_info() :: {node(), {non_neg_integer(), non_neg_integer()}}. +-type gc_stats() :: none | gc_run_stats(). + +-record(state, {tags = gb_trees:empty() :: gb_trees:tree(), + tag_cache = false :: false | gb_sets:set(), + cache_refresher :: pid(), + + nodes = [] :: [node_info()], + write_blacklist = [] :: [node()], + read_blacklist = [] :: [node()], + gc_blacklist = [] :: [node()], + safe_gc_blacklist = gb_sets:empty() :: gb_sets:set(), + gc_stats = none :: none | {gc_stats(), erlang:timestamp()}}). +-type state() :: #state{}. +-type replyto() :: {pid(), reference()}. + +-export_type([gc_stats/0, node_info/0]). + +%% =================================================================== +%% API functions + +-spec start_link() -> {ok, pid()}. +start_link() -> + lager:info("DDFS master starts"), + case gen_server:start_link({local, ?MODULE}, ?MODULE, [], []) of + {ok, Server} -> {ok, Server}; + {error, {already_started, Server}} -> {ok, Server} + end. + +-spec tag_operation(term(), tagname()) -> term(). +tag_operation(Op, Tag) -> + gen_server:call(?MODULE, {tag, Op, Tag}). +-spec tag_operation(term(), tagname(), non_neg_integer() | infinity) -> + term(). +tag_operation(Op, Tag, Timeout) -> + gen_server:call(?MODULE, {tag, Op, Tag}, Timeout). + +-spec tag_notify(term(), tagname()) -> ok. +tag_notify(Op, Tag) -> + gen_server:cast(?MODULE, {tag_notify, Op, Tag}). + +-spec get_nodeinfo(all) -> {ok, [node_info()]}. +get_nodeinfo(all) -> + gen_server:call(?MODULE, {get_nodeinfo, all}). + +-spec get_read_nodes() -> {ok, [node()], non_neg_integer()} | {error, term()}. +get_read_nodes() -> + gen_server:call(?MODULE, get_read_nodes, infinity). + +-spec gc_blacklist() -> {ok, [node()]}. +gc_blacklist() -> + gen_server:call(?MODULE, gc_blacklist). + +-spec gc_blacklist([node()]) -> ok. +gc_blacklist(Nodes) -> + gen_server:cast(?MODULE, {gc_blacklist, Nodes}). + +-spec gc_stats() -> {ok, none | {gc_stats(), erlang:timestamp()}} | {error, term()}. +gc_stats() -> + gen_server:call(?MODULE, gc_stats). + +-spec get_hosted_tags(host()) -> {ok, [tagname()]} | {error, term()}. +get_hosted_tags(Host) -> + gen_server:call(?MODULE, {get_hosted_tags, Host}). + +-spec choose_write_nodes(non_neg_integer(), [node()], [node()]) -> {ok, [node()]}. +choose_write_nodes(K, Include, Exclude) -> + gen_server:call(?MODULE, {choose_write_nodes, K, Include, Exclude}). + +-spec get_tags(gc) -> {ok, [tagname()], [node()]} | too_many_failed_nodes; + (safe) -> {ok, [binary()]} | too_many_failed_nodes. +get_tags(Mode) -> + get_tags(?MODULE, Mode, ?GET_TAG_TIMEOUT). + +-spec get_tags(server(), gc, non_neg_integer()) -> + {ok, [tagname()], [node()]} | too_many_failed_nodes; + (server(), safe, non_neg_integer()) -> + {ok, [binary()]} | too_many_failed_nodes. +get_tags(Server, Mode, Timeout) -> + disco_profile:timed_run( + fun() -> gen_server:call(Server, {get_tags, Mode}, Timeout) end, + get_tags). + +-spec new_blob(string()|object_name(), non_neg_integer(), [node()], [node()]) -> + too_many_replicas | {ok, [nonempty_string()]}. +new_blob(Obj, K, Include, Exclude) -> + gen_server:call(?MODULE, {new_blob, Obj, K, Include, Exclude}, infinity). + +-spec new_blob(server(), string()|object_name(), non_neg_integer(), [node()], [node()]) -> + too_many_replicas | {ok, [nonempty_string()]}. +new_blob(Master, Obj, K, Include, Exclude) -> + gen_server:call(Master, {new_blob, Obj, K, Include, Exclude}, infinity). + +-spec safe_gc_blacklist() -> {ok, [node()]} | {error, term()}. +safe_gc_blacklist() -> + gen_server:call(?MODULE, safe_gc_blacklist). + +-spec safe_gc_blacklist(gb_sets:set()) -> ok. +safe_gc_blacklist(SafeGCBlacklist) -> + gen_server:cast(?MODULE, {safe_gc_blacklist, SafeGCBlacklist}). + +-spec update_gc_stats(gc_run_stats()) -> ok. +update_gc_stats(Stats) -> + gen_server:cast(?MODULE, {update_gc_stats, Stats}). + +-type nodes_update() :: [{node(), boolean(), boolean()}]. +-spec update_nodes(nodes_update()) -> ok. +update_nodes(DDFSNodes) -> + gen_server:cast(?MODULE, {update_nodes, DDFSNodes}). + +-spec update_nodestats(gb_trees:tree()) -> ok. +update_nodestats(NewNodes) -> + gen_server:cast(?MODULE, {update_nodestats, NewNodes}). + +-spec update_tag_cache(gb_sets:set()) -> ok. +update_tag_cache(TagCache) -> + gen_server:cast(?MODULE, {update_tag_cache, TagCache}). + +-spec refresh_tag_cache() -> ok. +refresh_tag_cache() -> + gen_server:cast(?MODULE, refresh_tag_cache). + +%% =================================================================== +%% gen_server callbacks + +-spec init(_) -> gs_init(). +init(_Args) -> + _ = [disco_profile:new_histogram(Name) + || Name <- [get_tags, do_get_tags_all, do_get_tags_filter, + do_get_tags_safe, do_get_tags_gc]], + spawn_link(fun() -> monitor_diskspace() end), + spawn_link(fun() -> ddfs_gc:start_gc(disco:get_setting("DDFS_DATA")) end), + Refresher = spawn_link(fun() -> refresh_tag_cache_proc() end), + put(put_port, disco:get_setting("DDFS_PUT_PORT")), + {ok, #state{cache_refresher = Refresher}}. + +-type choose_write_nodes_msg() :: {choose_write_nodes, non_neg_integer(), [node()], [node()]}. +-type new_blob_msg() :: {new_blob, string() | object_name(), non_neg_integer(), [node()]}. +-type tag_msg() :: {tag, ddfs_tag:call_msg(), tagname()}. +-spec handle_call(dbg_state_msg(), from(), state()) -> + gs_reply(state()); + ({get_nodeinfo, all}, from(), state()) -> + gs_reply({ok, [node_info()]}); + (get_read_nodes, from(), state()) -> + gs_reply({ok, [node()], non_neg_integer}); + (gc_blacklist, from(), state()) -> + gs_reply({ok, [node()]}); + (gc_stats, from(), state()) -> + gs_reply({ok, gc_stats(), erlang:timestamp()}); + (choose_write_nodes_msg(), from(), state()) -> + gs_reply({ok, [node()]}); + (new_blob_msg(), from(), state()) -> + gs_reply(new_blob_result()); + (tag_msg(), from(), state()) -> + gs_reply({error, nonodes}) | gs_noreply(); + ({get_tags, gc | safe}, from(), state()) -> + gs_noreply(); + ({get_hosted_tags, host()}, from(), state()) -> + gs_noreply(); + (safe_gc_blacklist, from(), state()) -> + gs_reply({ok, [node()]}). +handle_call(dbg_get_state, _, S) -> + {reply, S, S}; + +handle_call({get_nodeinfo, all}, _From, #state{nodes = Nodes} = S) -> + {reply, {ok, Nodes}, S}; + +handle_call(get_read_nodes, _F, #state{nodes = Nodes, read_blacklist = RB} = S) -> + {reply, do_get_readable_nodes(Nodes, RB), S}; + +handle_call(gc_blacklist, _F, #state{gc_blacklist = Nodes} = S) -> + {reply, {ok, Nodes}, S}; + +handle_call(gc_stats, _F, #state{gc_stats = Stats} = S) -> + {reply, {ok, Stats}, S}; + +handle_call({choose_write_nodes, K, Include, Exclude}, _, + #state{nodes = N, write_blacklist = WBL, gc_blacklist = GBL} = S) -> + BL = lists:umerge(WBL, GBL), + {reply, do_choose_write_nodes(N, K, Include, Exclude, BL), S}; + +handle_call({new_blob, Obj, K, Include, Exclude}, _, + #state{nodes = N, gc_blacklist = GBL, write_blacklist = WBL} = S) -> + BL = lists:umerge(WBL, GBL), + {reply, do_new_blob(Obj, K, Include, Exclude, BL, N), S}; + +handle_call({tag, _M, _Tag}, _From, #state{nodes = []} = S) -> + {reply, {error, no_nodes}, S}; + +handle_call({tag, M, Tag}, From, S) -> + {noreply, do_tag_request(M, Tag, From, S)}; + +handle_call({get_tags, Mode}, From, #state{nodes = Nodes} = S) -> + spawn(fun() -> + gen_server:reply(From, do_get_tags(Mode, [N || {N, _} <- Nodes])) + end), + {noreply, S}; + +handle_call({get_hosted_tags, Host}, From, S) -> + spawn(fun() -> gen_server:reply(From, ddfs_gc:hosted_tags(Host)) end), + {noreply, S}; + +handle_call(safe_gc_blacklist, _From, #state{safe_gc_blacklist = SBL} = S) -> + {reply, {ok, gb_sets:to_list(SBL)}, S}. + +-spec handle_cast({tag_notify, ddfs_tag:cast_msg(), tagname()} + | {gc_blacklist, [node()]} + | {safe_gc_blacklist, gb_sets:set()} + | {update_gc_stats, gc_stats()} + | {update_tag_cache, gb_sets:set()} + | refresh_tag_cache + | {update_nodes, nodes_update()} + | {update_nodestats, gb_trees:tree()}, + state()) -> gs_noreply(). +handle_cast({tag_notify, M, Tag}, S) -> + {noreply, do_tag_notify(M, Tag, S)}; + +handle_cast({gc_blacklist, Nodes}, #state{safe_gc_blacklist = SBL} = S) -> + BLSet = gb_sets:from_list(Nodes), + NewSBL = gb_sets:intersection(BLSet, SBL), + {noreply, S#state{gc_blacklist = gb_sets:to_list(BLSet), + safe_gc_blacklist = NewSBL}}; + +handle_cast({safe_gc_blacklist, SafeBlacklist}, #state{gc_blacklist = BL} = S) -> + SBL = gb_sets:intersection(SafeBlacklist, gb_sets:from_list(BL)), + {noreply, S#state{safe_gc_blacklist = SBL}}; + +handle_cast({update_gc_stats, Stats}, S) -> + {noreply, S#state{gc_stats = {Stats, now()}}}; + +handle_cast({update_tag_cache, TagCache}, S) -> + {noreply, S#state{tag_cache = TagCache}}; + +handle_cast(refresh_tag_cache, #state{cache_refresher = Refresher} = S) -> + Refresher ! refresh, + {noreply, S}; + +handle_cast({update_nodes, NewNodes}, S) -> + {noreply, do_update_nodes(NewNodes, S)}; + +handle_cast({update_nodestats, NewNodes}, S) -> + {noreply, do_update_nodestats(NewNodes, S)}. + +-spec handle_info({'DOWN', _, _, pid(), _}, state()) -> gs_noreply(). +handle_info({'DOWN', _, _, Pid, _}, S) -> + {noreply, do_tag_exit(Pid, S)}. + +%% =================================================================== +%% gen_server callback stubs + +-spec terminate(term(), state()) -> ok. +terminate(Reason, _State) -> + lager:warning("DDFS master died: ~p", [Reason]). + +-spec code_change(term(), state(), term()) -> {ok, state()}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%% =================================================================== +%% internal functions + +-spec do_get_readable_nodes([node_info()], [node()]) -> + {ok, [node()], non_neg_integer()}. +do_get_readable_nodes(Nodes, ReadBlacklist) -> + NodeSet = gb_sets:from_ordset(lists:sort([Node || {Node, _} <- Nodes])), + BlackSet = gb_sets:from_ordset(ReadBlacklist), + ReadableNodeSet = gb_sets:subtract(NodeSet, BlackSet), + {ok, gb_sets:to_list(ReadableNodeSet), gb_sets:size(BlackSet)}. + +-spec do_choose_write_nodes([node_info()], non_neg_integer(), [node()], [node()], [node()]) -> + {ok, [node()]}. +do_choose_write_nodes(Nodes, K, Include, Exclude, BlackList) -> + % Include is the list of nodes that must be included + % + % Node selection algorithm: + % 1. try to choose K nodes randomly from all the nodes which have + % more than ?MIN_FREE_SPACE bytes free space available and which + % are not excluded or blacklisted. + % 2. if K nodes cannot be found this way, choose the K emptiest + % nodes which are not excluded or blacklisted. + Primary = ([N || {N, {Free, _Total}} <- Nodes, Free > ?MIN_FREE_SPACE / 1024] + -- (Exclude ++ BlackList)), + if length(Primary) >= K -> + {ok, Include ++ disco_util:choose_random(Primary -- Include , K - length(Include))}; + true -> + Preferred = [N || {N, _} <- lists:reverse(lists:keysort(2, Nodes))], + Secondary = Include ++ lists:sublist(Preferred -- (Include ++ Exclude ++ BlackList), + K - length(Include)), + {ok, Secondary} + end. + +-type new_blob_result() :: too_many_replicas | {ok, [nonempty_string()]}. +-spec do_new_blob(string()|object_name(), non_neg_integer(), [node()], [node()], [node()], [node_info()]) -> + new_blob_result(). +do_new_blob(_Obj, K, _Include, _Exclude, _BlackList, Nodes) when K > length(Nodes) -> + too_many_replicas; +do_new_blob(Obj, K, Include, Exclude, BlackList, Nodes) -> + {ok, WriteNodes} = do_choose_write_nodes(Nodes, K, Include, Exclude, BlackList), + Urls = [["http://", disco:host(N), ":", get(put_port), "/ddfs/", Obj] + || N <- WriteNodes], + {ok, Urls}. + +% Tag request: Start a new tag server if one doesn't exist already. Forward +% the request to the tag server. + +-spec get_tag_pid(tagname(), gb_trees:tree(), false | gb_sets:set()) -> + {pid(), gb_trees:tree()}. +get_tag_pid(Tag, Tags, Cache) -> + case gb_trees:lookup(Tag, Tags) of + none -> + NotFound = (Cache =/= false + andalso not gb_sets:is_element(Tag, Cache)), + {ok, Server} = ddfs_tag:start(Tag, NotFound), + erlang:monitor(process, Server), + {Server, gb_trees:insert(Tag, Server, Tags)}; + {value, P} -> + {P, Tags} + end. + +-spec do_tag_request(term(), tagname(), replyto(), state()) -> + state(). +do_tag_request(M, Tag, From, #state{tags = Tags, tag_cache = Cache} = S) -> + {Pid, TagsN} = get_tag_pid(Tag, Tags, Cache), + gen_server:cast(Pid, {M, From}), + S#state{tags = TagsN, + tag_cache = Cache =/= false andalso gb_sets:add(Tag, Cache)}. + +-spec do_tag_notify(term(), tagname(), state()) -> state(). +do_tag_notify(M, Tag, #state{tags = Tags, tag_cache = Cache} = S) -> + {Pid, TagsN} = get_tag_pid(Tag, Tags, Cache), + gen_server:cast(Pid, {notify, M}), + S#state{tags = TagsN, + tag_cache = Cache =/= false andalso gb_sets:add(Tag, Cache)}. + +-spec do_update_nodes(nodes_update(), state()) -> state(). +do_update_nodes(NewNodes, #state{nodes = Nodes, tags = Tags} = S) -> + WriteBlacklist = lists:sort([Node || {Node, false, _} <- NewNodes]), + ReadBlacklist = lists:sort([Node || {Node, _, false} <- NewNodes]), + OldNodes = gb_trees:from_orddict(Nodes), + UpdatedNodes = lists:keysort(1, [case gb_trees:lookup(Node, OldNodes) of + none -> + {Node, {0, 0}}; + {value, OldStats} -> + {Node, OldStats} + end || {Node, _WB, _RB} <- NewNodes]), + if + UpdatedNodes =/= Nodes -> + _ = [gen_server:cast(Pid, {die, none}) || Pid <- gb_trees:values(Tags)], + spawn(fun() -> + {ok, ReadableNodes, RBSize} = + do_get_readable_nodes(UpdatedNodes, ReadBlacklist), + refresh_tag_cache(ReadableNodes, RBSize) + end), + S#state{nodes = UpdatedNodes, + write_blacklist = WriteBlacklist, + read_blacklist = ReadBlacklist, + tag_cache = false, + tags = gb_trees:empty()}; + true -> + S#state{write_blacklist = WriteBlacklist, + read_blacklist = ReadBlacklist} + end. + +-spec do_update_nodestats(gb_trees:tree(), state()) -> state(). +do_update_nodestats(NewNodes, #state{nodes = Nodes} = S) -> + UpdatedNodes = [case gb_trees:lookup(Node, NewNodes) of + none -> + {Node, Stats}; + {value, NewStats} -> + {Node, NewStats} + end || {Node, Stats} <- Nodes], + S#state{nodes = UpdatedNodes}. + +-spec do_tag_exit(pid(), state()) -> state(). +do_tag_exit(Pid, S) -> + NewTags = [X || {_, V} = X <- gb_trees:to_list(S#state.tags), V =/= Pid], + S#state{tags = gb_trees:from_orddict(NewTags)}. + +-spec do_get_tags(all | filter, [node()]) -> {[node()], [node()], [binary()]}; + (safe, [node()]) -> {ok, [binary()]} | too_many_failed_nodes; + (gc, [node()]) -> {ok, [binary()], [node()]} | too_many_failed_nodes. +do_get_tags(all, Nodes) -> + disco_profile:timed_run( + fun() -> + {Replies, Failed} = + gen_server:multi_call(Nodes, ddfs_node, get_tags, ?NODE_TIMEOUT), + {OkNodes, Tags} = lists:unzip(Replies), + {OkNodes, Failed, lists:usort(lists:flatten(Tags))} + end, do_get_tags_all); + +do_get_tags(filter, Nodes) -> + disco_profile:timed_run( + fun() -> + {OkNodes, Failed, Tags} = do_get_tags(all, Nodes), + case tag_operation(get_tagnames, <<"+deleted">>, ?NODEOP_TIMEOUT) of + {ok, Deleted} -> + TagSet = gb_sets:from_ordset(Tags), + DelSet = gb_sets:insert(<<"+deleted">>, Deleted), + NotDeleted = gb_sets:to_list(gb_sets:subtract(TagSet, DelSet)), + {OkNodes, Failed, NotDeleted}; + E -> + E + end + end, do_get_tags_filter); + +do_get_tags(safe, Nodes) -> + disco_profile:timed_run( + fun() -> + TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")), + case do_get_tags(filter, Nodes) of + {_OkNodes, Failed, Tags} when length(Failed) < TagMinK -> + {ok, Tags}; + _ -> + too_many_failed_nodes + end + end, do_get_tags_safe); + +% The returned tag list may include +deleted. +do_get_tags(gc, Nodes) -> + disco_profile:timed_run( + fun() -> + {OkNodes, Failed, Tags} = do_get_tags(all, Nodes), + TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")), + case length(Failed) < TagMinK of + false -> + too_many_failed_nodes; + true -> + case tag_operation(get_tagnames, <<"+deleted">>, ?NODEOP_TIMEOUT) of + {ok, Deleted} -> + TagSet = gb_sets:from_ordset(Tags), + NotDeleted = gb_sets:subtract(TagSet, Deleted), + {ok, gb_sets:to_list(NotDeleted), OkNodes}; + E -> + E + end + end + end, do_get_tags_gc). + +% Timeouts in this call by the below processes can cause ddfs_master +% itself to crash, since the processes are linked to it. +-spec safe_get_read_nodes() -> {ok, [node()], non_neg_integer()} | error. +safe_get_read_nodes() -> + try get_read_nodes() of + {ok, _ReadableNodes, _RBSize} = RN -> + RN; + E -> + lager:error("unexpected response retrieving readable nodes: ~p", [E]), + error + catch + K:E -> + lager:error("error retrieving readable nodes: ~p:~p", [K, E]), + error + end. + +-spec monitor_diskspace() -> no_return(). +monitor_diskspace() -> + case safe_get_read_nodes() of + {ok, ReadableNodes, _RBSize} -> + {Space, _F} = gen_server:multi_call(ReadableNodes, + ddfs_node, + get_diskspace, + ?NODE_TIMEOUT), + update_nodestats(gb_trees:from_orddict(lists:keysort(1, Space))); + error -> + ok + end, + timer:sleep(?DISKSPACE_INTERVAL), + monitor_diskspace(). + +-spec refresh_tag_cache_proc() -> no_return(). +refresh_tag_cache_proc() -> + case safe_get_read_nodes() of + {ok, ReadableNodes, RBSize} -> + refresh_tag_cache(ReadableNodes, RBSize); + error -> + ok + end, + receive + refresh -> + ok + after ?TAG_CACHE_INTERVAL -> + ok + end, + refresh_tag_cache_proc(). + +-spec refresh_tag_cache([node()], non_neg_integer()) -> ok. +refresh_tag_cache(Nodes, BLSize) -> + TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")), + {Replies, Failed} = + gen_server:multi_call(Nodes, ddfs_node, get_tags, ?NODE_TIMEOUT), + if Nodes =/= [], length(Failed) + BLSize < TagMinK -> + {_OkNodes, Tags} = lists:unzip(Replies), + update_tag_cache(gb_sets:from_list(lists:flatten(Tags))); + true -> ok + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl new file mode 100644 index 0000000000..2920b67fc5 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl @@ -0,0 +1,19 @@ + +-type tokentype() :: 'read' | 'write'. +-type user_attr() :: [{binary(), binary()}]. +% An 'internal' token is also used by internal consumers, but never stored. +-type token() :: 'null' | binary(). + +-type tagname() :: binary(). +-type tagid() :: binary(). + +-type attrib() :: 'urls' | 'read_token' | 'write_token' | {'user', binary()}. + +-record(tagcontent, {id :: tagid(), + last_modified :: binary(), + read_token = null :: token(), + write_token = null :: token(), + urls = [] :: [[binary()]], + user = [] :: user_attr()}). + +-type tagcontent() :: #tagcontent{}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl new file mode 100644 index 0000000000..d579e9a7d7 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl @@ -0,0 +1,16 @@ +% This is a set of type utilities to be used when spec-cing the +% callbacks of a gen_server implementation. It should be included in +% the impl module, which needs to define the state() type. + +-type gs_init() :: {ok, state()}. +-type gs_reply(T) :: {reply, (T), state()}. +-type gs_noreply() :: {noreply, state()}. +-type gs_noreply_t() :: {noreply, state(), non_neg_integer()}. +-type gs_stop(T) :: {stop, (T), state()}. + +% Generic utilities. + +-type server() :: pid() | atom() | {atom(), node()}. +-type from() :: {pid(), term()}. + +-type dbg_state_msg() :: dbg_get_state. diff --git a/lib/dialyzer/test/small_SUITE_data/src/ditrap.erl b/lib/dialyzer/test/small_SUITE_data/src/ditrap.erl new file mode 100644 index 0000000000..2d75f25bd5 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ditrap.erl @@ -0,0 +1,47 @@ +%% A bug reported by Tail-f Systems. The problem is that record types +%% are included without properly limiting their depth. + +-module(ditrap). + +-define(tref(T), ?MODULE:T). +-define(fref(T), ?MODULE:T). + +-export_type([ module_rec/0 + , typedef_rec/0 + , type_spec_fun/0 + ]). + +-record(type, { + base :: 'builtin' | external:random_type() | ?tref(typedef_rec()), + type_spec_fun :: ?fref(type_spec_fun()) + }). + +-record(typedef, {type :: #type{}}). + +-record(typedefs, { + map :: ?tref(typedef_rec()), + parent :: 'undefined' | #typedefs{} + }). + +-record(sn, { + module :: ?tref(module_rec()), + typedefs :: #typedefs{}, + type :: 'undefined' | #type{}, + keys :: 'undefined' | [#sn{}], + children = [] :: [#sn{}] + }). + +-record(augment, {children = [] :: [#sn{}]}). + +-record(module, { + submodules = [] :: [{#module{}, external:pos()}], + typedefs = #typedefs{} :: #typedefs{}, + children = [] :: [#sn{}], + remote_augments = [] :: [{ModuleName :: atom(), [#augment{}]}], + local_augments = [] :: [#augment{}] + }). + +-type typedef_rec() :: #typedef{}. +-type module_rec() :: #module{}. + +-type type_spec_fun() :: undefined | fun((#type{}, #module{}) -> any()). diff --git a/lib/dialyzer/test/small_SUITE_data/src/eep37.erl b/lib/dialyzer/test/small_SUITE_data/src/eep37.erl new file mode 100644 index 0000000000..2818688f95 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/eep37.erl @@ -0,0 +1,15 @@ +-module(eep37). + +-compile(export_all). + +-spec self() -> fun(() -> fun()). +self() -> + fun Self() -> Self end. + +-spec fact() -> fun((non_neg_integer()) -> non_neg_integer()). +fact() -> + fun Fact(N) when N > 0 -> + N * Fact(N - 1); + Fact(0) -> + 1 + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl b/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl index b58fa732cb..3acc5ca065 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl @@ -1,6 +1,6 @@ %% -%% The Original Code is RabbitMQ. -%% +%% This is stripped down code from RabbitMQ. It is used to report an +%% invalid type specification for function list_vhost_permissions/1. %% The Initial Developer of the Original Code is VMware, Inc. %% @@ -38,7 +38,7 @@ vhost_perms_info_keys() -> -spec list_vhost_permissions(vhost()) -> infos(). list_vhost_permissions(VHostPath) -> - list_permissions(vhost_perms_info_keys(), rabbit_foo:some_list()). + list_permissions(vhost_perms_info_keys(), some_mod:some_function()). filter_props(Keys, Props) -> [T || T = {K, _} <- Props, lists:member(K, Keys)]. diff --git a/lib/dialyzer/test/small_SUITE_data/src/fun2ms.erl b/lib/dialyzer/test/small_SUITE_data/src/fun2ms.erl new file mode 100644 index 0000000000..9e7df85e4c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/fun2ms.erl @@ -0,0 +1,21 @@ +-module(fun2ms). +-export([return/0]). +-include_lib("stdlib/include/ms_transform.hrl"). + +-record(snapshot, {id :: integer(), arg1 :: atom(), arg2 :: tuple()}). + +return() -> + TableId = ets:new(table, [public, {keypos, #snapshot.id}]), + + ets:insert(TableId, [#snapshot{id = 1, arg1 = hard, arg2 = {1,2}}, + #snapshot{id = 2, arg1 = rock, arg2 = {1,2}}, + #snapshot{id = 3, arg1 = hallelujah, arg2 = + {1,2}}]), + + + Example = ets:fun2ms( + fun(#snapshot{id = Arg1, arg1 = Arg2}) -> + {Arg1, Arg2} + end), + + ets:select(TableId, Example). diff --git a/lib/dialyzer/test/small_SUITE_data/src/fun_arity.erl b/lib/dialyzer/test/small_SUITE_data/src/fun_arity.erl new file mode 100644 index 0000000000..850d2fd331 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/fun_arity.erl @@ -0,0 +1,127 @@ +%%-------------------------------------------------------------------------- +%% Module which contains calls to funs of different arity. +%%-------------------------------------------------------------------------- +-module(fun_arity). + +-export([f_0_ok/0, f_0_ko/0]). +-export([f_1_ok/0, f_1_ko/0]). + +-export([fa_0_ok/0, fa_0_ko/0]). +-export([fa_1_ok/0, fa_1_ko/0]). + +-export([mfa_0_ok/0, mfa_0_ko/0, mf/0]). +-export([mfa_1_ok/0, mfa_1_ko/0, mf/1]). + +-export([mfa_ne_0_ok/0, mfa_ne_0_ko/0]). +-export([mfa_ne_1_ok/0, mfa_ne_1_ko/0]). + +-export([mfa_nd_0_ok/0, mfa_nd_0_ko/0]). +-export([mfa_nd_1_ok/0, mfa_nd_1_ko/0]). + +-export(['Mfa_0_ok'/1, 'Mfa_0_ko'/1]). +-export(['Mfa_1_ok'/1, 'Mfa_1_ko'/1]). + +-export(['mFa_0_ok'/1, 'mFa_0_ko'/1]). +-export(['mFa_1_ok'/1, 'mFa_1_ko'/1]). + +-export(['MFa_0_ok'/2, 'MFa_0_ko'/2]). +-export(['MFa_1_ok'/2, 'MFa_1_ko'/2]). + +%%-------------------------------------------------------------------------- + +%% Funs like "fun(...) -> ... end". + +f_0_ok() -> (fun_f_0())(). +f_0_ko() -> (fun_f_0())(1). +fun_f_0() -> fun() -> ok end. + +f_1_ok() -> (fun_f_1())(1). +f_1_ko() -> (fun_f_1())(). +fun_f_1() -> fun(_) -> ok end . + +%%-------------------------------------------------------------------------- + +%% Funs like "fun F/A" when F is literal atom and A is literal +%% non-negative integer. + +fa_0_ok() -> (fun_fa_0())(). +fa_0_ko() -> (fun_fa_0())(1). +fun_fa_0() -> fun f/0. +f() -> ok. + +fa_1_ok() -> (fun_fa_1())(1). +fa_1_ko() -> (fun_fa_1())(). +fun_fa_1() -> fun f/1. +f(_) -> ok. + +%%-------------------------------------------------------------------------- + +%% Funs like "fun M:F/A" when M and F are literal atoms, A is literal +%% non-negative integer and function is (defined and) exported. + +mfa_0_ok() -> (fun_mfa_0())(). +mfa_0_ko() -> (fun_mfa_0())(1). +fun_mfa_0() -> fun ?MODULE:mf/0. +mf() -> ok. + +mfa_1_ok() -> (fun_mfa_1())(1). +mfa_1_ko() -> (fun_mfa_1())(). +fun_mfa_1() -> fun ?MODULE:mf/1. +mf(_) -> ok. + +%% Funs like "fun M:F/A" when M and F are literal atoms, A is literal +%% non-negative integer and function is defined but not exported. + +mfa_ne_0_ok() -> (fun_mfa_ne_0())(). +mfa_ne_0_ko() -> (fun_mfa_ne_0())(1). +fun_mfa_ne_0() -> fun ?MODULE:mf_ne/0. +mf_ne() -> ok. + +mfa_ne_1_ok() -> (fun_mfa_ne_1())(1). +mfa_ne_1_ko() -> (fun_mfa_ne_1())(). +fun_mfa_ne_1() -> fun ?MODULE:mf_ne/1. +mf_ne(_) -> ok. + +%% Funs like "fun M:F/A" when M and F are literal atoms, A is literal +%% non-negative integer and function is not defined. + +mfa_nd_0_ok() -> (fun_mfa_nd_0())(). +mfa_nd_0_ko() -> (fun_mfa_nd_0())(1). +fun_mfa_nd_0() -> fun ?MODULE:mf_nd/0. + +mfa_nd_1_ok() -> (fun_mfa_nd_1())(1). +mfa_nd_1_ko() -> (fun_mfa_nd_1())(). +fun_mfa_nd_1() -> fun ?MODULE:mf_nd/1. + +%% Funs like "fun M:F/A" when M is variable, F is literal atoms and A +%% is literal non-negative integer. + +'Mfa_0_ok'(M) -> ('fun_Mfa_0'(M))(). +'Mfa_0_ko'(M) -> ('fun_Mfa_0'(M))(1). +'fun_Mfa_0'(M) -> fun M:f/0. + +'Mfa_1_ok'(M) -> ('fun_Mfa_1'(M))(1). +'Mfa_1_ko'(M) -> ('fun_Mfa_1'(M))(). +'fun_Mfa_1'(M) -> fun M:f/1. + +%% Funs like "fun M:F/A" when M is literal atom, F is variable and A +%% is literal non-negative integer. + +'mFa_0_ok'(F) -> ('fun_mFa_0'(F))(). +'mFa_0_ko'(F) -> ('fun_mFa_0'(F))(1). +'fun_mFa_0'(F) -> fun ?MODULE:F/0. + +'mFa_1_ok'(F) -> ('fun_mFa_1'(F))(1). +'mFa_1_ko'(F) -> ('fun_mFa_1'(F))(). +'fun_mFa_1'(F) -> fun ?MODULE:F/1. + +%% Funs like "fun M:F/A" when M and F are variables and A is literal +%% non-negative integer. + +'MFa_0_ok'(M, F) -> ('fun_MFa_0'(M, F))(). +'MFa_0_ko'(M, F) -> ('fun_MFa_0'(M, F))(1). +'fun_MFa_0'(M, F) -> fun M:F/0. + +'MFa_1_ok'(M, F) -> ('fun_MFa_1'(M, F))(1). +'MFa_1_ko'(M, F) -> ('fun_MFa_1'(M, F))(). +'fun_MFa_1'(M, F) -> fun M:F/1. diff --git a/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl new file mode 100644 index 0000000000..f4cbf31160 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl @@ -0,0 +1,83 @@ +-module(funs_from_outside).
+
+-export([run1/2, run2/2, run3/2]).
+-export([test1/1, test2/1]).
+
+%%------------------------------------------------------------------------------
+
+run1(X, Y) ->
+ testa(fun do_something/1, X, Y).
+
+testa(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ error -> error
+ end.
+
+do_nothing(_) -> {ok, nothing}.
+
+do_something(_) -> {ok, something}.
+
+even(X) ->
+ X rem 2 =:= 0.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run2(X, Y) ->
+ testb(fun do_something/1, X, Y).
+
+testb(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ error -> error
+ end.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run3(X, Y) ->
+ testc(fun do_something_2/1, X, Y).
+
+testc(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ %% This pattern can match.
+ error -> error
+ end.
+
+do_something_2(foo) -> {ok, something};
+do_something_2(_) -> error.
+
+%%------------------------------------------------------------------------------
+
+test1(Fun) ->
+ F = case get(test1) of
+ test1_t -> Fun;
+ test1_f -> fun fok/0
+ end,
+ error = F().
+
+fok() -> ok.
+
+%%------------------------------------------------------------------------------
+
+test2(Fun) ->
+ F = case get(test1) of
+ test1_t -> fun fok/0;
+ test1_f -> fun fok/0
+ end,
+ error = F().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/gencall.erl b/lib/dialyzer/test/small_SUITE_data/src/gencall.erl index d2875c9df1..762be55007 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/gencall.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/gencall.erl @@ -7,6 +7,6 @@ f() -> gen_server:call(1,2,3), ets:lookup(1,2,3), - gencall2:foo(), + some_mod:some_function(), gencall:foo(), gen_server:handle_cast(1,2). diff --git a/lib/dialyzer/test/small_SUITE_data/src/gs_make.erl b/lib/dialyzer/test/small_SUITE_data/src/gs_make.erl index 2842e773c4..a942ebe374 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/gs_make.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/gs_make.erl @@ -1,13 +1,14 @@ -%% ``The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved via the world wide web at http://www.erlang.org/. +%% ``Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings diff --git a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl index ff5ee6bac4..f9547d4929 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl @@ -1,3 +1,8 @@ +%% With the patch introduced to avoid false warnings in +%% user_SUITE_data/src/wpc_hlines.erl we can unfortunately no longer precisely +%% catch problems like this one... The refinement procedure is still enough to +%% keep some of the details, nevertheless. + -module(higher_order_discrepancy). -export([test/1]). diff --git a/lib/dialyzer/test/small_SUITE_data/src/inv_mult.erl b/lib/dialyzer/test/small_SUITE_data/src/inv_mult.erl new file mode 100644 index 0000000000..3413556813 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/inv_mult.erl @@ -0,0 +1,15 @@ +%% Dialyzer was too constraining when checking the relation between the +%% arguments and result of a multiplication. We should not constrain an argument +%% if the other operand *may* be zero. +%% +%% Bug found by Kostis Sagonas, fixed by Stavros Aronis + +-module(inv_mult). +-compile(export_all). + +main(L) -> + N = -1 * length(L), + fact(N). + +fact(0) -> 1; +fact(N) -> N * fact(N-1). diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl new file mode 100644 index 0000000000..c26787fe24 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl @@ -0,0 +1,5 @@ +-module(scala_data). + +-export_type([data/0]). + +-opaque data() :: {'data', term()}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl new file mode 100644 index 0000000000..4e981f3b74 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl @@ -0,0 +1,8 @@ +-module(scala_user). + +-export([is_list/2]). + +-spec is_list(atom(), scala_data:data()) -> boolean(). + +is_list( List,Data) when is_list(List) -> true; +is_list(Tuple,Data) when is_tuple(Tuple) -> false. diff --git a/lib/dialyzer/test/small_SUITE_data/src/keydel.erl b/lib/dialyzer/test/small_SUITE_data/src/keydel.erl new file mode 100644 index 0000000000..18a5c0670c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/keydel.erl @@ -0,0 +1,29 @@ +-module(keydel). + +-export([store/3]). + +-record(att, {f}). + +-type attachment() :: list(). + +-opaque att() :: #att{} | attachment(). + +-spec store(atom(), any(), att()) -> att(). +store(Field, undefined, Att) when is_list(Att) -> + lists:keydelete(Field, 1, Att); +store(Field, Value, Att) when is_list(Att) -> + lists:keystore(Field, 1, Att, {Field, Value}); +store(Field, Value, Att) -> + store(Field, Value, upgrade(Att)). + + +-spec upgrade(#att{}) -> attachment(). +upgrade(#att{} = Att) -> + Map = lists:zip( + record_info(fields, att), + lists:seq(2, record_info(size, att)) + ), + %% Don't store undefined elements since that is default + [{F, element(I, Att)} || {F, I} <- Map, element(I, Att) /= undefined]; +upgrade(Att) -> + Att. diff --git a/lib/dialyzer/test/small_SUITE_data/src/limit.erl b/lib/dialyzer/test/small_SUITE_data/src/limit.erl new file mode 100644 index 0000000000..97ee585b77 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/limit.erl @@ -0,0 +1,20 @@ +%% Misc cases where Dialyzer would fail with system_limit or crash + +-module(limit). + +-export([tu/0, big/1, b2/0]). + +tu() -> + erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}]). + +big(<<Int:1152921504606846976/unit:128,0,_/binary>>) -> {5,Int}. + +b2() -> + Maxbig = maxbig(), + _ = bnot Maxbig, + ok. + +maxbig() -> + %% We assume that the maximum arity is (1 bsl 19) - 1. + Ws = erlang:system_info(wordsize), + (((1 bsl ((16777184 * (Ws div 4))-1)) - 1) bsl 1) + 1. diff --git a/lib/dialyzer/test/small_SUITE_data/src/literals.erl b/lib/dialyzer/test/small_SUITE_data/src/literals.erl new file mode 100644 index 0000000000..354a0f4cdc --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/literals.erl @@ -0,0 +1,33 @@ +-module(literals). + +%% Bad records inside structures used to be ignored. The reason: +%% v3_core:unfold() does not annotate the parts of a literal. +%% This example does not work perfectly yet. + +-export([t1/0, t2/0, t3/0, t4/0, m1/1, m2/1, m3/1, m4/1]). + +-record(r, {id :: integer}). + +t1() -> + #r{id = a}. % violation + +t2() -> + [#r{id = a}]. % violation + +t3() -> + {#r{id = a}}. % violation + +t4() -> + #{a => #r{id = a}}. % violation + +m1(#r{id = a}) -> % violation + ok. + +m2([#r{id = a}]) -> % violation + ok. + +m3({#r{id = a}}) -> % can never match; not so good + ok. + +m4(#{a := #r{id = a}}) -> % violation + ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/loopy.erl b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl new file mode 100644 index 0000000000..28125ec3d9 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl @@ -0,0 +1,17 @@ +%% ERL-157, OTP-13653. +%% Would cause Dialyzer to go into an infinite loop. + +-module(loopy). + +-export([loop/1]). + + +-spec loop(Args) -> ok when + Args :: [{Module, Args}], + Module :: module(), + Args :: any(). +loop([{Module, Args} | Rest]) -> + Module:init(Args), + loop(Rest); +loop([]) -> + ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl new file mode 100644 index 0000000000..597358d16a --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl @@ -0,0 +1,53 @@ +%% +%% File: maps1.erl +%% Author: Björn-Egil Dahlberg +%% Created: 2014-01-17 +%% + +-module(maps1). + +-compile([export_all]). + + +-export([recv/3, decode/1]). + +%-record(can_pkt, {id, data :: binary(), timestamp}). + +-type can_pkt() :: #{ id => term(), data => binary(), timestamp => term() }. +-type channel() :: atom() | pid() | {atom(),_}. + +-spec recv(<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R. +recv(Packet, Fun, Chan) -> + #{id := Can_id, data := Can_data} = P = decode(Packet), + Fun(P). + +-spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char(),_ => _}. +decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, + Data:Len/binary, _/binary>>) -> + #{id => Id, data => Data, timestamp => Timestamp}. + + + +t1() -> + #{bar=>fun t2/0}. + +t2() -> ok. + +-type map_state() :: #{ id => integer(), val => term() }. + +-spec update(map_state(), term()) -> map_state(). + +update(#{ id := Id, val := Val } = M, X) when is_integer(Id) -> + M#{ val := [Val,X] }. + +t3() -> + foo(#{greger => 3, #{arne=>anka} => 45}, 1). + +foo(#{} = M, b) -> %% Error + M#{alfa => 42, beta := 1337}. + +t4() -> + case #{} of + #{} -> ok; + Mod -> Mod:function(#{literal => map}, another_arg) %% Error + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl new file mode 100644 index 0000000000..19e61a7944 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl @@ -0,0 +1,11 @@ +%% +%% File: maps_difftype.erl +%% Author: Björn-Egil Dahlberg +%% Created: 2014-04-29 +%% +-module(maps_difftype). + +-export([empty_mismatch/1]). + +empty_mismatch(Tuple) when is_tuple(Tuple) -> + case Tuple of #{} -> ok end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_redef2.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_redef2.erl new file mode 100644 index 0000000000..945b2a9144 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maps_redef2.erl @@ -0,0 +1,23 @@ +%% In 17, the linter says that map(A) redefines 'type map', which is +%% allowed until next release. However, Dialyzer used to replace +%% map(A) with #{}, which resulted in warnings. + +-module(maps_redef2). + +-export([t/0]). + +-type map(_A) :: integer(). + +t() -> + M = new(), + t1(M). + +-spec t1(map(_)) -> map(_). + +t1(A) -> + A + A. + +-spec new() -> map(_). + +new() -> + 3. diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_sum.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_sum.erl new file mode 100644 index 0000000000..a73ac555c9 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maps_sum.erl @@ -0,0 +1,31 @@ +-module(maps_sum). +-export([correct1/1, + wrong1/1, + wrong2/1]). + +-spec correct1(#{atom() => term()}) -> integer(). + +correct1(Data) -> + maps:fold(fun (_Key, Value, Acc) when is_integer(Value) -> + Acc + Value; + (_Key, _Value, Acc) -> + Acc + end, 0, Data). + +-spec wrong1([{atom(),term()}]) -> integer(). + +wrong1(Data) -> + maps:fold(fun (_Key, Value, Acc) when is_integer(Value) -> + Acc + Value; + (_Key, _Value, Acc) -> + Acc + end, 0, Data). + +-spec wrong2(#{atom() => term()}) -> integer(). + +wrong2(Data) -> + lists:foldl(fun (_Key, Value, Acc) when is_integer(Value) -> + Acc + Value; + (_Key, _Value, Acc) -> + Acc + end, 0, Data). diff --git a/lib/dialyzer/test/small_SUITE_data/src/maybe_improper.erl b/lib/dialyzer/test/small_SUITE_data/src/maybe_improper.erl index 1743d81493..6d2a35b7c8 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/maybe_improper.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/maybe_improper.erl @@ -1,7 +1,18 @@ +%%%======================================================================== +%%% Tests handling of maybe improper lists +%%%======================================================================== -module(maybe_improper). --export([s/1]). +-export([s/1, t/0]). -spec s(maybe_improper_list(X,Y)) -> {[X], maybe_improper_list(X,Y)}. -s(A) -> - lists:split(2,A). +s(L) -> + lists:split(2, L). + +%% Having a remote type in the 'tail' of the list crashed dialyzer. +%% The problem was fixed for R16B03. +-type t_mil() :: maybe_improper_list(integer(), orddict:orddict()). + +-spec t() -> t_mil(). +t() -> + [42 | []]. diff --git a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl index 16533a9caa..7242ac2016 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl @@ -1,4 +1,6 @@ %%% This is to ensure that "on_load" functions are never reported as unused. +%%% In addition, all functions called by a function in an on_load attribute +%%% should be considered as called by an entry point of the module. -module(on_load). @@ -8,4 +10,7 @@ foo() -> ok. -bar() -> ok. +bar() -> gazonk(17). + +gazonk(N) when N < 42 -> gazonk(N+1); +gazonk(42) -> ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/overloaded1.erl b/lib/dialyzer/test/small_SUITE_data/src/overloaded1.erl index 0af4f7446f..074a93e2fe 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/overloaded1.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/overloaded1.erl @@ -1,5 +1,5 @@ %%----------------------------------------------------------------------------- -%% Test that tests overloaded contratcs. +%% Test that tests overloaded contracts. %% In December 2008 it works as far as intersection types are concerned (test1) %% However, it does NOT work as far as type variables are concerned (test2) %%----------------------------------------------------------------------------- @@ -16,13 +16,13 @@ test2() -> -type mod() :: atom(). --spec foo(ATM, list()) -> {'ok', ATM} | {'error', _} when is_subtype(ATM, mod()) - ; (MFA, list()) -> {'ok', MFA} | {'error', _} when is_subtype(MFA, mfa()). +-spec foo(ATM, list()) -> {'ok', ATM} | {'error', _} when ATM :: mod() + ; (MFA, list()) -> {'ok', MFA} | {'error', _} when MFA :: mfa(). foo(F, _) when is_atom(F) -> case atom_to_list(F) of - [42|_] -> {ok, F}; - _Other -> {error, mod:bar(F)} + [42|_] -> {ok, F}; + _Other -> {error, some_mod:some_function()} end; foo({M,F,A}, _) -> case A =:= 0 of diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef.erl b/lib/dialyzer/test/small_SUITE_data/src/predef.erl new file mode 100644 index 0000000000..ee9073aa67 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/predef.erl @@ -0,0 +1,67 @@ +-module(predef). + +-export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, + queue/1, set/1, tid/0, tid2/0]). + +-export_type([array/0, digraph/0, gb_set/0]). + +%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque +%% types were ignored but did not generate any warning. +-opaque array() :: atom(). +-opaque digraph() :: atom(). +-opaque gb_set() :: atom(). +-type dict() :: atom(). +-type gb_tree() :: atom(). +-type queue() :: atom(). +-type set() :: atom(). +-type tid() :: atom(). + +-spec array(array()) -> array:array(). + +array(A) -> + array:relax(A). + +-spec dict(dict()) -> dict:dict(). + +dict(D) -> + dict:store(1, a, D). + +-spec digraph(digraph()) -> [digraph:edge()]. + +digraph(G) -> + digraph:edges(G). + +-spec digraph2(digraph:graph()) -> [digraph:edge()]. + +digraph2(G) -> + digraph:edges(G). + +-spec gb_set(gb_set()) -> gb_sets:set(). + +gb_set(S) -> + gb_sets:balance(S). + +-spec gb_tree(gb_tree()) -> gb_trees:tree(). + +gb_tree(S) -> + gb_trees:balance(S). + +-spec queue(queue()) -> queue:queue(). + +queue(Q) -> + queue:reverse(Q). + +-spec set(set()) -> sets:set(). + +set(S) -> + sets:union([S]). + +-spec tid() -> tid(). + +tid() -> + ets:new(tid, []). + +-spec tid2() -> ets:tid(). + +tid2() -> + ets:new(tid, []). diff --git a/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl b/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl new file mode 100644 index 0000000000..3dbf5ab7a7 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl @@ -0,0 +1,8 @@ +%% Prettyprint bitstrings. + +-module(pretty_bitstring). + +-export([t/0]). + +t() -> + binary:copy(<<1,2,3:3>>,2). diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl index 54cc2601bd..b250c6ee65 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/record_construct.erl @@ -7,7 +7,7 @@ t_loc() -> #r_loc{}. -record(r_opa, {a :: atom(), - b = gb_sets:new() :: gb_set(), + b = gb_sets:new() :: gb_sets:set(), c = 42 :: boolean(), d, % untyped on purpose e = false :: boolean()}). diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_update.erl b/lib/dialyzer/test/small_SUITE_data/src/record_update.erl new file mode 100644 index 0000000000..bad7a0a929 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/record_update.erl @@ -0,0 +1,10 @@ +-module(record_update). + +-export([quux/2]). + +-record(foo, {bar :: atom()}). + +-spec quux(#foo{}, string()) -> #foo{}. + +quux(Foo, NotBar) -> + Foo#foo{ bar = NotBar }. diff --git a/lib/dialyzer/test/small_SUITE_data/src/confusing_record_warning.erl b/lib/dialyzer/test/small_SUITE_data/src/relevant_record_warning.erl index 8af74e0914..3ff65458df 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/confusing_record_warning.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/relevant_record_warning.erl @@ -1,3 +1,7 @@ +%% Formerly confusing_record_warning.erl. +%% The warning output is relevant as of Erlang/OTP 17.1. +%% The original comment kept below. + %%--------------------------------------------------------------------- %% A user complained that dialyzer produces a weird warning for the %% following program. I explained to him that there is an implicit @@ -9,7 +13,7 @@ %% The pattern {'r', [_]} can never match the type any() %% We should clearly give some less confusing warning in this case. %%--------------------------------------------------------------------- --module(confusing_record_warning). +-module(relevant_record_warning). -export([test/1]). diff --git a/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl b/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl new file mode 100644 index 0000000000..d83f2e3234 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl @@ -0,0 +1,11 @@ +-module(remote_field). + +-type f(T) :: {ssl:sslsocket(), T}. + +-record(r1, { f1 :: f(_) }). +-type r1(T) :: #r1{ f1 :: {ssl:sslsocket(), T} }. + +-record(state, { + r :: r1(T), + arg :: T + }). diff --git a/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl b/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl new file mode 100644 index 0000000000..35687e22ec --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl @@ -0,0 +1,17 @@ +-module(remote_field2). + +-export([handle_cast/2]). + +-record(state, {tcp_socket :: inet:socket()}). + +-spec handle_cast(_,_) -> + {noreply,_} | + {stop,{shutdown,connection_closed}, + #state{tcp_socket :: port()}}. +handle_cast({send, Message}, #state{tcp_socket = TCPSocket} = State) -> + case gen_tcp:send(TCPSocket, Message) of + ok -> + {noreply, State}; + {error, closed} -> + {stop, {shutdown, connection_closed}, State} + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/request1.erl b/lib/dialyzer/test/small_SUITE_data/src/request1.erl new file mode 100644 index 0000000000..a6c4ab8dbd --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/request1.erl @@ -0,0 +1,12 @@ +-module(request1). + +-export([a/0]). + +-dialyzer(unmatched_returns). + +a() -> + b(), + 1. + +b() -> + {a, b}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/suppress_request.erl b/lib/dialyzer/test/small_SUITE_data/src/suppress_request.erl new file mode 100644 index 0000000000..c4275fa110 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/suppress_request.erl @@ -0,0 +1,50 @@ +-module(suppress_request). + +-export([test1/1, test1_b/1, test2/0, test2_b/0, + test3/0, test3_b/0, test4/0, test4_b/0]). + +-dialyzer({[specdiffs], test1/1}). +-spec test1(a | b) -> ok. % spec is subtype +test1(A) -> + ok = test1_1(A). + +-spec test1_b(a | b) -> ok. % spec is subtype (suppressed by default) +test1_b(A) -> + ok = test1_1(A). + +-spec test1_1(a | b | c) -> ok. +test1_1(_) -> + ok. + +-dialyzer(unmatched_returns). +test2() -> + tuple(), % unmatched + ok. + +test2_b() -> + tuple(), % unmatched + ok. + +-dialyzer({[no_return, no_match], [test3/0]}). +test3() -> % no local return (suppressed) + A = fun(_) -> + 1 + end, + A = 2. % can never succeed (suppressed) + +test3_b() -> % no local return (requested by default) + A = fun(_) -> + 1 + end, + A = 2. % can never succeed (requested by default) + +-dialyzer(no_improper_lists). +test4() -> + [1 | 2]. % improper list (suppressed) + +-dialyzer({no_improper_lists, test4_b/0}). +test4_b() -> + [1 | 2]. % improper list (suppressed) + +tuple() -> + {a, b}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/suppression1.erl b/lib/dialyzer/test/small_SUITE_data/src/suppression1.erl new file mode 100644 index 0000000000..00534704c3 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/suppression1.erl @@ -0,0 +1,33 @@ +-module(suppression1). + +-export([a/1, b/1, c/0]). + +-dialyzer({nowarn_function, a/1}). + +-spec a(_) -> integer(). + +a(_) -> + A = fun(_) -> + B = fun(_) -> + x = 7 + end, + B = 1 + end, + A. + +-spec b(_) -> integer(). + +-dialyzer({nowarn_function, b/1}). + +b(_) -> + A = fun(_) -> + 1 + end, + A = 2. + +-record(r, {a = a :: integer()}). + +-dialyzer({nowarn_function, c/0}). + +c() -> + #r{}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/suppression2.erl b/lib/dialyzer/test/small_SUITE_data/src/suppression2.erl new file mode 100644 index 0000000000..4cba53fdce --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/suppression2.erl @@ -0,0 +1,32 @@ +-module(suppression2). + +-export([a/1, b/1, c/0]). + +-dialyzer({nowarn_function, [a/1, b/1, c/0]}). +-dialyzer([no_undefined_callbacks]). + +-behaviour(not_a_behaviour). + +-spec a(_) -> integer(). + +a(_) -> + A = fun(_) -> + B = fun(_) -> + x = 7 + end, + B = 1 + end, + A. + +-spec b(_) -> integer(). + +b(_) -> + A = fun(_) -> + 1 + end, + A = 2. + +-record(r, {a = a :: integer()}). + +c() -> + #r{}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/suppression3.erl b/lib/dialyzer/test/small_SUITE_data/src/suppression3.erl new file mode 100644 index 0000000000..4a745cffc2 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/suppression3.erl @@ -0,0 +1,17 @@ +-module(suppression3). + +-export([a/1, b/1]). + +-dialyzer({nowarn_function, a/1}). + +-spec a(_) -> integer(). + +a(A) -> + ?MODULE:missing(A). + +-dialyzer({no_missing_calls, b/1}). + +-spec b(_) -> integer(). + +b(A) -> + ?MODULE:missing(A). diff --git a/lib/dialyzer/test/small_SUITE_data/src/trec.erl b/lib/dialyzer/test/small_SUITE_data/src/trec.erl index ba50c3b401..516358f7c6 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/trec.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/trec.erl @@ -8,7 +8,7 @@ -module(trec). -export([test/0, mk_foo_exp/2]). --record(foo, {a :: integer(), b :: [atom()]}). +-record(foo, {a :: integer() | 'undefined', b :: [atom()]}). %% %% For these functions we currently get the following warnings: @@ -18,20 +18,22 @@ %% ('undefined',atom()) %% 3. Function mk_foo_loc/2 has no local return %% -%% Arguably, the second warning is not what most users have in mind -%% when they wrote the type declarations in the 'foo' record, so no -%% doubt they'll find it confusing. But note that it is also inconsistent! -%% How come there is a success typing for a function that has no local return? +%% Arguably, the second warning is not what most users have in mind when +%% they wrote the type declarations in the 'foo' record, so no doubt +%% they'll find it confusing. But note that it is also quite confusing! +%% Many users may be wondering: How come there is a success typing for a +%% function that has no local return? Running typer on this module +%% reveals a success typing for this function that is interesting indeed. %% test() -> - mk_foo_loc(42, bar:f()). + mk_foo_loc(42, some_mod:some_function()). mk_foo_loc(A, B) -> #foo{a = A, b = [A,B]}. %% -%% For this function we currently get "has no local return" but we get -%% no reason; I want us to get a reason. +%% For this function we used to get a "has no local return" warning +%% but we got no reason. This has now been fixed. %% mk_foo_exp(A, B) when is_integer(A) -> #foo{a = A, b = [A,B]}. diff --git a/lib/dialyzer/test/small_SUITE_data/undefined.erl b/lib/dialyzer/test/small_SUITE_data/undefined.erl new file mode 100644 index 0000000000..8549f2e161 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/undefined.erl @@ -0,0 +1,29 @@ +-module(undefined). + +-export([t/0]). + +%% As of OTP 19.0 'undefined' is no longer added to fields with a type +%% declaration but without an initializer. The pretty printing of +%% records (erl_types:t_to_string()) is updated to reflect this: if a +%% field is of type 'undefined', it is output if 'undefined' is not in +%% the declared type of the field. (It used to be the case that the +%% singleton type 'undefined' was never output.) +%% +%% One consequence is shown by the example below: the warning about +%% the record construction violating the the declared type shows +%% #r{..., d::'undefined', ...} which is meant to be of help to the +%% user, who could otherwise get confused the first time (s)he gets +%% confronted by the warning. + +-record(r, + { + a = {fi}, + b = {a,b} :: list(), % violation + c = {a,b} :: list(), + d :: list(), % violation + e = [] :: list(), + f = undefined :: list() % violation + }). + +t() -> + #r{c = []}. |