Repository: SonarSource/sonar-csharp
Branch: master
Commit: cf16e98336f4
Files: 6476
Total size: 21.9 MB
Directory structure:
gitextract_opbg6mf9/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── GitHub.shproj
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-FalsePositive.yml
│ │ ├── 2-FalseNegative.yml
│ │ ├── 3-AD0001.yml
│ │ ├── 4-NewRule.yml
│ │ └── config.yml
│ └── workflows/
│ ├── LabelIssue.yml
│ └── SlackNotification.yml
├── .gitignore
├── .globalconfig
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── SECURITY.md
├── analyzers/
│ ├── .runsettings
│ ├── CI.NuGet.Config
│ ├── CodeAnalysis.targets
│ ├── Common.targets
│ ├── README.md
│ ├── SonarAnalyzer.sln
│ ├── Version.targets
│ ├── packaging/
│ │ ├── Licenses/
│ │ │ └── THIRD_PARTY_LICENSES/
│ │ │ ├── Google.Protobuf-LICENSE.txt
│ │ │ ├── Roslyn-LICENSE.txt
│ │ │ └── StyleCop.Analyzers-LICENSE.txt
│ │ ├── SonarAnalyzer.CFG.nuspec
│ │ ├── SonarAnalyzer.CSharp.Styling.nuspec
│ │ ├── SonarAnalyzer.CSharp.nuspec
│ │ ├── SonarAnalyzer.VisualBasic.nuspec
│ │ ├── tools-cs/
│ │ │ ├── install.ps1
│ │ │ └── uninstall.ps1
│ │ └── tools-vbnet/
│ │ ├── install.ps1
│ │ └── uninstall.ps1
│ ├── rspec/
│ │ ├── cs/
│ │ │ ├── S100.html
│ │ │ ├── S100.json
│ │ │ ├── S1006.html
│ │ │ ├── S1006.json
│ │ │ ├── S101.html
│ │ │ ├── S101.json
│ │ │ ├── S103.html
│ │ │ ├── S103.json
│ │ │ ├── S104.html
│ │ │ ├── S104.json
│ │ │ ├── S1048.html
│ │ │ ├── S1048.json
│ │ │ ├── S105.html
│ │ │ ├── S105.json
│ │ │ ├── S106.html
│ │ │ ├── S106.json
│ │ │ ├── S1066.html
│ │ │ ├── S1066.json
│ │ │ ├── S1067.html
│ │ │ ├── S1067.json
│ │ │ ├── S107.html
│ │ │ ├── S107.json
│ │ │ ├── S1075.html
│ │ │ ├── S1075.json
│ │ │ ├── S108.html
│ │ │ ├── S108.json
│ │ │ ├── S109.html
│ │ │ ├── S109.json
│ │ │ ├── S110.html
│ │ │ ├── S110.json
│ │ │ ├── S1104.html
│ │ │ ├── S1104.json
│ │ │ ├── S1109.html
│ │ │ ├── S1109.json
│ │ │ ├── S1110.html
│ │ │ ├── S1110.json
│ │ │ ├── S1116.html
│ │ │ ├── S1116.json
│ │ │ ├── S1117.html
│ │ │ ├── S1117.json
│ │ │ ├── S1118.html
│ │ │ ├── S1118.json
│ │ │ ├── S112.html
│ │ │ ├── S112.json
│ │ │ ├── S1121.html
│ │ │ ├── S1121.json
│ │ │ ├── S1123.html
│ │ │ ├── S1123.json
│ │ │ ├── S1125.html
│ │ │ ├── S1125.json
│ │ │ ├── S1128.html
│ │ │ ├── S1128.json
│ │ │ ├── S113.html
│ │ │ ├── S113.json
│ │ │ ├── S1133.html
│ │ │ ├── S1133.json
│ │ │ ├── S1134.html
│ │ │ ├── S1134.json
│ │ │ ├── S1135.html
│ │ │ ├── S1135.json
│ │ │ ├── S1144.html
│ │ │ ├── S1144.json
│ │ │ ├── S1147.html
│ │ │ ├── S1147.json
│ │ │ ├── S1151.html
│ │ │ ├── S1151.json
│ │ │ ├── S1155.html
│ │ │ ├── S1155.json
│ │ │ ├── S1163.html
│ │ │ ├── S1163.json
│ │ │ ├── S1168.html
│ │ │ ├── S1168.json
│ │ │ ├── S1172.html
│ │ │ ├── S1172.json
│ │ │ ├── S1185.html
│ │ │ ├── S1185.json
│ │ │ ├── S1186.html
│ │ │ ├── S1186.json
│ │ │ ├── S1192.html
│ │ │ ├── S1192.json
│ │ │ ├── S1199.html
│ │ │ ├── S1199.json
│ │ │ ├── S1200.html
│ │ │ ├── S1200.json
│ │ │ ├── S1206.html
│ │ │ ├── S1206.json
│ │ │ ├── S121.html
│ │ │ ├── S121.json
│ │ │ ├── S1210.html
│ │ │ ├── S1210.json
│ │ │ ├── S1215.html
│ │ │ ├── S1215.json
│ │ │ ├── S122.html
│ │ │ ├── S122.json
│ │ │ ├── S1226.html
│ │ │ ├── S1226.json
│ │ │ ├── S1227.html
│ │ │ ├── S1227.json
│ │ │ ├── S1244.html
│ │ │ ├── S1244.json
│ │ │ ├── S125.html
│ │ │ ├── S125.json
│ │ │ ├── S126.html
│ │ │ ├── S126.json
│ │ │ ├── S1264.html
│ │ │ ├── S1264.json
│ │ │ ├── S127.html
│ │ │ ├── S127.json
│ │ │ ├── S1301.html
│ │ │ ├── S1301.json
│ │ │ ├── S1309.html
│ │ │ ├── S1309.json
│ │ │ ├── S131.html
│ │ │ ├── S131.json
│ │ │ ├── S1312.html
│ │ │ ├── S1312.json
│ │ │ ├── S1313.html
│ │ │ ├── S1313.json
│ │ │ ├── S134.html
│ │ │ ├── S134.json
│ │ │ ├── S138.html
│ │ │ ├── S138.json
│ │ │ ├── S1449.html
│ │ │ ├── S1449.json
│ │ │ ├── S1450.html
│ │ │ ├── S1450.json
│ │ │ ├── S1451.html
│ │ │ ├── S1451.json
│ │ │ ├── S1479.html
│ │ │ ├── S1479.json
│ │ │ ├── S1481.html
│ │ │ ├── S1481.json
│ │ │ ├── S1541.html
│ │ │ ├── S1541.json
│ │ │ ├── S1607.html
│ │ │ ├── S1607.json
│ │ │ ├── S1643.html
│ │ │ ├── S1643.json
│ │ │ ├── S1656.html
│ │ │ ├── S1656.json
│ │ │ ├── S1659.html
│ │ │ ├── S1659.json
│ │ │ ├── S1694.html
│ │ │ ├── S1694.json
│ │ │ ├── S1696.html
│ │ │ ├── S1696.json
│ │ │ ├── S1698.html
│ │ │ ├── S1698.json
│ │ │ ├── S1699.html
│ │ │ ├── S1699.json
│ │ │ ├── S1751.html
│ │ │ ├── S1751.json
│ │ │ ├── S1764.html
│ │ │ ├── S1764.json
│ │ │ ├── S1821.html
│ │ │ ├── S1821.json
│ │ │ ├── S1848.html
│ │ │ ├── S1848.json
│ │ │ ├── S1854.html
│ │ │ ├── S1854.json
│ │ │ ├── S1858.html
│ │ │ ├── S1858.json
│ │ │ ├── S1862.html
│ │ │ ├── S1862.json
│ │ │ ├── S1871.html
│ │ │ ├── S1871.json
│ │ │ ├── S1905.html
│ │ │ ├── S1905.json
│ │ │ ├── S1939.html
│ │ │ ├── S1939.json
│ │ │ ├── S1940.html
│ │ │ ├── S1940.json
│ │ │ ├── S1944.html
│ │ │ ├── S1944.json
│ │ │ ├── S1994.html
│ │ │ ├── S1994.json
│ │ │ ├── S2053.html
│ │ │ ├── S2053.json
│ │ │ ├── S2068.html
│ │ │ ├── S2068.json
│ │ │ ├── S2077.html
│ │ │ ├── S2077.json
│ │ │ ├── S2092.html
│ │ │ ├── S2092.json
│ │ │ ├── S2094.html
│ │ │ ├── S2094.json
│ │ │ ├── S2114.html
│ │ │ ├── S2114.json
│ │ │ ├── S2115.html
│ │ │ ├── S2115.json
│ │ │ ├── S2123.html
│ │ │ ├── S2123.json
│ │ │ ├── S2139.html
│ │ │ ├── S2139.json
│ │ │ ├── S2148.html
│ │ │ ├── S2148.json
│ │ │ ├── S2156.html
│ │ │ ├── S2156.json
│ │ │ ├── S2166.html
│ │ │ ├── S2166.json
│ │ │ ├── S2178.html
│ │ │ ├── S2178.json
│ │ │ ├── S2183.html
│ │ │ ├── S2183.json
│ │ │ ├── S2184.html
│ │ │ ├── S2184.json
│ │ │ ├── S2187.html
│ │ │ ├── S2187.json
│ │ │ ├── S2190.html
│ │ │ ├── S2190.json
│ │ │ ├── S2197.html
│ │ │ ├── S2197.json
│ │ │ ├── S2198.html
│ │ │ ├── S2198.json
│ │ │ ├── S2201.html
│ │ │ ├── S2201.json
│ │ │ ├── S2219.html
│ │ │ ├── S2219.json
│ │ │ ├── S2221.html
│ │ │ ├── S2221.json
│ │ │ ├── S2222.html
│ │ │ ├── S2222.json
│ │ │ ├── S2223.html
│ │ │ ├── S2223.json
│ │ │ ├── S2225.html
│ │ │ ├── S2225.json
│ │ │ ├── S2234.html
│ │ │ ├── S2234.json
│ │ │ ├── S2245.html
│ │ │ ├── S2245.json
│ │ │ ├── S2251.html
│ │ │ ├── S2251.json
│ │ │ ├── S2252.html
│ │ │ ├── S2252.json
│ │ │ ├── S2257.html
│ │ │ ├── S2257.json
│ │ │ ├── S2259.html
│ │ │ ├── S2259.json
│ │ │ ├── S2275.html
│ │ │ ├── S2275.json
│ │ │ ├── S2290.html
│ │ │ ├── S2290.json
│ │ │ ├── S2291.html
│ │ │ ├── S2291.json
│ │ │ ├── S2292.html
│ │ │ ├── S2292.json
│ │ │ ├── S2302.html
│ │ │ ├── S2302.json
│ │ │ ├── S2306.html
│ │ │ ├── S2306.json
│ │ │ ├── S2325.html
│ │ │ ├── S2325.json
│ │ │ ├── S2326.html
│ │ │ ├── S2326.json
│ │ │ ├── S2327.html
│ │ │ ├── S2327.json
│ │ │ ├── S2328.html
│ │ │ ├── S2328.json
│ │ │ ├── S2330.html
│ │ │ ├── S2330.json
│ │ │ ├── S2333.html
│ │ │ ├── S2333.json
│ │ │ ├── S2339.html
│ │ │ ├── S2339.json
│ │ │ ├── S2342.html
│ │ │ ├── S2342.json
│ │ │ ├── S2344.html
│ │ │ ├── S2344.json
│ │ │ ├── S2345.html
│ │ │ ├── S2345.json
│ │ │ ├── S2346.html
│ │ │ ├── S2346.json
│ │ │ ├── S2357.html
│ │ │ ├── S2357.json
│ │ │ ├── S2360.html
│ │ │ ├── S2360.json
│ │ │ ├── S2365.html
│ │ │ ├── S2365.json
│ │ │ ├── S2368.html
│ │ │ ├── S2368.json
│ │ │ ├── S2372.html
│ │ │ ├── S2372.json
│ │ │ ├── S2376.html
│ │ │ ├── S2376.json
│ │ │ ├── S2386.html
│ │ │ ├── S2386.json
│ │ │ ├── S2387.html
│ │ │ ├── S2387.json
│ │ │ ├── S2436.html
│ │ │ ├── S2436.json
│ │ │ ├── S2437.html
│ │ │ ├── S2437.json
│ │ │ ├── S2445.html
│ │ │ ├── S2445.json
│ │ │ ├── S2479.html
│ │ │ ├── S2479.json
│ │ │ ├── S2486.html
│ │ │ ├── S2486.json
│ │ │ ├── S2551.html
│ │ │ ├── S2551.json
│ │ │ ├── S2583.html
│ │ │ ├── S2583.json
│ │ │ ├── S2589.html
│ │ │ ├── S2589.json
│ │ │ ├── S2612.html
│ │ │ ├── S2612.json
│ │ │ ├── S2629.html
│ │ │ ├── S2629.json
│ │ │ ├── S2674.html
│ │ │ ├── S2674.json
│ │ │ ├── S2681.html
│ │ │ ├── S2681.json
│ │ │ ├── S2688.html
│ │ │ ├── S2688.json
│ │ │ ├── S2692.html
│ │ │ ├── S2692.json
│ │ │ ├── S2696.html
│ │ │ ├── S2696.json
│ │ │ ├── S2699.html
│ │ │ ├── S2699.json
│ │ │ ├── S2701.html
│ │ │ ├── S2701.json
│ │ │ ├── S2737.html
│ │ │ ├── S2737.json
│ │ │ ├── S2743.html
│ │ │ ├── S2743.json
│ │ │ ├── S2755.html
│ │ │ ├── S2755.json
│ │ │ ├── S2757.html
│ │ │ ├── S2757.json
│ │ │ ├── S2760.html
│ │ │ ├── S2760.json
│ │ │ ├── S2761.html
│ │ │ ├── S2761.json
│ │ │ ├── S2857.html
│ │ │ ├── S2857.json
│ │ │ ├── S2925.html
│ │ │ ├── S2925.json
│ │ │ ├── S2930.html
│ │ │ ├── S2930.json
│ │ │ ├── S2931.html
│ │ │ ├── S2931.json
│ │ │ ├── S2933.html
│ │ │ ├── S2933.json
│ │ │ ├── S2934.html
│ │ │ ├── S2934.json
│ │ │ ├── S2952.html
│ │ │ ├── S2952.json
│ │ │ ├── S2953.html
│ │ │ ├── S2953.json
│ │ │ ├── S2955.html
│ │ │ ├── S2955.json
│ │ │ ├── S2970.html
│ │ │ ├── S2970.json
│ │ │ ├── S2971.html
│ │ │ ├── S2971.json
│ │ │ ├── S2995.html
│ │ │ ├── S2995.json
│ │ │ ├── S2996.html
│ │ │ ├── S2996.json
│ │ │ ├── S2997.html
│ │ │ ├── S2997.json
│ │ │ ├── S3005.html
│ │ │ ├── S3005.json
│ │ │ ├── S3010.html
│ │ │ ├── S3010.json
│ │ │ ├── S3011.html
│ │ │ ├── S3011.json
│ │ │ ├── S3052.html
│ │ │ ├── S3052.json
│ │ │ ├── S3059.html
│ │ │ ├── S3059.json
│ │ │ ├── S3060.html
│ │ │ ├── S3060.json
│ │ │ ├── S3063.html
│ │ │ ├── S3063.json
│ │ │ ├── S3168.html
│ │ │ ├── S3168.json
│ │ │ ├── S3169.html
│ │ │ ├── S3169.json
│ │ │ ├── S3172.html
│ │ │ ├── S3172.json
│ │ │ ├── S3215.html
│ │ │ ├── S3215.json
│ │ │ ├── S3216.html
│ │ │ ├── S3216.json
│ │ │ ├── S3217.html
│ │ │ ├── S3217.json
│ │ │ ├── S3218.html
│ │ │ ├── S3218.json
│ │ │ ├── S3220.html
│ │ │ ├── S3220.json
│ │ │ ├── S3234.html
│ │ │ ├── S3234.json
│ │ │ ├── S3235.html
│ │ │ ├── S3235.json
│ │ │ ├── S3236.html
│ │ │ ├── S3236.json
│ │ │ ├── S3237.html
│ │ │ ├── S3237.json
│ │ │ ├── S3240.html
│ │ │ ├── S3240.json
│ │ │ ├── S3241.html
│ │ │ ├── S3241.json
│ │ │ ├── S3242.html
│ │ │ ├── S3242.json
│ │ │ ├── S3244.html
│ │ │ ├── S3244.json
│ │ │ ├── S3246.html
│ │ │ ├── S3246.json
│ │ │ ├── S3247.html
│ │ │ ├── S3247.json
│ │ │ ├── S3249.html
│ │ │ ├── S3249.json
│ │ │ ├── S3251.html
│ │ │ ├── S3251.json
│ │ │ ├── S3253.html
│ │ │ ├── S3253.json
│ │ │ ├── S3254.html
│ │ │ ├── S3254.json
│ │ │ ├── S3256.html
│ │ │ ├── S3256.json
│ │ │ ├── S3257.html
│ │ │ ├── S3257.json
│ │ │ ├── S3260.html
│ │ │ ├── S3260.json
│ │ │ ├── S3261.html
│ │ │ ├── S3261.json
│ │ │ ├── S3262.html
│ │ │ ├── S3262.json
│ │ │ ├── S3263.html
│ │ │ ├── S3263.json
│ │ │ ├── S3264.html
│ │ │ ├── S3264.json
│ │ │ ├── S3265.html
│ │ │ ├── S3265.json
│ │ │ ├── S3267.html
│ │ │ ├── S3267.json
│ │ │ ├── S3329.html
│ │ │ ├── S3329.json
│ │ │ ├── S3330.html
│ │ │ ├── S3330.json
│ │ │ ├── S3343.html
│ │ │ ├── S3343.json
│ │ │ ├── S3346.html
│ │ │ ├── S3346.json
│ │ │ ├── S3353.html
│ │ │ ├── S3353.json
│ │ │ ├── S3358.html
│ │ │ ├── S3358.json
│ │ │ ├── S3363.html
│ │ │ ├── S3363.json
│ │ │ ├── S3366.html
│ │ │ ├── S3366.json
│ │ │ ├── S3376.html
│ │ │ ├── S3376.json
│ │ │ ├── S3397.html
│ │ │ ├── S3397.json
│ │ │ ├── S3398.html
│ │ │ ├── S3398.json
│ │ │ ├── S3400.html
│ │ │ ├── S3400.json
│ │ │ ├── S3415.html
│ │ │ ├── S3415.json
│ │ │ ├── S3416.html
│ │ │ ├── S3416.json
│ │ │ ├── S3427.html
│ │ │ ├── S3427.json
│ │ │ ├── S3431.html
│ │ │ ├── S3431.json
│ │ │ ├── S3433.html
│ │ │ ├── S3433.json
│ │ │ ├── S3440.html
│ │ │ ├── S3440.json
│ │ │ ├── S3441.html
│ │ │ ├── S3441.json
│ │ │ ├── S3442.html
│ │ │ ├── S3442.json
│ │ │ ├── S3443.html
│ │ │ ├── S3443.json
│ │ │ ├── S3444.html
│ │ │ ├── S3444.json
│ │ │ ├── S3445.html
│ │ │ ├── S3445.json
│ │ │ ├── S3447.html
│ │ │ ├── S3447.json
│ │ │ ├── S3449.html
│ │ │ ├── S3449.json
│ │ │ ├── S3450.html
│ │ │ ├── S3450.json
│ │ │ ├── S3451.html
│ │ │ ├── S3451.json
│ │ │ ├── S3453.html
│ │ │ ├── S3453.json
│ │ │ ├── S3456.html
│ │ │ ├── S3456.json
│ │ │ ├── S3457.html
│ │ │ ├── S3457.json
│ │ │ ├── S3458.html
│ │ │ ├── S3458.json
│ │ │ ├── S3459.html
│ │ │ ├── S3459.json
│ │ │ ├── S3464.html
│ │ │ ├── S3464.json
│ │ │ ├── S3466.html
│ │ │ ├── S3466.json
│ │ │ ├── S3532.html
│ │ │ ├── S3532.json
│ │ │ ├── S3597.html
│ │ │ ├── S3597.json
│ │ │ ├── S3598.html
│ │ │ ├── S3598.json
│ │ │ ├── S3600.html
│ │ │ ├── S3600.json
│ │ │ ├── S3603.html
│ │ │ ├── S3603.json
│ │ │ ├── S3604.html
│ │ │ ├── S3604.json
│ │ │ ├── S3610.html
│ │ │ ├── S3610.json
│ │ │ ├── S3626.html
│ │ │ ├── S3626.json
│ │ │ ├── S3655.html
│ │ │ ├── S3655.json
│ │ │ ├── S3717.html
│ │ │ ├── S3717.json
│ │ │ ├── S3776.html
│ │ │ ├── S3776.json
│ │ │ ├── S3869.html
│ │ │ ├── S3869.json
│ │ │ ├── S3871.html
│ │ │ ├── S3871.json
│ │ │ ├── S3872.html
│ │ │ ├── S3872.json
│ │ │ ├── S3874.html
│ │ │ ├── S3874.json
│ │ │ ├── S3875.html
│ │ │ ├── S3875.json
│ │ │ ├── S3876.html
│ │ │ ├── S3876.json
│ │ │ ├── S3877.html
│ │ │ ├── S3877.json
│ │ │ ├── S3878.html
│ │ │ ├── S3878.json
│ │ │ ├── S3880.html
│ │ │ ├── S3880.json
│ │ │ ├── S3881.html
│ │ │ ├── S3881.json
│ │ │ ├── S3884.html
│ │ │ ├── S3884.json
│ │ │ ├── S3885.html
│ │ │ ├── S3885.json
│ │ │ ├── S3887.html
│ │ │ ├── S3887.json
│ │ │ ├── S3889.html
│ │ │ ├── S3889.json
│ │ │ ├── S3897.html
│ │ │ ├── S3897.json
│ │ │ ├── S3898.html
│ │ │ ├── S3898.json
│ │ │ ├── S3900.html
│ │ │ ├── S3900.json
│ │ │ ├── S3902.html
│ │ │ ├── S3902.json
│ │ │ ├── S3903.html
│ │ │ ├── S3903.json
│ │ │ ├── S3904.html
│ │ │ ├── S3904.json
│ │ │ ├── S3906.html
│ │ │ ├── S3906.json
│ │ │ ├── S3908.html
│ │ │ ├── S3908.json
│ │ │ ├── S3909.html
│ │ │ ├── S3909.json
│ │ │ ├── S3923.html
│ │ │ ├── S3923.json
│ │ │ ├── S3925.html
│ │ │ ├── S3925.json
│ │ │ ├── S3926.html
│ │ │ ├── S3926.json
│ │ │ ├── S3927.html
│ │ │ ├── S3927.json
│ │ │ ├── S3928.html
│ │ │ ├── S3928.json
│ │ │ ├── S3937.html
│ │ │ ├── S3937.json
│ │ │ ├── S3949.html
│ │ │ ├── S3949.json
│ │ │ ├── S3956.html
│ │ │ ├── S3956.json
│ │ │ ├── S3962.html
│ │ │ ├── S3962.json
│ │ │ ├── S3963.html
│ │ │ ├── S3963.json
│ │ │ ├── S3966.html
│ │ │ ├── S3966.json
│ │ │ ├── S3967.html
│ │ │ ├── S3967.json
│ │ │ ├── S3971.html
│ │ │ ├── S3971.json
│ │ │ ├── S3972.html
│ │ │ ├── S3972.json
│ │ │ ├── S3973.html
│ │ │ ├── S3973.json
│ │ │ ├── S3981.html
│ │ │ ├── S3981.json
│ │ │ ├── S3984.html
│ │ │ ├── S3984.json
│ │ │ ├── S3990.html
│ │ │ ├── S3990.json
│ │ │ ├── S3992.html
│ │ │ ├── S3992.json
│ │ │ ├── S3993.html
│ │ │ ├── S3993.json
│ │ │ ├── S3994.html
│ │ │ ├── S3994.json
│ │ │ ├── S3995.html
│ │ │ ├── S3995.json
│ │ │ ├── S3996.html
│ │ │ ├── S3996.json
│ │ │ ├── S3997.html
│ │ │ ├── S3997.json
│ │ │ ├── S3998.html
│ │ │ ├── S3998.json
│ │ │ ├── S4000.html
│ │ │ ├── S4000.json
│ │ │ ├── S4002.html
│ │ │ ├── S4002.json
│ │ │ ├── S4004.html
│ │ │ ├── S4004.json
│ │ │ ├── S4005.html
│ │ │ ├── S4005.json
│ │ │ ├── S4015.html
│ │ │ ├── S4015.json
│ │ │ ├── S4016.html
│ │ │ ├── S4016.json
│ │ │ ├── S4017.html
│ │ │ ├── S4017.json
│ │ │ ├── S4018.html
│ │ │ ├── S4018.json
│ │ │ ├── S4019.html
│ │ │ ├── S4019.json
│ │ │ ├── S4022.html
│ │ │ ├── S4022.json
│ │ │ ├── S4023.html
│ │ │ ├── S4023.json
│ │ │ ├── S4025.html
│ │ │ ├── S4025.json
│ │ │ ├── S4026.html
│ │ │ ├── S4026.json
│ │ │ ├── S4027.html
│ │ │ ├── S4027.json
│ │ │ ├── S4035.html
│ │ │ ├── S4035.json
│ │ │ ├── S4036.html
│ │ │ ├── S4036.json
│ │ │ ├── S4039.html
│ │ │ ├── S4039.json
│ │ │ ├── S4040.html
│ │ │ ├── S4040.json
│ │ │ ├── S4041.html
│ │ │ ├── S4041.json
│ │ │ ├── S4047.html
│ │ │ ├── S4047.json
│ │ │ ├── S4049.html
│ │ │ ├── S4049.json
│ │ │ ├── S4050.html
│ │ │ ├── S4050.json
│ │ │ ├── S4052.html
│ │ │ ├── S4052.json
│ │ │ ├── S4055.html
│ │ │ ├── S4055.json
│ │ │ ├── S4056.html
│ │ │ ├── S4056.json
│ │ │ ├── S4057.html
│ │ │ ├── S4057.json
│ │ │ ├── S4058.html
│ │ │ ├── S4058.json
│ │ │ ├── S4059.html
│ │ │ ├── S4059.json
│ │ │ ├── S4060.html
│ │ │ ├── S4060.json
│ │ │ ├── S4061.html
│ │ │ ├── S4061.json
│ │ │ ├── S4069.html
│ │ │ ├── S4069.json
│ │ │ ├── S4070.html
│ │ │ ├── S4070.json
│ │ │ ├── S4136.html
│ │ │ ├── S4136.json
│ │ │ ├── S4143.html
│ │ │ ├── S4143.json
│ │ │ ├── S4144.html
│ │ │ ├── S4144.json
│ │ │ ├── S4158.html
│ │ │ ├── S4158.json
│ │ │ ├── S4159.html
│ │ │ ├── S4159.json
│ │ │ ├── S4200.html
│ │ │ ├── S4200.json
│ │ │ ├── S4201.html
│ │ │ ├── S4201.json
│ │ │ ├── S4210.html
│ │ │ ├── S4210.json
│ │ │ ├── S4211.html
│ │ │ ├── S4211.json
│ │ │ ├── S4212.html
│ │ │ ├── S4212.json
│ │ │ ├── S4214.html
│ │ │ ├── S4214.json
│ │ │ ├── S4220.html
│ │ │ ├── S4220.json
│ │ │ ├── S4225.html
│ │ │ ├── S4225.json
│ │ │ ├── S4226.html
│ │ │ ├── S4226.json
│ │ │ ├── S4260.html
│ │ │ ├── S4260.json
│ │ │ ├── S4261.html
│ │ │ ├── S4261.json
│ │ │ ├── S4275.html
│ │ │ ├── S4275.json
│ │ │ ├── S4277.html
│ │ │ ├── S4277.json
│ │ │ ├── S4347.html
│ │ │ ├── S4347.json
│ │ │ ├── S4423.html
│ │ │ ├── S4423.json
│ │ │ ├── S4426.html
│ │ │ ├── S4426.json
│ │ │ ├── S4428.html
│ │ │ ├── S4428.json
│ │ │ ├── S4433.html
│ │ │ ├── S4433.json
│ │ │ ├── S4456.html
│ │ │ ├── S4456.json
│ │ │ ├── S4457.html
│ │ │ ├── S4457.json
│ │ │ ├── S4462.html
│ │ │ ├── S4462.json
│ │ │ ├── S4487.html
│ │ │ ├── S4487.json
│ │ │ ├── S4502.html
│ │ │ ├── S4502.json
│ │ │ ├── S4507.html
│ │ │ ├── S4507.json
│ │ │ ├── S4524.html
│ │ │ ├── S4524.json
│ │ │ ├── S4545.html
│ │ │ ├── S4545.json
│ │ │ ├── S4581.html
│ │ │ ├── S4581.json
│ │ │ ├── S4583.html
│ │ │ ├── S4583.json
│ │ │ ├── S4586.html
│ │ │ ├── S4586.json
│ │ │ ├── S4635.html
│ │ │ ├── S4635.json
│ │ │ ├── S4663.html
│ │ │ ├── S4663.json
│ │ │ ├── S4790.html
│ │ │ ├── S4790.json
│ │ │ ├── S4792.html
│ │ │ ├── S4792.json
│ │ │ ├── S4830.html
│ │ │ ├── S4830.json
│ │ │ ├── S5034.html
│ │ │ ├── S5034.json
│ │ │ ├── S5042.html
│ │ │ ├── S5042.json
│ │ │ ├── S5122.html
│ │ │ ├── S5122.json
│ │ │ ├── S5332.html
│ │ │ ├── S5332.json
│ │ │ ├── S5344.html
│ │ │ ├── S5344.json
│ │ │ ├── S5443.html
│ │ │ ├── S5443.json
│ │ │ ├── S5445.html
│ │ │ ├── S5445.json
│ │ │ ├── S5542.html
│ │ │ ├── S5542.json
│ │ │ ├── S5547.html
│ │ │ ├── S5547.json
│ │ │ ├── S5659.html
│ │ │ ├── S5659.json
│ │ │ ├── S5693.html
│ │ │ ├── S5693.json
│ │ │ ├── S5753.html
│ │ │ ├── S5753.json
│ │ │ ├── S5766.html
│ │ │ ├── S5766.json
│ │ │ ├── S5773.html
│ │ │ ├── S5773.json
│ │ │ ├── S5856.html
│ │ │ ├── S5856.json
│ │ │ ├── S6354.html
│ │ │ ├── S6354.json
│ │ │ ├── S6377.html
│ │ │ ├── S6377.json
│ │ │ ├── S6418.html
│ │ │ ├── S6418.json
│ │ │ ├── S6419.html
│ │ │ ├── S6419.json
│ │ │ ├── S6420.html
│ │ │ ├── S6420.json
│ │ │ ├── S6421.html
│ │ │ ├── S6421.json
│ │ │ ├── S6422.html
│ │ │ ├── S6422.json
│ │ │ ├── S6423.html
│ │ │ ├── S6423.json
│ │ │ ├── S6424.html
│ │ │ ├── S6424.json
│ │ │ ├── S6444.html
│ │ │ ├── S6444.json
│ │ │ ├── S6507.html
│ │ │ ├── S6507.json
│ │ │ ├── S6513.html
│ │ │ ├── S6513.json
│ │ │ ├── S6561.html
│ │ │ ├── S6561.json
│ │ │ ├── S6562.html
│ │ │ ├── S6562.json
│ │ │ ├── S6563.html
│ │ │ ├── S6563.json
│ │ │ ├── S6566.html
│ │ │ ├── S6566.json
│ │ │ ├── S6575.html
│ │ │ ├── S6575.json
│ │ │ ├── S6580.html
│ │ │ ├── S6580.json
│ │ │ ├── S6585.html
│ │ │ ├── S6585.json
│ │ │ ├── S6588.html
│ │ │ ├── S6588.json
│ │ │ ├── S6602.html
│ │ │ ├── S6602.json
│ │ │ ├── S6603.html
│ │ │ ├── S6603.json
│ │ │ ├── S6605.html
│ │ │ ├── S6605.json
│ │ │ ├── S6607.html
│ │ │ ├── S6607.json
│ │ │ ├── S6608.html
│ │ │ ├── S6608.json
│ │ │ ├── S6609.html
│ │ │ ├── S6609.json
│ │ │ ├── S6610.html
│ │ │ ├── S6610.json
│ │ │ ├── S6612.html
│ │ │ ├── S6612.json
│ │ │ ├── S6613.html
│ │ │ ├── S6613.json
│ │ │ ├── S6617.html
│ │ │ ├── S6617.json
│ │ │ ├── S6618.html
│ │ │ ├── S6618.json
│ │ │ ├── S6640.html
│ │ │ ├── S6640.json
│ │ │ ├── S6664.html
│ │ │ ├── S6664.json
│ │ │ ├── S6667.html
│ │ │ ├── S6667.json
│ │ │ ├── S6668.html
│ │ │ ├── S6668.json
│ │ │ ├── S6669.html
│ │ │ ├── S6669.json
│ │ │ ├── S6670.html
│ │ │ ├── S6670.json
│ │ │ ├── S6672.html
│ │ │ ├── S6672.json
│ │ │ ├── S6673.html
│ │ │ ├── S6673.json
│ │ │ ├── S6674.html
│ │ │ ├── S6674.json
│ │ │ ├── S6675.html
│ │ │ ├── S6675.json
│ │ │ ├── S6677.html
│ │ │ ├── S6677.json
│ │ │ ├── S6678.html
│ │ │ ├── S6678.json
│ │ │ ├── S6781.html
│ │ │ ├── S6781.json
│ │ │ ├── S6797.html
│ │ │ ├── S6797.json
│ │ │ ├── S6798.html
│ │ │ ├── S6798.json
│ │ │ ├── S6800.html
│ │ │ ├── S6800.json
│ │ │ ├── S6802.html
│ │ │ ├── S6802.json
│ │ │ ├── S6803.html
│ │ │ ├── S6803.json
│ │ │ ├── S6930.html
│ │ │ ├── S6930.json
│ │ │ ├── S6931.html
│ │ │ ├── S6931.json
│ │ │ ├── S6932.html
│ │ │ ├── S6932.json
│ │ │ ├── S6934.html
│ │ │ ├── S6934.json
│ │ │ ├── S6960.html
│ │ │ ├── S6960.json
│ │ │ ├── S6961.html
│ │ │ ├── S6961.json
│ │ │ ├── S6962.html
│ │ │ ├── S6962.json
│ │ │ ├── S6964.html
│ │ │ ├── S6964.json
│ │ │ ├── S6965.html
│ │ │ ├── S6965.json
│ │ │ ├── S6966.html
│ │ │ ├── S6966.json
│ │ │ ├── S6967.html
│ │ │ ├── S6967.json
│ │ │ ├── S6968.html
│ │ │ ├── S6968.json
│ │ │ ├── S7039.html
│ │ │ ├── S7039.json
│ │ │ ├── S7130.html
│ │ │ ├── S7130.json
│ │ │ ├── S7131.html
│ │ │ ├── S7131.json
│ │ │ ├── S7133.html
│ │ │ ├── S7133.json
│ │ │ ├── S818.html
│ │ │ ├── S818.json
│ │ │ ├── S8367.html
│ │ │ ├── S8367.json
│ │ │ ├── S8368.html
│ │ │ ├── S8368.json
│ │ │ ├── S8380.html
│ │ │ ├── S8380.json
│ │ │ ├── S8381.html
│ │ │ ├── S8381.json
│ │ │ ├── S881.html
│ │ │ ├── S881.json
│ │ │ ├── S907.html
│ │ │ ├── S907.json
│ │ │ ├── S927.html
│ │ │ ├── S927.json
│ │ │ └── Sonar_way_profile.json
│ │ └── vbnet/
│ │ ├── S101.html
│ │ ├── S101.json
│ │ ├── S103.html
│ │ ├── S103.json
│ │ ├── S104.html
│ │ ├── S104.json
│ │ ├── S1048.html
│ │ ├── S1048.json
│ │ ├── S105.html
│ │ ├── S105.json
│ │ ├── S1066.html
│ │ ├── S1066.json
│ │ ├── S1067.html
│ │ ├── S1067.json
│ │ ├── S107.html
│ │ ├── S107.json
│ │ ├── S1075.html
│ │ ├── S1075.json
│ │ ├── S108.html
│ │ ├── S108.json
│ │ ├── S1110.html
│ │ ├── S1110.json
│ │ ├── S112.html
│ │ ├── S112.json
│ │ ├── S1123.html
│ │ ├── S1123.json
│ │ ├── S1125.html
│ │ ├── S1125.json
│ │ ├── S1133.html
│ │ ├── S1133.json
│ │ ├── S1134.html
│ │ ├── S1134.json
│ │ ├── S1135.html
│ │ ├── S1135.json
│ │ ├── S114.html
│ │ ├── S114.json
│ │ ├── S1147.html
│ │ ├── S1147.json
│ │ ├── S1151.html
│ │ ├── S1151.json
│ │ ├── S1155.html
│ │ ├── S1155.json
│ │ ├── S1163.html
│ │ ├── S1163.json
│ │ ├── S117.html
│ │ ├── S117.json
│ │ ├── S1172.html
│ │ ├── S1172.json
│ │ ├── S1186.html
│ │ ├── S1186.json
│ │ ├── S119.html
│ │ ├── S119.json
│ │ ├── S1192.html
│ │ ├── S1192.json
│ │ ├── S1197.html
│ │ ├── S1197.json
│ │ ├── S122.html
│ │ ├── S122.json
│ │ ├── S1226.html
│ │ ├── S1226.json
│ │ ├── S126.html
│ │ ├── S126.json
│ │ ├── S1301.html
│ │ ├── S1301.json
│ │ ├── S131.html
│ │ ├── S131.json
│ │ ├── S1313.html
│ │ ├── S1313.json
│ │ ├── S134.html
│ │ ├── S134.json
│ │ ├── S138.html
│ │ ├── S138.json
│ │ ├── S139.html
│ │ ├── S139.json
│ │ ├── S1451.html
│ │ ├── S1451.json
│ │ ├── S1479.html
│ │ ├── S1479.json
│ │ ├── S1481.html
│ │ ├── S1481.json
│ │ ├── S1541.html
│ │ ├── S1541.json
│ │ ├── S1542.html
│ │ ├── S1542.json
│ │ ├── S1643.html
│ │ ├── S1643.json
│ │ ├── S1645.html
│ │ ├── S1645.json
│ │ ├── S1654.html
│ │ ├── S1654.json
│ │ ├── S1656.html
│ │ ├── S1656.json
│ │ ├── S1659.html
│ │ ├── S1659.json
│ │ ├── S1751.html
│ │ ├── S1751.json
│ │ ├── S1764.html
│ │ ├── S1764.json
│ │ ├── S1821.html
│ │ ├── S1821.json
│ │ ├── S1862.html
│ │ ├── S1862.json
│ │ ├── S1871.html
│ │ ├── S1871.json
│ │ ├── S1940.html
│ │ ├── S1940.json
│ │ ├── S1944.html
│ │ ├── S1944.json
│ │ ├── S2053.html
│ │ ├── S2053.json
│ │ ├── S2068.html
│ │ ├── S2068.json
│ │ ├── S2077.html
│ │ ├── S2077.json
│ │ ├── S2094.html
│ │ ├── S2094.json
│ │ ├── S2166.html
│ │ ├── S2166.json
│ │ ├── S2178.html
│ │ ├── S2178.json
│ │ ├── S2222.html
│ │ ├── S2222.json
│ │ ├── S2225.html
│ │ ├── S2225.json
│ │ ├── S2234.html
│ │ ├── S2234.json
│ │ ├── S2257.html
│ │ ├── S2257.json
│ │ ├── S2259.html
│ │ ├── S2259.json
│ │ ├── S2302.html
│ │ ├── S2302.json
│ │ ├── S2304.html
│ │ ├── S2304.json
│ │ ├── S2339.html
│ │ ├── S2339.json
│ │ ├── S2340.html
│ │ ├── S2340.json
│ │ ├── S2342.html
│ │ ├── S2342.json
│ │ ├── S2343.html
│ │ ├── S2343.json
│ │ ├── S2344.html
│ │ ├── S2344.json
│ │ ├── S2345.html
│ │ ├── S2345.json
│ │ ├── S2346.html
│ │ ├── S2346.json
│ │ ├── S2347.html
│ │ ├── S2347.json
│ │ ├── S2348.html
│ │ ├── S2348.json
│ │ ├── S2349.html
│ │ ├── S2349.json
│ │ ├── S2352.html
│ │ ├── S2352.json
│ │ ├── S2354.html
│ │ ├── S2354.json
│ │ ├── S2355.html
│ │ ├── S2355.json
│ │ ├── S2357.html
│ │ ├── S2357.json
│ │ ├── S2358.html
│ │ ├── S2358.json
│ │ ├── S2359.html
│ │ ├── S2359.json
│ │ ├── S2360.html
│ │ ├── S2360.json
│ │ ├── S2362.html
│ │ ├── S2362.json
│ │ ├── S2363.html
│ │ ├── S2363.json
│ │ ├── S2364.html
│ │ ├── S2364.json
│ │ ├── S2365.html
│ │ ├── S2365.json
│ │ ├── S2366.html
│ │ ├── S2366.json
│ │ ├── S2367.html
│ │ ├── S2367.json
│ │ ├── S2368.html
│ │ ├── S2368.json
│ │ ├── S2369.html
│ │ ├── S2369.json
│ │ ├── S2370.html
│ │ ├── S2370.json
│ │ ├── S2372.html
│ │ ├── S2372.json
│ │ ├── S2373.html
│ │ ├── S2373.json
│ │ ├── S2374.html
│ │ ├── S2374.json
│ │ ├── S2375.html
│ │ ├── S2375.json
│ │ ├── S2376.html
│ │ ├── S2376.json
│ │ ├── S2387.html
│ │ ├── S2387.json
│ │ ├── S2429.html
│ │ ├── S2429.json
│ │ ├── S2437.html
│ │ ├── S2437.json
│ │ ├── S2551.html
│ │ ├── S2551.json
│ │ ├── S2583.html
│ │ ├── S2583.json
│ │ ├── S2589.html
│ │ ├── S2589.json
│ │ ├── S2612.html
│ │ ├── S2612.json
│ │ ├── S2692.html
│ │ ├── S2692.json
│ │ ├── S2737.html
│ │ ├── S2737.json
│ │ ├── S2757.html
│ │ ├── S2757.json
│ │ ├── S2761.html
│ │ ├── S2761.json
│ │ ├── S2925.html
│ │ ├── S2925.json
│ │ ├── S2951.html
│ │ ├── S2951.json
│ │ ├── S3011.html
│ │ ├── S3011.json
│ │ ├── S3063.html
│ │ ├── S3063.json
│ │ ├── S3329.html
│ │ ├── S3329.json
│ │ ├── S3358.html
│ │ ├── S3358.json
│ │ ├── S3363.html
│ │ ├── S3363.json
│ │ ├── S3385.html
│ │ ├── S3385.json
│ │ ├── S3431.html
│ │ ├── S3431.json
│ │ ├── S3449.html
│ │ ├── S3449.json
│ │ ├── S3453.html
│ │ ├── S3453.json
│ │ ├── S3464.html
│ │ ├── S3464.json
│ │ ├── S3466.html
│ │ ├── S3466.json
│ │ ├── S3598.html
│ │ ├── S3598.json
│ │ ├── S3603.html
│ │ ├── S3603.json
│ │ ├── S3655.html
│ │ ├── S3655.json
│ │ ├── S3776.html
│ │ ├── S3776.json
│ │ ├── S3860.html
│ │ ├── S3860.json
│ │ ├── S3866.html
│ │ ├── S3866.json
│ │ ├── S3869.html
│ │ ├── S3869.json
│ │ ├── S3871.html
│ │ ├── S3871.json
│ │ ├── S3878.html
│ │ ├── S3878.json
│ │ ├── S3884.html
│ │ ├── S3884.json
│ │ ├── S3889.html
│ │ ├── S3889.json
│ │ ├── S3898.html
│ │ ├── S3898.json
│ │ ├── S3900.html
│ │ ├── S3900.json
│ │ ├── S3903.html
│ │ ├── S3903.json
│ │ ├── S3904.html
│ │ ├── S3904.json
│ │ ├── S3923.html
│ │ ├── S3923.json
│ │ ├── S3926.html
│ │ ├── S3926.json
│ │ ├── S3927.html
│ │ ├── S3927.json
│ │ ├── S3949.html
│ │ ├── S3949.json
│ │ ├── S3966.html
│ │ ├── S3966.json
│ │ ├── S3981.html
│ │ ├── S3981.json
│ │ ├── S3990.html
│ │ ├── S3990.json
│ │ ├── S3992.html
│ │ ├── S3992.json
│ │ ├── S3998.html
│ │ ├── S3998.json
│ │ ├── S4025.html
│ │ ├── S4025.json
│ │ ├── S4036.html
│ │ ├── S4036.json
│ │ ├── S4060.html
│ │ ├── S4060.json
│ │ ├── S4136.html
│ │ ├── S4136.json
│ │ ├── S4143.html
│ │ ├── S4143.json
│ │ ├── S4144.html
│ │ ├── S4144.json
│ │ ├── S4158.html
│ │ ├── S4158.json
│ │ ├── S4159.html
│ │ ├── S4159.json
│ │ ├── S4201.html
│ │ ├── S4201.json
│ │ ├── S4210.html
│ │ ├── S4210.json
│ │ ├── S4225.html
│ │ ├── S4225.json
│ │ ├── S4260.html
│ │ ├── S4260.json
│ │ ├── S4275.html
│ │ ├── S4275.json
│ │ ├── S4277.html
│ │ ├── S4277.json
│ │ ├── S4423.html
│ │ ├── S4423.json
│ │ ├── S4428.html
│ │ ├── S4428.json
│ │ ├── S4507.html
│ │ ├── S4507.json
│ │ ├── S4545.html
│ │ ├── S4545.json
│ │ ├── S4581.html
│ │ ├── S4581.json
│ │ ├── S4583.html
│ │ ├── S4583.json
│ │ ├── S4586.html
│ │ ├── S4586.json
│ │ ├── S4663.html
│ │ ├── S4663.json
│ │ ├── S4790.html
│ │ ├── S4790.json
│ │ ├── S4792.html
│ │ ├── S4792.json
│ │ ├── S4830.html
│ │ ├── S4830.json
│ │ ├── S5042.html
│ │ ├── S5042.json
│ │ ├── S5443.html
│ │ ├── S5443.json
│ │ ├── S5445.html
│ │ ├── S5445.json
│ │ ├── S5542.html
│ │ ├── S5542.json
│ │ ├── S5547.html
│ │ ├── S5547.json
│ │ ├── S5659.html
│ │ ├── S5659.json
│ │ ├── S5693.html
│ │ ├── S5693.json
│ │ ├── S5753.html
│ │ ├── S5753.json
│ │ ├── S5773.html
│ │ ├── S5773.json
│ │ ├── S5856.html
│ │ ├── S5856.json
│ │ ├── S5944.html
│ │ ├── S5944.json
│ │ ├── S6145.html
│ │ ├── S6145.json
│ │ ├── S6146.html
│ │ ├── S6146.json
│ │ ├── S6354.html
│ │ ├── S6354.json
│ │ ├── S6418.html
│ │ ├── S6418.json
│ │ ├── S6444.html
│ │ ├── S6444.json
│ │ ├── S6513.html
│ │ ├── S6513.json
│ │ ├── S6561.html
│ │ ├── S6561.json
│ │ ├── S6562.html
│ │ ├── S6562.json
│ │ ├── S6563.html
│ │ ├── S6563.json
│ │ ├── S6566.html
│ │ ├── S6566.json
│ │ ├── S6575.html
│ │ ├── S6575.json
│ │ ├── S6580.html
│ │ ├── S6580.json
│ │ ├── S6585.html
│ │ ├── S6585.json
│ │ ├── S6588.html
│ │ ├── S6588.json
│ │ ├── S6602.html
│ │ ├── S6602.json
│ │ ├── S6603.html
│ │ ├── S6603.json
│ │ ├── S6605.html
│ │ ├── S6605.json
│ │ ├── S6607.html
│ │ ├── S6607.json
│ │ ├── S6608.html
│ │ ├── S6608.json
│ │ ├── S6609.html
│ │ ├── S6609.json
│ │ ├── S6610.html
│ │ ├── S6610.json
│ │ ├── S6612.html
│ │ ├── S6612.json
│ │ ├── S6613.html
│ │ ├── S6613.json
│ │ ├── S6617.html
│ │ ├── S6617.json
│ │ ├── S6930.html
│ │ ├── S6930.json
│ │ ├── S6931.html
│ │ ├── S6931.json
│ │ ├── S7130.html
│ │ ├── S7130.json
│ │ ├── S7131.html
│ │ ├── S7131.json
│ │ ├── S7133.html
│ │ ├── S7133.json
│ │ ├── S907.html
│ │ ├── S907.json
│ │ ├── S927.html
│ │ ├── S927.json
│ │ └── Sonar_way_profile.json
│ ├── src/
│ │ ├── AssemblyInfo.Shared.cs
│ │ ├── Directory.Build.targets
│ │ ├── RuleCatalog.targets
│ │ ├── RuleDescriptorGenerator/
│ │ │ ├── Descriptors/
│ │ │ │ ├── Rule.cs
│ │ │ │ └── RuleParameter.cs
│ │ │ ├── Program.cs
│ │ │ ├── RuleDescriptorGenerator.csproj
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.CFG/
│ │ │ ├── CfgSerializer/
│ │ │ │ ├── CfgSerializer.RoslynCfgWalker.cs
│ │ │ │ ├── CfgSerializer.RoslynLvaWalker.cs
│ │ │ │ ├── CfgSerializer.SonarCfgWalker.cs
│ │ │ │ ├── CfgSerializer.cs
│ │ │ │ └── DotWriter.cs
│ │ │ ├── Common/
│ │ │ │ ├── RoslynVersion.cs
│ │ │ │ └── UniqueQueue.cs
│ │ │ ├── Extensions/
│ │ │ │ ├── BasicBlockExtensions.cs
│ │ │ │ ├── ControlFlowGraphExtensions.cs
│ │ │ │ ├── ControlFlowRegionExtensions.cs
│ │ │ │ ├── DictionaryExtensions.cs
│ │ │ │ ├── ExpressionSyntaxExtensions.cs
│ │ │ │ ├── IEnumerableExtensions.cs
│ │ │ │ ├── IOperationExtensions.cs
│ │ │ │ ├── IsPatternExpressionSyntaxWrapperExtensions.cs
│ │ │ │ ├── PatternSyntaxWrapperExtensions.cs
│ │ │ │ ├── PropertyInfoExtensions.cs
│ │ │ │ ├── SemanticModelExtensions.cs
│ │ │ │ ├── StringExtensions.cs
│ │ │ │ ├── SyntaxNodeExtensions.cs
│ │ │ │ └── UnaryPatternSyntaxWrapperExtensions.cs
│ │ │ ├── LiveVariableAnalysis/
│ │ │ │ ├── LiveVariableAnalysisBase.cs
│ │ │ │ └── RoslynLiveVariableAnalysis.cs
│ │ │ ├── Operations/
│ │ │ │ └── Utilities/
│ │ │ │ ├── OperationExecutionOrder.cs
│ │ │ │ └── OperationFinder.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── Roslyn/
│ │ │ │ ├── BasicBlock.cs
│ │ │ │ ├── CfgAllPathValidator.cs
│ │ │ │ ├── ControlFlowBranch.cs
│ │ │ │ ├── ControlFlowGraph.cs
│ │ │ │ ├── ControlFlowGraphCache.cs
│ │ │ │ ├── ControlFlowRegion.cs
│ │ │ │ └── TypeLoader.cs
│ │ │ ├── Sonar/
│ │ │ │ ├── AbstractControlFlowGraphBuilder.cs
│ │ │ │ ├── BlockIdProvider.cs
│ │ │ │ ├── Blocks/
│ │ │ │ │ ├── BinaryBranchBlock.cs
│ │ │ │ │ ├── BinaryBranchingSimpleBlock.cs
│ │ │ │ │ ├── Block.cs
│ │ │ │ │ ├── BranchBlock.cs
│ │ │ │ │ ├── ExitBlock.cs
│ │ │ │ │ ├── ForInitializerBlock.cs
│ │ │ │ │ ├── ForeachCollectionProducerBlock.cs
│ │ │ │ │ ├── JumpBlock.cs
│ │ │ │ │ ├── LockBlock.cs
│ │ │ │ │ ├── SimpleBlock.cs
│ │ │ │ │ ├── TemporaryBlock.cs
│ │ │ │ │ └── UsingEndBlock.cs
│ │ │ │ ├── CSharpControlFlowGraph.cs
│ │ │ │ ├── CSharpControlFlowGraphBuilder.cs
│ │ │ │ ├── CfgAllPathValidator.cs
│ │ │ │ └── IControlFlowGraph.cs
│ │ │ ├── SonarAnalyzer.CFG.csproj
│ │ │ ├── Syntax/
│ │ │ │ └── Utilities/
│ │ │ │ └── SyntaxClassifierBase.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.CSharp/
│ │ │ ├── Metrics/
│ │ │ │ ├── CSharpCognitiveComplexityMetric.cs
│ │ │ │ ├── CSharpCyclomaticComplexityMetric.cs
│ │ │ │ ├── CSharpExecutableLinesMetric.cs
│ │ │ │ └── CSharpMetrics.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── Rules/
│ │ │ │ ├── AbstractClassToInterface.cs
│ │ │ │ ├── AbstractTypesShouldNotHaveConstructors.cs
│ │ │ │ ├── AllBranchesShouldNotHaveSameImplementation.cs
│ │ │ │ ├── AlwaysSetDateTimeKind.cs
│ │ │ │ ├── AnonymousDelegateEventUnsubscribe.cs
│ │ │ │ ├── ArgumentSpecifiedForCallerInfoParameter.cs
│ │ │ │ ├── ArrayCovariance.cs
│ │ │ │ ├── ArrayPassedAsParams.cs
│ │ │ │ ├── AspNet/
│ │ │ │ │ ├── AnnotateApiActionsWithHttpVerb.cs
│ │ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromController.cs
│ │ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromControllerCodeFix.cs
│ │ │ │ │ ├── AvoidUnderPosting.cs
│ │ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.cs
│ │ │ │ │ ├── CallModelStateIsValid.cs
│ │ │ │ │ ├── ControllersHaveMixedResponsibilities.cs
│ │ │ │ │ ├── ControllersReuseClient.cs
│ │ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.cs
│ │ │ │ │ ├── SpecifyRouteAttribute.cs
│ │ │ │ │ └── UseAspNetModelBinding.cs
│ │ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.cs
│ │ │ │ ├── AssertionsShouldBeComplete.cs
│ │ │ │ ├── AssignmentInsideSubExpression.cs
│ │ │ │ ├── AsyncAwaitIdentifier.cs
│ │ │ │ ├── AsyncVoidMethod.cs
│ │ │ │ ├── AvoidDateTimeNowForBenchmarking.cs
│ │ │ │ ├── AvoidExcessiveClassCoupling.cs
│ │ │ │ ├── AvoidExcessiveInheritance.cs
│ │ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.cs
│ │ │ │ ├── AvoidUnsealedAttributes.cs
│ │ │ │ ├── BeginInvokePairedWithEndInvoke.cs
│ │ │ │ ├── BinaryOperationWithIdenticalExpressions.cs
│ │ │ │ ├── BlazorQueryParameterRoutableComponent.cs
│ │ │ │ ├── BooleanCheckInverted.cs
│ │ │ │ ├── BooleanCheckInvertedCodeFix.cs
│ │ │ │ ├── BooleanLiteralUnnecessary.cs
│ │ │ │ ├── BooleanLiteralUnnecessaryCodeFix.cs
│ │ │ │ ├── BreakOutsideSwitch.cs
│ │ │ │ ├── BypassingAccessibility.cs
│ │ │ │ ├── CallToAsyncMethodShouldNotBeBlocking.cs
│ │ │ │ ├── CallerInformationParametersShouldBeLast.cs
│ │ │ │ ├── CastConcreteTypeToInterface.cs
│ │ │ │ ├── CastShouldNotBeDuplicated.cs
│ │ │ │ ├── CatchEmpty.cs
│ │ │ │ ├── CatchRethrow.cs
│ │ │ │ ├── CatchRethrowCodeFix.cs
│ │ │ │ ├── CertificateValidationCheck.cs
│ │ │ │ ├── CheckArgumentException.cs
│ │ │ │ ├── CheckFileLicense.cs
│ │ │ │ ├── CheckFileLicenseCodeFix.cs
│ │ │ │ ├── ClassAndMethodName.cs
│ │ │ │ ├── ClassNamedException.cs
│ │ │ │ ├── ClassNotInstantiatable.cs
│ │ │ │ ├── ClassShouldNotBeEmpty.cs
│ │ │ │ ├── ClassWithEqualityShouldImplementIEquatable.cs
│ │ │ │ ├── ClassWithOnlyStaticMember.cs
│ │ │ │ ├── CloudNative/
│ │ │ │ │ ├── AzureFunctionsCatchExceptions.cs
│ │ │ │ │ ├── AzureFunctionsLogFailures.cs
│ │ │ │ │ ├── AzureFunctionsReuseClients.cs
│ │ │ │ │ ├── AzureFunctionsStateless.cs
│ │ │ │ │ └── DurableEntityInterfaceRestrictions.cs
│ │ │ │ ├── CognitiveComplexity.cs
│ │ │ │ ├── CollectionEmptinessChecking.cs
│ │ │ │ ├── CollectionEmptinessCheckingCodeFix.cs
│ │ │ │ ├── CollectionPropertiesShouldBeReadOnly.cs
│ │ │ │ ├── CollectionQuerySimplification.cs
│ │ │ │ ├── CollectionsShouldImplementGenericInterface.cs
│ │ │ │ ├── CommentKeyword.cs
│ │ │ │ ├── CommentedOutCode.cs
│ │ │ │ ├── CommentedOutCodeCodeFix.cs
│ │ │ │ ├── CommentsShouldNotBeEmpty.cs
│ │ │ │ ├── ComparableInterfaceImplementation.cs
│ │ │ │ ├── CompareNaN.cs
│ │ │ │ ├── ConditionalSimplification.cs
│ │ │ │ ├── ConditionalSimplificationCodeFix.cs
│ │ │ │ ├── ConditionalStructureSameCondition.cs
│ │ │ │ ├── ConditionalStructureSameImplementation.cs
│ │ │ │ ├── ConditionalsShouldStartOnNewLine.cs
│ │ │ │ ├── ConditionalsWithSameCondition.cs
│ │ │ │ ├── ConstructorArgumentValueShouldExist.cs
│ │ │ │ ├── ConstructorOverridableCall.cs
│ │ │ │ ├── ConsumeValueTaskCorrectly.cs
│ │ │ │ ├── ControlCharacterInString.cs
│ │ │ │ ├── CryptographicKeyShouldNotBeTooShort.cs
│ │ │ │ ├── DangerousGetHandleShouldNotBeCalled.cs
│ │ │ │ ├── DatabasePasswordsShouldBeSecure.cs
│ │ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.cs
│ │ │ │ ├── DateTimeFormatShouldNotBeHardcoded.cs
│ │ │ │ ├── DeadStores.RoslynCfg.cs
│ │ │ │ ├── DeadStores.SonarCfg.cs
│ │ │ │ ├── DeadStores.cs
│ │ │ │ ├── DebugAssertHasNoSideEffects.cs
│ │ │ │ ├── DebuggerDisplayUsesExistingMembers.cs
│ │ │ │ ├── DeclareEventHandlersCorrectly.cs
│ │ │ │ ├── DeclareTypesInNamespaces.cs
│ │ │ │ ├── DefaultSectionShouldBeFirstOrLast.cs
│ │ │ │ ├── DelegateSubtraction.cs
│ │ │ │ ├── DisposableMemberInNonDisposableClass.cs
│ │ │ │ ├── DisposableNotDisposed.cs
│ │ │ │ ├── DisposableReturnedFromUsing.cs
│ │ │ │ ├── DisposableTypesNeedFinalizers.cs
│ │ │ │ ├── DisposeFromDispose.cs
│ │ │ │ ├── DisposeNotImplementingDispose.cs
│ │ │ │ ├── DoNotCallAssemblyGetExecutingAssemblyMethod.cs
│ │ │ │ ├── DoNotCallAssemblyLoadInvalidMethods.cs
│ │ │ │ ├── DoNotCallExitMethods.cs
│ │ │ │ ├── DoNotCallGCCollectMethod.cs
│ │ │ │ ├── DoNotCallGCSuppressFinalize.cs
│ │ │ │ ├── DoNotCallMethodsCsharpBase.cs
│ │ │ │ ├── DoNotCatchNullReferenceException.cs
│ │ │ │ ├── DoNotCatchSystemException.cs
│ │ │ │ ├── DoNotCheckZeroSizeCollection.cs
│ │ │ │ ├── DoNotCopyArraysInProperties.cs
│ │ │ │ ├── DoNotDecreaseMemberVisibility.cs
│ │ │ │ ├── DoNotExposeListT.cs
│ │ │ │ ├── DoNotHardcodeCredentials.cs
│ │ │ │ ├── DoNotHardcodeSecrets.cs
│ │ │ │ ├── DoNotHideBaseClassMethods.cs
│ │ │ │ ├── DoNotInstantiateSharedClasses.cs
│ │ │ │ ├── DoNotLockOnSharedResource.cs
│ │ │ │ ├── DoNotLockWeakIdentityObjects.cs
│ │ │ │ ├── DoNotMarkEnumsWithFlags.cs
│ │ │ │ ├── DoNotNestTernaryOperators.cs
│ │ │ │ ├── DoNotNestTypesInArguments.cs
│ │ │ │ ├── DoNotOverloadOperatorEqual.cs
│ │ │ │ ├── DoNotOverwriteCollectionElements.cs
│ │ │ │ ├── DoNotShiftByZeroOrIntSize.cs
│ │ │ │ ├── DoNotTestThisWithIsOperator.cs
│ │ │ │ ├── DoNotThrowFromDestructors.cs
│ │ │ │ ├── DoNotUseCollectionInItsOwnMethodCalls.cs
│ │ │ │ ├── DoNotUseDateTimeNow.cs
│ │ │ │ ├── DoNotUseLiteralBoolInAssertions.cs
│ │ │ │ ├── DoNotUseOutRefParameters.cs
│ │ │ │ ├── DoNotWriteToStandardOutput.cs
│ │ │ │ ├── DontMixIncrementOrDecrementWithOtherOperators.cs
│ │ │ │ ├── DontUseTraceSwitchLevels.cs
│ │ │ │ ├── DontUseTraceWrite.cs
│ │ │ │ ├── EmptyMethod.cs
│ │ │ │ ├── EmptyMethodCodeFix.cs
│ │ │ │ ├── EmptyNamespace.cs
│ │ │ │ ├── EmptyNamespaceCodeFix.cs
│ │ │ │ ├── EmptyNestedBlock.cs
│ │ │ │ ├── EmptyStatement.cs
│ │ │ │ ├── EmptyStatementCodeFix.cs
│ │ │ │ ├── EncryptionAlgorithmsShouldBeSecure.cs
│ │ │ │ ├── EnumNameHasEnumSuffix.cs
│ │ │ │ ├── EnumNameShouldFollowRegex.cs
│ │ │ │ ├── EnumStorageNeedsToBeInt32.cs
│ │ │ │ ├── EnumerableSumInUnchecked.cs
│ │ │ │ ├── EnumsShouldNotBeNamedReserved.cs
│ │ │ │ ├── EqualityOnFloatingPoint.cs
│ │ │ │ ├── EqualityOnModulus.cs
│ │ │ │ ├── EquatableClassShouldBeSealed.cs
│ │ │ │ ├── EscapeLambdaParameterTypeNamedScoped.cs
│ │ │ │ ├── EventHandlerDelegateShouldHaveProperArguments.cs
│ │ │ │ ├── ExceptionRethrow.cs
│ │ │ │ ├── ExceptionRethrowCodeFix.cs
│ │ │ │ ├── ExceptionShouldNotBeThrownFromUnexpectedMethods.cs
│ │ │ │ ├── ExceptionsNeedStandardConstructors.cs
│ │ │ │ ├── ExceptionsShouldBeLogged.cs
│ │ │ │ ├── ExceptionsShouldBeLoggedOrThrown.cs
│ │ │ │ ├── ExceptionsShouldBePublic.cs
│ │ │ │ ├── ExceptionsShouldBeUsed.cs
│ │ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.cs
│ │ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.cs
│ │ │ │ ├── ExpressionComplexity.cs
│ │ │ │ ├── ExtensionMethodShouldBeInSeparateNamespace.cs
│ │ │ │ ├── ExtensionMethodShouldNotExtendObject.cs
│ │ │ │ ├── FieldShadowsParentField.cs
│ │ │ │ ├── FieldShouldBeReadonly.cs
│ │ │ │ ├── FieldShouldBeReadonlyCodeFix.cs
│ │ │ │ ├── FieldShouldNotBePublic.cs
│ │ │ │ ├── FieldsShouldBeEncapsulatedInProperties.cs
│ │ │ │ ├── FileLines.cs
│ │ │ │ ├── FileShouldEndWithEmptyNewLine.cs
│ │ │ │ ├── FinalizerShouldNotBeEmpty.cs
│ │ │ │ ├── FindInsteadOfFirstOrDefault.cs
│ │ │ │ ├── FlagsEnumWithoutInitializer.cs
│ │ │ │ ├── FlagsEnumZeroMember.cs
│ │ │ │ ├── ForLoopConditionAlwaysFalse.cs
│ │ │ │ ├── ForLoopCounterChanged.cs
│ │ │ │ ├── ForLoopCounterCondition.cs
│ │ │ │ ├── ForLoopIncrementSign.cs
│ │ │ │ ├── ForeachLoopExplicitConversion.cs
│ │ │ │ ├── ForeachLoopExplicitConversionCodeFix.cs
│ │ │ │ ├── FrameworkTypeNaming.cs
│ │ │ │ ├── FunctionComplexity.cs
│ │ │ │ ├── FunctionNestingDepth.cs
│ │ │ │ ├── GenericInheritanceShouldNotBeRecursive.cs
│ │ │ │ ├── GenericLoggerInjectionShouldMatchEnclosingType.cs
│ │ │ │ ├── GenericReadonlyFieldPropertyAssignment.cs
│ │ │ │ ├── GenericReadonlyFieldPropertyAssignmentCodeFix.cs
│ │ │ │ ├── GenericTypeParameterEmptinessChecking.cs
│ │ │ │ ├── GenericTypeParameterEmptinessCheckingCodeFix.cs
│ │ │ │ ├── GenericTypeParameterInOut.cs
│ │ │ │ ├── GenericTypeParameterUnused.cs
│ │ │ │ ├── GenericTypeParametersRequired.cs
│ │ │ │ ├── GetHashCodeEqualsOverride.cs
│ │ │ │ ├── GetHashCodeMutable.cs
│ │ │ │ ├── GetHashCodeMutableCodeFix.cs
│ │ │ │ ├── GetTypeWithIsAssignableFrom.cs
│ │ │ │ ├── GetTypeWithIsAssignableFromCodeFix.cs
│ │ │ │ ├── GotoStatement.cs
│ │ │ │ ├── GuardConditionOnEqualsOverride.cs
│ │ │ │ ├── Hotspots/
│ │ │ │ │ ├── ClearTextProtocolsAreSensitive.cs
│ │ │ │ │ ├── CommandPath.cs
│ │ │ │ │ ├── ConfiguringLoggers.cs
│ │ │ │ │ ├── CookieShouldBeHttpOnly.cs
│ │ │ │ │ ├── CookieShouldBeSecure.cs
│ │ │ │ │ ├── CreatingHashAlgorithms.cs
│ │ │ │ │ ├── DeliveringDebugFeaturesInProduction.cs
│ │ │ │ │ ├── DisablingCsrfProtection.cs
│ │ │ │ │ ├── DisablingRequestValidation.cs
│ │ │ │ │ ├── DoNotUseRandom.cs
│ │ │ │ │ ├── ExecutingSqlQueries.cs
│ │ │ │ │ ├── ExpandingArchives.cs
│ │ │ │ │ ├── HardcodedIpAddress.cs
│ │ │ │ │ ├── InsecureDeserialization.cs
│ │ │ │ │ ├── PermissiveCors.cs
│ │ │ │ │ ├── PubliclyWritableDirectories.cs
│ │ │ │ │ ├── RequestsWithExcessiveLength.cs
│ │ │ │ │ ├── SpecifyTimeoutOnRegex.cs
│ │ │ │ │ ├── UnsafeCodeBlocks.cs
│ │ │ │ │ └── UsingNonstandardCryptography.cs
│ │ │ │ ├── IdentifiersNamedExtensionShouldBeEscaped.cs
│ │ │ │ ├── IdentifiersNamedFieldShouldBeEscaped.cs
│ │ │ │ ├── IfChainWithoutElse.cs
│ │ │ │ ├── IfCollapsible.cs
│ │ │ │ ├── ImplementIDisposableCorrectly.cs
│ │ │ │ ├── ImplementISerializableCorrectly.cs
│ │ │ │ ├── ImplementSerializationMethodsCorrectly.cs
│ │ │ │ ├── IndentSingleLineFollowingConditional.cs
│ │ │ │ ├── IndexOfCheckAgainstZero.cs
│ │ │ │ ├── InfiniteRecursion.RoslynCfg.cs
│ │ │ │ ├── InfiniteRecursion.SonarCfg.cs
│ │ │ │ ├── InfiniteRecursion.cs
│ │ │ │ ├── InheritedCollidingInterfaceMembers.cs
│ │ │ │ ├── InitializeStaticFieldsInline.cs
│ │ │ │ ├── InsecureContentSecurityPolicy.cs
│ │ │ │ ├── InsecureEncryptionAlgorithm.cs
│ │ │ │ ├── InsecureTemporaryFilesCreation.cs
│ │ │ │ ├── InsteadOfAny.cs
│ │ │ │ ├── InterfaceMethodsShouldBeCallableByChildTypes.cs
│ │ │ │ ├── InterfacesShouldNotBeEmpty.cs
│ │ │ │ ├── InvalidCastToInterface.cs
│ │ │ │ ├── InvocationResolvesToOverrideWithParams.cs
│ │ │ │ ├── IssueSuppression.cs
│ │ │ │ ├── JSInvokableMethodsShouldBePublic.cs
│ │ │ │ ├── JwtSigned.cs
│ │ │ │ ├── LdapConnectionShouldBeSecure.cs
│ │ │ │ ├── LineLength.cs
│ │ │ │ ├── LinkedListPropertiesInsteadOfMethods.cs
│ │ │ │ ├── LinkedListPropertiesInsteadOfMethodsCodeFix.cs
│ │ │ │ ├── LiteralSuffixUpperCase.cs
│ │ │ │ ├── LiteralSuffixUpperCaseCodeFix.cs
│ │ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParameters.cs
│ │ │ │ ├── LockedFieldShouldBeReadonly.cs
│ │ │ │ ├── LoggerFieldsShouldBePrivateStaticReadonly.cs
│ │ │ │ ├── LoggerMembersNamesShouldComply.cs
│ │ │ │ ├── LoggersShouldBeNamedForEnclosingType.cs
│ │ │ │ ├── LoggingArgumentsShouldBePassedCorrectly.cs
│ │ │ │ ├── LoopsAndLinq.cs
│ │ │ │ ├── LooseFilePermissions.cs
│ │ │ │ ├── LossOfFractionInDivision.cs
│ │ │ │ ├── MagicNumberShouldNotBeUsed.cs
│ │ │ │ ├── MarkAssemblyWithAssemblyVersionAttribute.cs
│ │ │ │ ├── MarkAssemblyWithClsCompliantAttribute.cs
│ │ │ │ ├── MarkAssemblyWithComVisibleAttribute.cs
│ │ │ │ ├── MarkAssemblyWithNeutralResourcesLanguageAttribute.cs
│ │ │ │ ├── MarkWindowsFormsMainWithStaThread.cs
│ │ │ │ ├── MemberInitializedToDefault.cs
│ │ │ │ ├── MemberInitializedToDefaultCodeFix.cs
│ │ │ │ ├── MemberInitializerRedundant.RoslynCfg.cs
│ │ │ │ ├── MemberInitializerRedundant.SonarCfg.cs
│ │ │ │ ├── MemberInitializerRedundant.cs
│ │ │ │ ├── MemberOverrideCallsBaseMember.cs
│ │ │ │ ├── MemberOverrideCallsBaseMemberCodeFix.cs
│ │ │ │ ├── MemberShadowsOuterStaticMember.cs
│ │ │ │ ├── MemberShouldBeStatic.cs
│ │ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.cs
│ │ │ │ ├── MessageTemplates/
│ │ │ │ │ ├── IMessageTemplateCheck.cs
│ │ │ │ │ ├── LoggingTemplatePlaceHoldersShouldBeInOrder.cs
│ │ │ │ │ ├── MessageTemplateAnalyzer.cs
│ │ │ │ │ ├── MessageTemplateExtractor.cs
│ │ │ │ │ ├── MessageTemplatesShouldBeCorrect.cs
│ │ │ │ │ ├── NamedPlaceholdersShouldBeUnique.cs
│ │ │ │ │ └── UsePascalCaseForNamedPlaceHolders.cs
│ │ │ │ ├── MethodOverloadOptionalParameter.cs
│ │ │ │ ├── MethodOverloadsShouldBeGrouped.cs
│ │ │ │ ├── MethodOverrideAddsParams.cs
│ │ │ │ ├── MethodOverrideAddsParamsCodeFix.cs
│ │ │ │ ├── MethodOverrideChangedDefaultValue.cs
│ │ │ │ ├── MethodOverrideChangedDefaultValueCodeFix.cs
│ │ │ │ ├── MethodOverrideNoParams.cs
│ │ │ │ ├── MethodOverrideNoParamsCodeFix.cs
│ │ │ │ ├── MethodParameterMissingOptional.cs
│ │ │ │ ├── MethodParameterMissingOptionalCodeFix.cs
│ │ │ │ ├── MethodParameterUnused.cs
│ │ │ │ ├── MethodParameterUnusedCodeFix.cs
│ │ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.cs
│ │ │ │ ├── MethodShouldNotOnlyReturnConstant.cs
│ │ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.cs
│ │ │ │ ├── MethodsShouldNotHaveTooManyLines.cs
│ │ │ │ ├── MethodsShouldUseBaseTypes.cs
│ │ │ │ ├── MultilineBlocksWithoutBrace.cs
│ │ │ │ ├── MultipleVariableDeclaration.cs
│ │ │ │ ├── MultipleVariableDeclarationCodeFix.cs
│ │ │ │ ├── MutableFieldsShouldNotBe.cs
│ │ │ │ ├── MutableFieldsShouldNotBePublicReadonly.cs
│ │ │ │ ├── MutableFieldsShouldNotBePublicStatic.cs
│ │ │ │ ├── NameOfShouldBeUsed.cs
│ │ │ │ ├── NativeMethodsShouldBeWrapped.cs
│ │ │ │ ├── NestedCodeBlock.cs
│ │ │ │ ├── NoExceptionsInFinally.cs
│ │ │ │ ├── NonAsyncTaskShouldNotReturnNull.cs
│ │ │ │ ├── NonDerivedPrivateClassesShouldBeSealed.cs
│ │ │ │ ├── NonFlagsEnumInBitwiseOperation.cs
│ │ │ │ ├── NonFlagsEnumInBitwiseOperationCodeFix.cs
│ │ │ │ ├── NormalizeStringsToUppercase.cs
│ │ │ │ ├── NotAssignedPrivateMember.cs
│ │ │ │ ├── NumberPatternShouldBeRegular.cs
│ │ │ │ ├── ObjectCreatedDropped.cs
│ │ │ │ ├── ObjectShouldBeInitializedCorrectlyBase.cs
│ │ │ │ ├── ObsoleteAttributes.cs
│ │ │ │ ├── OperatorOverloadsShouldHaveNamedAlternatives.cs
│ │ │ │ ├── OperatorsShouldBeOverloadedConsistently.cs
│ │ │ │ ├── OptionalParameter.cs
│ │ │ │ ├── OptionalParameterNotPassedToBaseCall.cs
│ │ │ │ ├── OptionalParameterWithDefaultValue.cs
│ │ │ │ ├── OptionalParameterWithDefaultValueCodeFix.cs
│ │ │ │ ├── OptionalRefOutParameter.cs
│ │ │ │ ├── OptionalRefOutParameterCodeFix.cs
│ │ │ │ ├── OrderByRepeated.cs
│ │ │ │ ├── OrderByRepeatedCodeFix.cs
│ │ │ │ ├── OverrideGetHashCodeOnOverridingEquals.cs
│ │ │ │ ├── PInvokesShouldNotBeVisible.cs
│ │ │ │ ├── ParameterAssignedTo.cs
│ │ │ │ ├── ParameterNameMatchesOriginal.cs
│ │ │ │ ├── ParameterNamesShouldNotDuplicateMethodNames.cs
│ │ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.cs
│ │ │ │ ├── ParameterValidationInAsyncShouldBeWrapped.cs
│ │ │ │ ├── ParameterValidationInYieldShouldBeWrapped.cs
│ │ │ │ ├── ParametersCorrectOrder.cs
│ │ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttribute.cs
│ │ │ │ ├── PartialMethodNoImplementation.cs
│ │ │ │ ├── PasswordsShouldBeStoredCorrectly.cs
│ │ │ │ ├── PointersShouldBePrivate.cs
│ │ │ │ ├── PreferGuidEmpty.cs
│ │ │ │ ├── PreferGuidEmptyCodeFix.cs
│ │ │ │ ├── PreferJaggedArraysOverMultidimensional.cs
│ │ │ │ ├── PrivateFieldUsedAsLocalVariable.cs
│ │ │ │ ├── PrivateStaticMethodUsedOnlyByNestedClass.cs
│ │ │ │ ├── PropertiesAccessCorrectField.cs
│ │ │ │ ├── PropertiesShouldBePreferred.cs
│ │ │ │ ├── PropertyGetterWithThrow.cs
│ │ │ │ ├── PropertyNamesShouldNotMatchGetMethods.cs
│ │ │ │ ├── PropertyToAutoProperty.cs
│ │ │ │ ├── PropertyWriteOnly.cs
│ │ │ │ ├── ProvideDeserializationMethodsForOptionalFields.cs
│ │ │ │ ├── PublicConstantField.cs
│ │ │ │ ├── PublicMethodWithMultidimensionalArray.cs
│ │ │ │ ├── PureAttributeOnVoidMethod.cs
│ │ │ │ ├── RedundancyInConstructorDestructorDeclaration.cs
│ │ │ │ ├── RedundancyInConstructorDestructorDeclarationCodeFix.cs
│ │ │ │ ├── RedundantArgument.cs
│ │ │ │ ├── RedundantArgumentCodeFix.cs
│ │ │ │ ├── RedundantCast.cs
│ │ │ │ ├── RedundantCastCodeFix.cs
│ │ │ │ ├── RedundantConditionalAroundAssignment.cs
│ │ │ │ ├── RedundantConditionalAroundAssignmentCodeFix.cs
│ │ │ │ ├── RedundantDeclaration.cs
│ │ │ │ ├── RedundantDeclarationCodeFix.cs
│ │ │ │ ├── RedundantInheritanceList.cs
│ │ │ │ ├── RedundantInheritanceListCodeFix.cs
│ │ │ │ ├── RedundantJumpStatement.cs
│ │ │ │ ├── RedundantModifier.cs
│ │ │ │ ├── RedundantModifierCodeFix.cs
│ │ │ │ ├── RedundantNullCheck.cs
│ │ │ │ ├── RedundantNullCheckCodeFix.cs
│ │ │ │ ├── RedundantNullableTypeComparison.cs
│ │ │ │ ├── RedundantParentheses.cs
│ │ │ │ ├── RedundantParenthesesCodeFix.cs
│ │ │ │ ├── RedundantParenthesesObjectsCreation.cs
│ │ │ │ ├── RedundantPropertyNamesInAnonymousClass.cs
│ │ │ │ ├── RedundantPropertyNamesInAnonymousClassCodeFix.cs
│ │ │ │ ├── RedundantToArrayCall.cs
│ │ │ │ ├── RedundantToArrayCallCodeFix.cs
│ │ │ │ ├── RedundantToStringCall.cs
│ │ │ │ ├── RedundantToStringCallCodeFix.cs
│ │ │ │ ├── ReferenceEqualityCheckWhenEqualsExists.cs
│ │ │ │ ├── ReferenceEqualsOnValueType.cs
│ │ │ │ ├── RegularExpressions/
│ │ │ │ │ └── RegexMustHaveValidSyntax.cs
│ │ │ │ ├── RequireAttributeUsageAttribute.cs
│ │ │ │ ├── ReturnEmptyCollectionInsteadOfNull.cs
│ │ │ │ ├── ReturnTypeNamedPartialShouldBeEscaped.cs
│ │ │ │ ├── ReturnValueIgnored.cs
│ │ │ │ ├── ReuseClientBase.cs
│ │ │ │ ├── ReversedOperators.cs
│ │ │ │ ├── RightCurlyBraceStartsLine.cs
│ │ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.cs
│ │ │ │ ├── SelfAssignment.cs
│ │ │ │ ├── SerializationConstructorsShouldBeSecured.cs
│ │ │ │ ├── SetLocaleForDataTypes.cs
│ │ │ │ ├── SetPropertiesInsteadOfMethods.cs
│ │ │ │ ├── ShiftDynamicNotInteger.cs
│ │ │ │ ├── ShouldImplementExportedInterfaces.cs
│ │ │ │ ├── SingleStatementPerLine.cs
│ │ │ │ ├── SpecifyIFormatProviderOrCultureInfo.cs
│ │ │ │ ├── SpecifyStringComparison.cs
│ │ │ │ ├── SqlKeywordsDelimitedBySpace.cs
│ │ │ │ ├── StaticFieldInGenericClass.cs
│ │ │ │ ├── StaticFieldInitializerOrder.cs
│ │ │ │ ├── StaticFieldVisible.cs
│ │ │ │ ├── StaticFieldWrittenFrom.cs
│ │ │ │ ├── StaticFieldWrittenFromInstanceConstructor.cs
│ │ │ │ ├── StaticFieldWrittenFromInstanceMember.cs
│ │ │ │ ├── StaticSealedClassProtectedMembers.cs
│ │ │ │ ├── StreamReadStatement.cs
│ │ │ │ ├── StringConcatenationInLoop.cs
│ │ │ │ ├── StringFormatValidator.cs
│ │ │ │ ├── StringLiteralShouldNotBeDuplicated.cs
│ │ │ │ ├── StringOffsetMethods.cs
│ │ │ │ ├── StringOperationWithoutCulture.cs
│ │ │ │ ├── StringOrIntegralTypesForIndexers.cs
│ │ │ │ ├── SuppressFinalizeUseless.cs
│ │ │ │ ├── SuppressFinalizeUselessCodeFix.cs
│ │ │ │ ├── SwaggerActionReturnType.cs
│ │ │ │ ├── SwitchCaseFallsThroughToDefault.cs
│ │ │ │ ├── SwitchCaseFallsThroughToDefaultCodeFix.cs
│ │ │ │ ├── SwitchCasesMinimumThree.cs
│ │ │ │ ├── SwitchDefaultClauseEmpty.cs
│ │ │ │ ├── SwitchDefaultClauseEmptyCodeFix.cs
│ │ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements.cs
│ │ │ │ ├── SwitchShouldNotBeNested.cs
│ │ │ │ ├── SwitchWithoutDefault.cs
│ │ │ │ ├── TabCharacter.cs
│ │ │ │ ├── TaskConfigureAwait.cs
│ │ │ │ ├── TestClassShouldHaveTestMethod.cs
│ │ │ │ ├── TestMethodShouldContainAssertion.cs
│ │ │ │ ├── TestMethodShouldHaveCorrectSignature.cs
│ │ │ │ ├── TestMethodShouldNotBeIgnored.cs
│ │ │ │ ├── TestsShouldNotUseThreadSleep.cs
│ │ │ │ ├── ThisShouldNotBeExposedFromConstructors.cs
│ │ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalled.cs
│ │ │ │ ├── ThreadStaticNonStaticField.cs
│ │ │ │ ├── ThreadStaticNonStaticFieldCodeFix.cs
│ │ │ │ ├── ThreadStaticWithInitializer.cs
│ │ │ │ ├── ThrowReservedExceptions.cs
│ │ │ │ ├── ToStringShouldNotReturnNull.cs
│ │ │ │ ├── TooManyGenericParameters.cs
│ │ │ │ ├── TooManyLabelsInSwitch.cs
│ │ │ │ ├── TooManyLoggingCalls.cs
│ │ │ │ ├── TooManyParameters.cs
│ │ │ │ ├── TrackNotImplementedException.cs
│ │ │ │ ├── TryStatementsWithIdenticalCatchShouldBeMerged.cs
│ │ │ │ ├── TypeExaminationOnSystemType.cs
│ │ │ │ ├── TypeMemberVisibility.cs
│ │ │ │ ├── TypeNamesShouldNotMatchNamespaces.cs
│ │ │ │ ├── TypesShouldNotExtendOutdatedBaseTypes.cs
│ │ │ │ ├── UnaryPrefixOperatorRepeated.cs
│ │ │ │ ├── UnaryPrefixOperatorRepeatedCodeFix.cs
│ │ │ │ ├── UnchangedLocalVariablesShouldBeConst.cs
│ │ │ │ ├── UnchangedLocalVariablesShouldBeConstCodeFix.cs
│ │ │ │ ├── UnconditionalJumpStatement.cs
│ │ │ │ ├── UninvokedEventDeclaration.cs
│ │ │ │ ├── UnnecessaryBitwiseOperation.cs
│ │ │ │ ├── UnnecessaryBitwiseOperationCodeFix.cs
│ │ │ │ ├── UnnecessaryMathematicalComparison.cs
│ │ │ │ ├── UnnecessaryUsings.cs
│ │ │ │ ├── UnnecessaryUsingsCodeFix.cs
│ │ │ │ ├── UnusedPrivateMember.cs
│ │ │ │ ├── UnusedPrivateMemberCodeFix.cs
│ │ │ │ ├── UnusedReturnValue.cs
│ │ │ │ ├── UnusedStringBuilder.cs
│ │ │ │ ├── UriShouldNotBeHardcoded.cs
│ │ │ │ ├── UseAwaitableMethod.cs
│ │ │ │ ├── UseCharOverloadOfStringMethods.cs
│ │ │ │ ├── UseCharOverloadOfStringMethodsCodeFix.cs
│ │ │ │ ├── UseConstantLoggingTemplate.cs
│ │ │ │ ├── UseConstantsWhereAppropriate.cs
│ │ │ │ ├── UseCurlyBraces.cs
│ │ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.cs
│ │ │ │ ├── UseFindSystemTimeZoneById.cs
│ │ │ │ ├── UseGenericEventHandlerInstances.cs
│ │ │ │ ├── UseGenericWithRefParameters.cs
│ │ │ │ ├── UseIFormatProviderForParsingDateAndTime.cs
│ │ │ │ ├── UseIndexingInsteadOfLinqMethods.cs
│ │ │ │ ├── UseLambdaParameterInConcurrentDictionary.cs
│ │ │ │ ├── UseNumericLiteralSeparator.cs
│ │ │ │ ├── UseParamsForVariableArguments.cs
│ │ │ │ ├── UseShortCircuitingOperator.cs
│ │ │ │ ├── UseShortCircuitingOperatorCodeFix.cs
│ │ │ │ ├── UseStringCreate.cs
│ │ │ │ ├── UseStringIsNullOrEmpty.cs
│ │ │ │ ├── UseTestableTimeProvider.cs
│ │ │ │ ├── UseTrueForAll.cs
│ │ │ │ ├── UseUnixEpoch.cs
│ │ │ │ ├── UseUnixEpochCodeFix.cs
│ │ │ │ ├── UseUriInsteadOfString.cs
│ │ │ │ ├── UseValueParameter.cs
│ │ │ │ ├── UseWhereBeforeOrderBy.cs
│ │ │ │ ├── UseWhileLoopInstead.cs
│ │ │ │ ├── Utilities/
│ │ │ │ │ ├── AnalysisWarningAnalyzer.cs
│ │ │ │ │ ├── CopyPasteTokenAnalyzer.cs
│ │ │ │ │ ├── FileMetadataAnalyzer.cs
│ │ │ │ │ ├── LogAnalyzer.cs
│ │ │ │ │ ├── MetricsAnalyzer.cs
│ │ │ │ │ ├── SymbolReferenceAnalyzer.cs
│ │ │ │ │ ├── TelemetryAnalyzer.cs
│ │ │ │ │ ├── TestMethodDeclarationsAnalyzer.cs
│ │ │ │ │ └── TokenTypeAnalyzer.cs
│ │ │ │ ├── ValueTypeShouldImplementIEquatable.cs
│ │ │ │ ├── ValuesUselesslyIncremented.cs
│ │ │ │ ├── VariableShadowsField.cs
│ │ │ │ ├── VariableUnused.cs
│ │ │ │ ├── VirtualEventField.cs
│ │ │ │ ├── VirtualEventFieldCodeFix.cs
│ │ │ │ ├── WcfMissingContractAttribute.cs
│ │ │ │ ├── WcfNonVoidOneWay.cs
│ │ │ │ ├── WeakSslTlsProtocols.cs
│ │ │ │ ├── XMLSignatureCheck.cs
│ │ │ │ ├── XXE/
│ │ │ │ │ └── XmlReaderSettingsValidator.cs
│ │ │ │ └── XmlExternalEntityShouldNotBeParsed.cs
│ │ │ ├── SonarAnalyzer.CSharp.csproj
│ │ │ ├── Syntax/
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── ExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── IMethodSymbolExtensions.cs
│ │ │ │ │ ├── IfStatementSyntaxExtensions.cs
│ │ │ │ │ ├── InvocationExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── SwitchSectionSyntaxExtensions.cs
│ │ │ │ │ └── SyntaxNodeExtensions.cs
│ │ │ │ └── Utilities/
│ │ │ │ ├── AttributeSyntaxSymbolMapping.cs
│ │ │ │ ├── SymbolUsage.cs
│ │ │ │ └── SymbolUsageCollector.cs
│ │ │ ├── Walkers/
│ │ │ │ ├── CatchLoggingInvocationWalker.cs
│ │ │ │ └── ParameterValidationInMethodWalker.cs
│ │ │ ├── packages.lock.json
│ │ │ └── sonarpedia.json
│ │ ├── SonarAnalyzer.CSharp.Core/
│ │ │ ├── Common/
│ │ │ │ ├── DescriptorFactory.cs
│ │ │ │ └── SyntaxConstants.cs
│ │ │ ├── Extensions/
│ │ │ │ ├── CSharpCompilationExtensions.cs
│ │ │ │ ├── IAnalyzerConfigurationExtensions.cs
│ │ │ │ ├── ICompilationReportExtensions.cs
│ │ │ │ ├── IFieldSymbolExtensions.cs
│ │ │ │ ├── ILocalSymbolExtensions.cs
│ │ │ │ ├── IMethodSymbolExtensions.cs
│ │ │ │ ├── ISymbolExtensions.cs
│ │ │ │ ├── ITupleOperationWrapperExtensions.cs
│ │ │ │ ├── ITypeSymbolExtensions.cs
│ │ │ │ ├── LanguageVersionExtensions.cs
│ │ │ │ ├── SonarAnalysisContextExtensions.cs
│ │ │ │ ├── SonarCompilationStartAnalysisContextExtensions.cs
│ │ │ │ ├── SonarParametrizedAnalysisContextExtensions.cs
│ │ │ │ └── SonarSyntaxNodeReportingContextExtensions.cs
│ │ │ ├── Facade/
│ │ │ │ ├── CSharpFacade.cs
│ │ │ │ └── Implementation/
│ │ │ │ ├── CSharpSyntaxFacade.cs
│ │ │ │ ├── CSharpSyntaxKindFacade.cs
│ │ │ │ └── CSharpTrackerFacade.cs
│ │ │ ├── LiveVariableAnalysis/
│ │ │ │ └── SonarCSharpLiveVariableAnalysis.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── RegularExpressions/
│ │ │ │ └── MessageTemplatesParser.cs
│ │ │ ├── SonarAnalyzer.CSharp.Core.csproj
│ │ │ ├── Syntax/
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── AccessorDeclarationSyntaxExtensions.cs
│ │ │ │ │ ├── ArgumentListSyntaxExtensions.cs
│ │ │ │ │ ├── ArgumentSyntaxExtensions.cs
│ │ │ │ │ ├── AssignmentExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── AttributeListSyntaxExtensions.cs
│ │ │ │ │ ├── AttributeSyntaxExtensions.cs
│ │ │ │ │ ├── AwaitExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── BaseArgumentListSyntaxExtensions.cs
│ │ │ │ │ ├── BaseMethodDeclarationSyntaxExtensions.cs
│ │ │ │ │ ├── BlockSyntaxExtensions.cs
│ │ │ │ │ ├── CompilationUnitSyntaxExtensions.cs
│ │ │ │ │ ├── ExpressionSyntaxExtensions.Roslyn.cs
│ │ │ │ │ ├── ExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── InterpolatedStringExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── InvocationExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── LocalFunctionStatementSyntaxWrapperExtensions.cs
│ │ │ │ │ ├── MemberAccessExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── MethodDeclarationSyntaxExtensions.cs
│ │ │ │ │ ├── ObjectCreationExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── ParameterSyntaxExtensions.cs
│ │ │ │ │ ├── PropertyDeclarationSyntaxExtensions.cs
│ │ │ │ │ ├── StatementSyntaxExtensions.cs
│ │ │ │ │ ├── SwitchExpressionSyntaxWrapperExtensions.cs
│ │ │ │ │ ├── SwitchStatementSyntaxExtensions.cs
│ │ │ │ │ ├── SyntaxNodeExtensions.Roslyn.cs
│ │ │ │ │ ├── SyntaxNodeExtensionsCSharp.cs
│ │ │ │ │ ├── SyntaxTokenExtensions.cs
│ │ │ │ │ ├── SyntaxTokenListExtensions.cs
│ │ │ │ │ ├── SyntaxTriviaExtensions.cs
│ │ │ │ │ ├── TupleExpressionSyntaxExtensions.cs
│ │ │ │ │ ├── TypeDeclarationSyntaxExtensions.cs
│ │ │ │ │ ├── TypeSyntaxExtensions.cs
│ │ │ │ │ └── VariableDesignationSyntaxWrapperExtensions.cs
│ │ │ │ └── Utilities/
│ │ │ │ ├── CSharpAttributeParameterLookup.cs
│ │ │ │ ├── CSharpEquivalenceChecker.cs
│ │ │ │ ├── CSharpExpressionNumericConverter.cs
│ │ │ │ ├── CSharpGeneratedCodeRecognizer.cs
│ │ │ │ ├── CSharpMethodParameterLookup.cs
│ │ │ │ ├── CSharpRemovableDeclarationCollector.cs
│ │ │ │ ├── CSharpStringInterpolationConstantValueResolver.cs
│ │ │ │ ├── CSharpSyntaxClassifier.cs
│ │ │ │ ├── MutedSyntaxWalker.cs
│ │ │ │ └── SafeCSharpSyntaxWalker.cs
│ │ │ ├── Trackers/
│ │ │ │ ├── CSharpArgumentTracker.cs
│ │ │ │ ├── CSharpAssignmentFinder.cs
│ │ │ │ ├── CSharpBaseTypeTracker.cs
│ │ │ │ ├── CSharpBuilderPatternCondition.cs
│ │ │ │ ├── CSharpConstantValueFinder.cs
│ │ │ │ ├── CSharpElementAccessTracker.cs
│ │ │ │ ├── CSharpFieldAccessTracker.cs
│ │ │ │ ├── CSharpInvocationTracker.cs
│ │ │ │ ├── CSharpMethodDeclarationTracker.cs
│ │ │ │ ├── CSharpObjectCreationTracker.cs
│ │ │ │ ├── CSharpObjectInitializationTracker.cs
│ │ │ │ └── CSharpPropertyAccessTracker.cs
│ │ │ ├── Wrappers/
│ │ │ │ ├── IMethodDeclaration.cs
│ │ │ │ ├── MethodDeclarationFactory.cs
│ │ │ │ └── ObjectCreationFactory.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.CSharp.Styling/
│ │ │ ├── Common/
│ │ │ │ ├── DescriptorFactory.cs
│ │ │ │ ├── OrderDescriptor.cs
│ │ │ │ └── StylingAnalyzer.cs
│ │ │ ├── Extensions/
│ │ │ │ ├── LambdaExpressionSyntaxExtensions.cs
│ │ │ │ ├── MemberDeclarationSyntaxExtensions.cs
│ │ │ │ └── SyntaxNodeExtensions.cs
│ │ │ ├── Rules/
│ │ │ │ ├── AllArgumentsOnSameLine.cs
│ │ │ │ ├── AllParametersBase.cs
│ │ │ │ ├── AllParametersOnSameColumn.cs
│ │ │ │ ├── AllParametersOnSameLine.cs
│ │ │ │ ├── ArrowLocation.cs
│ │ │ │ ├── AvoidArrangeActAssertComment.cs
│ │ │ │ ├── AvoidDeconstruction.cs
│ │ │ │ ├── AvoidGet.cs
│ │ │ │ ├── AvoidListForEach.cs
│ │ │ │ ├── AvoidNullable.cs
│ │ │ │ ├── AvoidPrimaryConstructor.cs
│ │ │ │ ├── AvoidUnusedInterpolation.cs
│ │ │ │ ├── AvoidValueTuple.cs
│ │ │ │ ├── AvoidVarPattern.cs
│ │ │ │ ├── FieldOrdering.cs
│ │ │ │ ├── FileScopeNamespace.cs
│ │ │ │ ├── HelperInTypeName.cs
│ │ │ │ ├── IndentArgument.cs
│ │ │ │ ├── IndentBase.cs
│ │ │ │ ├── IndentInvocation.cs
│ │ │ │ ├── IndentOperator.cs
│ │ │ │ ├── IndentRawString.cs
│ │ │ │ ├── IndentTernary.cs
│ │ │ │ ├── InitializerLine.cs
│ │ │ │ ├── LambdaParameterName.cs
│ │ │ │ ├── LocalFunctionLocation.cs
│ │ │ │ ├── MemberAccessLine.cs
│ │ │ │ ├── MemberVisibilityOrdering.cs
│ │ │ │ ├── MethodExpressionBodyLine.cs
│ │ │ │ ├── MoveMethodToDedicatedExtensionClass.cs
│ │ │ │ ├── NamespaceName.cs
│ │ │ │ ├── NullPatternMatching.cs
│ │ │ │ ├── OperatorLocation.cs
│ │ │ │ ├── PropertyOrdering.cs
│ │ │ │ ├── ProtectedFieldsCase.cs
│ │ │ │ ├── SeparateDeclarations.cs
│ │ │ │ ├── TermaryLine.cs
│ │ │ │ ├── TypeMemberOrdering.cs
│ │ │ │ ├── UseDifferentMember.cs
│ │ │ │ ├── UseField.cs
│ │ │ │ ├── UseInnermostRegistrationContext.cs
│ │ │ │ ├── UseLinqExtensions.cs
│ │ │ │ ├── UseNullInsteadOfDefault.cs
│ │ │ │ ├── UsePositiveLogic.cs
│ │ │ │ ├── UseRawString.cs
│ │ │ │ ├── UseRegexSafeIsMatch.cs
│ │ │ │ ├── UseShortName.cs
│ │ │ │ └── UseVar.cs
│ │ │ ├── SonarAnalyzer.CSharp.Styling.csproj
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.Core/
│ │ │ ├── AnalysisContext/
│ │ │ │ ├── IAnalysisContext.cs
│ │ │ │ ├── IReport.cs
│ │ │ │ ├── IReportingContext.cs
│ │ │ │ ├── IssueReporter.cs
│ │ │ │ ├── ReportingContext.cs
│ │ │ │ ├── ShouldAnalyzeTreeCache.cs
│ │ │ │ ├── SonarAnalysisContext.cs
│ │ │ │ ├── SonarAnalysisContextBase.cs
│ │ │ │ ├── SonarCodeBlockReportingContext.cs
│ │ │ │ ├── SonarCodeBlockStartAnalysisContext.cs
│ │ │ │ ├── SonarCodeFixContext.cs
│ │ │ │ ├── SonarCompilationReportingContext.cs
│ │ │ │ ├── SonarCompilationStartAnalysisContext.cs
│ │ │ │ ├── SonarParametrizedAnalysisContext.cs
│ │ │ │ ├── SonarSemanticModelReportingContext.cs
│ │ │ │ ├── SonarSymbolReportingContext.cs
│ │ │ │ ├── SonarSymbolStartAnalysisContext.cs
│ │ │ │ ├── SonarSyntaxNodeReportingContext.cs
│ │ │ │ └── SonarSyntaxTreeReportingContext.cs
│ │ │ ├── Analyzers/
│ │ │ │ ├── DiagnosticDescriptorFactory.cs
│ │ │ │ ├── HotspotDiagnosticAnalyzer.cs
│ │ │ │ ├── ParametrizedDiagnosticAnalyzer.cs
│ │ │ │ ├── SonarCodeFix.cs
│ │ │ │ ├── SonarDiagnosticAnalyzer.cs
│ │ │ │ └── TrackerHotspotDiagnosticAnalyzer.cs
│ │ │ ├── Common/
│ │ │ │ ├── AnalyzerAdditionalFile.cs
│ │ │ │ ├── AnalyzerConfiguration.cs
│ │ │ │ ├── AnalyzerLanguage.cs
│ │ │ │ ├── BidirectionalDictionary.cs
│ │ │ │ ├── Constants.cs
│ │ │ │ ├── Conversions.cs
│ │ │ │ ├── DisjointSets.cs
│ │ │ │ ├── DocumentBasedFixAllProvider.cs
│ │ │ │ ├── HashCode.cs
│ │ │ │ ├── IAnalyzerConfiguration.cs
│ │ │ │ ├── IRuleLoader.cs
│ │ │ │ ├── ISafeSyntaxWalker.cs
│ │ │ │ ├── IsExternalInit.cs
│ │ │ │ ├── MultiValueDictionary.cs
│ │ │ │ ├── NaturalLanguageDetector.cs
│ │ │ │ ├── NodeAndModel.cs
│ │ │ │ ├── NodeAndSymbol.cs
│ │ │ │ ├── NodeSymbolAndModel.cs
│ │ │ │ ├── Pair.cs
│ │ │ │ ├── PropertyType.cs
│ │ │ │ ├── RuleDescriptor.cs
│ │ │ │ ├── RuleLoader.cs
│ │ │ │ ├── RuleParameterAttribute.cs
│ │ │ │ ├── SecondaryLocation.cs
│ │ │ │ ├── ShannonEntropy.cs
│ │ │ │ ├── SourceScope.cs
│ │ │ │ ├── TokenAndModel.cs
│ │ │ │ ├── TopLevelStatements.cs
│ │ │ │ ├── UnexpectedLanguageException.cs
│ │ │ │ └── UnexpectedValueException.cs
│ │ │ ├── Configuration/
│ │ │ │ ├── AnalysisConfig.cs
│ │ │ │ ├── AnalysisConfigReader.cs
│ │ │ │ ├── ConfigSetting.cs
│ │ │ │ ├── FilesToAnalyzeProvider.cs
│ │ │ │ ├── ParameterLoader.cs
│ │ │ │ ├── ProjectConfig.cs
│ │ │ │ ├── ProjectConfigReader.cs
│ │ │ │ ├── ProjectType.cs
│ │ │ │ ├── ProjectTypeCache.cs
│ │ │ │ ├── SonarLintXml.cs
│ │ │ │ └── SonarLintXmlReader.cs
│ │ │ ├── Extensions/
│ │ │ │ ├── AnalyzerOptionsExtensions.cs
│ │ │ │ ├── CompilationExtensions.cs
│ │ │ │ ├── DiagnosticDescriptorExtensions.cs
│ │ │ │ ├── DictionaryExtensions.cs
│ │ │ │ ├── HashSetExtensions.cs
│ │ │ │ ├── IAnalysisContextExtensions.cs
│ │ │ │ ├── ICompilationReportExtensions.cs
│ │ │ │ ├── IEnumerableExtensions.cs
│ │ │ │ ├── ITreeReportExtensions.cs
│ │ │ │ ├── IXmlLineInfoExtensions.cs
│ │ │ │ ├── RegexExtensions.cs
│ │ │ │ ├── SonarAnalysisContextExtensions.cs
│ │ │ │ ├── StackExtensions.cs
│ │ │ │ ├── StringExtensions.cs
│ │ │ │ ├── XAttributeExtensions.cs
│ │ │ │ └── XElementExtensions.cs
│ │ │ ├── Facade/
│ │ │ │ ├── ILanguageFacade.cs
│ │ │ │ ├── ISyntaxKindFacade.cs
│ │ │ │ ├── ITrackerFacade.cs
│ │ │ │ └── SyntaxFacade.cs
│ │ │ ├── Json/
│ │ │ │ ├── JsonException.cs
│ │ │ │ ├── JsonNode.cs
│ │ │ │ ├── JsonSerializer.cs
│ │ │ │ ├── JsonWalker.cs
│ │ │ │ └── Parsing/
│ │ │ │ ├── Kind.cs
│ │ │ │ ├── LexicalAnalyzer.cs
│ │ │ │ ├── Symbol.cs
│ │ │ │ └── SyntaxAnalyzer.cs
│ │ │ ├── Metrics/
│ │ │ │ ├── CognitiveComplexity.cs
│ │ │ │ ├── FileComments.cs
│ │ │ │ └── MetricsBase.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── Protobuf/
│ │ │ │ └── AnalyzerReport.proto
│ │ │ ├── RegularExpressions/
│ │ │ │ ├── NamingPatterns.cs
│ │ │ │ ├── RegexContext.cs
│ │ │ │ └── WildcardPatternMatcher.cs
│ │ │ ├── Rules/
│ │ │ │ ├── AllBranchesShouldNotHaveSameImplementationBase.cs
│ │ │ │ ├── AlwaysSetDateTimeKindBase.cs
│ │ │ │ ├── ArrayPassedAsParamsBase.cs
│ │ │ │ ├── AspNet/
│ │ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutesBase.cs
│ │ │ │ │ └── RouteTemplateShouldNotStartWithSlashBase.cs
│ │ │ │ ├── AvoidDateTimeNowForBenchmarkingBase.cs
│ │ │ │ ├── AvoidUnsealedAttributesBase.cs
│ │ │ │ ├── BeginInvokePairedWithEndInvokeBase.cs
│ │ │ │ ├── BinaryOperationWithIdenticalExpressionsBase.cs
│ │ │ │ ├── BooleanCheckInvertedBase.cs
│ │ │ │ ├── BooleanLiteralUnnecessaryBase.cs
│ │ │ │ ├── BypassingAccessibilityBase.cs
│ │ │ │ ├── CatchRethrowBase.cs
│ │ │ │ ├── CertificateValidationCheckBase.cs
│ │ │ │ ├── CheckFileLicenseBase.cs
│ │ │ │ ├── ClassNamedExceptionBase.cs
│ │ │ │ ├── ClassNotInstantiatableBase.cs
│ │ │ │ ├── ClassShouldNotBeEmptyBase.cs
│ │ │ │ ├── CognitiveComplexityBase.cs
│ │ │ │ ├── CollectionEmptinessCheckingBase.cs
│ │ │ │ ├── CommentKeywordBase.cs
│ │ │ │ ├── CommentsShouldNotBeEmptyBase.cs
│ │ │ │ ├── ConditionalStructureSameConditionBase.cs
│ │ │ │ ├── ConditionalStructureSameImplementationBase.cs
│ │ │ │ ├── ConstructorArgumentValueShouldExistBase.cs
│ │ │ │ ├── DangerousGetHandleShouldNotBeCalledBase.cs
│ │ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKeyBase.cs
│ │ │ │ ├── DateTimeFormatShouldNotBeHardcodedBase.cs
│ │ │ │ ├── DebuggerDisplayUsesExistingMembersBase.cs
│ │ │ │ ├── DeclareTypesInNamespacesBase.cs
│ │ │ │ ├── DoNotCallInsecureSecurityAlgorithmBase.cs
│ │ │ │ ├── DoNotCallMethodsBase.cs
│ │ │ │ ├── DoNotCheckZeroSizeCollectionBase.cs
│ │ │ │ ├── DoNotHardcodeBase.cs
│ │ │ │ ├── DoNotHardcodeCredentialsBase.cs
│ │ │ │ ├── DoNotHardcodeSecretsBase.cs
│ │ │ │ ├── DoNotInstantiateSharedClassesBase.cs
│ │ │ │ ├── DoNotLockOnSharedResourceBase.cs
│ │ │ │ ├── DoNotLockWeakIdentityObjectsBase.cs
│ │ │ │ ├── DoNotNestTernaryOperatorsBase.cs
│ │ │ │ ├── DoNotOverwriteCollectionElementsBase.cs
│ │ │ │ ├── DoNotThrowFromDestructorsBase.cs
│ │ │ │ ├── DoNotUseDateTimeNowBase.cs
│ │ │ │ ├── EmptyMethodBase.cs
│ │ │ │ ├── EmptyNestedBlockBase.cs
│ │ │ │ ├── EncryptionAlgorithmsShouldBeSecureBase.cs
│ │ │ │ ├── EnumNameHasEnumSuffixBase.cs
│ │ │ │ ├── EnumNameShouldFollowRegexBase.cs
│ │ │ │ ├── ExceptionsShouldBePublicBase.cs
│ │ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustificationBase.cs
│ │ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsedBase.cs
│ │ │ │ ├── ExpressionComplexityBase.cs
│ │ │ │ ├── ExtensionMethodShouldNotExtendObjectBase.cs
│ │ │ │ ├── FieldShadowsParentFieldBase.cs
│ │ │ │ ├── FieldShouldNotBePublicBase.cs
│ │ │ │ ├── FileLinesBase.cs
│ │ │ │ ├── FindInsteadOfFirstOrDefaultBase.cs
│ │ │ │ ├── FlagsEnumWithoutInitializerBase.cs
│ │ │ │ ├── FlagsEnumZeroMemberBase.cs
│ │ │ │ ├── FunctionComplexityBase.cs
│ │ │ │ ├── FunctionNestingDepthBase.cs
│ │ │ │ ├── GenericInheritanceShouldNotBeRecursiveBase.cs
│ │ │ │ ├── GotoStatementBase.cs
│ │ │ │ ├── Hotspots/
│ │ │ │ │ ├── CommandPathBase.cs
│ │ │ │ │ ├── ConfiguringLoggersBase.cs
│ │ │ │ │ ├── CreatingHashAlgorithmsBase.cs
│ │ │ │ │ ├── DeliveringDebugFeaturesInProductionBase.cs
│ │ │ │ │ ├── DisablingRequestValidationBase.cs
│ │ │ │ │ ├── ExecutingSqlQueriesBase.cs
│ │ │ │ │ ├── ExpandingArchivesBase.cs
│ │ │ │ │ ├── HardcodedIpAddressBase.cs
│ │ │ │ │ ├── PubliclyWritableDirectoriesBase.cs
│ │ │ │ │ ├── RequestsWithExcessiveLengthBase.cs
│ │ │ │ │ ├── SpecifyTimeoutOnRegexBase.cs
│ │ │ │ │ └── UsingNonstandardCryptographyBase.cs
│ │ │ │ ├── IfChainWithoutElseBase.cs
│ │ │ │ ├── IfCollapsibleBase.cs
│ │ │ │ ├── ImplementSerializationMethodsCorrectlyBase.cs
│ │ │ │ ├── IndexOfCheckAgainstZeroBase.cs
│ │ │ │ ├── InsecureEncryptionAlgorithmBase.cs
│ │ │ │ ├── InsecureTemporaryFilesCreationBase.cs
│ │ │ │ ├── InsteadOfAnyBase.cs
│ │ │ │ ├── InvalidCastToInterfaceBase.cs
│ │ │ │ ├── JwtSignedBase.cs
│ │ │ │ ├── LineLengthBase.cs
│ │ │ │ ├── LinkedListPropertiesInsteadOfMethodsBase.cs
│ │ │ │ ├── LooseFilePermissionsBase.cs
│ │ │ │ ├── LooseFilePermissionsConfig.cs
│ │ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeBase.cs
│ │ │ │ ├── MarkAssemblyWithAttributeBase.cs
│ │ │ │ ├── MarkAssemblyWithClsCompliantAttributeBase.cs
│ │ │ │ ├── MarkAssemblyWithComVisibleAttributeBase.cs
│ │ │ │ ├── MarkWindowsFormsMainWithStaThreadBase.cs
│ │ │ │ ├── MethodOverloadsShouldBeGroupedBase.cs
│ │ │ │ ├── MethodParameterUnusedBase.cs
│ │ │ │ ├── MethodsShouldNotHaveIdenticalImplementationsBase.cs
│ │ │ │ ├── MethodsShouldNotHaveTooManyLinesBase.cs
│ │ │ │ ├── MultipleVariableDeclarationBase.cs
│ │ │ │ ├── MultipleVariableDeclarationCodeFixBase.cs
│ │ │ │ ├── NameOfShouldBeUsedBase.cs
│ │ │ │ ├── NoExceptionsInFinallyBase.cs
│ │ │ │ ├── NonAsyncTaskShouldNotReturnNullBase.cs
│ │ │ │ ├── ObsoleteAttributesBase.cs
│ │ │ │ ├── OptionalParameterBase.cs
│ │ │ │ ├── OptionalParameterNotPassedToBaseCallBase.cs
│ │ │ │ ├── ParameterAssignedToBase.cs
│ │ │ │ ├── ParameterNameMatchesOriginalBase.cs
│ │ │ │ ├── ParametersCorrectOrderBase.cs
│ │ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttributeBase.cs
│ │ │ │ ├── PreferGuidEmptyBase.cs
│ │ │ │ ├── PropertiesAccessCorrectFieldBase.cs
│ │ │ │ ├── PropertyGetterWithThrowBase.cs
│ │ │ │ ├── PropertyWriteOnlyBase.cs
│ │ │ │ ├── ProvideDeserializationMethodsForOptionalFieldsBase.cs
│ │ │ │ ├── PublicConstantFieldBase.cs
│ │ │ │ ├── PublicMethodWithMultidimensionalArrayBase.cs
│ │ │ │ ├── PureAttributeOnVoidMethodBase.cs
│ │ │ │ ├── RedundantNullCheckBase.cs
│ │ │ │ ├── RedundantParenthesesBase.cs
│ │ │ │ ├── RegularExpressions/
│ │ │ │ │ └── RegexMustHaveValidSyntaxBase.cs
│ │ │ │ ├── ReversedOperatorsBase.cs
│ │ │ │ ├── SecurityPInvokeMethodShouldNotBeCalledBase.cs
│ │ │ │ ├── SelfAssignmentBase.cs
│ │ │ │ ├── SetPropertiesInsteadOfMethodsBase.cs
│ │ │ │ ├── ShiftDynamicNotIntegerBase.cs
│ │ │ │ ├── ShouldImplementExportedInterfacesBase.cs
│ │ │ │ ├── SingleStatementPerLineBase.cs
│ │ │ │ ├── StringConcatenationInLoopBase.cs
│ │ │ │ ├── StringLiteralShouldNotBeDuplicatedBase.cs
│ │ │ │ ├── SwitchCasesMinimumThreeBase.cs
│ │ │ │ ├── SwitchSectionShouldNotHaveTooManyStatementsBase.cs
│ │ │ │ ├── SwitchShouldNotBeNestedBase.cs
│ │ │ │ ├── SwitchWithoutDefaultBase.cs
│ │ │ │ ├── TabCharacterBase.cs
│ │ │ │ ├── TestsShouldNotUseThreadSleepBase.cs
│ │ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalledBase.cs
│ │ │ │ ├── ThrowReservedExceptionsBase.cs
│ │ │ │ ├── ToStringShouldNotReturnNullBase.cs
│ │ │ │ ├── TooManyLabelsInSwitchBase.cs
│ │ │ │ ├── TooManyParametersBase.cs
│ │ │ │ ├── UnaryPrefixOperatorRepeatedBase.cs
│ │ │ │ ├── UnconditionalJumpStatementBase.cs
│ │ │ │ ├── UnnecessaryBitwiseOperationBase.cs
│ │ │ │ ├── UnnecessaryBitwiseOperationCodeFixBase.cs
│ │ │ │ ├── UnusedStringBuilderBase.cs
│ │ │ │ ├── UriShouldNotBeHardcodedBase.cs
│ │ │ │ ├── UseCharOverloadOfStringMethodsBase.cs
│ │ │ │ ├── UseDateTimeOffsetInsteadOfDateTimeBase.cs
│ │ │ │ ├── UseFindSystemTimeZoneByIdBase.cs
│ │ │ │ ├── UseIFormatProviderForParsingDateAndTimeBase.cs
│ │ │ │ ├── UseIndexingInsteadOfLinqMethodsBase.cs
│ │ │ │ ├── UseLambdaParameterInConcurrentDictionaryBase.cs
│ │ │ │ ├── UseShortCircuitingOperatorBase.cs
│ │ │ │ ├── UseShortCircuitingOperatorCodeFixBase.cs
│ │ │ │ ├── UseTestableTimeProviderBase.cs
│ │ │ │ ├── UseTrueForAllBase.cs
│ │ │ │ ├── UseUnixEpochBase.cs
│ │ │ │ ├── UseUnixEpochCodeFixBase.cs
│ │ │ │ ├── UseWhereBeforeOrderByBase.cs
│ │ │ │ ├── Utilities/
│ │ │ │ │ ├── AnalysisWarningAnalyzerBase.cs
│ │ │ │ │ ├── CopyPasteTokenAnalyzerBase.cs
│ │ │ │ │ ├── FileMetadataAnalyzerBase.cs
│ │ │ │ │ ├── LogAnalyzerBase.cs
│ │ │ │ │ ├── MethodDeclarationInfoComparer.cs
│ │ │ │ │ ├── MetricsAnalyzerBase.cs
│ │ │ │ │ ├── SymbolReferenceAnalyzerBase.cs
│ │ │ │ │ ├── TelemetryAnalyzerBase.cs
│ │ │ │ │ ├── TestMethodDeclarationsAnalyzerBase.cs
│ │ │ │ │ ├── TokenTypeAnalyzerBase.cs
│ │ │ │ │ └── UtilityAnalyzerBase.cs
│ │ │ │ ├── ValueTypeShouldImplementIEquatableBase.cs
│ │ │ │ ├── VariableUnusedBase.cs
│ │ │ │ ├── WcfNonVoidOneWayBase.cs
│ │ │ │ └── WeakSslTlsProtocolsBase.cs
│ │ │ ├── Semantics/
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── IMethodSymbolExtensions.cs
│ │ │ │ │ ├── INamedTypeSymbolExtensions.cs
│ │ │ │ │ ├── INamespaceSymbolExtensions.cs
│ │ │ │ │ ├── IParameterSymbolExtensions.cs
│ │ │ │ │ ├── IPropertySymbolExtensions.cs
│ │ │ │ │ ├── ISymbolExtensions.Roslyn.cs
│ │ │ │ │ ├── ISymbolExtensions.cs
│ │ │ │ │ ├── ITypeParameterSymbolExtensions.cs
│ │ │ │ │ ├── ITypeSymbolExtensions.cs
│ │ │ │ │ ├── KnownAssemblyExtensions.cs
│ │ │ │ │ ├── SemanticModelExtensions.cs
│ │ │ │ │ └── SymbolInfoExtensions.cs
│ │ │ │ ├── KnownAssembly.Predicates.cs
│ │ │ │ ├── KnownAssembly.cs
│ │ │ │ ├── KnownMethods.cs
│ │ │ │ ├── KnownType.Implementation.cs
│ │ │ │ ├── KnownType.cs
│ │ │ │ └── NetFrameworkVersionProvider.cs
│ │ │ ├── SonarAnalyzer.Core.csproj
│ │ │ ├── Syntax/
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── AccessibilityExtensions.cs
│ │ │ │ │ ├── AttributeDataExtensions.cs
│ │ │ │ │ ├── BinaryOperatorKindExtensions.cs
│ │ │ │ │ ├── ComparisonKindExtensions.cs
│ │ │ │ │ ├── CountComparisonResultExtensions.cs
│ │ │ │ │ ├── FileLinePositionSpanExtensions.cs
│ │ │ │ │ ├── LinePositionExtensions.cs
│ │ │ │ │ ├── LocationExtensions.cs
│ │ │ │ │ ├── SyntaxNodeExtensions.Roslyn.cs
│ │ │ │ │ ├── SyntaxNodeExtensions.cs
│ │ │ │ │ ├── SyntaxTokenExtensions.cs
│ │ │ │ │ ├── SyntaxTreeExtensions.cs
│ │ │ │ │ └── SyntaxTriviaExtensions.cs
│ │ │ │ └── Utilities/
│ │ │ │ ├── AccessorAccess.cs
│ │ │ │ ├── ComparisonKind.cs
│ │ │ │ ├── CountComparisionResult.cs
│ │ │ │ ├── EquivalenceChecker.cs
│ │ │ │ ├── ExpressionNumericConverterBase.cs
│ │ │ │ ├── GeneratedCodeRecognizer.cs
│ │ │ │ ├── IExpressionNumericConverter.cs
│ │ │ │ ├── MethodParameterLookupBase.cs
│ │ │ │ ├── RemovableDeclarationCollectorBase.cs
│ │ │ │ ├── StringInterpolationConstantValueResolver.cs
│ │ │ │ └── VisualIndentComparer.cs
│ │ │ ├── Trackers/
│ │ │ │ ├── ArgumentContext.cs
│ │ │ │ ├── ArgumentDescriptor.cs
│ │ │ │ ├── ArgumentTracker.cs
│ │ │ │ ├── AssignmentFinder.cs
│ │ │ │ ├── BaseContext.cs
│ │ │ │ ├── BaseTypeContext.cs
│ │ │ │ ├── BaseTypeTracker.cs
│ │ │ │ ├── BuilderPatternCondition.cs
│ │ │ │ ├── BuilderPatternDescriptor.cs
│ │ │ │ ├── ConstantValueFinder.cs
│ │ │ │ ├── ElementAccessContext.cs
│ │ │ │ ├── ElementAccessTracker.cs
│ │ │ │ ├── FieldAccessContext.cs
│ │ │ │ ├── FieldAccessTracker.cs
│ │ │ │ ├── InvocationContext.cs
│ │ │ │ ├── InvocationTracker.cs
│ │ │ │ ├── MemberDescriptor.cs
│ │ │ │ ├── MethodDeclarationContext.cs
│ │ │ │ ├── MethodDeclarationTracker.cs
│ │ │ │ ├── ObjectCreationContext.cs
│ │ │ │ ├── ObjectCreationTracker.cs
│ │ │ │ ├── PropertyAccessContext.cs
│ │ │ │ ├── PropertyAccessTracker.cs
│ │ │ │ ├── SyntaxBaseContext.cs
│ │ │ │ ├── SyntaxTrackerBase.cs
│ │ │ │ ├── TrackerBase.cs
│ │ │ │ └── TrackerInput.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.Shared/
│ │ │ ├── LoggingFrameworkMethods.cs
│ │ │ ├── SonarAnalyzer.Shared.projitems
│ │ │ ├── SonarAnalyzer.Shared.shproj
│ │ │ └── Syntax/
│ │ │ └── Extensions/
│ │ │ ├── MemberAccessExpressionSyntaxExtensionsShared.cs
│ │ │ ├── StatementSyntaxExtensionsShared.cs
│ │ │ ├── SyntaxNodeExtensionsShared.cs
│ │ │ └── SyntaxTokenExtensionsShared.cs
│ │ ├── SonarAnalyzer.ShimLayer/
│ │ │ ├── Common/
│ │ │ │ ├── ISyntaxWrapper.cs
│ │ │ │ └── SyntaxNodeTypes.cs
│ │ │ ├── PartialTypes/
│ │ │ │ ├── BaseNamespaceDeclarationSyntaxWrapper.cs
│ │ │ │ ├── BaseObjectCreationExpressionSyntaxWrapper.cs
│ │ │ │ └── CommonForEachStatementSyntaxWrapper.cs
│ │ │ ├── SonarAnalyzer.ShimLayer.csproj
│ │ │ ├── TemporaryLightUp/
│ │ │ │ ├── LightupHelpers.cs
│ │ │ │ ├── SeparatedSyntaxListWrapper`1.cs
│ │ │ │ ├── SyntaxWrapper`1.cs
│ │ │ │ └── TryGetValueAccessor`3.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.ShimLayer.CodeGeneration/
│ │ │ ├── GeneratorSyntaxExtensions.cs
│ │ │ ├── LICENSE
│ │ │ ├── OperationLightupGenerator.cs
│ │ │ ├── RoslynHashCode.cs
│ │ │ ├── SonarAnalyzer.ShimLayer.CodeGeneration.csproj
│ │ │ ├── SyntaxLightupGenerator.cs
│ │ │ ├── XElementExtensions.cs
│ │ │ ├── XmlSyntaxFactory.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.ShimLayer.Generator/
│ │ │ ├── Factory.cs
│ │ │ ├── Model/
│ │ │ │ ├── MemberDescriptor.cs
│ │ │ │ ├── ModelBuilder.cs
│ │ │ │ ├── StrategyModel.cs
│ │ │ │ ├── TypeDescriptor.cs
│ │ │ │ └── TypeLoader.cs
│ │ │ ├── ShimLayerGenerator.cs
│ │ │ ├── SonarAnalyzer.ShimLayer.Generator.csproj
│ │ │ ├── Strategies/
│ │ │ │ ├── IOperationStrategy.cs
│ │ │ │ ├── NewEnumStrategy.cs
│ │ │ │ ├── NoChangeStrategy.cs
│ │ │ │ ├── PartialEnumStrategy.cs
│ │ │ │ ├── SeparatedSyntaxListStrategy.cs
│ │ │ │ ├── SkipStrategy.cs
│ │ │ │ ├── Strategy.cs
│ │ │ │ ├── SyntaxNodeExtendStrategy.cs
│ │ │ │ └── SyntaxNodeWrapStrategy.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.ShimLayer.Lightup/
│ │ │ ├── AnalysisContext/
│ │ │ │ ├── CompilationStartAnalysisContextExtensions.cs
│ │ │ │ └── SymbolStartAnalysisContextWrapper.cs
│ │ │ ├── AnalyzerConfigOptionsProviderWrapper.cs
│ │ │ ├── AnalyzerConfigOptionsWrapper.cs
│ │ │ ├── AnalyzerOptionsExtensions.cs
│ │ │ ├── ArgumentSyntaxExtensions.cs
│ │ │ ├── BaseMethodDeclarationSyntaxExtensions.cs
│ │ │ ├── CSharp7.md
│ │ │ ├── CSharp71.md
│ │ │ ├── CSharp72.md
│ │ │ ├── CSharp73.md
│ │ │ ├── CSharp8.md
│ │ │ ├── IFieldSymbolExtensions.cs
│ │ │ ├── INamedTypeSymbolExtensions.cs
│ │ │ ├── ISyntaxWrapper`1.cs
│ │ │ ├── ITypeParameterSymbolExtensions.cs
│ │ │ ├── ITypeSymbolExtensions.cs
│ │ │ ├── LICENSE
│ │ │ ├── LightupHelpers.cs
│ │ │ ├── OperationInterfaces.xml
│ │ │ ├── README.md
│ │ │ ├── SeparatedSyntaxListWrapper`1.cs
│ │ │ ├── Sonar/
│ │ │ │ ├── CaptureId.cs
│ │ │ │ ├── CompilationOptionsWrapper.cs
│ │ │ │ ├── IEventSymbolExtensions.cs
│ │ │ │ ├── IMethodSymbolExtensions.cs
│ │ │ │ ├── INamedTypeSymbolExtensions.Sonar.cs
│ │ │ │ ├── IOperationWrapper.cs
│ │ │ │ ├── IOperationWrapperSonar.cs
│ │ │ │ ├── IPropertySymbolExtensions.cs
│ │ │ │ ├── ISymbolNullableExtensions.cs
│ │ │ │ ├── ITypeSymbolExtension.cs
│ │ │ │ ├── NullabilityInfo.cs
│ │ │ │ ├── SyntaxTreeOptionsProviderWrapper.cs
│ │ │ │ └── TypeInfoExtensions.cs
│ │ │ ├── SonarAnalyzer.ShimLayer.Lightup.csproj
│ │ │ ├── StatementSyntaxExtensions.cs
│ │ │ ├── Syntax.xml
│ │ │ ├── SyntaxFactoryEx.cs
│ │ │ ├── SyntaxFactsEx.cs
│ │ │ ├── SyntaxWrapper`1.cs
│ │ │ ├── TryGetValueAccessor`3.cs
│ │ │ ├── WrapperHelper.cs
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.SourceGenerators/
│ │ │ ├── RuleCatalogGenerator.cs
│ │ │ ├── SonarAnalyzer.SourceGenerators.csproj
│ │ │ └── packages.lock.json
│ │ ├── SonarAnalyzer.VisualBasic/
│ │ │ ├── Metrics/
│ │ │ │ ├── VisualBasicCognitiveComplexityMetric.cs
│ │ │ │ ├── VisualBasicExecutableLinesMetric.cs
│ │ │ │ └── VisualBasicMetrics.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── Rules/
│ │ │ │ ├── AllBranchesShouldNotHaveSameImplementation.cs
│ │ │ │ ├── AlwaysSetDateTimeKind.cs
│ │ │ │ ├── ArrayCreationLongSyntax.cs
│ │ │ │ ├── ArrayCreationLongSyntaxCodeFix.cs
│ │ │ │ ├── ArrayDesignatorOnVariable.cs
│ │ │ │ ├── ArrayDesignatorOnVariableCodeFix.cs
│ │ │ │ ├── ArrayInitializationMultipleStatements.cs
│ │ │ │ ├── ArrayPassedAsParams.cs
│ │ │ │ ├── AspNet/
│ │ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.cs
│ │ │ │ │ └── RouteTemplateShouldNotStartWithSlash.cs
│ │ │ │ ├── AvoidDateTimeNowForBenchmarking.cs
│ │ │ │ ├── AvoidUnsealedAttributes.cs
│ │ │ │ ├── BeginInvokePairedWithEndInvoke.cs
│ │ │ │ ├── BinaryOperationWithIdenticalExpressions.cs
│ │ │ │ ├── BooleanCheckInverted.cs
│ │ │ │ ├── BooleanLiteralUnnecessary.cs
│ │ │ │ ├── BypassingAccessibility.cs
│ │ │ │ ├── CatchRethrow.cs
│ │ │ │ ├── CertificateValidationCheck.cs
│ │ │ │ ├── CheckFileLicense.cs
│ │ │ │ ├── ClassNamedException.cs
│ │ │ │ ├── ClassNotInstantiatable.cs
│ │ │ │ ├── ClassShouldNotBeEmpty.cs
│ │ │ │ ├── CognitiveComplexity.cs
│ │ │ │ ├── CollectionEmptinessChecking.cs
│ │ │ │ ├── CommentKeyword.cs
│ │ │ │ ├── CommentLineEnd.cs
│ │ │ │ ├── CommentsShouldNotBeEmpty.cs
│ │ │ │ ├── ConditionalStructureSameCondition.cs
│ │ │ │ ├── ConditionalStructureSameImplementation.cs
│ │ │ │ ├── ConstructorArgumentValueShouldExist.cs
│ │ │ │ ├── DangerousGetHandleShouldNotBeCalled.cs
│ │ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.cs
│ │ │ │ ├── DateTimeFormatShouldNotBeHardcoded.cs
│ │ │ │ ├── DebuggerDisplayUsesExistingMembers.cs
│ │ │ │ ├── DeclareTypesInNamespaces.cs
│ │ │ │ ├── DoNotCheckZeroSizeCollection.cs
│ │ │ │ ├── DoNotHardcodeCredentials.cs
│ │ │ │ ├── DoNotHardcodeSecrets.cs
│ │ │ │ ├── DoNotInstantiateSharedClasses.cs
│ │ │ │ ├── DoNotLockOnSharedResource.cs
│ │ │ │ ├── DoNotLockWeakIdentityObjects.cs
│ │ │ │ ├── DoNotNestTernaryOperators.cs
│ │ │ │ ├── DoNotOverwriteCollectionElements.cs
│ │ │ │ ├── DoNotThrowFromDestructors.cs
│ │ │ │ ├── DoNotUseByVal.cs
│ │ │ │ ├── DoNotUseByValCodeFix.cs
│ │ │ │ ├── DoNotUseDateTimeNow.cs
│ │ │ │ ├── DoNotUseIIf.cs
│ │ │ │ ├── DoNotUseIIfCodeFix.cs
│ │ │ │ ├── EmptyMethod.cs
│ │ │ │ ├── EmptyNestedBlock.cs
│ │ │ │ ├── EncryptionAlgorithmsShouldBeSecure.cs
│ │ │ │ ├── EndStatementUsage.cs
│ │ │ │ ├── EnumNameHasEnumSuffix.cs
│ │ │ │ ├── EventNameContainsBeforeOrAfter.cs
│ │ │ │ ├── ExceptionsShouldBePublic.cs
│ │ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.cs
│ │ │ │ ├── ExitStatementUsage.cs
│ │ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.cs
│ │ │ │ ├── ExpressionComplexity.cs
│ │ │ │ ├── ExtensionMethodShouldNotExtendObject.cs
│ │ │ │ ├── FieldShadowsParentField.cs
│ │ │ │ ├── FieldShouldNotBePublic.cs
│ │ │ │ ├── FileLines.cs
│ │ │ │ ├── FindInsteadOfFirstOrDefault.cs
│ │ │ │ ├── FlagsEnumWithoutInitializer.cs
│ │ │ │ ├── FlagsEnumZeroMember.cs
│ │ │ │ ├── FunctionComplexity.cs
│ │ │ │ ├── FunctionNestingDepth.cs
│ │ │ │ ├── GenericInheritanceShouldNotBeRecursive.cs
│ │ │ │ ├── GotoStatement.cs
│ │ │ │ ├── Hotspots/
│ │ │ │ │ ├── CommandPath.cs
│ │ │ │ │ ├── ConfiguringLoggers.cs
│ │ │ │ │ ├── CreatingHashAlgorithms.cs
│ │ │ │ │ ├── DeliveringDebugFeaturesInProduction.cs
│ │ │ │ │ ├── DisablingRequestValidation.cs
│ │ │ │ │ ├── ExecutingSqlQueries.cs
│ │ │ │ │ ├── ExpandingArchives.cs
│ │ │ │ │ ├── HardcodedIpAddress.cs
│ │ │ │ │ ├── PubliclyWritableDirectories.cs
│ │ │ │ │ ├── RequestsWithExcessiveLength.cs
│ │ │ │ │ ├── SpecifyTimeoutOnRegex.cs
│ │ │ │ │ └── UsingNonstandardCryptography.cs
│ │ │ │ ├── IfChainWithoutElse.cs
│ │ │ │ ├── IfCollapsible.cs
│ │ │ │ ├── ImplementSerializationMethodsCorrectly.cs
│ │ │ │ ├── IndexOfCheckAgainstZero.cs
│ │ │ │ ├── IndexedPropertyWithMultipleParameters.cs
│ │ │ │ ├── InsecureEncryptionAlgorithm.cs
│ │ │ │ ├── InsecureTemporaryFilesCreation.cs
│ │ │ │ ├── InsteadOfAny.cs
│ │ │ │ ├── InvalidCastToInterface.cs
│ │ │ │ ├── JwtSigned.cs
│ │ │ │ ├── LineContinuation.cs
│ │ │ │ ├── LineLength.cs
│ │ │ │ ├── LinkedListPropertiesInsteadOfMethods.cs
│ │ │ │ ├── LooseFilePermissions.cs
│ │ │ │ ├── MarkAssemblyWithAssemblyVersionAttribute.cs
│ │ │ │ ├── MarkAssemblyWithClsCompliantAttribute.cs
│ │ │ │ ├── MarkAssemblyWithComVisibleAttribute.cs
│ │ │ │ ├── MarkWindowsFormsMainWithStaThread.cs
│ │ │ │ ├── MethodOverloadsShouldBeGrouped.cs
│ │ │ │ ├── MethodParameterUnused.cs
│ │ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.cs
│ │ │ │ ├── MethodsShouldNotHaveTooManyLines.cs
│ │ │ │ ├── MultipleVariableDeclaration.cs
│ │ │ │ ├── MultipleVariableDeclarationCodeFix.cs
│ │ │ │ ├── NameOfShouldBeUsed.cs
│ │ │ │ ├── Naming/
│ │ │ │ │ ├── ClassName.cs
│ │ │ │ │ ├── EnumNameShouldFollowRegex.cs
│ │ │ │ │ ├── EnumerationValueName.cs
│ │ │ │ │ ├── EventHandlerName.cs
│ │ │ │ │ ├── EventName.cs
│ │ │ │ │ ├── FieldNameChecker.cs
│ │ │ │ │ ├── FunctionName.cs
│ │ │ │ │ ├── InterfaceName.cs
│ │ │ │ │ ├── LocalVariableName.cs
│ │ │ │ │ ├── NamespaceName.cs
│ │ │ │ │ ├── ParameterName.cs
│ │ │ │ │ ├── PrivateConstantFieldName.cs
│ │ │ │ │ ├── PrivateFieldName.cs
│ │ │ │ │ ├── PrivateSharedReadonlyFieldName.cs
│ │ │ │ │ ├── PropertyName.cs
│ │ │ │ │ ├── PublicConstantFieldName.cs
│ │ │ │ │ ├── PublicFieldName.cs
│ │ │ │ │ ├── PublicSharedReadonlyFieldName.cs
│ │ │ │ │ └── TypeParameterName.cs
│ │ │ │ ├── NegatedIsExpression.cs
│ │ │ │ ├── NegatedIsExpressionCodeFix.cs
│ │ │ │ ├── NoExceptionsInFinally.cs
│ │ │ │ ├── NonAsyncTaskShouldNotReturnNull.cs
│ │ │ │ ├── ObsoleteAttributes.cs
│ │ │ │ ├── OnErrorStatement.cs
│ │ │ │ ├── OptionExplicitOn.cs
│ │ │ │ ├── OptionStrictOn.cs
│ │ │ │ ├── OptionalParameter.cs
│ │ │ │ ├── OptionalParameterNotPassedToBaseCall.cs
│ │ │ │ ├── ParameterAssignedTo.cs
│ │ │ │ ├── ParameterNameMatchesOriginal.cs
│ │ │ │ ├── ParametersCorrectOrder.cs
│ │ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttribute.cs
│ │ │ │ ├── PreferGuidEmpty.cs
│ │ │ │ ├── PropertiesAccessCorrectField.cs
│ │ │ │ ├── PropertyGetterWithThrow.cs
│ │ │ │ ├── PropertyWithArrayType.cs
│ │ │ │ ├── PropertyWriteOnly.cs
│ │ │ │ ├── ProvideDeserializationMethodsForOptionalFields.cs
│ │ │ │ ├── PublicConstantField.cs
│ │ │ │ ├── PublicMethodWithMultidimensionalArray.cs
│ │ │ │ ├── PureAttributeOnVoidMethod.cs
│ │ │ │ ├── RedundantExitSelect.cs
│ │ │ │ ├── RedundantNullCheck.cs
│ │ │ │ ├── RedundantParentheses.cs
│ │ │ │ ├── RegularExpressions/
│ │ │ │ │ └── RegexMustHaveValidSyntax.cs
│ │ │ │ ├── ReversedOperators.cs
│ │ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.cs
│ │ │ │ ├── SelfAssignment.cs
│ │ │ │ ├── SetPropertiesInsteadOfMethods.cs
│ │ │ │ ├── ShiftDynamicNotInteger.cs
│ │ │ │ ├── ShouldImplementExportedInterfaces.cs
│ │ │ │ ├── SimpleDoLoop.cs
│ │ │ │ ├── SingleStatementPerLine.cs
│ │ │ │ ├── StringConcatenationInLoop.cs
│ │ │ │ ├── StringConcatenationWithPlus.cs
│ │ │ │ ├── StringConcatenationWithPlusCodeFix.cs
│ │ │ │ ├── StringLiteralShouldNotBeDuplicated.cs
│ │ │ │ ├── SwitchCasesMinimumThree.cs
│ │ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements.cs
│ │ │ │ ├── SwitchShouldNotBeNested.cs
│ │ │ │ ├── SwitchWithoutDefault.cs
│ │ │ │ ├── TabCharacter.cs
│ │ │ │ ├── TestsShouldNotUseThreadSleep.cs
│ │ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalled.cs
│ │ │ │ ├── ThrowReservedExceptions.cs
│ │ │ │ ├── ToStringShouldNotReturnNull.cs
│ │ │ │ ├── TooManyLabelsInSwitch.cs
│ │ │ │ ├── TooManyParameters.cs
│ │ │ │ ├── UnaryPrefixOperatorRepeated.cs
│ │ │ │ ├── UnconditionalJumpStatement.cs
│ │ │ │ ├── UnnecessaryBitwiseOperation.cs
│ │ │ │ ├── UnnecessaryBitwiseOperationCodeFix.cs
│ │ │ │ ├── UnsignedTypesUsage.cs
│ │ │ │ ├── UnusedStringBuilder.cs
│ │ │ │ ├── UriShouldNotBeHardcoded.cs
│ │ │ │ ├── UseCharOverloadOfStringMethods.cs
│ │ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.cs
│ │ │ │ ├── UseFindSystemTimeZoneById.cs
│ │ │ │ ├── UseIFormatProviderForParsingDateAndTime.cs
│ │ │ │ ├── UseIndexingInsteadOfLinqMethods.cs
│ │ │ │ ├── UseLambdaParameterInConcurrentDictionary.cs
│ │ │ │ ├── UseReturnStatement.cs
│ │ │ │ ├── UseShortCircuitingOperator.cs
│ │ │ │ ├── UseShortCircuitingOperatorCodeFix.cs
│ │ │ │ ├── UseTestableTimeProvider.cs
│ │ │ │ ├── UseTrueForAll.cs
│ │ │ │ ├── UseUnixEpoch.cs
│ │ │ │ ├── UseUnixEpochCodeFix.cs
│ │ │ │ ├── UseWhereBeforeOrderBy.cs
│ │ │ │ ├── UseWithStatement.cs
│ │ │ │ ├── Utilities/
│ │ │ │ │ ├── AnalysisWarningAnalyzer.cs
│ │ │ │ │ ├── CopyPasteTokenAnalyzer.cs
│ │ │ │ │ ├── FileMetadataAnalyzer.cs
│ │ │ │ │ ├── LogAnalyzer.cs
│ │ │ │ │ ├── MetricsAnalyzer.cs
│ │ │ │ │ ├── SymbolReferenceAnalyzer.cs
│ │ │ │ │ ├── TelemetryAnalyzer.cs
│ │ │ │ │ ├── TestMethodDeclarationsAnalyzer.cs
│ │ │ │ │ └── TokenTypeAnalyzer.cs
│ │ │ │ ├── ValueTypeShouldImplementIEquatable.cs
│ │ │ │ ├── VariableUnused.cs
│ │ │ │ ├── WcfNonVoidOneWay.cs
│ │ │ │ └── WeakSslTlsProtocols.cs
│ │ │ ├── SonarAnalyzer.VisualBasic.csproj
│ │ │ ├── packages.lock.json
│ │ │ └── sonarpedia.json
│ │ └── SonarAnalyzer.VisualBasic.Core/
│ │ ├── Common/
│ │ │ └── DescriptorFactory.cs
│ │ ├── Extensions/
│ │ │ ├── ICompilationReportExtensions.cs
│ │ │ ├── ISymbolExtensions.cs
│ │ │ ├── SonarAnalysisContextExtensions.cs
│ │ │ ├── SonarCompilationStartAnalysisContextExtensions.cs
│ │ │ ├── SonarParametrizedAnalysisContextExtensions.cs
│ │ │ └── SonarSyntaxNodeReportingContextExtensions.cs
│ │ ├── Facade/
│ │ │ ├── Implementation/
│ │ │ │ ├── VisualBasicSyntaxFacade.cs
│ │ │ │ ├── VisualBasicSyntaxKindFacade.cs
│ │ │ │ └── VisualBasicTrackerFacade.cs
│ │ │ └── VisualBasicFacade.cs
│ │ ├── Properties/
│ │ │ └── AssemblyInfo.cs
│ │ ├── SonarAnalyzer.VisualBasic.Core.csproj
│ │ ├── Syntax/
│ │ │ ├── Extensions/
│ │ │ │ ├── ArgumentListSyntaxExtensions.cs
│ │ │ │ ├── ArgumentSyntaxExtensions.cs
│ │ │ │ ├── AttributeSyntaxExtensions.cs
│ │ │ │ ├── ExpressionSyntaxExtensions.Roslyn.cs
│ │ │ │ ├── ExpressionSyntaxExtensions.cs
│ │ │ │ ├── InterpolatedStringExpressionSyntaxExtensions.cs
│ │ │ │ ├── InvocationExpressionSyntaxExtensions.cs
│ │ │ │ ├── MemberAccessExpressionSyntaxExtensions.cs
│ │ │ │ ├── MethodBlockBaseSyntaxExtensions.cs
│ │ │ │ ├── MethodBlockSyntaxExtensions.cs
│ │ │ │ ├── ObjectCreationExpressionSyntaxExtensions.cs
│ │ │ │ ├── StatementSyntaxExtensions.cs
│ │ │ │ ├── SyntaxNodeExtensions.Roslyn.cs
│ │ │ │ ├── SyntaxNodeExtensionsVisualBasic.cs
│ │ │ │ ├── SyntaxTokenExtensions.cs
│ │ │ │ ├── SyntaxTriviaExtensions.cs
│ │ │ │ └── VisualBasicCompilationExtensions.cs
│ │ │ └── Utilities/
│ │ │ ├── SafeVisualBasicSyntaxWalker.cs
│ │ │ ├── VisualBasicAttributeParameterLookup.cs
│ │ │ ├── VisualBasicEquivalenceChecker.cs
│ │ │ ├── VisualBasicExpressionNumericConverter.cs
│ │ │ ├── VisualBasicGeneratedCodeRecognizer.cs
│ │ │ ├── VisualBasicMethodParameterLookup.cs
│ │ │ ├── VisualBasicRemovableDeclarationCollector.cs
│ │ │ ├── VisualBasicStringInterpolationConstantValueResolver.cs
│ │ │ └── VisualBasicSyntaxClassifier.cs
│ │ ├── Trackers/
│ │ │ ├── VisualBasicArgumentTracker.cs
│ │ │ ├── VisualBasicAssignmentFinder.cs
│ │ │ ├── VisualBasicBaseTypeTracker.cs
│ │ │ ├── VisualBasicBuilderPatternCondition.cs
│ │ │ ├── VisualBasicConstantValueFinder.cs
│ │ │ ├── VisualBasicElementAccessTracker.cs
│ │ │ ├── VisualBasicFieldAccessTracker.cs
│ │ │ ├── VisualBasicInvocationTracker.cs
│ │ │ ├── VisualBasicMethodDeclarationTracker.cs
│ │ │ ├── VisualBasicObjectCreationTracker.cs
│ │ │ └── VisualBasicPropertyAccessTracker.cs
│ │ └── packages.lock.json
│ ├── stylecop.json
│ └── tests/
│ ├── Directory.Build.targets
│ ├── FrameworkMocks/
│ │ └── src/
│ │ ├── Directory.Build.targets
│ │ ├── Mocks.sln
│ │ ├── Mscorlib3.5Mock/
│ │ │ ├── Debugger.cs
│ │ │ ├── mscorlib.csproj
│ │ │ └── packages.lock.json
│ │ ├── Mscorlib4.0Mock/
│ │ │ ├── Debugger.cs
│ │ │ ├── mscorlib.csproj
│ │ │ └── packages.lock.json
│ │ ├── Mscorlib4.0MockWithIo/
│ │ │ ├── Debugger.cs
│ │ │ ├── UnmanagedMemoryStream.cs
│ │ │ ├── mscorlib.csproj
│ │ │ └── packages.lock.json
│ │ └── Mscorlib4.8Mock/
│ │ ├── Debugger.cs
│ │ ├── UnmanagedMemoryStream.cs
│ │ ├── mscorlib.csproj
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.CSharp.Core.Test/
│ │ ├── Extensions/
│ │ │ ├── BaseArgumentListSyntaxExtensionsTest.cs
│ │ │ ├── BaseMethodDeclarationSyntaxExtensionsTest.cs
│ │ │ ├── ISymbolExtensionsTest.cs
│ │ │ ├── TypeDeclarationSyntaxExtensionsTest.cs
│ │ │ └── TypeSyntaxExtensionsTest.cs
│ │ ├── Facade/
│ │ │ ├── CSharpFacadeTest.cs
│ │ │ └── Implementation/
│ │ │ └── CSharpSyntaxFacadeTest.cs
│ │ ├── RegularExpressions/
│ │ │ └── MessageTemplateParserTest.cs
│ │ ├── SonarAnalyzer.CSharp.Core.Test.csproj
│ │ ├── Syntax/
│ │ │ ├── Extensions/
│ │ │ │ ├── ArgumentSyntaxExtensionsTest.cs
│ │ │ │ ├── AssignmentExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── AttributeDataExtensionsTest.cs
│ │ │ │ ├── AttributeSyntaxExtensionsTest.cs
│ │ │ │ ├── AwaitExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── BlockSyntaxExtensionsTest.cs
│ │ │ │ ├── DiagnosticDescriptorExtensionsTest.cs
│ │ │ │ ├── ExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── ITupleOperationWrapperExtensionsTest.cs
│ │ │ │ ├── InterpolatedStringExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── InvocationExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── ObjectCreationExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── PatternSyntaxWrapperExtensionsTest.cs
│ │ │ │ ├── PropertyDeclarationSyntaxExtensionsTest.cs
│ │ │ │ ├── StatementSyntaxExtensionsTest.cs
│ │ │ │ ├── SyntaxNodeExtensionsCSharpTest.cs
│ │ │ │ ├── SyntaxTokenExtensionsTest.cs
│ │ │ │ ├── SyntaxTreeExtensionsTest.cs
│ │ │ │ ├── TupleExpressionSyntaxExtensionsTest.cs
│ │ │ │ ├── TypeExtensionsTest.cs
│ │ │ │ └── VariableDesignationSyntaxWrapperTest.cs
│ │ │ └── Utilities/
│ │ │ └── SafeCSharpSyntaxWalkerTest.cs
│ │ ├── Trackers/
│ │ │ └── FieldAccessTrackerTest.cs
│ │ ├── Wrappers/
│ │ │ ├── MethodDeclarationFactoryTest.cs
│ │ │ └── ObjectCreationFactoryTest.cs
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.CSharp.Styling.Test/
│ │ ├── Common/
│ │ │ ├── StylingRuleTest.cs
│ │ │ └── StylingVerifierBuilder.cs
│ │ ├── Rules/
│ │ │ ├── AllArgumentsOnSameLineTest.cs
│ │ │ ├── AllParametersOnSameColumnTest.cs
│ │ │ ├── AllParametersOnSameLineTest.cs
│ │ │ ├── ArrowLocationTest.cs
│ │ │ ├── AvoidArrangeActAssertCommentTest.cs
│ │ │ ├── AvoidDeconstructionTest.cs
│ │ │ ├── AvoidGetTest.cs
│ │ │ ├── AvoidListForEachTest.cs
│ │ │ ├── AvoidNullableTest.cs
│ │ │ ├── AvoidPrimaryConstructorTest.cs
│ │ │ ├── AvoidUnusedInterpolationTest.cs
│ │ │ ├── AvoidValueTupleTest.cs
│ │ │ ├── AvoidVarPatternTest.cs
│ │ │ ├── FieldOrderingTest.cs
│ │ │ ├── FileScopeNamespaceTest.cs
│ │ │ ├── HelperInTypeNameTest.cs
│ │ │ ├── IndentArgumentTest.cs
│ │ │ ├── IndentInvocationTest.cs
│ │ │ ├── IndentOperatorTest.cs
│ │ │ ├── IndentRawStringTest.cs
│ │ │ ├── IndentTernaryTest.cs
│ │ │ ├── InitializerLineTest.cs
│ │ │ ├── LambdaParameterNameTest.cs
│ │ │ ├── LocalFunctionLocationTest.cs
│ │ │ ├── MemberAccessLineTest.cs
│ │ │ ├── MemberVisibilityOrderingTest.cs
│ │ │ ├── MethodExpressionBodyLineTest.cs
│ │ │ ├── MoveMethodToDedicatedExtensionClassTest.cs
│ │ │ ├── NamespaceNameTest.cs
│ │ │ ├── NullPatternMatchingTest.cs
│ │ │ ├── OperatorLocationTest.cs
│ │ │ ├── PropertyOrderingTest.cs
│ │ │ ├── ProtectedFieldsCaseTest.cs
│ │ │ ├── SeparateDeclarationsTest.cs
│ │ │ ├── TernaryLineTest.cs
│ │ │ ├── TypeMemberOrderingTest.cs
│ │ │ ├── UseDifferentMemberTest.cs
│ │ │ ├── UseFieldTest.cs
│ │ │ ├── UseInnermostRegistrationContextTest.cs
│ │ │ ├── UseLinqExtensionsTest.cs
│ │ │ ├── UseNullInsteadOfDefaultTest.cs
│ │ │ ├── UsePositiveLogicTest.cs
│ │ │ ├── UseRawStringTest.cs
│ │ │ ├── UseRegexSafeIsMatchTest.cs
│ │ │ ├── UseShortNameTest.cs
│ │ │ └── UseVarTest.cs
│ │ ├── SonarAnalyzer.CSharp.Styling.Test.csproj
│ │ ├── TestCases/
│ │ │ ├── AllArgumentsOnSameLine.cs
│ │ │ ├── AllParametersOnSameColumn.cs
│ │ │ ├── AllParametersOnSameLine.cs
│ │ │ ├── ArrowLocation.cs
│ │ │ ├── AvoidArrangeActAssertComment.cs
│ │ │ ├── AvoidDeconstruction.TopLevelStatements.cs
│ │ │ ├── AvoidDeconstruction.cs
│ │ │ ├── AvoidGet.cs
│ │ │ ├── AvoidListForEach.cs
│ │ │ ├── AvoidNullable.cs
│ │ │ ├── AvoidPrimaryConstructor.cs
│ │ │ ├── AvoidUnusedInterpolation.cs
│ │ │ ├── AvoidValueTuple.cs
│ │ │ ├── AvoidVarPattern.cs
│ │ │ ├── FieldOrdering.cs
│ │ │ ├── FileScopeNamespace.Compliant.cs
│ │ │ ├── FileScopeNamespace.cs
│ │ │ ├── HelperInTypeName.cs
│ │ │ ├── IndentArgument.cs
│ │ │ ├── IndentInvocation.cs
│ │ │ ├── IndentOperator.cs
│ │ │ ├── IndentRawString.cs
│ │ │ ├── IndentTernary.cs
│ │ │ ├── InitializerLine.cs
│ │ │ ├── LambdaParameterName.cs
│ │ │ ├── LocalFunctionLocation.TopLevelStatements.cs
│ │ │ ├── LocalFunctionLocation.cs
│ │ │ ├── MemberAccessLine.cs
│ │ │ ├── MemberVisibilityOrdering.cs
│ │ │ ├── MethodExpressionBodyLine.cs
│ │ │ ├── MoveMethodToDedicatedExtensionClass.cs
│ │ │ ├── NullPatternMatching.cs
│ │ │ ├── OperatorLocation.cs
│ │ │ ├── PropertyOrdering.cs
│ │ │ ├── ProtectedFieldsCase.cs
│ │ │ ├── SeparateDeclarations.cs
│ │ │ ├── TernaryLine.cs
│ │ │ ├── TypeMemberOrdering.cs
│ │ │ ├── UseDifferentMember.cs
│ │ │ ├── UseField.cs
│ │ │ ├── UseInnermostRegistrationContext.cs
│ │ │ ├── UseLinqExtensions.cs
│ │ │ ├── UseNullInsteadOfDefault.cs
│ │ │ ├── UsePositiveLogic.cs
│ │ │ ├── UseRawString.cs
│ │ │ ├── UseRegexSafeIsMatch.cs
│ │ │ ├── UseShortName.cs
│ │ │ └── UseVar.cs
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.Core.Test/
│ │ ├── AnalysisContext/
│ │ │ ├── IssueReporterTest.cs
│ │ │ ├── SonarCodeBlockReportingContextTest.cs
│ │ │ ├── SonarCodeBlockStartAnalysisContextTest.cs
│ │ │ ├── SonarCodeFixContextTest.cs
│ │ │ ├── SonarCompilationReportingContextTest.cs
│ │ │ ├── SonarCompilationStartAnalysisContextTest.cs
│ │ │ ├── SonarSemanticModelReportingContextTest.cs
│ │ │ ├── SonarSymbolReportingContextTest.cs
│ │ │ ├── SonarSyntaxNodeReportingContextTest.cs
│ │ │ └── SonarSyntaxTreeReportingContextTest.cs
│ │ ├── Analyzers/
│ │ │ └── DiagnosticDescriptorFactoryTest.cs
│ │ ├── Common/
│ │ │ ├── AnalyzerAdditionalFileTest.cs
│ │ │ ├── AnalyzerConfigurationTest.cs
│ │ │ ├── AnalyzerLanguageTest.cs
│ │ │ ├── BidirectionalDictionaryTest.cs
│ │ │ ├── DisjointSetsTest.cs
│ │ │ ├── FixAllProviders/
│ │ │ │ └── DocumentBasedFixAllProviderTest.cs
│ │ │ ├── HashCodeTest.cs
│ │ │ ├── MultiValueDictionaryTest.cs
│ │ │ ├── NaturalLanguageDetectorTest.cs
│ │ │ ├── RuleLoaderTest.cs
│ │ │ ├── ShannonEntropyTest.cs
│ │ │ └── UnexpectedLanguageExceptionTest.cs
│ │ ├── Configuration/
│ │ │ ├── AnalysisConfigReaderTest.cs
│ │ │ ├── FilesToAnalyzeProviderTest.cs
│ │ │ ├── ProjectConfigReaderTest.cs
│ │ │ ├── ProjectTypeCacheTest.cs
│ │ │ ├── SonarLintXmlReaderTest.cs
│ │ │ └── SonarLintXmlTest.cs
│ │ ├── Extensions/
│ │ │ ├── BasicBlockExtensionsTest.cs
│ │ │ ├── CompilationExtensionsTest.cs
│ │ │ ├── ControlFlowGraphExtensionsTest.cs
│ │ │ ├── DictionaryExtensionsTest.cs
│ │ │ ├── IEnumerableExtensionsTest.cs
│ │ │ ├── IXmlLineInfoExtensionsTest.cs
│ │ │ ├── RegexExtensionsTest.cs
│ │ │ └── XAttributeExtensionsTest.cs
│ │ ├── Json/
│ │ │ ├── JsonNodeTest.cs
│ │ │ ├── JsonSerializerTest.cs
│ │ │ ├── JsonWalkerTest.cs
│ │ │ ├── LexicalAnalyzerTest.cs
│ │ │ └── SyntaxAnalyzerTest.cs
│ │ ├── RegularExpressions/
│ │ │ ├── RegexContextTest.cs
│ │ │ └── WildcardPatternMatcherTest.cs
│ │ ├── Semantics/
│ │ │ ├── Extensions/
│ │ │ │ ├── IMethodSymbolExtensionsTest.cs
│ │ │ │ ├── INamedTypeSymbolExtensionsTest.cs
│ │ │ │ ├── INamespaceSymbolExtensionsTest.cs
│ │ │ │ ├── IParameterSymbolExtensionsTest.cs
│ │ │ │ ├── IPropertySymbolExtensionsTest.cs
│ │ │ │ ├── ISymbolExtensionsTest.cs
│ │ │ │ └── ITypeSymbolExtensionsTest.cs
│ │ │ ├── KnownAssemblyTest.cs
│ │ │ ├── KnownMethodsTest.cs
│ │ │ ├── KnownTypeTest.cs
│ │ │ └── NetFrameworkVersionProviderTest.cs
│ │ ├── SonarAnalyzer.Core.Test.csproj
│ │ ├── Syntax/
│ │ │ ├── Extensions/
│ │ │ │ ├── ComparisonKindExtensionsTest.cs
│ │ │ │ ├── CountComparisonResultExtensionsTest.cs
│ │ │ │ ├── LocationExtensionsTest.cs
│ │ │ │ ├── SyntaxNodeExtensionsTest.cs
│ │ │ │ └── SyntaxTokenExtensionsTest.cs
│ │ │ └── Utilities/
│ │ │ ├── GeneratedCodeRecognizerTest.cs
│ │ │ └── VisualIndentComparerTest.cs
│ │ ├── TestResources/
│ │ │ ├── FilesToAnalyze/
│ │ │ │ ├── EmptyFilesToAnalyze.txt
│ │ │ │ ├── FilesToAnalyze.txt
│ │ │ │ └── FilesToAnalyzeWithInvalidValues.txt
│ │ │ ├── SonarLintXml/
│ │ │ │ ├── All_properties_cs/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── All_properties_vbnet/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── Duplicated_Properties/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── Incorrect_value_type/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── Missing_properties/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── Partially_missing_properties/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ └── PropertiesCSharpTrueVbnetFalse/
│ │ │ │ └── SonarLint.xml
│ │ │ └── SonarProjectConfig/
│ │ │ ├── Invalid_DifferentClassName/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── Invalid_DifferentNamespace/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── Invalid_Xml/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingAnalysisConfigPath/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingFilesToAnalyzePath/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingOutPath/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingProjectPath/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingProjectType/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── MissingTargetFramework/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── Path_MixedSeparators/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── Path_Unix/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ ├── Path_Windows/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ └── UnexpectedProjectTypeValue/
│ │ │ └── SonarProjectConfig.xml
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.Memory.Test/
│ │ ├── DotMemorySetupTest.cs
│ │ ├── MSTestSettings.cs
│ │ ├── Registrations/
│ │ │ └── SonarSyntaxNodeReportingContextMemoryTest.cs
│ │ ├── RunMemoryTest.ps1
│ │ ├── SonarAnalyzer.Memory.Test.csproj
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.ShimLayer.Generator.Test/
│ │ ├── FactoryTest.cs
│ │ ├── Model/
│ │ │ ├── ModelBuilderTest.cs
│ │ │ └── TypeLoaderTest.cs
│ │ ├── Snapshots/
│ │ │ ├── Snap#AccessorDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#AliasQualifiedNameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#AllowsConstraintClauseSyntax.g.cs.verified.cs
│ │ │ ├── Snap#AllowsConstraintSyntax.g.cs.verified.cs
│ │ │ ├── Snap#AnonymousFunctionExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#AnonymousMethodExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ArgumentKind.g.cs.verified.cs
│ │ │ ├── Snap#ArgumentSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ArrayTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BaseExpressionColonSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BaseMethodDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BaseNamespaceDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BaseObjectCreationExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BaseParameterSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BasicBlockKind.g.cs.verified.cs
│ │ │ ├── Snap#BinaryOperatorKind.g.cs.verified.cs
│ │ │ ├── Snap#BinaryPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BlockSyntax.g.cs.verified.cs
│ │ │ ├── Snap#BranchKind.g.cs.verified.cs
│ │ │ ├── Snap#BreakStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CaseKind.g.cs.verified.cs
│ │ │ ├── Snap#CasePatternSwitchLabelSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CheckedStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ClassDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ClassOrStructConstraintSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CollectionElementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CollectionExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CommonForEachStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ConstantPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ConstructorDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ContinueStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ControlFlowBranchSemantics.g.cs.verified.cs
│ │ │ ├── Snap#ControlFlowConditionKind.g.cs.verified.cs
│ │ │ ├── Snap#ControlFlowRegionKind.g.cs.verified.cs
│ │ │ ├── Snap#ConversionOperatorDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ConversionOperatorMemberCrefSyntax.g.cs.verified.cs
│ │ │ ├── Snap#CrefParameterSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DeclarationExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DeclarationPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DefaultConstraintSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DestructorDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DiscardDesignationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DiscardPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#DoStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#EmptyStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#EnumMemberDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#EventDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExpressionColonSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExpressionElementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExpressionOrPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExpressionStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExtensionBlockDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ExtensionMemberCrefSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FieldExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FileScopedNamespaceDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FixedStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ForEachStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ForEachVariableStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ForStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerCallingConventionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerParameterListSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerParameterSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerUnmanagedCallingConventionListSyntax.g.cs.verified.cs
│ │ │ ├── Snap#FunctionPointerUnmanagedCallingConventionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#GeneratedKind.g.cs.verified.cs
│ │ │ ├── Snap#GenericNameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#GlobalStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#GotoStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#IAddressOfOperation.g.cs.verified.cs
│ │ │ ├── Snap#IAnonymousFunctionOperation.g.cs.verified.cs
│ │ │ ├── Snap#IAnonymousObjectCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IArgumentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IArrayCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IArrayElementReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IArrayInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IAttributeOperation.g.cs.verified.cs
│ │ │ ├── Snap#IAwaitOperation.g.cs.verified.cs
│ │ │ ├── Snap#IBinaryOperation.g.cs.verified.cs
│ │ │ ├── Snap#IBinaryPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IBlockOperation.g.cs.verified.cs
│ │ │ ├── Snap#IBranchOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICatchClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICaughtExceptionOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICoalesceAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICoalesceOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICollectionElementInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICollectionExpressionOperation.g.cs.verified.cs
│ │ │ ├── Snap#ICompoundAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConditionalAccessInstanceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConditionalAccessOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConditionalOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConstantPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConstructorBodyOperation.g.cs.verified.cs
│ │ │ ├── Snap#IConversionOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDeclarationExpressionOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDeclarationPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDeconstructionAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDefaultCaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDefaultValueOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDelegateCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDiscardOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDiscardPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDynamicIndexerAccessOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDynamicInvocationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDynamicMemberReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IDynamicObjectCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IEmptyOperation.g.cs.verified.cs
│ │ │ ├── Snap#IEndOperation.g.cs.verified.cs
│ │ │ ├── Snap#IEventAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IEventReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IExpressionStatementOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFieldInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFieldReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFlowAnonymousFunctionOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFlowCaptureOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFlowCaptureReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IForEachLoopOperation.g.cs.verified.cs
│ │ │ ├── Snap#IForLoopOperation.g.cs.verified.cs
│ │ │ ├── Snap#IForToLoopOperation.g.cs.verified.cs
│ │ │ ├── Snap#IFunctionPointerInvocationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IImplicitIndexerReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IIncrementOrDecrementOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInlineArrayAccessOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInstanceReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringAdditionOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringAppendOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringContentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringHandlerArgumentPlaceholderOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringHandlerCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolatedStringTextOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInterpolationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInvalidOperation.g.cs.verified.cs
│ │ │ ├── Snap#IInvocationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IIsNullOperation.g.cs.verified.cs
│ │ │ ├── Snap#IIsPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IIsTypeOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILabeledOperation.g.cs.verified.cs
│ │ │ ├── Snap#IListPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILiteralOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILocalFunctionOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILocalReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILockOperation.g.cs.verified.cs
│ │ │ ├── Snap#ILoopOperation.g.cs.verified.cs
│ │ │ ├── Snap#IMemberInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IMemberReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IMethodBodyBaseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IMethodBodyOperation.g.cs.verified.cs
│ │ │ ├── Snap#IMethodReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#INameOfOperation.g.cs.verified.cs
│ │ │ ├── Snap#INegatedPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IObjectCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IObjectOrCollectionInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IOmittedArgumentOperation.g.cs.verified.cs
│ │ │ ├── Snap#IOperation.g.cs.verified.cs
│ │ │ ├── Snap#IParameterInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IParameterReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IParenthesizedOperation.g.cs.verified.cs
│ │ │ ├── Snap#IPatternCaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IPropertyInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IPropertyReferenceOperation.g.cs.verified.cs
│ │ │ ├── Snap#IPropertySubpatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRaiseEventOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRangeCaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRangeOperation.g.cs.verified.cs
│ │ │ ├── Snap#IReDimClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IReDimOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRecursivePatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRelationalCaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#IRelationalPatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IReturnOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISimpleAssignmentOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISingleValueCaseClauseOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISizeOfOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISlicePatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISpreadOperation.g.cs.verified.cs
│ │ │ ├── Snap#IStaticLocalInitializationSemaphoreOperation.g.cs.verified.cs
│ │ │ ├── Snap#IStopOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISwitchCaseOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISwitchExpressionArmOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISwitchExpressionOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISwitchOperation.g.cs.verified.cs
│ │ │ ├── Snap#ISymbolInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IThrowOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITranslatedQueryOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITryOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITupleBinaryOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITupleOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITypeOfOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITypeParameterObjectCreationOperation.g.cs.verified.cs
│ │ │ ├── Snap#ITypePatternOperation.g.cs.verified.cs
│ │ │ ├── Snap#IUnaryOperation.g.cs.verified.cs
│ │ │ ├── Snap#IUsingDeclarationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IUsingOperation.g.cs.verified.cs
│ │ │ ├── Snap#IUtf8StringOperation.g.cs.verified.cs
│ │ │ ├── Snap#IVariableDeclarationGroupOperation.g.cs.verified.cs
│ │ │ ├── Snap#IVariableDeclarationOperation.g.cs.verified.cs
│ │ │ ├── Snap#IVariableDeclaratorOperation.g.cs.verified.cs
│ │ │ ├── Snap#IVariableInitializerOperation.g.cs.verified.cs
│ │ │ ├── Snap#IWhileLoopOperation.g.cs.verified.cs
│ │ │ ├── Snap#IWithOperation.g.cs.verified.cs
│ │ │ ├── Snap#IdentifierNameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#IfStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#IgnoredDirectiveTriviaSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ImplicitObjectCreationExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ImplicitStackAllocArrayCreationExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#IncrementalGeneratorOutputKind.g.cs.verified.cs
│ │ │ ├── Snap#IncrementalStepRunReason.g.cs.verified.cs
│ │ │ ├── Snap#InstanceReferenceKind.g.cs.verified.cs
│ │ │ ├── Snap#InstrumentationKind.g.cs.verified.cs
│ │ │ ├── Snap#InterfaceDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#InterpolatedStringArgumentPlaceholderKind.g.cs.verified.cs
│ │ │ ├── Snap#IsPatternExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LabeledStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LambdaExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LanguageVersion.g.cs.verified.cs
│ │ │ ├── Snap#LineDirectivePositionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LineOrSpanDirectiveTriviaSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LineSpanDirectiveTriviaSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ListPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LocalDeclarationStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LocalFunctionStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LockStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#LoopKind.g.cs.verified.cs
│ │ │ ├── Snap#MemberDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#MetadataImportOptions.g.cs.verified.cs
│ │ │ ├── Snap#MethodKind.g.cs.verified.cs
│ │ │ ├── Snap#NameColonSyntax.g.cs.verified.cs
│ │ │ ├── Snap#NameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#NamespaceDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#NullableAnnotation.g.cs.verified.cs
│ │ │ ├── Snap#NullableContext.g.cs.verified.cs
│ │ │ ├── Snap#NullableContextOptions.g.cs.verified.cs
│ │ │ ├── Snap#NullableDirectiveTriviaSyntax.g.cs.verified.cs
│ │ │ ├── Snap#NullableFlowState.g.cs.verified.cs
│ │ │ ├── Snap#NullableTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#OmittedTypeArgumentSyntax.g.cs.verified.cs
│ │ │ ├── Snap#OperationKind.g.cs.verified.cs
│ │ │ ├── Snap#OperatorDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#OperatorMemberCrefSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ParenthesizedLambdaExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ParenthesizedPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ParenthesizedVariableDesignationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#PatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#Platform.g.cs.verified.cs
│ │ │ ├── Snap#PointerTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#PositionalPatternClauseSyntax.g.cs.verified.cs
│ │ │ ├── Snap#PredefinedTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#PrimaryConstructorBaseTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#PropertyPatternClauseSyntax.g.cs.verified.cs
│ │ │ ├── Snap#QualifiedNameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RangeExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RecordDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RecursivePatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RefExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RefKind.g.cs.verified.cs
│ │ │ ├── Snap#RefStructConstraintSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RefTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RelationalPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ReturnStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#RuntimeCapability.g.cs.verified.cs
│ │ │ ├── Snap#SarifVersion.g.cs.verified.cs
│ │ │ ├── Snap#ScopedKind.g.cs.verified.cs
│ │ │ ├── Snap#ScopedTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SemanticEditKind.g.cs.verified.cs
│ │ │ ├── Snap#SemanticModelOptions.g.cs.verified.cs
│ │ │ ├── Snap#ShebangDirectiveTriviaSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SimpleLambdaExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SimpleNameSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SingleVariableDesignationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SlicePatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SpecialType.g.cs.verified.cs
│ │ │ ├── Snap#SpreadElementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#StackAllocArrayCreationExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#StatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#StructDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SubpatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SwitchExpressionArmSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SwitchExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SwitchStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#SymbolDisplayLocalOptions.g.cs.verified.cs
│ │ │ ├── Snap#SymbolDisplayMemberOptions.g.cs.verified.cs
│ │ │ ├── Snap#SymbolDisplayMiscellaneousOptions.g.cs.verified.cs
│ │ │ ├── Snap#SymbolDisplayParameterOptions.g.cs.verified.cs
│ │ │ ├── Snap#SymbolDisplayPartKind.g.cs.verified.cs
│ │ │ ├── Snap#SymbolKind.g.cs.verified.cs
│ │ │ ├── Snap#SyntaxKind.g.cs.verified.cs
│ │ │ ├── Snap#ThrowExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#ThrowStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TryStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TupleElementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TupleExpressionSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TupleTypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TypeDeclarationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TypeKind.g.cs.verified.cs
│ │ │ ├── Snap#TypePatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#TypeSyntax.g.cs.verified.cs
│ │ │ ├── Snap#UnaryOperatorKind.g.cs.verified.cs
│ │ │ ├── Snap#UnaryPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#UnsafeStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#UsingDirectiveSyntax.g.cs.verified.cs
│ │ │ ├── Snap#UsingStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#VarPatternSyntax.g.cs.verified.cs
│ │ │ ├── Snap#VariableDesignationSyntax.g.cs.verified.cs
│ │ │ ├── Snap#WhenClauseSyntax.g.cs.verified.cs
│ │ │ ├── Snap#WhileStatementSyntax.g.cs.verified.cs
│ │ │ ├── Snap#WithExpressionSyntax.g.cs.verified.cs
│ │ │ └── Snap#YieldStatementSyntax.g.cs.verified.cs
│ │ ├── SonarAnalyzer.ShimLayer.Generator.Test.csproj
│ │ ├── Strategies/
│ │ │ ├── NewEnumStrategyTest.cs
│ │ │ ├── NoChangeStrategyTest.cs
│ │ │ ├── PartialEnumStrategyTest.cs
│ │ │ ├── SkipStrategyTest.cs
│ │ │ ├── SyntaxNodeExtendStrategyTest.cs
│ │ │ └── SyntaxNodeWrapStrategyTest.cs
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.Test/
│ │ ├── AnalysisContext/
│ │ │ ├── SonarAnalysisContextBaseTest.ShouldAnalyzeTree.cs
│ │ │ ├── SonarAnalysisContextBaseTest.cs
│ │ │ ├── SonarAnalysisContextTest.Parametrized.cs
│ │ │ ├── SonarAnalysisContextTest.Register.cs
│ │ │ └── SonarAnalysisContextTest.cs
│ │ ├── CFG/
│ │ │ ├── CfgSerializer/
│ │ │ │ └── DotWriterTest.cs
│ │ │ ├── Extensions/
│ │ │ │ └── IOperationExtensionsTest.cs
│ │ │ ├── Roslyn/
│ │ │ │ ├── BasicBlockTest.cs
│ │ │ │ ├── CaptureIdTest.cs
│ │ │ │ ├── CfgAllPathValidatorTest.cs
│ │ │ │ ├── ControlFlowBranchTest.cs
│ │ │ │ ├── ControlFlowRegionTest.cs
│ │ │ │ ├── RoslynCfgSerializerTest.cs
│ │ │ │ ├── RoslynControlFlowGraphTest.cs
│ │ │ │ └── RoslynLvaSerializerTest.cs
│ │ │ └── Sonar/
│ │ │ ├── BlockIdProviderTest.cs
│ │ │ ├── SonarCfgSerializerTest.cs
│ │ │ └── SonarControlFlowGraphTest.cs
│ │ ├── Common/
│ │ │ ├── ConcurrentExecutionTest.cs
│ │ │ ├── MetricsTest.cs
│ │ │ ├── ParameterLoaderTest.cs
│ │ │ ├── RuleCatalogTest.cs
│ │ │ ├── SecurityHotspotTest.cs
│ │ │ ├── UnchangedFilesTest.cs
│ │ │ ├── UniqueQueueTest.cs
│ │ │ └── XUnitVersions.cs
│ │ ├── Extensions/
│ │ │ ├── ICompilationReportExtensionsTests.cs
│ │ │ ├── StringExtensionsTest.cs
│ │ │ ├── SyntaxNodeExtensionsTest.cs
│ │ │ └── SyntaxTokenExtensionsSharedTest.cs
│ │ ├── LiveVariableAnalysis/
│ │ │ ├── RoslynLiveVariableAnalysisTest.FlowCaptureOperation.cs
│ │ │ ├── RoslynLiveVariableAnalysisTest.LocalFunction.cs
│ │ │ ├── RoslynLiveVariableAnalysisTest.TryCatchFinally.cs
│ │ │ ├── RoslynLiveVariableAnalysisTest.cs
│ │ │ └── SonarLiveVariableAnalysisTest.cs
│ │ ├── Metrics/
│ │ │ ├── CSharpExecutableLinesMetricTest.cs
│ │ │ └── VisualBasicExecutableLinesMetricTest.cs
│ │ ├── Operations/
│ │ │ └── Utilities/
│ │ │ ├── OperationExecutionOrderTest.cs
│ │ │ └── OperationFinderTest.cs
│ │ ├── Rules/
│ │ │ ├── AbstractClassToInterfaceTest.cs
│ │ │ ├── AbstractTypesShouldNotHaveConstructorsTest.cs
│ │ │ ├── AllBranchesShouldNotHaveSameImplementationTest.cs
│ │ │ ├── AlwaysSetDateTimeKindTest.cs
│ │ │ ├── AnonymousDelegateEventUnsubscribeTest.cs
│ │ │ ├── ArgumentSpecifiedForCallerInfoParameterTest.cs
│ │ │ ├── ArrayCovarianceTest.cs
│ │ │ ├── ArrayCreationLongSyntaxTest.cs
│ │ │ ├── ArrayDesignatorOnVariableTest.cs
│ │ │ ├── ArrayInitializationMultipleStatementsTest.cs
│ │ │ ├── ArrayPassedAsParamsTest.cs
│ │ │ ├── AspNet/
│ │ │ │ ├── AnnotateApiActionsWithHttpVerbTest.cs
│ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromControllerTest.cs
│ │ │ │ ├── AvoidUnderPostingTest.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutesTest.cs
│ │ │ │ ├── CallModelStateIsValidTest.cs
│ │ │ │ ├── ControllersHaveMixedResponsibilitiesTest.cs
│ │ │ │ ├── ControllersReuseClientTest.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlashTest.cs
│ │ │ │ ├── SpecifyRouteAttributeTest.cs
│ │ │ │ └── UseAspNetModelBindingTest.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrderTest.cs
│ │ │ ├── AssertionsShouldBeCompleteTest.cs
│ │ │ ├── AssignmentInsideSubExpressionTest.cs
│ │ │ ├── AsyncAwaitIdentifierTest.cs
│ │ │ ├── AsyncVoidMethodTest.cs
│ │ │ ├── AvoidDateTimeNowForBenchmarkingTest.cs
│ │ │ ├── AvoidExcessiveClassCouplingTest.cs
│ │ │ ├── AvoidExcessiveInheritanceTest.cs
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazorTest.cs
│ │ │ ├── AvoidUnsealedAttributesTest.cs
│ │ │ ├── BeginInvokePairedWithEndInvokeTest.cs
│ │ │ ├── BinaryOperationWithIdenticalExpressionsTest.cs
│ │ │ ├── BlazorQueryParameterRoutableComponentTest.cs
│ │ │ ├── BooleanCheckInvertedTest.cs
│ │ │ ├── BooleanLiteralUnnecessaryTest.cs
│ │ │ ├── BreakOutsideSwitchTest.cs
│ │ │ ├── BypassingAccessibilityTest.cs
│ │ │ ├── CallToAsyncMethodShouldNotBeBlockingTest.cs
│ │ │ ├── CallerInformationParametersShouldBeLastTest.cs
│ │ │ ├── CastConcreteTypeToInterfaceTest.cs
│ │ │ ├── CastShouldNotBeDuplicatedTest.cs
│ │ │ ├── CatchEmptyTest.cs
│ │ │ ├── CatchRethrowTest.cs
│ │ │ ├── CertificateValidationCheckTest.cs
│ │ │ ├── CheckArgumentExceptionTest.cs
│ │ │ ├── CheckFileLicenseTest.cs
│ │ │ ├── ClassAndMethodNameTest.cs
│ │ │ ├── ClassNamedExceptionTest.cs
│ │ │ ├── ClassNotInstantiatableTest.cs
│ │ │ ├── ClassShouldNotBeEmptyTest.cs
│ │ │ ├── ClassWithEqualityShouldImplementIEquatableTest.cs
│ │ │ ├── ClassWithOnlyStaticMemberTest.cs
│ │ │ ├── CloudNative/
│ │ │ │ ├── AzureFunctionsCatchExceptionsTest.cs
│ │ │ │ ├── AzureFunctionsLogFailuresTest.cs
│ │ │ │ ├── AzureFunctionsReuseClientsTest.cs
│ │ │ │ ├── AzureFunctionsStatelessTest.cs
│ │ │ │ └── DurableEntityInterfaceRestrictionsTest.cs
│ │ │ ├── CognitiveComplexityTest.cs
│ │ │ ├── CollectionEmptinessCheckingTest.cs
│ │ │ ├── CollectionPropertiesShouldBeReadOnlyTest.cs
│ │ │ ├── CollectionQuerySimplificationTest.cs
│ │ │ ├── CollectionsShouldImplementGenericInterfaceTest.cs
│ │ │ ├── CommentKeywordTest.cs
│ │ │ ├── CommentLineEndTest.cs
│ │ │ ├── CommentedOutCodeTest.cs
│ │ │ ├── CommentsShouldNotBeEmptyTest.cs
│ │ │ ├── ComparableInterfaceImplementationTest.cs
│ │ │ ├── CompareNaNTest.cs
│ │ │ ├── ConditionalSimplificationTest.cs
│ │ │ ├── ConditionalStructureSameConditionTest.cs
│ │ │ ├── ConditionalStructureSameImplementationTest.cs
│ │ │ ├── ConditionalsShouldStartOnNewLineTest.cs
│ │ │ ├── ConditionalsWithSameConditionTest.cs
│ │ │ ├── ConstructorArgumentValueShouldExistTest.cs
│ │ │ ├── ConstructorOverridableCallTest.cs
│ │ │ ├── ConsumeValueTaskCorrectlyTest.cs
│ │ │ ├── ControlCharacterInStringTest.cs
│ │ │ ├── CryptographicKeyShouldNotBeTooShortTest.cs
│ │ │ ├── DangerousGetHandleShouldNotBeCalledTest.cs
│ │ │ ├── DatabasePasswordsShouldBeSecureTest.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKeyTest.cs
│ │ │ ├── DateTimeFormatShouldNotBeHardcodedTest.cs
│ │ │ ├── DeadStoresTest.cs
│ │ │ ├── DebugAssertHasNoSideEffectsTest.cs
│ │ │ ├── DebuggerDisplayUsesExistingMembersTest.cs
│ │ │ ├── DeclareEventHandlersCorrectlyTest.cs
│ │ │ ├── DeclareTypesInNamespacesTest.cs
│ │ │ ├── DefaultSectionShouldBeFirstOrLastTest.cs
│ │ │ ├── DelegateSubtractionTest.cs
│ │ │ ├── DisposableMemberInNonDisposableClassTest.cs
│ │ │ ├── DisposableNotDisposedTest.cs
│ │ │ ├── DisposableReturnedFromUsingTest.cs
│ │ │ ├── DisposableTypesNeedFinalizersTest.cs
│ │ │ ├── DisposeFromDisposeTest.cs
│ │ │ ├── DisposeNotImplementingDisposeTest.cs
│ │ │ ├── DoNotCallAssemblyGetExecutingAssemblyTest.cs
│ │ │ ├── DoNotCallAssemblyLoadInvalidMethodsTest.cs
│ │ │ ├── DoNotCallExitMethodsTest.cs
│ │ │ ├── DoNotCallGCCollectMethodTest.cs
│ │ │ ├── DoNotCallGCSuppressFinalizeTest.cs
│ │ │ ├── DoNotCatchNullReferenceExceptionTest.cs
│ │ │ ├── DoNotCatchSystemExceptionTest.cs
│ │ │ ├── DoNotCheckZeroSizeCollectionTest.cs
│ │ │ ├── DoNotCopyArraysInPropertiesTest.cs
│ │ │ ├── DoNotDecreaseMemberVisibilityTest.cs
│ │ │ ├── DoNotExposeListTTest.cs
│ │ │ ├── DoNotHardcodeCredentialsTest.cs
│ │ │ ├── DoNotHardcodeSecretsTest.cs
│ │ │ ├── DoNotHideBaseClassMethodsTest.cs
│ │ │ ├── DoNotInstantiateSharedClassesTest.cs
│ │ │ ├── DoNotLockOnSharedResourceTest.cs
│ │ │ ├── DoNotLockWeakIdentityObjectsTest.cs
│ │ │ ├── DoNotMarkEnumsWithFlagsTest.cs
│ │ │ ├── DoNotNestTernaryOperatorsTest.cs
│ │ │ ├── DoNotNestTypesInArgumentsTest.cs
│ │ │ ├── DoNotOverloadOperatorEqualTest.cs
│ │ │ ├── DoNotOverwriteCollectionElementsTest.cs
│ │ │ ├── DoNotShiftByZeroOrIntSizeTest.cs
│ │ │ ├── DoNotTestThisWithIsOperatorTest.cs
│ │ │ ├── DoNotThrowFromDestructorsTest.cs
│ │ │ ├── DoNotUseByValTest.cs
│ │ │ ├── DoNotUseCollectionInItsOwnMethodCallsTest.cs
│ │ │ ├── DoNotUseDateTimeNowTest.cs
│ │ │ ├── DoNotUseIIfTest.cs
│ │ │ ├── DoNotUseLiteralBoolInAssertionsTest.cs
│ │ │ ├── DoNotUseOutRefParametersTest.cs
│ │ │ ├── DoNotWriteToStandardOutputTest.cs
│ │ │ ├── DontMixIncrementOrDecrementWithOtherOperatorsTest.cs
│ │ │ ├── DontUseTraceSwitchLevelsTest.cs
│ │ │ ├── DontUseTraceWriteTest.cs
│ │ │ ├── EmptyMethodTest.cs
│ │ │ ├── EmptyNamespaceTest.cs
│ │ │ ├── EmptyNestedBlockTest.cs
│ │ │ ├── EmptyStatementTest.cs
│ │ │ ├── EncryptionAlgorithmsShouldBeSecureTest.cs
│ │ │ ├── EndStatementUsageTest.cs
│ │ │ ├── EnumNameHasEnumSuffixTest.cs
│ │ │ ├── EnumNameShouldFollowRegexTest.cs
│ │ │ ├── EnumStorageNeedsToBeInt32Test.cs
│ │ │ ├── EnumerableSumInUncheckedTest.cs
│ │ │ ├── EnumerationValueNameTest.cs
│ │ │ ├── EnumsShouldNotBeNamedReservedTest.cs
│ │ │ ├── EqualityOnFloatingPointTest.cs
│ │ │ ├── EqualityOnModulusTest.cs
│ │ │ ├── EquatableClassShouldBeSealedTest.cs
│ │ │ ├── EscapeLambdaParameterTypeNamedScopedTest.cs
│ │ │ ├── EventHandlerDelegateShouldHaveProperArgumentsTest.cs
│ │ │ ├── EventHandlerNameTest.cs
│ │ │ ├── EventNameContainsBeforeOrAfterTest.cs
│ │ │ ├── EventNameTest.cs
│ │ │ ├── ExceptionRethrowTest.cs
│ │ │ ├── ExceptionShouldNotBeThrownFromUnexpectedMethodsTest.cs
│ │ │ ├── ExceptionsNeedStandardConstructorsTest.cs
│ │ │ ├── ExceptionsShouldBeLoggedOrThrownTest.cs
│ │ │ ├── ExceptionsShouldBeLoggedTest.cs
│ │ │ ├── ExceptionsShouldBePublicTest.cs
│ │ │ ├── ExceptionsShouldBeUsedTest.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustificationTest.cs
│ │ │ ├── ExitStatementUsageTest.cs
│ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsedTest.cs
│ │ │ ├── ExpressionComplexityTest.cs
│ │ │ ├── ExtensionMethodShouldBeInSeparateNamespaceTest.cs
│ │ │ ├── ExtensionMethodShouldNotExtendObjectTest.cs
│ │ │ ├── FieldShadowsParentFieldTest.cs
│ │ │ ├── FieldShouldBeReadonlyTest.cs
│ │ │ ├── FieldShouldNotBePublicTest.cs
│ │ │ ├── FieldsShouldBeEncapsulatedInPropertiesTest.cs
│ │ │ ├── FileLinesTest.cs
│ │ │ ├── FileShouldEndWithEmptyNewLineTest.cs
│ │ │ ├── FinalizerShouldNotBeEmptyTest.cs
│ │ │ ├── FindInsteadOfFirstOrDefaultTest.cs
│ │ │ ├── FlagsEnumWithoutInitializerTest.cs
│ │ │ ├── FlagsEnumZeroMemberTest.cs
│ │ │ ├── ForLoopConditionAlwaysFalseTest.cs
│ │ │ ├── ForLoopCounterChangedTest.cs
│ │ │ ├── ForLoopCounterConditionTest.cs
│ │ │ ├── ForLoopIncrementSignTest.cs
│ │ │ ├── ForeachLoopExplicitConversionTest.cs
│ │ │ ├── FrameworkTypeNamingTest.cs
│ │ │ ├── FunctionComplexityTest.cs
│ │ │ ├── FunctionNameTest.cs
│ │ │ ├── FunctionNestingDepthTest.cs
│ │ │ ├── GenericInheritanceShouldNotBeRecursiveTest.cs
│ │ │ ├── GenericLoggerInjectionShouldMatchEnclosingTypeTest.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignmentTest.cs
│ │ │ ├── GenericTypeParameterEmptinessCheckingTest.cs
│ │ │ ├── GenericTypeParameterInOutTest.cs
│ │ │ ├── GenericTypeParameterUnusedTest.cs
│ │ │ ├── GenericTypeParametersRequiredTest.cs
│ │ │ ├── GetHashCodeEqualsOverrideTest.cs
│ │ │ ├── GetHashCodeMutableTest.cs
│ │ │ ├── GetTypeWithIsAssignableFromTest.cs
│ │ │ ├── GotoStatementTest.cs
│ │ │ ├── GuardConditionOnEqualsOverrideTest.cs
│ │ │ ├── Hotspots/
│ │ │ │ ├── ClearTextProtocolsAreSensitiveTest.cs
│ │ │ │ ├── CommandPathTest.cs
│ │ │ │ ├── ConfiguringLoggersTest.cs
│ │ │ │ ├── CookieShouldBeHttpOnlyTest.cs
│ │ │ │ ├── CookieShouldBeSecureTest.cs
│ │ │ │ ├── CreatingHashAlgorithmsTest.cs
│ │ │ │ ├── DeliveringDebugFeaturesInProductionTest.cs
│ │ │ │ ├── DisablingCsrfProtectionTest.cs
│ │ │ │ ├── DisablingRequestValidationTest.cs
│ │ │ │ ├── DoNotUseRandomTest.cs
│ │ │ │ ├── ExecutingSqlQueriesTest.cs
│ │ │ │ ├── ExpandingArchivesTest.cs
│ │ │ │ ├── HardcodedIpAddressTest.cs
│ │ │ │ ├── InsecureDeserializationTest.cs
│ │ │ │ ├── PermissiveCorsTest.cs
│ │ │ │ ├── PubliclyWritableDirectoriesTest.cs
│ │ │ │ ├── RequestsWithExcessiveLengthTest.cs
│ │ │ │ ├── UnsafeCodeBlocksTest.cs
│ │ │ │ └── UsingNonstandardCryptographyTest.cs
│ │ │ ├── IdentifiersNamedExtensionShouldBeEscapedTest.cs
│ │ │ ├── IdentifiersNamedFieldShouldBeEscapedTest.cs
│ │ │ ├── IfChainWithoutElseTest.cs
│ │ │ ├── IfCollapsibleTest.cs
│ │ │ ├── ImplementIDisposableCorrectlyTest.cs
│ │ │ ├── ImplementISerializableCorrectlyTest.cs
│ │ │ ├── ImplementSerializationMethodsCorrectlyTest.cs
│ │ │ ├── IndentSingleLineFollowingConditionalTest.cs
│ │ │ ├── IndexOfCheckAgainstZeroTest.cs
│ │ │ ├── IndexedPropertyWithMultipleParametersTest.cs
│ │ │ ├── InfiniteRecursionTest.cs
│ │ │ ├── InheritedCollidingInterfaceMembersTest.cs
│ │ │ ├── InitializeStaticFieldsInlineTest.cs
│ │ │ ├── InsecureContentSecurityPolicyTest.cs
│ │ │ ├── InsecureEncryptionAlgorithmTest.cs
│ │ │ ├── InsecureTemporaryFilesCreationTest.cs
│ │ │ ├── InsteadOfAnyTest.cs
│ │ │ ├── InterfaceMethodsShouldBeCallableByChildTypesTest.cs
│ │ │ ├── InterfaceNameTest.cs
│ │ │ ├── InterfacesShouldNotBeEmptyTest.cs
│ │ │ ├── InvalidCastToInterfaceTest.cs
│ │ │ ├── InvocationResolvesToOverrideWithParamsTest.cs
│ │ │ ├── IssueSuppressionTest.cs
│ │ │ ├── JSInvokableMethodsShouldBePublicTest.cs
│ │ │ ├── JwtSignedTest.cs
│ │ │ ├── LdapConnectionShouldBeSecureTest.cs
│ │ │ ├── LineContinuationTest.cs
│ │ │ ├── LineLengthTest.cs
│ │ │ ├── LinkedListPropertiesInsteadOfMethodsTest.cs
│ │ │ ├── LiteralSuffixUpperCaseTest.cs
│ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParametersTest.cs
│ │ │ ├── LocalVariableNameTest.cs
│ │ │ ├── LockedFieldShouldBeReadonlyTest.cs
│ │ │ ├── LoggerFieldsShouldBePrivateStaticReadonlyTest.cs
│ │ │ ├── LoggerMembersNamesShouldComplyTest.cs
│ │ │ ├── LoggersShouldBeNamedForEnclosingTypeTest.cs
│ │ │ ├── LoggingArgumentsShouldBePassedCorrectlyTest.cs
│ │ │ ├── LoggingTemplatePlaceHoldersShouldBeInOrderTest.cs
│ │ │ ├── LoopsAndLinqTest.cs
│ │ │ ├── LooseFilePermissionsTest.cs
│ │ │ ├── LossOfFractionInDivisionTest.cs
│ │ │ ├── MagicNumberShouldNotBeUsedTest.cs
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeTest.cs
│ │ │ ├── MarkAssemblyWithAttributeUsageAttributeTest.cs
│ │ │ ├── MarkAssemblyWithClsCompliantAttributeTest.cs
│ │ │ ├── MarkAssemblyWithComVisibleAttributeTest.cs
│ │ │ ├── MarkAssemblyWithNeutralResourcesLanguageAttributeTest.cs
│ │ │ ├── MarkWindowsFormsMainWithStaThreadTest.cs
│ │ │ ├── MemberInitializedToDefaultTest.cs
│ │ │ ├── MemberInitializerRedundantTest.cs
│ │ │ ├── MemberOverrideCallsBaseMemberTest.cs
│ │ │ ├── MemberShadowsOuterStaticMemberTest.cs
│ │ │ ├── MemberShouldBeStaticTest.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributesTest.cs
│ │ │ ├── MessageTemplatesShouldBeCorrectTest.cs
│ │ │ ├── MethodOverloadOptionalParameterTest.cs
│ │ │ ├── MethodOverloadsShouldBeGroupedTest.cs
│ │ │ ├── MethodOverrideAddsParamsTest.cs
│ │ │ ├── MethodOverrideChangedDefaultValueTest.cs
│ │ │ ├── MethodOverrideNoParamsTest.cs
│ │ │ ├── MethodParameterMissingOptionalTest.cs
│ │ │ ├── MethodParameterUnusedTest.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicityTest.cs
│ │ │ ├── MethodShouldNotOnlyReturnConstantTest.cs
│ │ │ ├── MethodsShouldNotHaveIdenticalImplementationsTest.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLinesTest.cs
│ │ │ ├── MethodsShouldUseBaseTypesTest.cs
│ │ │ ├── MultilineBlocksWithoutBraceTest.cs
│ │ │ ├── MultipleVariableDeclarationTest.cs
│ │ │ ├── MutableFieldsShouldNotBePublicReadonlyTest.cs
│ │ │ ├── MutableFieldsShouldNotBePublicStaticTest.cs
│ │ │ ├── NameOfShouldBeUsedTest.cs
│ │ │ ├── NamedPlaceholdersShouldBeUniqueTest.cs
│ │ │ ├── NamespaceNameTest.cs
│ │ │ ├── NativeMethodsShouldBeWrappedTest.cs
│ │ │ ├── NegatedIsExpressionTest.cs
│ │ │ ├── NestedCodeBlockTest.cs
│ │ │ ├── NoExceptionsInFinallyTest.cs
│ │ │ ├── NonAsyncTaskShouldNotReturnNullTest.cs
│ │ │ ├── NonDerivedPrivateClassesShouldBeSealedTest.cs
│ │ │ ├── NonFlagsEnumInBitwiseOperationTest.cs
│ │ │ ├── NormalizeStringsToUppercaseTest.cs
│ │ │ ├── NotAssignedPrivateMemberTest.cs
│ │ │ ├── NumberPatternShouldBeRegularTest.cs
│ │ │ ├── ObjectCreatedDroppedTest.cs
│ │ │ ├── ObsoleteAttributesTest.cs
│ │ │ ├── OnErrorStatementTest.cs
│ │ │ ├── OperatorOverloadsShouldHaveNamedAlternativesTest.cs
│ │ │ ├── OperatorsShouldBeOverloadedConsistentlyTest.cs
│ │ │ ├── OptionExplicitOnTest.cs
│ │ │ ├── OptionStrictOnTest.cs
│ │ │ ├── OptionalParameterNotPassedToBaseCallTest.cs
│ │ │ ├── OptionalParameterTest.cs
│ │ │ ├── OptionalParameterWithDefaultValueTest.cs
│ │ │ ├── OptionalRefOutParameterTest.cs
│ │ │ ├── OrderByRepeatedTest.cs
│ │ │ ├── OverrideGetHashCodeOnOverridingEqualsTest.cs
│ │ │ ├── PInvokesShouldNotBeVisibleTest.cs
│ │ │ ├── ParameterAssignedToTest.cs
│ │ │ ├── ParameterNameMatchesOriginalTest.cs
│ │ │ ├── ParameterNameTest.cs
│ │ │ ├── ParameterNamesShouldNotDuplicateMethodNamesTest.cs
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraintTest.cs
│ │ │ ├── ParameterValidationInAsyncShouldBeWrappedTest.cs
│ │ │ ├── ParameterValidationInYieldShouldBeWrappedTest.cs
│ │ │ ├── ParametersCorrectOrderTest.cs
│ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttributeTest.cs
│ │ │ ├── PartialMethodNoImplementationTest.cs
│ │ │ ├── PasswordsShouldBeStoredCorrectlyTest.cs
│ │ │ ├── PointersShouldBePrivateTest.cs
│ │ │ ├── PreferGuidEmptyTest.cs
│ │ │ ├── PreferJaggedArraysOverMultidimensionalTest.cs
│ │ │ ├── PrivateConstantFieldNameTest.cs
│ │ │ ├── PrivateFieldNameTest.cs
│ │ │ ├── PrivateFieldUsedAsLocalVariableTest.cs
│ │ │ ├── PrivateSharedReadonlyFieldNameTest.cs
│ │ │ ├── PrivateStaticMethodUsedOnlyByNestedClassTest.cs
│ │ │ ├── PropertiesAccessCorrectFieldTest.cs
│ │ │ ├── PropertiesShouldBePreferredTest.cs
│ │ │ ├── PropertyGetterWithThrowTest.cs
│ │ │ ├── PropertyNameTest.cs
│ │ │ ├── PropertyNamesShouldNotMatchGetMethodsTest.cs
│ │ │ ├── PropertyToAutoPropertyTest.cs
│ │ │ ├── PropertyWithArrayTypeTest.cs
│ │ │ ├── PropertyWriteOnlyTest.cs
│ │ │ ├── ProvideDeserializationMethodsForOptionalFieldsTest.cs
│ │ │ ├── PublicConstantFieldNameTest.cs
│ │ │ ├── PublicConstantFieldTest.cs
│ │ │ ├── PublicFieldNameTest.cs
│ │ │ ├── PublicMethodWithMultidimensionalArrayTest.cs
│ │ │ ├── PublicSharedReadonlyFieldNameTest.cs
│ │ │ ├── PureAttributeOnVoidMethodTest.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclarationTest.cs
│ │ │ ├── RedundantArgumentTest.cs
│ │ │ ├── RedundantCastTest.cs
│ │ │ ├── RedundantConditionalAroundAssignmentTest.cs
│ │ │ ├── RedundantDeclarationTest.cs
│ │ │ ├── RedundantExitSelectTest.cs
│ │ │ ├── RedundantInheritanceListTest.cs
│ │ │ ├── RedundantJumpStatementTest.cs
│ │ │ ├── RedundantModifierTest.cs
│ │ │ ├── RedundantNullCheckTest.cs
│ │ │ ├── RedundantNullableTypeComparisonTest.cs
│ │ │ ├── RedundantParenthesesObjectCreationTest.cs
│ │ │ ├── RedundantParenthesesTest.cs
│ │ │ ├── RedundantPropertyNamesInAnonymousClassTest.cs
│ │ │ ├── RedundantToArrayCallTest.cs
│ │ │ ├── RedundantToStringCallTest.cs
│ │ │ ├── ReferenceEqualityCheckWhenEqualsExistsTest.cs
│ │ │ ├── ReferenceEqualsOnValueTypeTest.cs
│ │ │ ├── RegularExpressions/
│ │ │ │ └── RegexMustHaveValidSyntaxTest.cs
│ │ │ ├── RequireAttributeUsageAttributeTest.cs
│ │ │ ├── ReturnEmptyCollectionInsteadOfNullTest.cs
│ │ │ ├── ReturnTypeNamedPartialShouldBeEscapedTest.cs
│ │ │ ├── ReturnValueIgnoredTest.cs
│ │ │ ├── ReversedOperatorsTest.cs
│ │ │ ├── RightCurlyBraceStartsLineTest.cs
│ │ │ ├── SecurityPInvokeMethodShouldNotBeCalledTest.cs
│ │ │ ├── SelfAssignmentTest.cs
│ │ │ ├── SerializationConstructorsShouldBeSecuredTest.cs
│ │ │ ├── SetLocaleForDataTypesTest.cs
│ │ │ ├── SetPropertiesInsteadOfMethodsTest.cs
│ │ │ ├── ShiftDynamicNotIntegerTest.cs
│ │ │ ├── ShouldImplementExportedInterfacesTest.cs
│ │ │ ├── SimpleDoLoopTest.cs
│ │ │ ├── SingleStatementPerLineTest.cs
│ │ │ ├── SpecifyIFormatProviderOrCultureInfoTest.cs
│ │ │ ├── SpecifyStringComparisonTest.cs
│ │ │ ├── SpecifyTimeoutOnRegexTest.cs
│ │ │ ├── SqlKeywordsDelimitedBySpaceTest.cs
│ │ │ ├── StaticFieldInGenericClassTest.cs
│ │ │ ├── StaticFieldInitializerOrderTest.cs
│ │ │ ├── StaticFieldVisibleTest.cs
│ │ │ ├── StaticFieldWrittenFromInstanceConstructorTest.cs
│ │ │ ├── StaticFieldWrittenFromInstanceMemberTest.cs
│ │ │ ├── StaticSealedClassProtectedMembersTest.cs
│ │ │ ├── StreamReadStatementTest.cs
│ │ │ ├── StringConcatenationInLoopTest.cs
│ │ │ ├── StringConcatenationWithPlusTest.cs
│ │ │ ├── StringFormatValidatorTest.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicatedTest.cs
│ │ │ ├── StringOffsetMethodsTest.cs
│ │ │ ├── StringOperationWithoutCultureTest.cs
│ │ │ ├── StringOrIntegralTypesForIndexersTest.cs
│ │ │ ├── SuppressFinalizeUselessTest.cs
│ │ │ ├── SwaggerActionReturnTypeTest.cs
│ │ │ ├── SwitchCaseFallsThroughToDefaultTest.cs
│ │ │ ├── SwitchCasesMinimumThreeTest.cs
│ │ │ ├── SwitchDefaultClauseEmptyTest.cs
│ │ │ ├── SwitchSectionShouldNotHaveTooManyStatementsTest.cs
│ │ │ ├── SwitchShouldNotBeNestedTest.cs
│ │ │ ├── SwitchWithoutDefaultTest.cs
│ │ │ ├── TabCharacterTest.cs
│ │ │ ├── TaskConfigureAwaitTest.cs
│ │ │ ├── TestClassShouldHaveTestMethodTest.cs
│ │ │ ├── TestMethodShouldContainAssertionTest.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignatureTest.cs
│ │ │ ├── TestMethodShouldNotBeIgnoredTest.cs
│ │ │ ├── TestsShouldNotUseThreadSleepTest.cs
│ │ │ ├── ThisShouldNotBeExposedFromConstructorsTest.cs
│ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalledTest.cs
│ │ │ ├── ThreadStaticNonStaticFieldTest.cs
│ │ │ ├── ThreadStaticWithInitializerTest.cs
│ │ │ ├── ThrowReservedExceptionsTest.cs
│ │ │ ├── ToStringShouldNotReturnNullTest.cs
│ │ │ ├── TooManyGenericParametersTest.cs
│ │ │ ├── TooManyLabelsInSwitchTest.cs
│ │ │ ├── TooManyLoggingCallsTest.cs
│ │ │ ├── TooManyParametersTest.cs
│ │ │ ├── TrackNotImplementedExceptionTest.cs
│ │ │ ├── TryStatementsWithIdenticalCatchShouldBeMergedTest.cs
│ │ │ ├── TypeExaminationOnSystemTypeTest.cs
│ │ │ ├── TypeMemberVisibilityTest.cs
│ │ │ ├── TypeNamesShouldNotMatchNamespacesTest.cs
│ │ │ ├── TypeParameterNameTest.cs
│ │ │ ├── TypesShouldNotExtendOutdatedBaseTypesTest.cs
│ │ │ ├── UnaryPrefixOperatorRepeatedTest.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConstTest.cs
│ │ │ ├── UnconditionalJumpStatementTest.cs
│ │ │ ├── UninvokedEventDeclarationTest.cs
│ │ │ ├── UnnecessaryBitwiseOperationTest.cs
│ │ │ ├── UnnecessaryMathematicalComparisonTest.cs
│ │ │ ├── UnnecessaryUsingsTest.cs
│ │ │ ├── UnsignedTypesUsageTest.cs
│ │ │ ├── UnusedPrivateMemberTest.Constructors.cs
│ │ │ ├── UnusedPrivateMemberTest.Fields.cs
│ │ │ ├── UnusedPrivateMemberTest.Methods.cs
│ │ │ ├── UnusedPrivateMemberTest.Properties.cs
│ │ │ ├── UnusedPrivateMemberTest.Types.cs
│ │ │ ├── UnusedPrivateMemberTest.cs
│ │ │ ├── UnusedReturnValueTest.cs
│ │ │ ├── UnusedStringBuilderTest.cs
│ │ │ ├── UriShouldNotBeHardcodedTest.cs
│ │ │ ├── UseAwaitableMethodTest.cs
│ │ │ ├── UseCharOverloadOfStringMethodsTest.cs
│ │ │ ├── UseConstantLoggingTemplateTest.cs
│ │ │ ├── UseConstantsWhereAppropriateTest.cs
│ │ │ ├── UseCurlyBracesTest.cs
│ │ │ ├── UseDateTimeOffsetInsteadOfDateTimeTest.cs
│ │ │ ├── UseFindSystemTimeZoneByIdTest.cs
│ │ │ ├── UseGenericEventHandlerInstancesTest.cs
│ │ │ ├── UseGenericWithRefParametersTest.cs
│ │ │ ├── UseIFormatProviderForParsingDateAndTimeTest.cs
│ │ │ ├── UseIndexingInsteadOfLinqMethodsTest.cs
│ │ │ ├── UseLambdaParameterInConcurrentDictionaryTest.cs
│ │ │ ├── UseNumericLiteralSeparatorTest.cs
│ │ │ ├── UseParamsForVariableArgumentsTest.cs
│ │ │ ├── UsePascalCaseForNamedPlaceHoldersTest.cs
│ │ │ ├── UseReturnStatementTest.cs
│ │ │ ├── UseShortCircuitingOperatorTest.cs
│ │ │ ├── UseStringCreateTest.cs
│ │ │ ├── UseStringIsNullOrEmptyTest.cs
│ │ │ ├── UseTestableTimeProviderTest.cs
│ │ │ ├── UseTrueForAllTest.cs
│ │ │ ├── UseUnixEpochTest.cs
│ │ │ ├── UseUriInsteadOfStringTest.cs
│ │ │ ├── UseValueParameterTest.cs
│ │ │ ├── UseWhereBeforeOrderByTest.cs
│ │ │ ├── UseWhileLoopInsteadTest.cs
│ │ │ ├── UseWithStatementTest.cs
│ │ │ ├── Utilities/
│ │ │ │ ├── AnalysisWarningAnalyzerTest.cs
│ │ │ │ ├── CopyPasteTokenAnalyzerTest.cs
│ │ │ │ ├── FileMetadataAnalyzerTest.cs
│ │ │ │ ├── LogAnalyzerTest.cs
│ │ │ │ ├── MethodDeclarationInfoComparerTest.cs
│ │ │ │ ├── MetricsAnalyzerTest.cs
│ │ │ │ ├── SymbolReferenceAnalyzerTest.cs
│ │ │ │ ├── TelemetryAnalyzerTest.cs
│ │ │ │ ├── TestMethodDeclarationsAnalyzerTest.cs
│ │ │ │ ├── TokenTypeAnalyzerTest.Classifier.cs
│ │ │ │ ├── TokenTypeAnalyzerTest.ClassifierTestHarness.cs
│ │ │ │ ├── TokenTypeAnalyzerTest.cs
│ │ │ │ └── UtilityAnalyzerBaseTest.cs
│ │ │ ├── ValueTypeShouldImplementIEquatableTest.cs
│ │ │ ├── ValuesUselesslyIncrementedTest.cs
│ │ │ ├── VariableShadowsFieldTest.cs
│ │ │ ├── VariableUnusedTest.cs
│ │ │ ├── VirtualEventFieldTest.cs
│ │ │ ├── WcfMissingContractAttributeTest.cs
│ │ │ ├── WcfNonVoidOneWayTest.cs
│ │ │ ├── WeakSslTlsProtocolsTest.cs
│ │ │ ├── XMLSignatureCheckTest.cs
│ │ │ └── XmlExternalEntityShouldNotBeParsedTest.cs
│ │ ├── SonarAnalyzer.Test.csproj
│ │ ├── Syntax/
│ │ │ ├── Extensions/
│ │ │ │ ├── IfStatementSyntaxExtensionsTest.cs
│ │ │ │ ├── SwitchSectionSyntaxExtensionsTest.cs
│ │ │ │ └── SyntaxNodeExtensionsTest.CSharp.cs
│ │ │ └── Utilities/
│ │ │ ├── EquivalenceCheckerTest.cs
│ │ │ ├── MethodParameterLookupTest.cs
│ │ │ ├── RemovableDeclarationCollectorTest.cs
│ │ │ ├── SymbolUsageCollectorTest.cs
│ │ │ └── SyntaxClassifierTest.cs
│ │ ├── TestCases/
│ │ │ ├── AbstractClassToInterface.Latest.Partial.cs
│ │ │ ├── AbstractClassToInterface.Latest.cs
│ │ │ ├── AbstractClassToInterface.cs
│ │ │ ├── AbstractTypesShouldNotHaveConstructors.Latest.Partial.cs
│ │ │ ├── AbstractTypesShouldNotHaveConstructors.Latest.cs
│ │ │ ├── AbstractTypesShouldNotHaveConstructors.TopLevelStatements.cs
│ │ │ ├── AbstractTypesShouldNotHaveConstructors.cs
│ │ │ ├── AllBranchesShouldNotHaveSameImplementation.CSharp9.cs
│ │ │ ├── AllBranchesShouldNotHaveSameImplementation.cs
│ │ │ ├── AllBranchesShouldNotHaveSameImplementation.vb
│ │ │ ├── AlwaysSetDateTimeKind.cs
│ │ │ ├── AlwaysSetDateTimeKind.vb
│ │ │ ├── AnonymousDelegateEventUnsubscribe.Latest.cs
│ │ │ ├── AnonymousDelegateEventUnsubscribe.cs
│ │ │ ├── AppSettings/
│ │ │ │ ├── DatabasePasswordsShouldBeSecure/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ ├── ArrayInside/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── ConnectionStringComment/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── EmptyArray/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── EmptyFile/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── Null/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── PropertyKinds/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ ├── ValueKind/
│ │ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ │ └── WrongStructure/
│ │ │ │ │ │ └── appsettings.json
│ │ │ │ │ └── Values/
│ │ │ │ │ └── appsettings.json
│ │ │ │ ├── DoNotHardcodeCredentials/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── AppSettings.json
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── AppSettings.json
│ │ │ │ │ └── Valid/
│ │ │ │ │ ├── AppSettings.Custom.json
│ │ │ │ │ ├── AppSettings.Development.json
│ │ │ │ │ ├── AppSettings.Production.json
│ │ │ │ │ ├── AppSettings.json
│ │ │ │ │ └── OtherFile.json
│ │ │ │ └── DoNotHardcodeSecrets/
│ │ │ │ ├── Corrupt/
│ │ │ │ │ └── AppSettings.json
│ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ └── AppSettings.json
│ │ │ │ └── Valid/
│ │ │ │ ├── AppSettings.Custom.json
│ │ │ │ ├── AppSettings.Development.json
│ │ │ │ ├── AppSettings.Production.json
│ │ │ │ ├── AppSettings.json
│ │ │ │ └── OtherFile.json
│ │ │ ├── ArgumentSpecifiedForCallerInfoParameter.Latest.cs
│ │ │ ├── ArgumentSpecifiedForCallerInfoParameter.cs
│ │ │ ├── ArrayCovariance.CSharp9.cs
│ │ │ ├── ArrayCovariance.cs
│ │ │ ├── ArrayCreationLongSyntax.Fixed.vb
│ │ │ ├── ArrayCreationLongSyntax.vb
│ │ │ ├── ArrayDesignatorOnVariable.Fixed.vb
│ │ │ ├── ArrayDesignatorOnVariable.vb
│ │ │ ├── ArrayInitializationMultipleStatements.vb
│ │ │ ├── ArrayPassedAsParams.Latest.cs
│ │ │ ├── ArrayPassedAsParams.cs
│ │ │ ├── ArrayPassedAsParams.vb
│ │ │ ├── AspNet/
│ │ │ │ ├── AnnotateApiActionsWithHttpVerb.cs
│ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromController.cs
│ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromControllerCodeFix.Fixed.cs
│ │ │ │ ├── ApiControllersShouldNotDeriveDirectlyFromControllerCodeFix.cs
│ │ │ │ ├── AvoidUnderPosting.AutogeneratedModel.cs
│ │ │ │ ├── AvoidUnderPosting.Latest.Partial.cs
│ │ │ │ ├── AvoidUnderPosting.Latest.cs
│ │ │ │ ├── AvoidUnderPosting.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNet4x.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNet4x.vb
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore2AndAbove.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore2x.Latest.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore2x.vb
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore3AndAbove.Latest.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore3AndAbove.vb
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore8AndAbove.Latest.cs
│ │ │ │ ├── BackslashShouldBeAvoidedInAspNetRoutes.AspNetCore8AndAbove.vb
│ │ │ │ ├── CallModelStateIsValid.AutogeneratedController.cs
│ │ │ │ ├── CallModelStateIsValid.Latest.cs
│ │ │ │ ├── CallModelStateIsValid.cs
│ │ │ │ ├── ControllerReuseClient.CSharp12.cs
│ │ │ │ ├── ControllersHaveMixedResponsibilities.Latest.Partial.cs
│ │ │ │ ├── ControllersHaveMixedResponsibilities.Latest.cs
│ │ │ │ ├── ControllersReuseClient.CSharp9.cs
│ │ │ │ ├── ControllersReuseClient.Csharp8.cs
│ │ │ │ ├── ControllersReuseClient.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNet4x.PartialAutogenerated.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNet4x.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNet4x.vb
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNetCore.CSharp12.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNetCore.PartialAutogenerated.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNetCore.cs
│ │ │ │ ├── RouteTemplateShouldNotStartWithSlash.AspNetCore.vb
│ │ │ │ ├── SpecifyRouteAttribute.CSharp12.cs
│ │ │ │ └── UseAspNetModelBinding_AspNetCore_Latest.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.MsTest.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.NUnit.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.NUnit4.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.Xunit.cs
│ │ │ ├── AssertionArgsShouldBePassedInCorrectOrder.XunitV3.cs
│ │ │ ├── AssertionsShouldBeComplete.AllFrameworks.cs
│ │ │ ├── AssertionsShouldBeComplete.FluentAssertions.CSharp7.cs
│ │ │ ├── AssertionsShouldBeComplete.FluentAssertions.Latest.cs
│ │ │ ├── AssertionsShouldBeComplete.NFluent.Latest.cs
│ │ │ ├── AssertionsShouldBeComplete.NFluent.cs
│ │ │ ├── AssertionsShouldBeComplete.NSubstitute.cs
│ │ │ ├── AssignmentInsideSubExpression.Latest.cs
│ │ │ ├── AssignmentInsideSubExpression.TopLevelStatements.cs
│ │ │ ├── AssignmentInsideSubExpression.cs
│ │ │ ├── AsyncAwaitIdentifier.Latest.cs
│ │ │ ├── AsyncAwaitIdentifier.cs
│ │ │ ├── AsyncVoidMethod.Latest.cs
│ │ │ ├── AsyncVoidMethod.MsTestTestFramework.cs
│ │ │ ├── AsyncVoidMethod.VsUtFramework.cs
│ │ │ ├── AsyncVoidMethod.cs
│ │ │ ├── AvoidDateTimeNowForBenchmarking.cs
│ │ │ ├── AvoidDateTimeNowForBenchmarking.vb
│ │ │ ├── AvoidExcessiveClassCoupling.Latest.cs
│ │ │ ├── AvoidExcessiveClassCoupling.cs
│ │ │ ├── AvoidExcessiveInheritance_CustomValues.Records.cs
│ │ │ ├── AvoidExcessiveInheritance_CustomValues.cs
│ │ │ ├── AvoidExcessiveInheritance_DefaultValues.Concurrent.cs
│ │ │ ├── AvoidExcessiveInheritance_DefaultValues.FileScopedTypes.cs
│ │ │ ├── AvoidExcessiveInheritance_DefaultValues.Records.Concurrent.cs
│ │ │ ├── AvoidExcessiveInheritance_DefaultValues.Records.cs
│ │ │ ├── AvoidExcessiveInheritance_DefaultValues.cs
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.LoopsWithNoBody.razor
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.RenderFragment.razor
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.RenderFragmentConsumer.razor
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.cs
│ │ │ ├── AvoidLambdaExpressionInLoopsInBlazor.razor
│ │ │ ├── AvoidUnsealedAttributes.cs
│ │ │ ├── AvoidUnsealedAttributes.vb
│ │ │ ├── BeginInvokePairedWithEndInvoke.Latest.Partial.cs
│ │ │ ├── BeginInvokePairedWithEndInvoke.Latest.cs
│ │ │ ├── BeginInvokePairedWithEndInvoke.Partial.vb
│ │ │ ├── BeginInvokePairedWithEndInvoke.cs
│ │ │ ├── BeginInvokePairedWithEndInvoke.vb
│ │ │ ├── BinaryOperationWithIdenticalExpressions.CSharpLatest.cs
│ │ │ ├── BinaryOperationWithIdenticalExpressions.cs
│ │ │ ├── BinaryOperationWithIdenticalExpressions.vb
│ │ │ ├── BlazorQueryParameterRoutableComponent.Compliant.cs
│ │ │ ├── BlazorQueryParameterRoutableComponent.Latest.Partial.1.razor.cs
│ │ │ ├── BlazorQueryParameterRoutableComponent.Latest.Partial.2.razor.cs
│ │ │ ├── BlazorQueryParameterRoutableComponent.Latest.Partial.razor
│ │ │ ├── BlazorQueryParameterRoutableComponent.NoRoute.razor
│ │ │ ├── BlazorQueryParameterRoutableComponent.Noncompliant.cs
│ │ │ ├── BlazorQueryParameterRoutableComponent.razor
│ │ │ ├── BooleanCheckInverted.Fixed.Batch.cs
│ │ │ ├── BooleanCheckInverted.Fixed.cs
│ │ │ ├── BooleanCheckInverted.Latest.cs
│ │ │ ├── BooleanCheckInverted.cs
│ │ │ ├── BooleanCheckInverted.vb
│ │ │ ├── BooleanLiteralUnnecessary.Fixed.cs
│ │ │ ├── BooleanLiteralUnnecessary.Latest.Fixed.cs
│ │ │ ├── BooleanLiteralUnnecessary.Latest.cs
│ │ │ ├── BooleanLiteralUnnecessary.cs
│ │ │ ├── BooleanLiteralUnnecessary.vb
│ │ │ ├── BreakOutsideSwitch.cs
│ │ │ ├── BypassingAccessibility.Latest.cs
│ │ │ ├── BypassingAccessibility.cs
│ │ │ ├── BypassingAccessibility.vb
│ │ │ ├── CallToAsyncMethodShouldNotBeBlocking.Latest.cs
│ │ │ ├── CallToAsyncMethodShouldNotBeBlocking.TopLevelStatements.cs
│ │ │ ├── CallToAsyncMethodShouldNotBeBlocking.cs
│ │ │ ├── CallerInformationParametersShouldBeLast.Latest.Partial.cs
│ │ │ ├── CallerInformationParametersShouldBeLast.Latest.cs
│ │ │ ├── CallerInformationParametersShouldBeLast.cs
│ │ │ ├── CallerInformationParametersShouldBeLastInvalidSyntax.cs
│ │ │ ├── CastConcreteTypeToInterface.cs
│ │ │ ├── CastShouldNotBeDuplicated.Latest.cs
│ │ │ ├── CastShouldNotBeDuplicated.cs
│ │ │ ├── CastShouldNotBeDuplicated.cshtml
│ │ │ ├── CatchEmpty.cs
│ │ │ ├── CatchRethrow.Fixed.cs
│ │ │ ├── CatchRethrow.cs
│ │ │ ├── CatchRethrow.vb
│ │ │ ├── CertificateValidationCheck.Latest.Partial.cs
│ │ │ ├── CertificateValidationCheck.Latest.cs
│ │ │ ├── CertificateValidationCheck.TopLevelStatements.cs
│ │ │ ├── CertificateValidationCheck.cs
│ │ │ ├── CertificateValidationCheck.vb
│ │ │ ├── CheckArgumentException.Latest.cs
│ │ │ ├── CheckArgumentException.TopLevelStatements.cs
│ │ │ ├── CheckArgumentException.cs
│ │ │ ├── CheckFileLicense_CSharp9.Fixed.cs
│ │ │ ├── CheckFileLicense_CSharp9.cs
│ │ │ ├── CheckFileLicense_ComplexRegex.cs
│ │ │ ├── CheckFileLicense_Compliant.vb
│ │ │ ├── CheckFileLicense_DefaultValues.Fixed.cs
│ │ │ ├── CheckFileLicense_DefaultValues.cs
│ │ │ ├── CheckFileLicense_EmptyFile.cs
│ │ │ ├── CheckFileLicense_ForcingEmptyLinesKo.cs
│ │ │ ├── CheckFileLicense_ForcingEmptyLinesOk.cs
│ │ │ ├── CheckFileLicense_MultiLineLicenseStartWithNamespace.cs
│ │ │ ├── CheckFileLicense_MultiLineLicenseStartWithUsing.cs
│ │ │ ├── CheckFileLicense_MultiSingleLineLicenseStartWithAdditionalComment.cs
│ │ │ ├── CheckFileLicense_MultiSingleLineLicenseStartWithAdditionalCommentOnSameLine.cs
│ │ │ ├── CheckFileLicense_MultiSingleLineLicenseStartWithNamespace.cs
│ │ │ ├── CheckFileLicense_NoLicenseStartWithNamespace.Fixed.cs
│ │ │ ├── CheckFileLicense_NoLicenseStartWithNamespace.cs
│ │ │ ├── CheckFileLicense_NoLicenseStartWithUsing.Fixed.cs
│ │ │ ├── CheckFileLicense_NoLicenseStartWithUsing.cs
│ │ │ ├── CheckFileLicense_NonCompliant.vb
│ │ │ ├── CheckFileLicense_OutdatedLicenseStartWithNamespace.Fixed.cs
│ │ │ ├── CheckFileLicense_OutdatedLicenseStartWithNamespace.cs
│ │ │ ├── CheckFileLicense_OutdatedLicenseStartWithUsing.Fixed.cs
│ │ │ ├── CheckFileLicense_OutdatedLicenseStartWithUsing.cs
│ │ │ ├── CheckFileLicense_SingleLineLicenseStartWithNamespace.cs
│ │ │ ├── CheckFileLicense_SingleLineLicenseStartWithUsing.cs
│ │ │ ├── CheckFileLicense_YearDifference.Fixed.cs
│ │ │ ├── CheckFileLicense_YearDifference.cs
│ │ │ ├── ClassAndMethodName.MethodName.Latest.Partial.cs
│ │ │ ├── ClassAndMethodName.MethodName.Latest.cs
│ │ │ ├── ClassAndMethodName.MethodName.Partial.cs
│ │ │ ├── ClassAndMethodName.MethodName.cs
│ │ │ ├── ClassAndMethodName.Partial.cs
│ │ │ ├── ClassAndMethodName.Tests.cs
│ │ │ ├── ClassAndMethodName.TopLevelStatement.Test.cs
│ │ │ ├── ClassAndMethodName.TopLevelStatement.cs
│ │ │ ├── ClassAndMethodName.cs
│ │ │ ├── ClassAndMethodName.vb
│ │ │ ├── ClassNamedException.Interop.cs
│ │ │ ├── ClassNamedException.Interop.vb
│ │ │ ├── ClassNamedException.Latest.cs
│ │ │ ├── ClassNamedException.cs
│ │ │ ├── ClassNamedException.vb
│ │ │ ├── ClassNotInstantiatable.Latest.Partial.cs
│ │ │ ├── ClassNotInstantiatable.Latest.cs
│ │ │ ├── ClassNotInstantiatable.cs
│ │ │ ├── ClassNotInstantiatable.vb
│ │ │ ├── ClassShouldNotBeEmpty.Inheritance.cs
│ │ │ ├── ClassShouldNotBeEmpty.Inheritance.vb
│ │ │ ├── ClassShouldNotBeEmpty.Latest.Partial.cs
│ │ │ ├── ClassShouldNotBeEmpty.Latest.cs
│ │ │ ├── ClassShouldNotBeEmpty.cs
│ │ │ ├── ClassShouldNotBeEmpty.vb
│ │ │ ├── ClassWithEqualityShouldImplementIEquatable.Latest.cs
│ │ │ ├── ClassWithEqualityShouldImplementIEquatable.cs
│ │ │ ├── ClassWithOnlyStaticMember.Latest.Partial.cs
│ │ │ ├── ClassWithOnlyStaticMember.Latest.cs
│ │ │ ├── ClassWithOnlyStaticMember.TopLevelStatements.cs
│ │ │ ├── ClassWithOnlyStaticMember.cs
│ │ │ ├── CloudNative/
│ │ │ │ ├── AzureFunctionsCatchExceptions.cs
│ │ │ │ ├── AzureFunctionsLogFailures.cs
│ │ │ │ ├── AzureFunctionsReuseClients.ArmClient.cs
│ │ │ │ ├── AzureFunctionsReuseClients.CosmosClient.cs
│ │ │ │ ├── AzureFunctionsReuseClients.DocumentClient.cs
│ │ │ │ ├── AzureFunctionsReuseClients.HttpClient.CSharp9.cs
│ │ │ │ ├── AzureFunctionsReuseClients.HttpClient.cs
│ │ │ │ ├── AzureFunctionsReuseClients.ServiceBusV5.cs
│ │ │ │ ├── AzureFunctionsReuseClients.ServiceBusV7.cs
│ │ │ │ ├── AzureFunctionsReuseClients.Storage.cs
│ │ │ │ ├── AzureFunctionsStateless.Latest.Partial.cs
│ │ │ │ ├── AzureFunctionsStateless.Latest.cs
│ │ │ │ ├── AzureFunctionsStateless.cs
│ │ │ │ ├── DurableEntityInterfaceRestrictions.CSharp11.cs
│ │ │ │ └── DurableEntityInterfaceRestrictions.cs
│ │ │ ├── CognitiveComplexity.Latest.Partial.cs
│ │ │ ├── CognitiveComplexity.Latest.cs
│ │ │ ├── CognitiveComplexity.cs
│ │ │ ├── CognitiveComplexity.vb
│ │ │ ├── CollectionEmptinessChecking.Fixed.cs
│ │ │ ├── CollectionEmptinessChecking.Latest.cs
│ │ │ ├── CollectionEmptinessChecking.cs
│ │ │ ├── CollectionEmptinessChecking.vb
│ │ │ ├── CollectionPropertiesShouldBeReadOnly.Latest.cs
│ │ │ ├── CollectionPropertiesShouldBeReadOnly.cs
│ │ │ ├── CollectionPropertiesShouldBeReadOnly.razor
│ │ │ ├── CollectionPropertiesShouldBeReadOnly.razor.cs
│ │ │ ├── CollectionQuerySimplification.Latest.cs
│ │ │ ├── CollectionQuerySimplification.NetFx.cs
│ │ │ ├── CollectionQuerySimplification.TopLevelStatements.cs
│ │ │ ├── CollectionQuerySimplification.cs
│ │ │ ├── CollectionsShouldImplementGenericInterface.CSharp10.cs
│ │ │ ├── CollectionsShouldImplementGenericInterface.CSharp9.cs
│ │ │ ├── CollectionsShouldImplementGenericInterface.cs
│ │ │ ├── CommentFixme.cs
│ │ │ ├── CommentFixme.vb
│ │ │ ├── CommentLineEnd.vb
│ │ │ ├── CommentTodo.cs
│ │ │ ├── CommentTodo.vb
│ │ │ ├── CommentedOutCode.MultiLine.Fixed.cs
│ │ │ ├── CommentedOutCode.MultiLine.ToFix.cs
│ │ │ ├── CommentedOutCode.SingleLine.Fixed.cs
│ │ │ ├── CommentedOutCode.SingleLine.ToFix.cs
│ │ │ ├── CommentedOutCode.cs
│ │ │ ├── CommentedOutCode_Nonconcurrent.cs
│ │ │ ├── CommentsShouldNotBeEmpty.cs
│ │ │ ├── CommentsShouldNotBeEmpty.vb
│ │ │ ├── ComparableInterfaceImplementation.Latest.cs
│ │ │ ├── ComparableInterfaceImplementation.cs
│ │ │ ├── CompareNaN.Latest.cs
│ │ │ ├── CompareNaN.cs
│ │ │ ├── ConditionalSimplification.BeforeCSharp8.Fixed.cs
│ │ │ ├── ConditionalSimplification.BeforeCSharp8.cs
│ │ │ ├── ConditionalSimplification.CSharp8.Fixed.cs
│ │ │ ├── ConditionalSimplification.CSharp8.cs
│ │ │ ├── ConditionalSimplification.FromCSharp10.Fixed.cs
│ │ │ ├── ConditionalSimplification.FromCSharp10.cs
│ │ │ ├── ConditionalSimplification.FromCSharp8.Fixed.cs
│ │ │ ├── ConditionalSimplification.FromCSharp8.cs
│ │ │ ├── ConditionalSimplification.FromCSharp9.Fixed.cs
│ │ │ ├── ConditionalSimplification.FromCSharp9.cs
│ │ │ ├── ConditionalStructureSameCondition.CSharp10.cs
│ │ │ ├── ConditionalStructureSameCondition.CSharp9.cs
│ │ │ ├── ConditionalStructureSameCondition.cs
│ │ │ ├── ConditionalStructureSameCondition.vb
│ │ │ ├── ConditionalStructureSameImplementation_If.Latest.cs
│ │ │ ├── ConditionalStructureSameImplementation_If.cs
│ │ │ ├── ConditionalStructureSameImplementation_If.vb
│ │ │ ├── ConditionalStructureSameImplementation_Switch.Latest.cs
│ │ │ ├── ConditionalStructureSameImplementation_Switch.cs
│ │ │ ├── ConditionalStructureSameImplementation_Switch.vb
│ │ │ ├── ConditionalsShouldStartOnNewLine.Latest.cs
│ │ │ ├── ConditionalsShouldStartOnNewLine.cs
│ │ │ ├── ConditionalsWithSameCondition.CSharp10.cs
│ │ │ ├── ConditionalsWithSameCondition.CSharp9.TopLevelStatements.cs
│ │ │ ├── ConditionalsWithSameCondition.CSharp9.cs
│ │ │ ├── ConditionalsWithSameCondition.cs
│ │ │ ├── ConstructorArgumentValueShouldExist.Latest.Partial.cs
│ │ │ ├── ConstructorArgumentValueShouldExist.Latest.cs
│ │ │ ├── ConstructorArgumentValueShouldExist.cs
│ │ │ ├── ConstructorArgumentValueShouldExist.vb
│ │ │ ├── ConstructorOverridableCall.Latest.Partial.cs
│ │ │ ├── ConstructorOverridableCall.Latest.cs
│ │ │ ├── ConstructorOverridableCall.Nancy.cs
│ │ │ ├── ConstructorOverridableCall.cs
│ │ │ ├── ConsumeValueTaskCorrectly.Latest.cs
│ │ │ ├── ConsumeValueTaskCorrectly.cs
│ │ │ ├── ControlCharacterInString.Latest.cs
│ │ │ ├── ControlCharacterInString.cs
│ │ │ ├── CryptographicKeyShouldNotBeTooShort.BeforeNet7.cs
│ │ │ ├── CryptographicKeyShouldNotBeTooShort.Latest.cs
│ │ │ ├── CryptographicKeyShouldNotBeTooShort.cs
│ │ │ ├── DangerousGetHandleShouldNotBeCalled.CSharp9.cs
│ │ │ ├── DangerousGetHandleShouldNotBeCalled.cs
│ │ │ ├── DangerousGetHandleShouldNotBeCalled.vb
│ │ │ ├── DatabasePasswordsShouldBeSecure.Latest.cs
│ │ │ ├── DatabasePasswordsShouldBeSecure.Net5.cs
│ │ │ ├── DatabasePasswordsShouldBeSecure.NetCore31.cs
│ │ │ ├── DatabasePasswordsShouldBeSecure.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.EntityFrameworkCore.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.EntityFrameworkCore.vb
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.FluentApi.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.FluentApi.vb
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.Latest.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.NoReferenceToEntityFramework.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.NoReferenceToEntityFramework.vb
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.cs
│ │ │ ├── DateAndTimeShouldNotBeUsedasTypeForPrimaryKey.vb
│ │ │ ├── DateTimeFormatShouldNotBeHardcoded.Latest.cs
│ │ │ ├── DateTimeFormatShouldNotBeHardcoded.Net.vb
│ │ │ ├── DateTimeFormatShouldNotBeHardcoded.cs
│ │ │ ├── DateTimeFormatShouldNotBeHardcoded.vb
│ │ │ ├── DeadStores.Latest.cs
│ │ │ ├── DeadStores.RoslynCfg.cs
│ │ │ ├── DeadStores.SonarCfg.cs
│ │ │ ├── DebugAssertHasNoSideEffects.Latest.cs
│ │ │ ├── DebugAssertHasNoSideEffects.cs
│ │ │ ├── DebuggerDisplayUsesExistingMembers.Latest.cs
│ │ │ ├── DebuggerDisplayUsesExistingMembers.cs
│ │ │ ├── DebuggerDisplayUsesExistingMembers.vb
│ │ │ ├── DeclareEventHandlersCorrectly.cs
│ │ │ ├── DeclareTypesInNamespaces.FileScopedNamespace.cs
│ │ │ ├── DeclareTypesInNamespaces.Latest.cs
│ │ │ ├── DeclareTypesInNamespaces.TopLevelStatements.Partial.cs
│ │ │ ├── DeclareTypesInNamespaces.TopLevelStatements.cs
│ │ │ ├── DeclareTypesInNamespaces.cs
│ │ │ ├── DeclareTypesInNamespaces.vb
│ │ │ ├── DeclareTypesInNamespaces2.cs
│ │ │ ├── DeclareTypesInNamespaces2.vb
│ │ │ ├── DefaultSectionShouldBeFirstOrLast.CSharp9.cs
│ │ │ ├── DefaultSectionShouldBeFirstOrLast.cs
│ │ │ ├── DelegateSubtraction.Latest.cs
│ │ │ ├── DelegateSubtraction.TopLevelStatements.cs
│ │ │ ├── DelegateSubtraction.cs
│ │ │ ├── DisposableMemberInNonDisposableClass.CSharp9.cs
│ │ │ ├── DisposableMemberInNonDisposableClass.NetCore.cs
│ │ │ ├── DisposableMemberInNonDisposableClass.cs
│ │ │ ├── DisposableNotDisposed.ILogger.cs
│ │ │ ├── DisposableNotDisposed.Latest.cs
│ │ │ ├── DisposableNotDisposed.TopLevelStatements.cs
│ │ │ ├── DisposableNotDisposed.cs
│ │ │ ├── DisposableReturnedFromUsing.Latest.cs
│ │ │ ├── DisposableReturnedFromUsing.TopLevelStatements.cs
│ │ │ ├── DisposableReturnedFromUsing.cs
│ │ │ ├── DisposableTypesNeedFinalizers.CSharp11.cs
│ │ │ ├── DisposableTypesNeedFinalizers.CSharp9.cs
│ │ │ ├── DisposableTypesNeedFinalizers.cs
│ │ │ ├── DisposeFromDispose.CSharp10.cs
│ │ │ ├── DisposeFromDispose.CSharp7_2.cs
│ │ │ ├── DisposeFromDispose.CSharp8.cs
│ │ │ ├── DisposeFromDispose.CSharp9.Part1.cs
│ │ │ ├── DisposeFromDispose.CSharp9.Part2.cs
│ │ │ ├── DisposeNotImplementingDispose.Latest.Partial.cs
│ │ │ ├── DisposeNotImplementingDispose.Latest.cs
│ │ │ ├── DisposeNotImplementingDispose.TopLevelStatements.cs
│ │ │ ├── DisposeNotImplementingDispose.cs
│ │ │ ├── DoNotCallAssemblyGetExecutingAssembly.CSharp9.cs
│ │ │ ├── DoNotCallAssemblyGetExecutingAssembly.cs
│ │ │ ├── DoNotCallAssemblyLoadInvalidMethods.CSharp9.cs
│ │ │ ├── DoNotCallAssemblyLoadInvalidMethods.Evidence.cs
│ │ │ ├── DoNotCallAssemblyLoadInvalidMethods.cs
│ │ │ ├── DoNotCallExitMethods.CSharp9.cs
│ │ │ ├── DoNotCallExitMethods.cs
│ │ │ ├── DoNotCallGCCollectMethod.Latest.cs
│ │ │ ├── DoNotCallGCCollectMethod.cs
│ │ │ ├── DoNotCallGCCollectMethodAD0001.cs
│ │ │ ├── DoNotCallGCSuppressFinalize.Net5.cs
│ │ │ ├── DoNotCallGCSuppressFinalize.NetCore.cs
│ │ │ ├── DoNotCallGCSuppressFinalize.cs
│ │ │ ├── DoNotCatchNullReferenceException.CSharp9.cs
│ │ │ ├── DoNotCatchNullReferenceException.cs
│ │ │ ├── DoNotCatchSystemException.CSharp10.cs
│ │ │ ├── DoNotCatchSystemException.CSharp9.cs
│ │ │ ├── DoNotCatchSystemException.cs
│ │ │ ├── DoNotCheckZeroSizeCollection.Latest.cs
│ │ │ ├── DoNotCheckZeroSizeCollection.cs
│ │ │ ├── DoNotCheckZeroSizeCollection.vb
│ │ │ ├── DoNotCopyArraysInProperties.Latest.cs
│ │ │ ├── DoNotCopyArraysInProperties.cs
│ │ │ ├── DoNotDecreaseMemberVisibility.Concurrent.cs
│ │ │ ├── DoNotDecreaseMemberVisibility.Latest.Partial.cs
│ │ │ ├── DoNotDecreaseMemberVisibility.Latest.cs
│ │ │ ├── DoNotDecreaseMemberVisibility.cs
│ │ │ ├── DoNotExposeListT.Latest.cs
│ │ │ ├── DoNotExposeListT.cs
│ │ │ ├── DoNotHardcodeCredentials.CustomValues.cs
│ │ │ ├── DoNotHardcodeCredentials.CustomValues.vb
│ │ │ ├── DoNotHardcodeCredentials.DefaultValues.Latest.cs
│ │ │ ├── DoNotHardcodeCredentials.DefaultValues.cs
│ │ │ ├── DoNotHardcodeCredentials.DefaultValues.vb
│ │ │ ├── DoNotHardcodeCredentials.SecureString.cs
│ │ │ ├── DoNotHardcodeCredentials.SecureString.vb
│ │ │ ├── DoNotHardcodeSecrets.Latest.cs
│ │ │ ├── DoNotHardcodeSecrets.cs
│ │ │ ├── DoNotHardcodeSecrets.vb
│ │ │ ├── DoNotHideBaseClassMethods.Concurrent.cs
│ │ │ ├── DoNotHideBaseClassMethods.Latest.cs
│ │ │ ├── DoNotHideBaseClassMethods.cs
│ │ │ ├── DoNotInstantiateSharedClasses.cs
│ │ │ ├── DoNotInstantiateSharedClasses.vb
│ │ │ ├── DoNotLockOnSharedResource.Latest.cs
│ │ │ ├── DoNotLockOnSharedResource.cs
│ │ │ ├── DoNotLockOnSharedResource.vb
│ │ │ ├── DoNotLockWeakIdentityObjects.Latest.cs
│ │ │ ├── DoNotLockWeakIdentityObjects.cs
│ │ │ ├── DoNotLockWeakIdentityObjects.vb
│ │ │ ├── DoNotMarkEnumsWithFlags.cs
│ │ │ ├── DoNotNestTernaryOperators.cs
│ │ │ ├── DoNotNestTernaryOperators.vb
│ │ │ ├── DoNotNestTypesInArguments.Latest.cs
│ │ │ ├── DoNotNestTypesInArguments.Latest.partial.cs
│ │ │ ├── DoNotNestTypesInArguments.cs
│ │ │ ├── DoNotOverloadOperatorEqual.Latest.cs
│ │ │ ├── DoNotOverloadOperatorEqual.cs
│ │ │ ├── DoNotOverwriteCollectionElements.Latest.cs
│ │ │ ├── DoNotOverwriteCollectionElements.cs
│ │ │ ├── DoNotOverwriteCollectionElements.vb
│ │ │ ├── DoNotShiftByZeroOrIntSize.CSharp10.cs
│ │ │ ├── DoNotShiftByZeroOrIntSize.CSharp11.cs
│ │ │ ├── DoNotShiftByZeroOrIntSize.CSharp9.cs
│ │ │ ├── DoNotShiftByZeroOrIntSize.cs
│ │ │ ├── DoNotTestThisWithIsOperator.CSharp10.cs
│ │ │ ├── DoNotTestThisWithIsOperator.CSharp11.cs
│ │ │ ├── DoNotTestThisWithIsOperator.CSharp9.cs
│ │ │ ├── DoNotTestThisWithIsOperator.cs
│ │ │ ├── DoNotThrowFromDestructors.CSharp9.cs
│ │ │ ├── DoNotThrowFromDestructors.cs
│ │ │ ├── DoNotThrowFromDestructors.vb
│ │ │ ├── DoNotUseByVal.Fixed.vb
│ │ │ ├── DoNotUseByVal.vb
│ │ │ ├── DoNotUseCollectionInItsOwnMethodCalls.cs
│ │ │ ├── DoNotUseDateTimeNow.cs
│ │ │ ├── DoNotUseDateTimeNow.vb
│ │ │ ├── DoNotUseIIf.Fixed.vb
│ │ │ ├── DoNotUseIIf.vb
│ │ │ ├── DoNotUseLiteralBoolInAssertions.MsTest.cs
│ │ │ ├── DoNotUseLiteralBoolInAssertions.NUnit.cs
│ │ │ ├── DoNotUseLiteralBoolInAssertions.NUnit4.cs
│ │ │ ├── DoNotUseLiteralBoolInAssertions.Xunit.cs
│ │ │ ├── DoNotUseLiteralBoolInAssertions.XunitV3.cs
│ │ │ ├── DoNotUseOutRefParameters.CSharp11.cs
│ │ │ ├── DoNotUseOutRefParameters.CSharp9.cs
│ │ │ ├── DoNotUseOutRefParameters.cs
│ │ │ ├── DoNotWriteToStandardOutput.cs
│ │ │ ├── DoNotWriteToStandardOutput_Conditionals1.cs
│ │ │ ├── DoNotWriteToStandardOutput_Conditionals2.cs
│ │ │ ├── DontMixIncrementOrDecrementWithOtherOperators.Latest.cs
│ │ │ ├── DontMixIncrementOrDecrementWithOtherOperators.cs
│ │ │ ├── DontUseTraceSwitchLevels.cs
│ │ │ ├── DontUseTraceWrite.cs
│ │ │ ├── EmptyMethod.CSharp9.Comment.Fixed.cs
│ │ │ ├── EmptyMethod.CSharp9.Throw.Fixed.cs
│ │ │ ├── EmptyMethod.CSharp9.cs
│ │ │ ├── EmptyMethod.Comment.Fixed.cs
│ │ │ ├── EmptyMethod.Latest.cs
│ │ │ ├── EmptyMethod.OverrideVirtual.cs
│ │ │ ├── EmptyMethod.OverrideVirtual.vb
│ │ │ ├── EmptyMethod.Throw.Fixed.cs
│ │ │ ├── EmptyMethod.WithoutClosingBracket.Comment.Fixed.cs
│ │ │ ├── EmptyMethod.WithoutClosingBracket.cs
│ │ │ ├── EmptyMethod.cs
│ │ │ ├── EmptyMethod.vb
│ │ │ ├── EmptyNamespace.CSharp10.Empty.cs
│ │ │ ├── EmptyNamespace.CSharp10.Fixed.cs
│ │ │ ├── EmptyNamespace.CSharp10.NotEmpty.cs
│ │ │ ├── EmptyNamespace.Fixed.Batch.cs
│ │ │ ├── EmptyNamespace.Fixed.cs
│ │ │ ├── EmptyNamespace.cs
│ │ │ ├── EmptyNestedBlock.Latest.cs
│ │ │ ├── EmptyNestedBlock.cs
│ │ │ ├── EmptyNestedBlock.vb
│ │ │ ├── EmptyNestedBlock2.cs
│ │ │ ├── EmptyStatement.Fixed.cs
│ │ │ ├── EmptyStatement.Latest.cs
│ │ │ ├── EmptyStatement.TopLevelStatements.Fixed.cs
│ │ │ ├── EmptyStatement.TopLevelStatements.cs
│ │ │ ├── EmptyStatement.cs
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure.cs
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure.vb
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure_NetStandard21.Net48.vb
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure_NetStandard21.Net7.vb
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure_NetStandard21.cs
│ │ │ ├── EncryptionAlgorithmsShouldBeSecure_NetStandard21.vb
│ │ │ ├── EndStatementUsage.vb
│ │ │ ├── EnumNameHasEnumSuffix.cs
│ │ │ ├── EnumNameHasEnumSuffix.vb
│ │ │ ├── EnumNameShouldFollowRegex.cs
│ │ │ ├── EnumNameShouldFollowRegex.vb
│ │ │ ├── EnumStorageNeedsToBeInt32.cs
│ │ │ ├── EnumerableSumInUnchecked.CSharp11.cs
│ │ │ ├── EnumerableSumInUnchecked.CSharp9.cs
│ │ │ ├── EnumerableSumInUnchecked.cs
│ │ │ ├── EnumerationValueName.vb
│ │ │ ├── EnumsShouldNotBeNamedReserved.cs
│ │ │ ├── EqualityOnFloatingPoint.CSharp11.cs
│ │ │ ├── EqualityOnFloatingPoint.cs
│ │ │ ├── EqualityOnModulus.CSharp11.cs
│ │ │ ├── EqualityOnModulus.CSharp9.cs
│ │ │ ├── EqualityOnModulus.cs
│ │ │ ├── EquatableClassShouldBeSealed.CSharp9.cs
│ │ │ ├── EquatableClassShouldBeSealed.cs
│ │ │ ├── EscapeLambdaParameterTypeNamedScoped.CSharp11-13.cs
│ │ │ ├── EscapeLambdaParameterTypeNamedScoped.Latest.cs
│ │ │ ├── EscapeLambdaParameterTypeNamedScoped.TopLevelStatements.CSharp11-13.cs
│ │ │ ├── EscapeLambdaParameterTypeNamedScoped.TopLevelStatements.Latest.cs
│ │ │ ├── EventHandlerDelegateShouldHaveProperArguments.Latest.Partial.cs
│ │ │ ├── EventHandlerDelegateShouldHaveProperArguments.Latest.cs
│ │ │ ├── EventHandlerDelegateShouldHaveProperArguments.cs
│ │ │ ├── EventHandlerName.vb
│ │ │ ├── EventName.vb
│ │ │ ├── EventNameContainsBeforeOrAfter.vb
│ │ │ ├── ExceptionRethrow.Fixed.cs
│ │ │ ├── ExceptionRethrow.cs
│ │ │ ├── ExceptionShouldNotBeThrownFromUnexpectedMethods.Latest.Partial.cs
│ │ │ ├── ExceptionShouldNotBeThrownFromUnexpectedMethods.Latest.cs
│ │ │ ├── ExceptionShouldNotBeThrownFromUnexpectedMethods.cs
│ │ │ ├── ExceptionsNeedStandardConstructors.cs
│ │ │ ├── ExceptionsShouldBeLogged.cs
│ │ │ ├── ExceptionsShouldBeLoggedOrThrown.cs
│ │ │ ├── ExceptionsShouldBePublic.cs
│ │ │ ├── ExceptionsShouldBePublic.vb
│ │ │ ├── ExceptionsShouldBeUsed.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.CSharp10.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.CSharp9.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.net48.cs
│ │ │ ├── ExcludeFromCodeCoverageAttributesNeedJustification.vb
│ │ │ ├── ExitStatementUsage.vb
│ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.MsTest.cs
│ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.MsTest.vb
│ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.NUnit.cs
│ │ │ ├── ExpectedExceptionAttributeShouldNotBeUsed.NUnit.vb
│ │ │ ├── ExpressionComplexity.CSharp10.cs
│ │ │ ├── ExpressionComplexity.CSharp11.cs
│ │ │ ├── ExpressionComplexity.CSharp9.cs
│ │ │ ├── ExpressionComplexity.cs
│ │ │ ├── ExpressionComplexity.vb
│ │ │ ├── ExtensionMethodShouldBeInSeparateNamespace.CSharp10.cs
│ │ │ ├── ExtensionMethodShouldBeInSeparateNamespace.CSharp9.cs
│ │ │ ├── ExtensionMethodShouldBeInSeparateNamespace.GeneratedCode.cs
│ │ │ ├── ExtensionMethodShouldBeInSeparateNamespace.cs
│ │ │ ├── ExtensionMethodShouldNotExtendObject.cs
│ │ │ ├── ExtensionMethodShouldNotExtendObject.vb
│ │ │ ├── FieldShadowsParentField.CSharp9.cs
│ │ │ ├── FieldShadowsParentField.cs
│ │ │ ├── FieldShadowsParentField.vb
│ │ │ ├── FieldShouldBeReadonly.Fixed.cs
│ │ │ ├── FieldShouldBeReadonly.Latest.cs
│ │ │ ├── FieldShouldBeReadonly.cs
│ │ │ ├── FieldShouldNotBePublic.CSharp9.cs
│ │ │ ├── FieldShouldNotBePublic.cs
│ │ │ ├── FieldShouldNotBePublic.vb
│ │ │ ├── FieldsShouldBeEncapsulatedInProperties.CSharp12.cs
│ │ │ ├── FieldsShouldBeEncapsulatedInProperties.CSharp9.cs
│ │ │ ├── FieldsShouldBeEncapsulatedInProperties.Unity3D.cs
│ │ │ ├── FieldsShouldBeEncapsulatedInProperties.cs
│ │ │ ├── FieldsShouldNotDifferByCapitalization.CSharp9.cs
│ │ │ ├── FieldsShouldNotDifferByCapitalization.cs
│ │ │ ├── FieldsShouldNotDifferByCapitalization.vb
│ │ │ ├── FileLines20.cs
│ │ │ ├── FileLines20.vb
│ │ │ ├── FileLines9.cs
│ │ │ ├── FileLines9.vb
│ │ │ ├── FileShouldEndWithEmptyNewLine_EmptyFile.cs
│ │ │ ├── FileShouldEndWithEmptyNewLine_EmptyLine.cs
│ │ │ ├── FileShouldEndWithEmptyNewLine_NoEmptyLine.cs
│ │ │ ├── FinalizerShouldNotBeEmpty.CSharp9.cs
│ │ │ ├── FinalizerShouldNotBeEmpty.cs
│ │ │ ├── FindInsteadOfFirstOrDefault.Array.cs
│ │ │ ├── FindInsteadOfFirstOrDefault.Immutable.cs
│ │ │ ├── FindInsteadOfFirstOrDefault.Net.cs
│ │ │ ├── FindInsteadOfFirstOrDefault.Net.vb
│ │ │ ├── FindInsteadOfFirstOrDefault.cs
│ │ │ ├── FindInsteadOfFirstOrDefault.vb
│ │ │ ├── FlagsEnumWithoutInitializer.cs
│ │ │ ├── FlagsEnumWithoutInitializer.vb
│ │ │ ├── FlagsEnumZeroMember.cs
│ │ │ ├── FlagsEnumZeroMember.vb
│ │ │ ├── ForLoopConditionAlwaysFalse.CSharp11.cs
│ │ │ ├── ForLoopConditionAlwaysFalse.CSharp9.cs
│ │ │ ├── ForLoopConditionAlwaysFalse.cs
│ │ │ ├── ForLoopCounterChanged.Latest.cs
│ │ │ ├── ForLoopCounterChanged.cs
│ │ │ ├── ForLoopCounterCondition.cs
│ │ │ ├── ForLoopIncrementSign.cs
│ │ │ ├── ForeachLoopExplicitConversion.Fixed.cs
│ │ │ ├── ForeachLoopExplicitConversion.Latest.Fixed.cs
│ │ │ ├── ForeachLoopExplicitConversion.Latest.cs
│ │ │ ├── ForeachLoopExplicitConversion.cs
│ │ │ ├── FrameworkTypeNaming.cs
│ │ │ ├── FunctionComplexity.Latest.cs
│ │ │ ├── FunctionComplexity.cs
│ │ │ ├── FunctionComplexity.vb
│ │ │ ├── FunctionName.vb
│ │ │ ├── FunctionNestingDepth.CSharp9.cs
│ │ │ ├── FunctionNestingDepth.cs
│ │ │ ├── FunctionNestingDepth.vb
│ │ │ ├── GenericInheritanceShouldNotBeRecursive.CSharp9.cs
│ │ │ ├── GenericInheritanceShouldNotBeRecursive.cs
│ │ │ ├── GenericInheritanceShouldNotBeRecursive.vb
│ │ │ ├── GenericLoggerInjectionShouldMatchEnclosingType.Latest.cs
│ │ │ ├── GenericLoggerInjectionShouldMatchEnclosingType.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignment.AddConstraint.Fixed.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignment.Latest.Remove.Fixed.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignment.Latest.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignment.Remove.Fixed.cs
│ │ │ ├── GenericReadonlyFieldPropertyAssignment.cs
│ │ │ ├── GenericTypeParameterEmptinessChecking.Fixed.cs
│ │ │ ├── GenericTypeParameterEmptinessChecking.Latest.cs
│ │ │ ├── GenericTypeParameterEmptinessChecking.cs
│ │ │ ├── GenericTypeParameterInOut.Latest.cs
│ │ │ ├── GenericTypeParameterInOut.cs
│ │ │ ├── GenericTypeParameterUnused.Latest.Partial.cs
│ │ │ ├── GenericTypeParameterUnused.Latest.cs
│ │ │ ├── GenericTypeParameterUnused.Partial.cs
│ │ │ ├── GenericTypeParameterUnused.cs
│ │ │ ├── GenericTypeParametersRequired.cs
│ │ │ ├── GetHashCodeEqualsOverride.Latest.cs
│ │ │ ├── GetHashCodeEqualsOverride.cs
│ │ │ ├── GetHashCodeMutable.Fixed.cs
│ │ │ ├── GetHashCodeMutable.Latest.cs
│ │ │ ├── GetHashCodeMutable.cs
│ │ │ ├── GetTypeWithIsAssignableFrom.Fixed.Batch.cs
│ │ │ ├── GetTypeWithIsAssignableFrom.Fixed.cs
│ │ │ ├── GetTypeWithIsAssignableFrom.Latest.Fixed.cs
│ │ │ ├── GetTypeWithIsAssignableFrom.Latest.cs
│ │ │ ├── GetTypeWithIsAssignableFrom.cs
│ │ │ ├── GotoStatement.cs
│ │ │ ├── GotoStatement.vb
│ │ │ ├── GuardConditionOnEqualsOverride.cs
│ │ │ ├── Hotspots/
│ │ │ │ ├── ClearTextProtocolsAreSensitive.Latest.cs
│ │ │ │ ├── ClearTextProtocolsAreSensitive.NetFramework.cs
│ │ │ │ ├── ClearTextProtocolsAreSensitive.TopLevelStatements.cs
│ │ │ │ ├── ClearTextProtocolsAreSensitive.cs
│ │ │ │ ├── CommandPath.Latest.cs
│ │ │ │ ├── CommandPath.cs
│ │ │ │ ├── CommandPath.vb
│ │ │ │ ├── ConfiguringLoggers_AspNetCore.cs
│ │ │ │ ├── ConfiguringLoggers_AspNetCore.vb
│ │ │ │ ├── ConfiguringLoggers_AspNetCore6.cs
│ │ │ │ ├── ConfiguringLoggers_Log4Net.cs
│ │ │ │ ├── ConfiguringLoggers_Log4Net.vb
│ │ │ │ ├── ConfiguringLoggers_NLog.cs
│ │ │ │ ├── ConfiguringLoggers_NLog.vb
│ │ │ │ ├── ConfiguringLoggers_Serilog.cs
│ │ │ │ ├── ConfiguringLoggers_Serilog.vb
│ │ │ │ ├── CookieShouldBeHttpOnly.Latest.cs
│ │ │ │ ├── CookieShouldBeHttpOnly.TopLevelStatements.cs
│ │ │ │ ├── CookieShouldBeHttpOnly.cs
│ │ │ │ ├── CookieShouldBeHttpOnly_Nancy.cs
│ │ │ │ ├── CookieShouldBeHttpOnly_WithWebConfig.cs
│ │ │ │ ├── CookieShouldBeSecure.Latest.cs
│ │ │ │ ├── CookieShouldBeSecure.TopLevelStatements.cs
│ │ │ │ ├── CookieShouldBeSecure.cs
│ │ │ │ ├── CookieShouldBeSecure_Nancy.cs
│ │ │ │ ├── CookieShouldBeSecure_WithWebConfig.cs
│ │ │ │ ├── CreatingHashAlgorithms.Latest.cs
│ │ │ │ ├── CreatingHashAlgorithms.NET.vb
│ │ │ │ ├── CreatingHashAlgorithms.NetFramework.cs
│ │ │ │ ├── CreatingHashAlgorithms.NetFramework.vb
│ │ │ │ ├── CreatingHashAlgorithms.cs
│ │ │ │ ├── CreatingHashAlgorithms.vb
│ │ │ │ ├── DeliveringDebugFeaturesInProduction.Net7.cs
│ │ │ │ ├── DeliveringDebugFeaturesInProduction.NetCore2.cs
│ │ │ │ ├── DeliveringDebugFeaturesInProduction.NetCore2.vb
│ │ │ │ ├── DeliveringDebugFeaturesInProduction.NetCore3.cs
│ │ │ │ ├── DeliveringDebugFeaturesInProduction.NetCore3.vb
│ │ │ │ ├── DisablingCsrfProtection.Latest.cs
│ │ │ │ ├── DisablingRequestValidation.cs
│ │ │ │ ├── DisablingRequestValidation.vb
│ │ │ │ ├── DoNotUseRandom.cs
│ │ │ │ ├── ExecutingSqlQueries.AzureCosmos.cs
│ │ │ │ ├── ExecutingSqlQueries.Dapper.cs
│ │ │ │ ├── ExecutingSqlQueries.EF6.cs
│ │ │ │ ├── ExecutingSqlQueries.EntityFrameworkCore2.cs
│ │ │ │ ├── ExecutingSqlQueries.EntityFrameworkCore2.vb
│ │ │ │ ├── ExecutingSqlQueries.EntityFrameworkCoreLatest.cs
│ │ │ │ ├── ExecutingSqlQueries.EntityFrameworkCoreLatest.vb
│ │ │ │ ├── ExecutingSqlQueries.Latest.cs
│ │ │ │ ├── ExecutingSqlQueries.MicrosoftDataSqlClient.cs
│ │ │ │ ├── ExecutingSqlQueries.MySqlData.cs
│ │ │ │ ├── ExecutingSqlQueries.NHibernate.cs
│ │ │ │ ├── ExecutingSqlQueries.Net46.MonoSqlLite.cs
│ │ │ │ ├── ExecutingSqlQueries.Net46.MonoSqlLite.vb
│ │ │ │ ├── ExecutingSqlQueries.Net46.cs
│ │ │ │ ├── ExecutingSqlQueries.Net46.vb
│ │ │ │ ├── ExecutingSqlQueries.OracleManagedDataAccess.cs
│ │ │ │ ├── ExecutingSqlQueries.OrmLite.cs
│ │ │ │ ├── ExpandingArchives.Latest.cs
│ │ │ │ ├── ExpandingArchives.cs
│ │ │ │ ├── ExpandingArchives.vb
│ │ │ │ ├── HardcodedIpAddress.Latest.cs
│ │ │ │ ├── HardcodedIpAddress.cs
│ │ │ │ ├── HardcodedIpAddress.vb
│ │ │ │ ├── InsecureDeserialization.Latest.Partial.cs
│ │ │ │ ├── InsecureDeserialization.Latest.cs
│ │ │ │ ├── InsecureDeserialization.cs
│ │ │ │ ├── PermissiveCors.Latest.cs
│ │ │ │ ├── PermissiveCors.NetFramework.cs
│ │ │ │ ├── PubliclyWritableDirectories.Latest.cs
│ │ │ │ ├── PubliclyWritableDirectories.cs
│ │ │ │ ├── PubliclyWritableDirectories.vb
│ │ │ │ ├── RequestsWithExcessiveLength.Latest.cs
│ │ │ │ ├── RequestsWithExcessiveLength.cs
│ │ │ │ ├── RequestsWithExcessiveLength.vb
│ │ │ │ ├── RequestsWithExcessiveLength_CustomValues.cs
│ │ │ │ ├── RequestsWithExcessiveLength_CustomValues.vb
│ │ │ │ ├── SpecifyTimeoutOnRegex.DefaultMatchTimeout.cs
│ │ │ │ ├── SpecifyTimeoutOnRegex.Latest.cs
│ │ │ │ ├── SpecifyTimeoutOnRegex.cs
│ │ │ │ ├── SpecifyTimeoutOnRegex.vb
│ │ │ │ ├── UnsafeCodeBlocks.cs
│ │ │ │ ├── UsingNonstandardCryptography.CSharp10.cs
│ │ │ │ ├── UsingNonstandardCryptography.CSharp12.cs
│ │ │ │ ├── UsingNonstandardCryptography.CSharp9.cs
│ │ │ │ ├── UsingNonstandardCryptography.cs
│ │ │ │ └── UsingNonstandardCryptography.vb
│ │ │ ├── IdentifiersNamedExtensionShouldBeEscaped.BeforeCSharp14.cs
│ │ │ ├── IdentifiersNamedExtensionShouldBeEscaped.Latest.cs
│ │ │ ├── IdentifiersNamedFieldShouldBeEscaped.BeforeCSharp14.cs
│ │ │ ├── IdentifiersNamedFieldShouldBeEscaped.BeforeCSharp14.partial.cs
│ │ │ ├── IdentifiersNamedFieldShouldBeEscaped.CSharp9-13.cs
│ │ │ ├── IdentifiersNamedFieldShouldBeEscaped.Latest.cs
│ │ │ ├── IfChainWithoutElse.cs
│ │ │ ├── IfChainWithoutElse.vb
│ │ │ ├── IfCollapsible.cs
│ │ │ ├── IfCollapsible.vb
│ │ │ ├── ImplementIDisposableCorrectly.AbstractClass.cs
│ │ │ ├── ImplementIDisposableCorrectly.Latest.cs
│ │ │ ├── ImplementIDisposableCorrectly.cs
│ │ │ ├── ImplementIDisposableCorrectlyPartial1.cs
│ │ │ ├── ImplementIDisposableCorrectlyPartial2.cs
│ │ │ ├── ImplementISerializableCorrectly.Latest.Partial.cs
│ │ │ ├── ImplementISerializableCorrectly.Latest.cs
│ │ │ ├── ImplementISerializableCorrectly.cs
│ │ │ ├── ImplementSerializationMethodsCorrectly.Latest.cs
│ │ │ ├── ImplementSerializationMethodsCorrectly.cs
│ │ │ ├── ImplementSerializationMethodsCorrectly.vb
│ │ │ ├── IndentSingleLineFollowingConditional.Latest.cs
│ │ │ ├── IndentSingleLineFollowingConditional.cs
│ │ │ ├── IndexOfCheckAgainstZero.Latest.cs
│ │ │ ├── IndexOfCheckAgainstZero.cs
│ │ │ ├── IndexOfCheckAgainstZero.vb
│ │ │ ├── IndexedPropertyWithMultipleParameters.vb
│ │ │ ├── InfiniteRecursion.RoslynCfg.Latest.cs
│ │ │ ├── InfiniteRecursion.RoslynCfg.cs
│ │ │ ├── InfiniteRecursion.SonarCfg.cs
│ │ │ ├── InheritedCollidingInterfaceMembers.AnotherFile.cs
│ │ │ ├── InheritedCollidingInterfaceMembers.DifferentFile.cs
│ │ │ ├── InheritedCollidingInterfaceMembers.Latest.cs
│ │ │ ├── InheritedCollidingInterfaceMembers.cs
│ │ │ ├── InitializeStaticFieldsInline.cs
│ │ │ ├── InsecureContentSecurityPolicy.Latest.cs
│ │ │ ├── InsecureContentSecurityPolicy.cs
│ │ │ ├── InsecureEncryptionAlgorithm.Latest.cs
│ │ │ ├── InsecureEncryptionAlgorithm.Latest.vb
│ │ │ ├── InsecureEncryptionAlgorithm.cs
│ │ │ ├── InsecureEncryptionAlgorithm.vb
│ │ │ ├── InsecureTemporaryFilesCreation.Latest.cs
│ │ │ ├── InsecureTemporaryFilesCreation.cs
│ │ │ ├── InsecureTemporaryFilesCreation.vb
│ │ │ ├── InsteadOfAny.EntityFramework.Core.cs
│ │ │ ├── InsteadOfAny.EntityFramework.Core.vb
│ │ │ ├── InsteadOfAny.EntityFramework.Framework.cs
│ │ │ ├── InsteadOfAny.Latest.cs
│ │ │ ├── InsteadOfAny.cs
│ │ │ ├── InsteadOfAny.vb
│ │ │ ├── InterfaceMethodsShouldBeCallableByChildTypes.CSharp9.cs
│ │ │ ├── InterfaceMethodsShouldBeCallableByChildTypes.cs
│ │ │ ├── InterfaceName.vb
│ │ │ ├── InterfacesShouldNotBeEmpty.cs
│ │ │ ├── InvalidCastToInterface.Latest.cs
│ │ │ ├── InvalidCastToInterface.cs
│ │ │ ├── InvalidCastToInterface.vb
│ │ │ ├── InvocationResolvesToOverrideWithParams.Latest.cs
│ │ │ ├── InvocationResolvesToOverrideWithParams.TopLevelStatements.cs
│ │ │ ├── InvocationResolvesToOverrideWithParams.cs
│ │ │ ├── IssueSuppression.CSharp10.cs
│ │ │ ├── IssueSuppression.CSharp9.cs
│ │ │ ├── IssueSuppression.cs
│ │ │ ├── IssueSuppression2.cs
│ │ │ ├── JSInvokableMethodsShouldBePublic.Latest.cs
│ │ │ ├── JSInvokableMethodsShouldBePublic.cs
│ │ │ ├── JSInvokableMethodsShouldBePublic.razor
│ │ │ ├── JSInvokableMethodsShouldBePublic.razor.cs
│ │ │ ├── JwtSigned.Extensions.cs
│ │ │ ├── JwtSigned.Extensions.vb
│ │ │ ├── JwtSigned.Latest.cs
│ │ │ ├── JwtSigned.cs
│ │ │ ├── JwtSigned.vb
│ │ │ ├── LaunchSettings/
│ │ │ │ ├── DoNotHardcodeCredentials/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── launchSettings.json
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── launchSettings.json
│ │ │ │ │ └── Valid/
│ │ │ │ │ └── launchSettings.json
│ │ │ │ └── DoNotHardcodeSecrets/
│ │ │ │ ├── Corrupt/
│ │ │ │ │ └── launchSettings.json
│ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ └── launchSettings.json
│ │ │ │ └── Valid/
│ │ │ │ └── launchSettings.json
│ │ │ ├── LdapConnectionShouldBeSecure.Latest.cs
│ │ │ ├── LdapConnectionShouldBeSecure.cs
│ │ │ ├── LineContinuation.vb
│ │ │ ├── LineLength.cs
│ │ │ ├── LineLength.vb
│ │ │ ├── LinkedListPropertiesInsteadOfMethods.Fixed.cs
│ │ │ ├── LinkedListPropertiesInsteadOfMethods.cs
│ │ │ ├── LinkedListPropertiesInsteadOfMethods.vb
│ │ │ ├── LiteralSuffixUpperCase.Fixed.cs
│ │ │ ├── LiteralSuffixUpperCase.Latest.cs
│ │ │ ├── LiteralSuffixUpperCase.cs
│ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParameters.CSharp10.cs
│ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParameters.CSharp11.cs
│ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParameters.CSharp9.cs
│ │ │ ├── LiteralsShouldNotBePassedAsLocalizedParameters.cs
│ │ │ ├── LocalVariableName.vb
│ │ │ ├── LockedFieldShouldBeReadonly.Latest.cs
│ │ │ ├── LockedFieldShouldBeReadonly.cs
│ │ │ ├── LoggerFieldsShouldBePrivateStaticReadonly.cs
│ │ │ ├── LoggersShouldBeNamedForEnclosingType.NLog.cs
│ │ │ ├── LoggersShouldBeNamedForEnclosingType.cs
│ │ │ ├── LoggingArgumentsShouldBePassedCorrectly.cs
│ │ │ ├── LoggingTemplatePlaceHoldersShouldBeInOrder.cs
│ │ │ ├── LoopsAndLinq.Latest.cs
│ │ │ ├── LoopsAndLinq.cs
│ │ │ ├── LooseFilePermissions.Unix.cs
│ │ │ ├── LooseFilePermissions.Unix.vb
│ │ │ ├── LooseFilePermissions.Windows.CSharp10.cs
│ │ │ ├── LooseFilePermissions.Windows.CSharp11.cs
│ │ │ ├── LooseFilePermissions.Windows.CSharp12.cs
│ │ │ ├── LooseFilePermissions.Windows.CSharp9.cs
│ │ │ ├── LooseFilePermissions.Windows.cs
│ │ │ ├── LooseFilePermissions.Windows.vb
│ │ │ ├── LossOfFractionInDivision.Latest.cs
│ │ │ ├── LossOfFractionInDivision.cs
│ │ │ ├── MagicNumberShouldNotBeUsed.CSharp11.cs
│ │ │ ├── MagicNumberShouldNotBeUsed.cs
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttribute.cs
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttribute.vb
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeNoncompliant.cs
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeNoncompliant.vb
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeRazor.cs
│ │ │ ├── MarkAssemblyWithAssemblyVersionAttributeRazor.vb
│ │ │ ├── MarkAssemblyWithClsCompliantAttribute.cs
│ │ │ ├── MarkAssemblyWithClsCompliantAttribute.vb
│ │ │ ├── MarkAssemblyWithClsCompliantAttributeNoncompliant.cs
│ │ │ ├── MarkAssemblyWithClsCompliantAttributeNoncompliant.vb
│ │ │ ├── MarkAssemblyWithComVisibleAttribute.cs
│ │ │ ├── MarkAssemblyWithComVisibleAttribute.vb
│ │ │ ├── MarkAssemblyWithComVisibleAttributeNoncompliant.cs
│ │ │ ├── MarkAssemblyWithComVisibleAttributeNoncompliant.vb
│ │ │ ├── MarkAssemblyWithNeutralResourcesLanguageAttribute.cs
│ │ │ ├── MarkAssemblyWithNeutralResourcesLanguageAttributeNonCompliant.cs
│ │ │ ├── MarkAssemblyWithNeutralResourcesLanguageAttributeNoncompliant.Invalid.cs
│ │ │ ├── MarkWindowsFormsMainWithStaThread.cs
│ │ │ ├── MarkWindowsFormsMainWithStaThread.vb
│ │ │ ├── MarkWindowsFormsMainWithStaThread_NoWindowsForms.cs
│ │ │ ├── MarkWindowsFormsMainWithStaThread_NoWindowsForms.vb
│ │ │ ├── MemberInitializedToDefault.CSharp10.cs
│ │ │ ├── MemberInitializedToDefault.CSharp11.Fixed.cs
│ │ │ ├── MemberInitializedToDefault.CSharp11.cs
│ │ │ ├── MemberInitializedToDefault.CSharp8.cs
│ │ │ ├── MemberInitializedToDefault.CSharp9.cs
│ │ │ ├── MemberInitializedToDefault.Fixed.cs
│ │ │ ├── MemberInitializedToDefault.cs
│ │ │ ├── MemberInitializerRedundant.Fixed.cs
│ │ │ ├── MemberInitializerRedundant.Latest.Fixed.cs
│ │ │ ├── MemberInitializerRedundant.Latest.cs
│ │ │ ├── MemberInitializerRedundant.RoslynCfg.FlowCaptureBug.cs
│ │ │ ├── MemberInitializerRedundant.cs
│ │ │ ├── MemberOverrideCallsBaseMember.Fixed.cs
│ │ │ ├── MemberOverrideCallsBaseMember.Latest.Fixed.cs
│ │ │ ├── MemberOverrideCallsBaseMember.Latest.cs
│ │ │ ├── MemberOverrideCallsBaseMember.cs
│ │ │ ├── MemberShadowsOuterStaticMember.Latest.cs
│ │ │ ├── MemberShadowsOuterStaticMember.cs
│ │ │ ├── MemberShouldBeStatic.Latest.Partial.cs
│ │ │ ├── MemberShouldBeStatic.Latest.cs
│ │ │ ├── MemberShouldBeStatic.WinForms.cs
│ │ │ ├── MemberShouldBeStatic.Xaml.cs
│ │ │ ├── MemberShouldBeStatic.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.AssemblyLevel.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.Latest.Partial.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.Latest.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.Partial.cs
│ │ │ ├── MemberShouldNotHaveConflictingTransparencyAttributes.cs
│ │ │ ├── MessageTemplatesShouldBeCorrect.Latest.cs
│ │ │ ├── MessageTemplatesShouldBeCorrect.cs
│ │ │ ├── MethodOverloadOptionalParameter.Latest.Partial.cs
│ │ │ ├── MethodOverloadOptionalParameter.Latest.cs
│ │ │ ├── MethodOverloadOptionalParameter.cs
│ │ │ ├── MethodOverloadsShouldBeGrouped.Latest.cs
│ │ │ ├── MethodOverloadsShouldBeGrouped.cs
│ │ │ ├── MethodOverloadsShouldBeGrouped.vb
│ │ │ ├── MethodOverrideAddsParams.Fixed.cs
│ │ │ ├── MethodOverrideAddsParams.Latest.cs
│ │ │ ├── MethodOverrideAddsParams.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.Latest.Fixed.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.Latest.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.Remove.Fixed.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.Synchronize.Fixed.Batch.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.Synchronize.Fixed.cs
│ │ │ ├── MethodOverrideChangedDefaultValue.cs
│ │ │ ├── MethodOverrideNoParams.Fixed.cs
│ │ │ ├── MethodOverrideNoParams.Latest.cs
│ │ │ ├── MethodOverrideNoParams.cs
│ │ │ ├── MethodParameterMissingOptional.Fixed.cs
│ │ │ ├── MethodParameterMissingOptional.Latest.cs
│ │ │ ├── MethodParameterMissingOptional.TopLevelStatements.cs
│ │ │ ├── MethodParameterMissingOptional.cs
│ │ │ ├── MethodParameterUnused.Latest.cs
│ │ │ ├── MethodParameterUnused.RoslynCfg.Fixed.cs
│ │ │ ├── MethodParameterUnused.RoslynCfg.cs
│ │ │ ├── MethodParameterUnused.SonarCfg.cs
│ │ │ ├── MethodParameterUnused.vb
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.CSharp11.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.CSharp8.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.MVC.Core.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.MVC.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.MsTest.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.NUnit.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.Xunit.cs
│ │ │ ├── MethodShouldBeNamedAccordingToSynchronicity.cs
│ │ │ ├── MethodShouldNotOnlyReturnConstant.Latest.cs
│ │ │ ├── MethodShouldNotOnlyReturnConstant.cs
│ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.Latest.cs
│ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.TopLevelStatements.cs
│ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.cs
│ │ │ ├── MethodsShouldNotHaveIdenticalImplementations.vb
│ │ │ ├── MethodsShouldNotHaveTooManyLines.LocalFunctions.CSharp9.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines.LocalFunctions.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines_CustomValues.CSharp10.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines_CustomValues.CSharp9.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines_CustomValues.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines_CustomValues.vb
│ │ │ ├── MethodsShouldNotHaveTooManyLines_DefaultValues.cs
│ │ │ ├── MethodsShouldNotHaveTooManyLines_DefaultValues.vb
│ │ │ ├── MethodsShouldUseBaseTypes.AspControllers.cs
│ │ │ ├── MethodsShouldUseBaseTypes.CSharp8.cs
│ │ │ ├── MethodsShouldUseBaseTypes.CSharp9.cs
│ │ │ ├── MethodsShouldUseBaseTypes.Concurrent.cs
│ │ │ ├── MethodsShouldUseBaseTypes.cs
│ │ │ ├── MultilineBlocksWithoutBrace.Latest.cs
│ │ │ ├── MultilineBlocksWithoutBrace.cs
│ │ │ ├── MultipleVariableDeclaration.Fixed.cs
│ │ │ ├── MultipleVariableDeclaration.Fixed.vb
│ │ │ ├── MultipleVariableDeclaration.WrongIndentation.Fixed.cs
│ │ │ ├── MultipleVariableDeclaration.WrongIndentation.cs
│ │ │ ├── MultipleVariableDeclaration.cs
│ │ │ ├── MultipleVariableDeclaration.vb
│ │ │ ├── MutableFieldsShouldNotBePublicReadonly.Latest.cs
│ │ │ ├── MutableFieldsShouldNotBePublicReadonly.cs
│ │ │ ├── MutableFieldsShouldNotBePublicStatic.Latest.cs
│ │ │ ├── MutableFieldsShouldNotBePublicStatic.cs
│ │ │ ├── NameOfShouldBeUsed.CSharp11.cs
│ │ │ ├── NameOfShouldBeUsed.cs
│ │ │ ├── NameOfShouldBeUsed.vb
│ │ │ ├── NamedPlaceholdersShouldBeUnique.cs
│ │ │ ├── NamespaceName.vb
│ │ │ ├── NativeMethodsShouldBeWrapped.Latest.SourceGenerator.cs
│ │ │ ├── NativeMethodsShouldBeWrapped.Latest.cs
│ │ │ ├── NativeMethodsShouldBeWrapped.TopLevelStatements.cs
│ │ │ ├── NativeMethodsShouldBeWrapped.cs
│ │ │ ├── NegatedIsExpression.Fixed.vb
│ │ │ ├── NegatedIsExpression.vb
│ │ │ ├── NestedCodeBlock.Latest.cs
│ │ │ ├── NestedCodeBlock.TopLevelStatements.cs
│ │ │ ├── NestedCodeBlock.cs
│ │ │ ├── NoExceptionsInFinally.cs
│ │ │ ├── NoExceptionsInFinally.vb
│ │ │ ├── NonAsyncTaskShouldNotReturnNull.Latest.Partial.cs
│ │ │ ├── NonAsyncTaskShouldNotReturnNull.Latest.cs
│ │ │ ├── NonAsyncTaskShouldNotReturnNull.cs
│ │ │ ├── NonAsyncTaskShouldNotReturnNull.vb
│ │ │ ├── NonDerivedPrivateClassesShouldBeSealed.Latest.cs
│ │ │ ├── NonDerivedPrivateClassesShouldBeSealed.cs
│ │ │ ├── NonDerivedPrivateClassesShouldBeSealed_PartialClass.cs
│ │ │ ├── NonFlagsEnumInBitwiseOperation.Fixed.cs
│ │ │ ├── NonFlagsEnumInBitwiseOperation.cs
│ │ │ ├── NormalizeStringsToUppercase.CSharp11.cs
│ │ │ ├── NormalizeStringsToUppercase.cs
│ │ │ ├── NotAssignedPrivateMember.Latest.Partial.cs
│ │ │ ├── NotAssignedPrivateMember.Latest.cs
│ │ │ ├── NotAssignedPrivateMember.cs
│ │ │ ├── NotAssignedPrivateMember.razor
│ │ │ ├── NotAssignedPrivateMember.razor.cs
│ │ │ ├── NumberPatternShouldBeRegular.CSharp9.cs
│ │ │ ├── NumberPatternShouldBeRegular.cs
│ │ │ ├── ObjectCreatedDropped.Latest.cs
│ │ │ ├── ObjectCreatedDropped.cs
│ │ │ ├── ObsoleteAttributesNeedExplanation.Latest.cs
│ │ │ ├── ObsoleteAttributesNeedExplanation.VB14.vb
│ │ │ ├── ObsoleteAttributesNeedExplanation.cs
│ │ │ ├── ObsoleteAttributesNeedExplanation.vb
│ │ │ ├── OnErrorStatement.vb
│ │ │ ├── OperatorOverloadsShouldHaveNamedAlternatives.CSharp11.cs
│ │ │ ├── OperatorOverloadsShouldHaveNamedAlternatives.CSharp9.cs
│ │ │ ├── OperatorOverloadsShouldHaveNamedAlternatives.cs
│ │ │ ├── OperatorsShouldBeOverloadedConsistently.Latest.cs
│ │ │ ├── OperatorsShouldBeOverloadedConsistently.cs
│ │ │ ├── OptionalParameter.CSharp10.cs
│ │ │ ├── OptionalParameter.CSharp11.cs
│ │ │ ├── OptionalParameter.Web.cs
│ │ │ ├── OptionalParameter.cs
│ │ │ ├── OptionalParameter.vb
│ │ │ ├── OptionalParameterNotPassedToBaseCall.Latest.cs
│ │ │ ├── OptionalParameterNotPassedToBaseCall.cs
│ │ │ ├── OptionalParameterNotPassedToBaseCall.vb
│ │ │ ├── OptionalParameterWithDefaultValue.Fixed.cs
│ │ │ ├── OptionalParameterWithDefaultValue.Latest.cs
│ │ │ ├── OptionalParameterWithDefaultValue.cs
│ │ │ ├── OptionalRefOutParameter.Fixed.cs
│ │ │ ├── OptionalRefOutParameter.Latest.cs
│ │ │ ├── OptionalRefOutParameter.TopLevelStatements.cs
│ │ │ ├── OptionalRefOutParameter.cs
│ │ │ ├── OrderByRepeated.Fixed.cs
│ │ │ ├── OrderByRepeated.cs
│ │ │ ├── OverrideGetHashCodeOnOverridingEquals.Latest.cs
│ │ │ ├── OverrideGetHashCodeOnOverridingEquals.cs
│ │ │ ├── PInvokesShouldNotBeVisible.CSharp11.cs
│ │ │ ├── PInvokesShouldNotBeVisible.CSharp9.cs
│ │ │ ├── PInvokesShouldNotBeVisible.cs
│ │ │ ├── ParameterAssignedTo.Latest.cs
│ │ │ ├── ParameterAssignedTo.cs
│ │ │ ├── ParameterAssignedTo.vb
│ │ │ ├── ParameterName.CustomPattern.vb
│ │ │ ├── ParameterName.vb
│ │ │ ├── ParameterNameMatchesOriginal.Latest.Partial.cs
│ │ │ ├── ParameterNameMatchesOriginal.Latest.cs
│ │ │ ├── ParameterNameMatchesOriginal.cs
│ │ │ ├── ParameterNameMatchesOriginal.vb
│ │ │ ├── ParameterNamesShouldNotDuplicateMethodNames.cs
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.Conversion.razor
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.Latest.cs
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.Partial.razor
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.Partial.razor.cs
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.cs
│ │ │ ├── ParameterTypeShouldMatchRouteTypeConstraint.razor
│ │ │ ├── ParameterValidationInAsyncShouldBeWrapped.CSharp10.cs
│ │ │ ├── ParameterValidationInAsyncShouldBeWrapped.CSharp11.cs
│ │ │ ├── ParameterValidationInAsyncShouldBeWrapped.cs
│ │ │ ├── ParameterValidationInYieldShouldBeWrapped.Latest.cs
│ │ │ ├── ParameterValidationInYieldShouldBeWrapped.cs
│ │ │ ├── ParametersCorrectOrder.Latest.cs
│ │ │ ├── ParametersCorrectOrder.cs
│ │ │ ├── ParametersCorrectOrder.vb
│ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttribute.Latest.cs
│ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttribute.cs
│ │ │ ├── PartCreationPolicyShouldBeUsedWithExportAttribute.vb
│ │ │ ├── PartialMethodNoImplementation.Latest.Partial.cs
│ │ │ ├── PartialMethodNoImplementation.Latest.cs
│ │ │ ├── PartialMethodNoImplementation.cs
│ │ │ ├── PasswordsShouldBeStoredCorrectly.Core.cs
│ │ │ ├── PointersShouldBePrivate.CSharp11.cs
│ │ │ ├── PointersShouldBePrivate.CSharp9.cs
│ │ │ ├── PointersShouldBePrivate.cs
│ │ │ ├── PreferGuidEmpty.Fixed.cs
│ │ │ ├── PreferGuidEmpty.Latest.cs
│ │ │ ├── PreferGuidEmpty.cs
│ │ │ ├── PreferGuidEmpty.vb
│ │ │ ├── PreferJaggedArraysOverMultidimensional.Latest.cs
│ │ │ ├── PreferJaggedArraysOverMultidimensional.cs
│ │ │ ├── PrivateConstantFieldName.vb
│ │ │ ├── PrivateFieldName.vb
│ │ │ ├── PrivateFieldUsedAsLocalVariable.Latest.cs
│ │ │ ├── PrivateFieldUsedAsLocalVariable.cs
│ │ │ ├── PrivateSharedReadonlyFieldName.vb
│ │ │ ├── PrivateStaticMethodUsedOnlyByNestedClass.CSharpLatest.cs
│ │ │ ├── PrivateStaticMethodUsedOnlyByNestedClass.cs
│ │ │ ├── PropertiesAccessCorrectField.Latest.Partial.cs
│ │ │ ├── PropertiesAccessCorrectField.Latest.cs
│ │ │ ├── PropertiesAccessCorrectField.NetFramework.cs
│ │ │ ├── PropertiesAccessCorrectField.NetFramework.vb
│ │ │ ├── PropertiesAccessCorrectField.cs
│ │ │ ├── PropertiesAccessCorrectField.vb
│ │ │ ├── PropertiesShouldBePreferred.CSharp10.cs
│ │ │ ├── PropertiesShouldBePreferred.CSharp11.cs
│ │ │ ├── PropertiesShouldBePreferred.CSharp9.cs
│ │ │ ├── PropertiesShouldBePreferred.cs
│ │ │ ├── PropertyGetterWithThrow.Latest.Partial.cs
│ │ │ ├── PropertyGetterWithThrow.Latest.cs
│ │ │ ├── PropertyGetterWithThrow.cs
│ │ │ ├── PropertyGetterWithThrow.vb
│ │ │ ├── PropertyName.vb
│ │ │ ├── PropertyNamesShouldNotMatchGetMethods.Latest.Partial1.g.cs
│ │ │ ├── PropertyNamesShouldNotMatchGetMethods.Latest.Partial2.cs
│ │ │ ├── PropertyNamesShouldNotMatchGetMethods.Latest.cs
│ │ │ ├── PropertyNamesShouldNotMatchGetMethods.cs
│ │ │ ├── PropertyToAutoProperty.Latest.cs
│ │ │ ├── PropertyToAutoProperty.cs
│ │ │ ├── PropertyWithArrayType.vb
│ │ │ ├── PropertyWriteOnly.Latest.Partial.cs
│ │ │ ├── PropertyWriteOnly.Latest.cs
│ │ │ ├── PropertyWriteOnly.cs
│ │ │ ├── PropertyWriteOnly.vb
│ │ │ ├── ProvideDeserializationMethodsForOptionalFields.Latest.cs
│ │ │ ├── ProvideDeserializationMethodsForOptionalFields.cs
│ │ │ ├── ProvideDeserializationMethodsForOptionalFields.vb
│ │ │ ├── PublicConstantField.CSharp10.cs
│ │ │ ├── PublicConstantField.CSharp9.cs
│ │ │ ├── PublicConstantField.cs
│ │ │ ├── PublicConstantField.vb
│ │ │ ├── PublicConstantFieldName.vb
│ │ │ ├── PublicFieldName.vb
│ │ │ ├── PublicMethodWithMultidimensionalArray.Latest.Partial.cs
│ │ │ ├── PublicMethodWithMultidimensionalArray.Latest.cs
│ │ │ ├── PublicMethodWithMultidimensionalArray.cs
│ │ │ ├── PublicMethodWithMultidimensionalArray.vb
│ │ │ ├── PublicSharedReadonlyFieldName.vb
│ │ │ ├── PureAttributeOnVoidMethod.CSharp7.cs
│ │ │ ├── PureAttributeOnVoidMethod.Latest.cs
│ │ │ ├── PureAttributeOnVoidMethod.TopLevelStatements.cs
│ │ │ ├── PureAttributeOnVoidMethod.cs
│ │ │ ├── PureAttributeOnVoidMethod.vb
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.BaseCall.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp10.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp10.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp11.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp11.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp12.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp9.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.CSharp9.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.Constructor.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.Destructor.Fixed.cs
│ │ │ ├── RedundancyInConstructorDestructorDeclaration.cs
│ │ │ ├── RedundantArgument.Latest.cs
│ │ │ ├── RedundantArgument.Named.Fixed.cs
│ │ │ ├── RedundantArgument.NoNamed.Fixed.cs
│ │ │ ├── RedundantArgument.TopLevelStatements.cs
│ │ │ ├── RedundantArgument.cs
│ │ │ ├── RedundantCast.Fixed.cs
│ │ │ ├── RedundantCast.Latest.cs
│ │ │ ├── RedundantCast.cs
│ │ │ ├── RedundantConditionalAroundAssignment.Fixed.cs
│ │ │ ├── RedundantConditionalAroundAssignment.Latest.Fixed.cs
│ │ │ ├── RedundantConditionalAroundAssignment.Latest.cs
│ │ │ ├── RedundantConditionalAroundAssignment.cs
│ │ │ ├── RedundantDeclaration.ArraySize.Fixed.cs
│ │ │ ├── RedundantDeclaration.ArrayType.Fixed.cs
│ │ │ ├── RedundantDeclaration.CSharp10.Fixed.cs
│ │ │ ├── RedundantDeclaration.CSharp10.cs
│ │ │ ├── RedundantDeclaration.CSharp12.ArraySize.Fixed.cs
│ │ │ ├── RedundantDeclaration.CSharp12.LambdaParameterType.Fixed.cs
│ │ │ ├── RedundantDeclaration.CSharp12.cs
│ │ │ ├── RedundantDeclaration.CSharp9.Fixed.cs
│ │ │ ├── RedundantDeclaration.CSharp9.cs
│ │ │ ├── RedundantDeclaration.DelegateParameterList.Fixed.cs
│ │ │ ├── RedundantDeclaration.ExplicitDelegate.Fixed.cs
│ │ │ ├── RedundantDeclaration.ExplicitNullable.Fixed.cs
│ │ │ ├── RedundantDeclaration.LambdaParameterType.Fixed.cs
│ │ │ ├── RedundantDeclaration.ObjectInitializer.Fixed.cs
│ │ │ ├── RedundantDeclaration.cs
│ │ │ ├── RedundantExitSelect.vb
│ │ │ ├── RedundantInheritanceList.CSharp10.Fixed.cs
│ │ │ ├── RedundantInheritanceList.CSharp10.cs
│ │ │ ├── RedundantInheritanceList.CSharp9.Fixed.cs
│ │ │ ├── RedundantInheritanceList.CSharp9.cs
│ │ │ ├── RedundantInheritanceList.Fixed.cs
│ │ │ ├── RedundantInheritanceList.cs
│ │ │ ├── RedundantJumpStatement.Latest.cs
│ │ │ ├── RedundantJumpStatement.TopLevelStatements.cs
│ │ │ ├── RedundantJumpStatement.cs
│ │ │ ├── RedundantModifier.Fixed.Checked.cs
│ │ │ ├── RedundantModifier.Fixed.Partial.cs
│ │ │ ├── RedundantModifier.Fixed.Sealed.cs
│ │ │ ├── RedundantModifier.Fixed.Unsafe.cs
│ │ │ ├── RedundantModifier.Latest.Fixed.Checked.cs
│ │ │ ├── RedundantModifier.Latest.Fixed.Partial.cs
│ │ │ ├── RedundantModifier.Latest.Fixed.Sealed.cs
│ │ │ ├── RedundantModifier.Latest.Fixed.Unsafe.cs
│ │ │ ├── RedundantModifier.Latest.Partial.cs
│ │ │ ├── RedundantModifier.Latest.cs
│ │ │ ├── RedundantModifier.Preprocessor.Net.Partial.cs
│ │ │ ├── RedundantModifier.Preprocessor.Net.cs
│ │ │ ├── RedundantModifier.Preprocessor.NetFx.cs
│ │ │ ├── RedundantModifier.Preprocessor.cs
│ │ │ ├── RedundantModifier.cs
│ │ │ ├── RedundantNullCheck.CSharp10.Fixed.cs
│ │ │ ├── RedundantNullCheck.CSharp10.cs
│ │ │ ├── RedundantNullCheck.CSharp11.cs
│ │ │ ├── RedundantNullCheck.CSharp9.Fixed.cs
│ │ │ ├── RedundantNullCheck.CSharp9.cs
│ │ │ ├── RedundantNullCheck.Fixed.Batch.cs
│ │ │ ├── RedundantNullCheck.Fixed.cs
│ │ │ ├── RedundantNullCheck.cs
│ │ │ ├── RedundantNullCheck.vb
│ │ │ ├── RedundantNullableTypeComparison.cs
│ │ │ ├── RedundantParentheses.vb
│ │ │ ├── RedundantParenthesesExpression.cs
│ │ │ ├── RedundantParenthesesObjectCreation.CSharp10.cs
│ │ │ ├── RedundantParenthesesObjectCreation.CSharp11.cs
│ │ │ ├── RedundantParenthesesObjectCreation.CSharp9.cs
│ │ │ ├── RedundantParenthesesObjectCreation.Fixed.cs
│ │ │ ├── RedundantParenthesesObjectCreation.cs
│ │ │ ├── RedundantPropertyNamesInAnonymousClass.Fixed.cs
│ │ │ ├── RedundantPropertyNamesInAnonymousClass.Latest.Partial.cs
│ │ │ ├── RedundantPropertyNamesInAnonymousClass.Latest.cs
│ │ │ ├── RedundantPropertyNamesInAnonymousClass.cs
│ │ │ ├── RedundantToArrayCall.CSharp11.Fixed.cs
│ │ │ ├── RedundantToArrayCall.CSharp11.cs
│ │ │ ├── RedundantToArrayCall.Fixed.cs
│ │ │ ├── RedundantToArrayCall.cs
│ │ │ ├── RedundantToStringCall.CSharp10.cs
│ │ │ ├── RedundantToStringCall.CSharp11.cs
│ │ │ ├── RedundantToStringCall.CSharp9.cs
│ │ │ ├── RedundantToStringCall.Fixed.cs
│ │ │ ├── RedundantToStringCall.cs
│ │ │ ├── ReferenceEqualityCheckWhenEqualsExists.cs
│ │ │ ├── ReferenceEqualityCheckWhenEqualsExists2.cs
│ │ │ ├── ReferenceEqualsOnValueType.cs
│ │ │ ├── RegularExpressions/
│ │ │ │ ├── RegexMustHaveValidSyntax.Latest.cs
│ │ │ │ ├── RegexMustHaveValidSyntax.cs
│ │ │ │ └── RegexMustHaveValidSyntax.vb
│ │ │ ├── RemoveObsoleteCode.Latest.cs
│ │ │ ├── RemoveObsoleteCode.cs
│ │ │ ├── RemoveObsoleteCode.vb
│ │ │ ├── RequireAttributeUsageAttribute.CSharp11.cs
│ │ │ ├── RequireAttributeUsageAttribute.cs
│ │ │ ├── Resources/
│ │ │ │ ├── AnotherResources.Designer.cs
│ │ │ │ ├── AnotherResources.resx
│ │ │ │ ├── SomeResources.Designer.cs
│ │ │ │ └── SomeResources.resx
│ │ │ ├── ReturnEmptyCollectionInsteadOfNull.Latest.cs
│ │ │ ├── ReturnEmptyCollectionInsteadOfNull.TopLevelStatements.cs
│ │ │ ├── ReturnEmptyCollectionInsteadOfNull.cs
│ │ │ ├── ReturnTypeNamedPartialShouldBeEscaped.CSharp8-13.cs
│ │ │ ├── ReturnTypeNamedPartialShouldBeEscaped.Latest.cs
│ │ │ ├── ReturnTypeNamedPartialShouldBeEscaped.TopLevelStatements.CSharp9-13.cs
│ │ │ ├── ReturnTypeNamedPartialShouldBeEscaped.TopLevelStatements.Latest.cs
│ │ │ ├── ReturnValueIgnored.Latest.cs
│ │ │ ├── ReturnValueIgnored.TopLevelStatements.cs
│ │ │ ├── ReturnValueIgnored.cs
│ │ │ ├── ReversedOperators.Latest.cs
│ │ │ ├── ReversedOperators.TopLevelStatements.cs
│ │ │ ├── ReversedOperators.cs
│ │ │ ├── ReversedOperators.vb
│ │ │ ├── RightCurlyBraceStartsLine.cs
│ │ │ ├── RuleFailure/
│ │ │ │ ├── InvalidSyntax.cs
│ │ │ │ ├── InvalidSyntax.vb
│ │ │ │ ├── PerformanceTestCases.cs
│ │ │ │ ├── SpecialCases.cs
│ │ │ │ └── SpecialCases.vb
│ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.CSharp11.cs
│ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.CSharp12.cs
│ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.cs
│ │ │ ├── SecurityPInvokeMethodShouldNotBeCalled.vb
│ │ │ ├── SelfAssignment.Latest.Partial.cs
│ │ │ ├── SelfAssignment.Latest.cs
│ │ │ ├── SelfAssignment.TopLevelStatements.cs
│ │ │ ├── SelfAssignment.cs
│ │ │ ├── SelfAssignment.vb
│ │ │ ├── SerializationConstructorsShouldBeSecured.CSharp9.cs
│ │ │ ├── SerializationConstructorsShouldBeSecured.cs
│ │ │ ├── SerializationConstructorsShouldBeSecured_NoAssemblyAttribute.cs
│ │ │ ├── SerializationConstructorsShouldBeSecured_Part1.cs
│ │ │ ├── SerializationConstructorsShouldBeSecured_Part2.cs
│ │ │ ├── SetLocaleForDataTypes.CSharp10.cs
│ │ │ ├── SetLocaleForDataTypes.CSharp9.cs
│ │ │ ├── SetLocaleForDataTypes.cs
│ │ │ ├── SetPropertiesInsteadOfMethods.Latest.cs
│ │ │ ├── SetPropertiesInsteadOfMethods.cs
│ │ │ ├── SetPropertiesInsteadOfMethods.vb
│ │ │ ├── ShiftDynamicNotInteger.CSharp11.cs
│ │ │ ├── ShiftDynamicNotInteger.CSharp9.cs
│ │ │ ├── ShiftDynamicNotInteger.cs
│ │ │ ├── ShiftDynamicNotInteger.vb
│ │ │ ├── ShiftDynamicNotInteger2.vb
│ │ │ ├── ShouldImplementExportedInterfaces.CSharp9.cs
│ │ │ ├── ShouldImplementExportedInterfaces.System.Composition.cs
│ │ │ ├── ShouldImplementExportedInterfaces.cs
│ │ │ ├── ShouldImplementExportedInterfaces.vb
│ │ │ ├── ShouldImplementExportedInterfaces_Part1.cs
│ │ │ ├── ShouldImplementExportedInterfaces_Part2.cs
│ │ │ ├── SimpleDoLoop.vb
│ │ │ ├── SingleStatementPerLine.CSharp9.cs
│ │ │ ├── SingleStatementPerLine.cs
│ │ │ ├── SingleStatementPerLine.vb
│ │ │ ├── SingleStatementPerLine2.vb
│ │ │ ├── SpecifyIFormatProviderOrCultureInfo.Latest.cs
│ │ │ ├── SpecifyIFormatProviderOrCultureInfo.cs
│ │ │ ├── SpecifyStringComparison.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace.CSharp10.FileScopedNamespaceDeclaration.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace.CSharp10.GlobalUsing.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace.CSharp10.GlobalUsingConsumer.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace.Latest.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace_DefaultNamespace.cs
│ │ │ ├── SqlKeywordsDelimitedBySpace_InsideNamespace.cs
│ │ │ ├── StaticFieldInGenericClass.Latest.Partial.cs
│ │ │ ├── StaticFieldInGenericClass.Latest.cs
│ │ │ ├── StaticFieldInGenericClass.cs
│ │ │ ├── StaticFieldInitializerOrder.Latest.Partial.cs
│ │ │ ├── StaticFieldInitializerOrder.Latest.cs
│ │ │ ├── StaticFieldInitializerOrder.Partial.cs
│ │ │ ├── StaticFieldInitializerOrder.cs
│ │ │ ├── StaticFieldVisible.Latest.cs
│ │ │ ├── StaticFieldVisible.cs
│ │ │ ├── StaticFieldWrittenFromInstanceConstructor.Latest.Partial.cs
│ │ │ ├── StaticFieldWrittenFromInstanceConstructor.Latest.cs
│ │ │ ├── StaticFieldWrittenFromInstanceConstructor.cs
│ │ │ ├── StaticFieldWrittenFromInstanceMember.Latest.cs
│ │ │ ├── StaticFieldWrittenFromInstanceMember.TopLevelStatements.cs
│ │ │ ├── StaticFieldWrittenFromInstanceMember.cs
│ │ │ ├── StaticSealedClassProtectedMembers.Latest.Partial.cs
│ │ │ ├── StaticSealedClassProtectedMembers.Latest.cs
│ │ │ ├── StaticSealedClassProtectedMembers.cs
│ │ │ ├── StreamReadStatement.Latest.cs
│ │ │ ├── StreamReadStatement.cs
│ │ │ ├── StringConcatenationInLoop.Latest.cs
│ │ │ ├── StringConcatenationInLoop.cs
│ │ │ ├── StringConcatenationInLoop.vb
│ │ │ ├── StringConcatenationWithPlus.Fixed.vb
│ │ │ ├── StringConcatenationWithPlus.vb
│ │ │ ├── StringFormatValidator.EdgeCases.cs
│ │ │ ├── StringFormatValidator.Latest.cs
│ │ │ ├── StringFormatValidator.RuntimeExceptionFree.CSharp11.cs
│ │ │ ├── StringFormatValidator.RuntimeExceptionFree.cs
│ │ │ ├── StringFormatValidator.TypoFree.CSharp11.cs
│ │ │ ├── StringFormatValidator.TypoFree.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.Dapper.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.Dapper.vb
│ │ │ ├── StringLiteralShouldNotBeDuplicated.Latest.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.Partial.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.WithTopLevelStatements.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated.vb
│ │ │ ├── StringLiteralShouldNotBeDuplicated_Attributes.cs
│ │ │ ├── StringLiteralShouldNotBeDuplicated_Attributes.vb
│ │ │ ├── StringOffsetMethods.Latest.cs
│ │ │ ├── StringOffsetMethods.cs
│ │ │ ├── StringOperationWithoutCulture.CSharp10.cs
│ │ │ ├── StringOperationWithoutCulture.CSharp11.cs
│ │ │ ├── StringOperationWithoutCulture.cs
│ │ │ ├── StringOrIntegralTypesForIndexers.Latest.Partial.cs
│ │ │ ├── StringOrIntegralTypesForIndexers.Latest.cs
│ │ │ ├── StringOrIntegralTypesForIndexers.cs
│ │ │ ├── SuppressFinalizeUseless.CSharp9.cs
│ │ │ ├── SuppressFinalizeUseless.Fixed.cs
│ │ │ ├── SuppressFinalizeUseless.cs
│ │ │ ├── SwaggerActionReturnType.cs
│ │ │ ├── SwitchCaseFallsThroughToDefault.Fixed.cs
│ │ │ ├── SwitchCaseFallsThroughToDefault.TopLevelStatements.cs
│ │ │ ├── SwitchCaseFallsThroughToDefault.cs
│ │ │ ├── SwitchCasesMinimumThree.cs
│ │ │ ├── SwitchCasesMinimumThree.vb
│ │ │ ├── SwitchDefaultClauseEmpty.Fixed.cs
│ │ │ ├── SwitchDefaultClauseEmpty.cs
│ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements_CustomValue.cs
│ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements_CustomValue.vb
│ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements_DefaultValue.cs
│ │ │ ├── SwitchSectionShouldNotHaveTooManyStatements_DefaultValue.vb
│ │ │ ├── SwitchShouldNotBeNested.cs
│ │ │ ├── SwitchShouldNotBeNested.vb
│ │ │ ├── SwitchWithoutDefault.cs
│ │ │ ├── SwitchWithoutDefault.vb
│ │ │ ├── SyntaxWalker_InsufficientExecutionStackException.cs
│ │ │ ├── SyntaxWalker_InsufficientExecutionStackException.vb
│ │ │ ├── TabCharacter.cs
│ │ │ ├── TabCharacter.vb
│ │ │ ├── TaskConfigureAwait.NetCore.cs
│ │ │ ├── TaskConfigureAwait.NetFx.cs
│ │ │ ├── TestClassShouldHaveTestMethod.Latest.cs
│ │ │ ├── TestClassShouldHaveTestMethod.MsTest.cs
│ │ │ ├── TestClassShouldHaveTestMethod.NUnit.cs
│ │ │ ├── TestClassShouldHaveTestMethod.NUnit3.cs
│ │ │ ├── TestClassShouldHaveTestMethod.NUnit4.cs
│ │ │ ├── TestMethodShouldContainAssertion.Custom.Common.cs
│ │ │ ├── TestMethodShouldContainAssertion.Custom.V3.cs
│ │ │ ├── TestMethodShouldContainAssertion.Latest.cs
│ │ │ ├── TestMethodShouldContainAssertion.Moq.cs
│ │ │ ├── TestMethodShouldContainAssertion.MsTest.AnotherFile.cs
│ │ │ ├── TestMethodShouldContainAssertion.MsTest.Common.cs
│ │ │ ├── TestMethodShouldContainAssertion.MsTest.Latest.cs
│ │ │ ├── TestMethodShouldContainAssertion.MsTest.V3.cs
│ │ │ ├── TestMethodShouldContainAssertion.NUnit.FsCheck.cs
│ │ │ ├── TestMethodShouldContainAssertion.NUnit.cs
│ │ │ ├── TestMethodShouldContainAssertion.NUnit4.cs
│ │ │ ├── TestMethodShouldContainAssertion.SourceGenerators.cs
│ │ │ ├── TestMethodShouldContainAssertion.XUnit.FsCheck.cs
│ │ │ ├── TestMethodShouldContainAssertion.Xunit.Legacy.cs
│ │ │ ├── TestMethodShouldContainAssertion.Xunit.cs
│ │ │ ├── TestMethodShouldContainAssertion.XunitV3.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.Latest.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.Misc.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.MsTest.Legacy.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.MsTest.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.NUnit.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.Xunit.Legacy.cs
│ │ │ ├── TestMethodShouldHaveCorrectSignature.Xunit.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.Latest.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.MsTest.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.NUnit.V2.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.NUnit.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.Xunit.cs
│ │ │ ├── TestMethodShouldNotBeIgnored.Xunit.v1.cs
│ │ │ ├── TestsShouldNotUseThreadSleep.cs
│ │ │ ├── TestsShouldNotUseThreadSleep.vb
│ │ │ ├── ThisShouldNotBeExposedFromConstructors.CSharp10.cs
│ │ │ ├── ThisShouldNotBeExposedFromConstructors.CSharp9.cs
│ │ │ ├── ThisShouldNotBeExposedFromConstructors.cs
│ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalled.cs
│ │ │ ├── ThreadResumeOrSuspendShouldNotBeCalled.vb
│ │ │ ├── ThreadStaticNonStaticField.Fixed.cs
│ │ │ ├── ThreadStaticNonStaticField.Latest.Fixed.cs
│ │ │ ├── ThreadStaticNonStaticField.Latest.cs
│ │ │ ├── ThreadStaticNonStaticField.cs
│ │ │ ├── ThreadStaticWithInitializer.Latest.cs
│ │ │ ├── ThreadStaticWithInitializer.cs
│ │ │ ├── ThrowReservedExceptions.Latest.cs
│ │ │ ├── ThrowReservedExceptions.cs
│ │ │ ├── ThrowReservedExceptions.vb
│ │ │ ├── ToStringShouldNotReturnNull.Latest.cs
│ │ │ ├── ToStringShouldNotReturnNull.TopLevelStatements.cs
│ │ │ ├── ToStringShouldNotReturnNull.cs
│ │ │ ├── ToStringShouldNotReturnNull.vb
│ │ │ ├── TooManyGenericParameters.CustomValues.cs
│ │ │ ├── TooManyGenericParameters.DefaultValues.cs
│ │ │ ├── TooManyGenericParameters.Latest.cs
│ │ │ ├── TooManyGenericParameters.TopLevelStatements.cs
│ │ │ ├── TooManyLabelsInSwitch.Latest.cs
│ │ │ ├── TooManyLabelsInSwitch.cs
│ │ │ ├── TooManyLabelsInSwitch.vb
│ │ │ ├── TooManyLoggingCalls.Castle.Core.Logging.cs
│ │ │ ├── TooManyLoggingCalls.Log4Net.cs
│ │ │ ├── TooManyLoggingCalls.Microsoft.Extensions.Logging.cs
│ │ │ ├── TooManyLoggingCalls.NLog.cs
│ │ │ ├── TooManyLoggingCalls.Serilog.cs
│ │ │ ├── TooManyLoggingCalls.cs
│ │ │ ├── TooManyParameters_CustomValues.Latest.cs
│ │ │ ├── TooManyParameters_CustomValues.TopLevelStatements.cs
│ │ │ ├── TooManyParameters_CustomValues.cs
│ │ │ ├── TooManyParameters_CustomValues.vb
│ │ │ ├── TooManyParameters_DefaultValues.cs
│ │ │ ├── TooManyParameters_DefaultValues.vb
│ │ │ ├── TrackNotImplementedException.CSharp11.cs
│ │ │ ├── TrackNotImplementedException.cs
│ │ │ ├── TryStatementsWithIdenticalCatchShouldBeMerged.cs
│ │ │ ├── TypeExaminationOnSystemType.TopLevelStatements.cs
│ │ │ ├── TypeExaminationOnSystemType.cs
│ │ │ ├── TypeMemberVisibility.Latest.Partial.cs
│ │ │ ├── TypeMemberVisibility.Latest.cs
│ │ │ ├── TypeMemberVisibility.cs
│ │ │ ├── TypeNamesShouldNotMatchNamespaces.CSharp10.cs
│ │ │ ├── TypeNamesShouldNotMatchNamespaces.CSharp9.cs
│ │ │ ├── TypeNamesShouldNotMatchNamespaces.cs
│ │ │ ├── TypeParameterName.vb
│ │ │ ├── TypeParameterName_CustomRegex.vb
│ │ │ ├── TypesShouldNotExtendOutdatedBaseTypes.cs
│ │ │ ├── UnaryPrefixOperatorRepeated.Fixed.cs
│ │ │ ├── UnaryPrefixOperatorRepeated.Latest.cs
│ │ │ ├── UnaryPrefixOperatorRepeated.TopLevelStatements.cs
│ │ │ ├── UnaryPrefixOperatorRepeated.cs
│ │ │ ├── UnaryPrefixOperatorRepeated.vb
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.Fixed.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.Latest.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.ToFix.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.TopLevelStatements.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.cs
│ │ │ ├── UnchangedLocalVariablesShouldBeConst.cshtml.ide.g.cs
│ │ │ ├── UnconditionalJumpStatement.Latest.cs
│ │ │ ├── UnconditionalJumpStatement.cs
│ │ │ ├── UnconditionalJumpStatement.vb
│ │ │ ├── UninvokedEventDeclaration.Latest.Partial.cs
│ │ │ ├── UninvokedEventDeclaration.Latest.cs
│ │ │ ├── UninvokedEventDeclaration.cs
│ │ │ ├── UnnecessaryBitwiseOperation.Fixed.cs
│ │ │ ├── UnnecessaryBitwiseOperation.Fixed.vb
│ │ │ ├── UnnecessaryBitwiseOperation.Latest.cs
│ │ │ ├── UnnecessaryBitwiseOperation.TopLevelStatements.cs
│ │ │ ├── UnnecessaryBitwiseOperation.cs
│ │ │ ├── UnnecessaryBitwiseOperation.vb
│ │ │ ├── UnnecessaryMathematicalComparison.Latest.cs
│ │ │ ├── UnnecessaryMathematicalComparison.cs
│ │ │ ├── UnnecessaryUsings.CSharp10.Consumer.cs
│ │ │ ├── UnnecessaryUsings.CSharp10.FileScopedNamespace.Fixed.cs
│ │ │ ├── UnnecessaryUsings.CSharp10.FileScopedNamespace.cs
│ │ │ ├── UnnecessaryUsings.CSharp10.Global.cs
│ │ │ ├── UnnecessaryUsings.CSharp12.cs
│ │ │ ├── UnnecessaryUsings.CSharp9.cs
│ │ │ ├── UnnecessaryUsings.Fixed.Batch.cs
│ │ │ ├── UnnecessaryUsings.Fixed.cs
│ │ │ ├── UnnecessaryUsings.InheritedPropertyBase.cs
│ │ │ ├── UnnecessaryUsings.InheritedPropertyChild.cs
│ │ │ ├── UnnecessaryUsings.InheritedPropertyUsage.cs
│ │ │ ├── UnnecessaryUsings.TupleDeconstruct.NetCore.cs
│ │ │ ├── UnnecessaryUsings.TupleDeconstruct.NetFx.cs
│ │ │ ├── UnnecessaryUsings.cs
│ │ │ ├── UnnecessaryUsings2.cs
│ │ │ ├── UnnecessaryUsingsFNRepro.cs
│ │ │ ├── UnsignedTypesUsage.vb
│ │ │ ├── UnusedPrivateMember.CalledFromGenerated.cs
│ │ │ ├── UnusedPrivateMember.Fixed.Batch.cs
│ │ │ ├── UnusedPrivateMember.Fixed.cs
│ │ │ ├── UnusedPrivateMember.Generated.cs
│ │ │ ├── UnusedPrivateMember.Latest.Partial.cs
│ │ │ ├── UnusedPrivateMember.Latest.cs
│ │ │ ├── UnusedPrivateMember.Performance.cs
│ │ │ ├── UnusedPrivateMember.TopLevelStatements.cs
│ │ │ ├── UnusedPrivateMember.cs
│ │ │ ├── UnusedPrivateMember.part1.cs
│ │ │ ├── UnusedPrivateMember.part2.cs
│ │ │ ├── UnusedReturnValue.Latest.cs
│ │ │ ├── UnusedReturnValue.Partial.cs
│ │ │ ├── UnusedReturnValue.TopLevelStatements.cs
│ │ │ ├── UnusedReturnValue.cs
│ │ │ ├── UnusedStringBuilder.Latest.cs
│ │ │ ├── UnusedStringBuilder.cs
│ │ │ ├── UnusedStringBuilder.vb
│ │ │ ├── UriShouldNotBeHardcoded.AspNet.cs
│ │ │ ├── UriShouldNotBeHardcoded.AspNetCore.cs
│ │ │ ├── UriShouldNotBeHardcoded.Exceptions.cs
│ │ │ ├── UriShouldNotBeHardcoded.Latest.cs
│ │ │ ├── UriShouldNotBeHardcoded.cs
│ │ │ ├── UriShouldNotBeHardcoded.vb
│ │ │ ├── UseAwaitableMethod.Latest.cs
│ │ │ ├── UseAwaitableMethod.Moq.cs
│ │ │ ├── UseAwaitableMethod.TopLevelStatements.cs
│ │ │ ├── UseAwaitableMethod.cs
│ │ │ ├── UseAwaitableMethod_DbDataReader.cs
│ │ │ ├── UseAwaitableMethod_EF.cs
│ │ │ ├── UseAwaitableMethod_FluentValidation.cs
│ │ │ ├── UseAwaitableMethod_MongoDBDriver.cs
│ │ │ ├── UseAwaitableMethod_Sockets.cs
│ │ │ ├── UseCharOverloadOfStringMethods.Fixed.cs
│ │ │ ├── UseCharOverloadOfStringMethods.Framework.cs
│ │ │ ├── UseCharOverloadOfStringMethods.Framework.vb
│ │ │ ├── UseCharOverloadOfStringMethods.Latest.cs
│ │ │ ├── UseCharOverloadOfStringMethods.ToFix.cs
│ │ │ ├── UseCharOverloadOfStringMethods.cs
│ │ │ ├── UseCharOverloadOfStringMethods.vb
│ │ │ ├── UseConstantLoggingTemplate.cs
│ │ │ ├── UseConstantsWhereAppropriate.CSharp11.cs
│ │ │ ├── UseConstantsWhereAppropriate.cs
│ │ │ ├── UseCurlyBraces.CSharp7.cs
│ │ │ ├── UseCurlyBraces.cs
│ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.CSharp9.cs
│ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.Net.vb
│ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.cs
│ │ │ ├── UseDateTimeOffsetInsteadOfDateTime.vb
│ │ │ ├── UseFindSystemTimeZoneById.Net.cs
│ │ │ ├── UseFindSystemTimeZoneById.Net.vb
│ │ │ ├── UseFindSystemTimeZoneById.cs
│ │ │ ├── UseFindSystemTimeZoneById.vb
│ │ │ ├── UseGenericEventHandlerInstances.CSharp11.cs
│ │ │ ├── UseGenericEventHandlerInstances.CSharp9.cs
│ │ │ ├── UseGenericEventHandlerInstances.cs
│ │ │ ├── UseGenericWithRefParameters.cs
│ │ │ ├── UseIFormatProviderForParsingDateAndTime.cs
│ │ │ ├── UseIFormatProviderForParsingDateAndTime.vb
│ │ │ ├── UseIndexingInsteadOfLinqMethods.cs
│ │ │ ├── UseIndexingInsteadOfLinqMethods.vb
│ │ │ ├── UseLambdaParameterInConcurrentDictionary.CSharp8.cs
│ │ │ ├── UseLambdaParameterInConcurrentDictionary.vb
│ │ │ ├── UseNumericLiteralSeparator.CSharp9.cs
│ │ │ ├── UseNumericLiteralSeparator.cs
│ │ │ ├── UseParamsForVariableArguments.Latest.Partial.cs
│ │ │ ├── UseParamsForVariableArguments.Latest.cs
│ │ │ ├── UseParamsForVariableArguments.cs
│ │ │ ├── UsePascalCaseForNamedPlaceHolders.Latest.cs
│ │ │ ├── UsePascalCaseForNamedPlaceHolders.cs
│ │ │ ├── UseReturnStatement.vb
│ │ │ ├── UseShortCircuitingOperator.Fixed.cs
│ │ │ ├── UseShortCircuitingOperator.Fixed.vb
│ │ │ ├── UseShortCircuitingOperator.Latest.cs
│ │ │ ├── UseShortCircuitingOperator.TopLevelStatements.Fixed.cs
│ │ │ ├── UseShortCircuitingOperator.TopLevelStatements.cs
│ │ │ ├── UseShortCircuitingOperator.cs
│ │ │ ├── UseShortCircuitingOperator.vb
│ │ │ ├── UseStringCreate.CSharp10.cs
│ │ │ ├── UseStringCreate.cs
│ │ │ ├── UseStringIsNullOrEmpty.CSharp10.cs
│ │ │ ├── UseStringIsNullOrEmpty.CSharp11.cs
│ │ │ ├── UseStringIsNullOrEmpty.cs
│ │ │ ├── UseTestableTimeProvider.cs
│ │ │ ├── UseTestableTimeProvider.vb
│ │ │ ├── UseTrueForAll.Immutable.cs
│ │ │ ├── UseTrueForAll.cs
│ │ │ ├── UseTrueForAll.vb
│ │ │ ├── UseUnixEpoch.CSharp9.Fixed.cs
│ │ │ ├── UseUnixEpoch.CSharp9.cs
│ │ │ ├── UseUnixEpoch.Fixed.cs
│ │ │ ├── UseUnixEpoch.Fixed.vb
│ │ │ ├── UseUnixEpoch.Framework.cs
│ │ │ ├── UseUnixEpoch.Framework.vb
│ │ │ ├── UseUnixEpoch.cs
│ │ │ ├── UseUnixEpoch.vb
│ │ │ ├── UseUriInsteadOfString.Latest.Partial.cs
│ │ │ ├── UseUriInsteadOfString.Latest.cs
│ │ │ ├── UseUriInsteadOfString.TopLevelStatements.cs
│ │ │ ├── UseUriInsteadOfString.cs
│ │ │ ├── UseValueParameter.Latest.Partial.cs
│ │ │ ├── UseValueParameter.Latest.cs
│ │ │ ├── UseValueParameter.cs
│ │ │ ├── UseWhereBeforeOrderBy.cs
│ │ │ ├── UseWhereBeforeOrderBy.vb
│ │ │ ├── UseWhileLoopInstead.cs
│ │ │ ├── UseWithStatement.vb
│ │ │ ├── Utilities/
│ │ │ │ ├── CopyPasteTokenAnalyzer/
│ │ │ │ │ ├── Duplicated.Csharp10.cs
│ │ │ │ │ ├── Duplicated.cs
│ │ │ │ │ ├── DuplicatedDifferentLiterals.cs
│ │ │ │ │ ├── Razor.cshtml
│ │ │ │ │ ├── Razor.razor
│ │ │ │ │ ├── Unique.CSharp11.cs
│ │ │ │ │ ├── Unique.CSharp12.cs
│ │ │ │ │ ├── Unique.cs
│ │ │ │ │ └── Unique.vb
│ │ │ │ ├── FileMetadataAnalyzer/
│ │ │ │ │ ├── Razor.cshtml
│ │ │ │ │ ├── Razor.razor
│ │ │ │ │ ├── TEMPORARYGENERATEDFILE_class.cs
│ │ │ │ │ ├── autogenerated_comment.cs
│ │ │ │ │ ├── autogenerated_comment2.cs
│ │ │ │ │ ├── class.designer.cs
│ │ │ │ │ ├── class.g.cs
│ │ │ │ │ ├── class.g.something.cs
│ │ │ │ │ ├── class.generated.cs
│ │ │ │ │ ├── class_generated.cs
│ │ │ │ │ ├── compiler_generated.cs
│ │ │ │ │ ├── compiler_generated_attr.cs
│ │ │ │ │ ├── debugger_non_user_code.cs
│ │ │ │ │ ├── debugger_non_user_code_attr.cs
│ │ │ │ │ ├── generated_code_attr.cs
│ │ │ │ │ ├── generated_code_attr2.cs
│ │ │ │ │ ├── generated_code_attr_local_function.cs
│ │ │ │ │ ├── generated_region.cs
│ │ │ │ │ ├── generated_region_2.cs
│ │ │ │ │ └── normal_file.cs
│ │ │ │ ├── LogAnalyzer/
│ │ │ │ │ ├── GeneratedByContent.cs
│ │ │ │ │ ├── GeneratedByContent.vb
│ │ │ │ │ ├── GeneratedByName.generated.cs
│ │ │ │ │ ├── GeneratedByName.generated.vb
│ │ │ │ │ ├── Generated_cshtml.g.cs
│ │ │ │ │ ├── Generated_razor.g.cs
│ │ │ │ │ ├── Normal.cs
│ │ │ │ │ ├── Normal.vb
│ │ │ │ │ ├── Second.cs
│ │ │ │ │ └── Second.vb
│ │ │ │ ├── MethodDeclarationsAnalyzer/
│ │ │ │ │ ├── TestMethodDeclarations.Generated.g.cs
│ │ │ │ │ ├── TestMethodDeclarations.GlobalNamespace.cs
│ │ │ │ │ ├── TestMethodDeclarations.GlobalNamespace.vb
│ │ │ │ │ ├── TestMethodDeclarations.NoMethods.cs
│ │ │ │ │ ├── TestMethodDeclarations.NoMethods.vb
│ │ │ │ │ ├── TestMethodDeclarations.Partial.cs
│ │ │ │ │ ├── TestMethodDeclarations.Partial.vb
│ │ │ │ │ ├── TestMethodDeclarations.cs
│ │ │ │ │ └── TestMethodDeclarations.vb
│ │ │ │ ├── MetricsAnalyzer/
│ │ │ │ │ ├── AllMetrics.cs
│ │ │ │ │ ├── Component.razor
│ │ │ │ │ ├── Metrics.Latest.cs
│ │ │ │ │ ├── Razor.cshtml
│ │ │ │ │ ├── Razor.razor
│ │ │ │ │ └── _Imports.razor
│ │ │ │ ├── SymbolReferenceAnalyzer/
│ │ │ │ │ ├── Event.cs
│ │ │ │ │ ├── Event.vb
│ │ │ │ │ ├── ExtensionKeyword.cs
│ │ │ │ │ ├── Field.EscapedNonKeyword.cs
│ │ │ │ │ ├── Field.EscapedNonKeyword.vb
│ │ │ │ │ ├── Field.EscapedSequences.cs
│ │ │ │ │ ├── Field.ReservedKeyword.cs
│ │ │ │ │ ├── Field.ReservedKeyword.vb
│ │ │ │ │ ├── Field.cs
│ │ │ │ │ ├── Field.vb
│ │ │ │ │ ├── LocalFunction.cs
│ │ │ │ │ ├── Method.cs
│ │ │ │ │ ├── Method.vb
│ │ │ │ │ ├── Method_Partial1.cs
│ │ │ │ │ ├── Method_Partial2.cs
│ │ │ │ │ ├── MissingDeclaration.cs
│ │ │ │ │ ├── NamedType.ReservedKeyword.cs
│ │ │ │ │ ├── NamedType.ReservedKeyword.vb
│ │ │ │ │ ├── NamedType.cs
│ │ │ │ │ ├── NamedType.vb
│ │ │ │ │ ├── Parameter.ReservedKeyword.cs
│ │ │ │ │ ├── Parameter.ReservedKeyword.vb
│ │ │ │ │ ├── Parameter.cs
│ │ │ │ │ ├── Parameter.vb
│ │ │ │ │ ├── PartialConstructor.Partial.cs
│ │ │ │ │ ├── PartialConstructor.cs
│ │ │ │ │ ├── PartialEvent.Partial.cs
│ │ │ │ │ ├── PartialEvent.cs
│ │ │ │ │ ├── PrimaryConstructor.cs
│ │ │ │ │ ├── Property.FieldKeyword.cs
│ │ │ │ │ ├── Property.cs
│ │ │ │ │ ├── Property.vb
│ │ │ │ │ ├── Property_Partial1.cs
│ │ │ │ │ ├── Property_Partial2.cs
│ │ │ │ │ ├── Razor.cshtml
│ │ │ │ │ ├── Razor.razor
│ │ │ │ │ ├── Razor.razor.cs
│ │ │ │ │ ├── RazorComponent.razor
│ │ │ │ │ ├── Setter.cs
│ │ │ │ │ ├── ToDo.cs
│ │ │ │ │ ├── TokenThreshold.cs
│ │ │ │ │ ├── Tuples.cs
│ │ │ │ │ ├── Tuples.vb
│ │ │ │ │ ├── TypeParameter.cs
│ │ │ │ │ └── TypeParameter.vb
│ │ │ │ └── TokenTypeAnalyzer/
│ │ │ │ ├── IdentifierTokenThreshold.cs
│ │ │ │ ├── Identifiers.cs
│ │ │ │ ├── Identifiers.vb
│ │ │ │ ├── Razor.cshtml
│ │ │ │ ├── Razor.razor
│ │ │ │ ├── Tokens.Latest.cs
│ │ │ │ ├── Tokens.cs
│ │ │ │ ├── Tokens.vb
│ │ │ │ ├── Trivia.cs
│ │ │ │ └── Trivia.vb
│ │ │ ├── ValueTypeShouldImplementIEquatable.CSharp10.cs
│ │ │ ├── ValueTypeShouldImplementIEquatable.cs
│ │ │ ├── ValueTypeShouldImplementIEquatable.vb
│ │ │ ├── ValuesUselesslyIncremented.Latest.cs
│ │ │ ├── ValuesUselesslyIncremented.TopLevelStatements.cs
│ │ │ ├── ValuesUselesslyIncremented.cs
│ │ │ ├── VariableShadowsField.Latest.Partial.cs
│ │ │ ├── VariableShadowsField.Latest.cs
│ │ │ ├── VariableShadowsField.TopLevelStatements.cs
│ │ │ ├── VariableShadowsField.cs
│ │ │ ├── VariableUnused.Latest.cs
│ │ │ ├── VariableUnused.TopLevelStatements.cs
│ │ │ ├── VariableUnused.cs
│ │ │ ├── VariableUnused.vb
│ │ │ ├── VirtualEventField.Fixed.cs
│ │ │ ├── VirtualEventField.Latest.Fixed.cs
│ │ │ ├── VirtualEventField.Latest.Partial.cs
│ │ │ ├── VirtualEventField.Latest.cs
│ │ │ ├── VirtualEventField.cs
│ │ │ ├── WcfMissingContractAttribute.cs
│ │ │ ├── WcfNonVoidOneWay.cs
│ │ │ ├── WcfNonVoidOneWay.vb
│ │ │ ├── WeakSslTlsProtocols.CSharp12.cs
│ │ │ ├── WeakSslTlsProtocols.cs
│ │ │ ├── WeakSslTlsProtocols.vb
│ │ │ ├── WebConfig/
│ │ │ │ ├── CookieShouldBeHttpOnly/
│ │ │ │ │ ├── ConfigWithoutAttribute/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── Formatting/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── HttpOnlyCookiesConfig/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── NonHttpOnlyCookiesConfig/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── UnrelatedConfig/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── CookieShouldBeSecure/
│ │ │ │ │ ├── ConfigWithoutAttribute/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── Formatting/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── NonSecureCookieConfig/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── SecureCookieConfig/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── UnrelatedConfig/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── DatabasePasswordsShouldBeSecure/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── ExternalConfig/
│ │ │ │ │ │ ├── Web.config
│ │ │ │ │ │ └── external.config
│ │ │ │ │ ├── Formatting/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── Values/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── DisablingRequestValidation/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── EdgeValues/
│ │ │ │ │ │ ├── 3.9/
│ │ │ │ │ │ │ └── Web.config
│ │ │ │ │ │ ├── 5.6/
│ │ │ │ │ │ │ └── Web.config
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── Formatting/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── LowerCase/
│ │ │ │ │ │ └── web.config
│ │ │ │ │ ├── MultipleFiles/
│ │ │ │ │ │ ├── SubFolder/
│ │ │ │ │ │ │ └── Web.config
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── TransformCustom/
│ │ │ │ │ │ └── Web.Custom.config
│ │ │ │ │ ├── TransformDebug/
│ │ │ │ │ │ └── Web.Debug.config
│ │ │ │ │ ├── TransformRelease/
│ │ │ │ │ │ └── Web.Release.config
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── Values/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── DoNotHardcodeCredentials/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── Valid/
│ │ │ │ │ ├── Web.Custom.config
│ │ │ │ │ ├── Web.Debug.config
│ │ │ │ │ ├── Web.Release.config
│ │ │ │ │ └── Web.config
│ │ │ │ ├── DoNotHardcodeSecrets/
│ │ │ │ │ ├── Corrupt/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ │ └── Web.config
│ │ │ │ │ └── Valid/
│ │ │ │ │ ├── Web.Custom.config
│ │ │ │ │ ├── Web.Debug.config
│ │ │ │ │ ├── Web.Release.config
│ │ │ │ │ └── Web.config
│ │ │ │ └── RequestsWithExcessiveLength/
│ │ │ │ ├── Corrupt/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── UnexpectedContent/
│ │ │ │ │ └── Web.config
│ │ │ │ └── Values/
│ │ │ │ ├── ContentLength/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── ContentLength_Compliant/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── CornerCases/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── DefaultSettings/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── EmptySystemWeb/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── EmptySystemWebServer/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── InvalidConfig/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── NoSystemWeb/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── NoSystemWebServer/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── RequestAndContentLength/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── RequestLength/
│ │ │ │ │ └── Web.config
│ │ │ │ ├── SmallValues/
│ │ │ │ │ └── Web.config
│ │ │ │ └── ValidValues/
│ │ │ │ └── Web.config
│ │ │ ├── XMLSignatureCheck.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_AlwaysSafe.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XPathDocument_CSharp9.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XPathDocument_Net35.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XPathDocument_Net4.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XPathDocument_Net452.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument_CSharp10.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument_CSharp9.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument_Net35.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument_Net4.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlDocument_UnknownFrameworkVersion.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_CSharp9.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_ExternalParameter.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_Net35.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_Net4.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_Net452.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlReader_ParameterProvider.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlTextReader.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlTextReader_CSharp10.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlTextReader_CSharp9.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlTextReader_Net35.cs
│ │ │ ├── XmlExternalEntityShouldNotBeParsed_XmlTextReader_Net4.cs
│ │ │ └── XmlExternalEntityShouldNotBeParsed_XmlTextReader_UnknownFrameworkVersion.cs
│ │ ├── TestResources/
│ │ │ ├── ProjectOutFolderPath.txt
│ │ │ ├── SonarLintXml/
│ │ │ │ ├── All_properties_cs/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ ├── All_properties_vbnet/
│ │ │ │ │ └── SonarLint.xml
│ │ │ │ └── Invalid_Xml/
│ │ │ │ └── SonarLint.xml
│ │ │ └── SonarProjectConfig/
│ │ │ ├── Path_Unix/
│ │ │ │ └── SonarProjectConfig.xml
│ │ │ └── Path_Windows/
│ │ │ └── SonarProjectConfig.xml
│ │ ├── TestSuiteInitialization.cs
│ │ ├── Trackers/
│ │ │ ├── ArgumentTrackerTest.cs
│ │ │ ├── BaseTypeTrackerTest.cs
│ │ │ ├── BuilderPatternConditionTest.cs
│ │ │ ├── BuilderPatternDescriptorTest.cs
│ │ │ ├── ConstantValueFinderTest.cs
│ │ │ ├── ElementAccessTrackerTest.cs
│ │ │ ├── InvocationTrackerTest.cs
│ │ │ ├── MemberDescriptorTest.cs
│ │ │ ├── MethodDeclarationTrackerTest.cs
│ │ │ ├── ObjectCreationTrackerTest.cs
│ │ │ └── PropertyAccessTrackerTest.cs
│ │ ├── Wrappers/
│ │ │ ├── INamedTypeSymbolExtensionsTests.cs
│ │ │ ├── IOperationWrapperSonarTest.cs
│ │ │ ├── IPropertySymbolExtensionTest.cs
│ │ │ ├── ISymbolNullableExtensionsTest.cs
│ │ │ ├── RegisterSymbolStartActionWrapperTest.cs
│ │ │ └── TypeInfoExtensionsTest.cs
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.TestFramework/
│ │ ├── Analyzers/
│ │ │ ├── DummyAnalyzer.cs
│ │ │ ├── DummyAnalyzerWithLocation.cs
│ │ │ ├── DummyCodeFix.cs
│ │ │ ├── DummyUtilityAnalyzer.cs
│ │ │ ├── TestAnalyzer.cs
│ │ │ └── TestGeneratedCodeRecognizer.cs
│ │ ├── Build/
│ │ │ ├── LanguageOptions.cs
│ │ │ ├── ProjectBuilder.cs
│ │ │ ├── SnippetCompiler.cs
│ │ │ └── SolutionBuilder.cs
│ │ ├── Common/
│ │ │ ├── AnalysisScaffolding.cs
│ │ │ ├── AssertIgnoreScope.cs
│ │ │ ├── CurrentCultureScope.cs
│ │ │ ├── EditorConfigGenerator.cs
│ │ │ ├── EnvironmentVariableScope.cs
│ │ │ ├── FixAllDiagnosticProvider.cs
│ │ │ ├── LogTester.cs
│ │ │ ├── Paths.cs
│ │ │ ├── SdkPathProvider.cs
│ │ │ ├── TestCompiler.cs
│ │ │ ├── TestConstants.cs
│ │ │ ├── TestEnvironment.cs
│ │ │ └── TestFiles.cs
│ │ ├── Extensions/
│ │ │ ├── CompilationExtensions.cs
│ │ │ ├── DiagnosticDescriptorExtensions.cs
│ │ │ ├── StringAssertionsExtensions.cs
│ │ │ ├── SyntaxTreeExtensions.cs
│ │ │ └── TypeExtensions.cs
│ │ ├── MetadataReferences/
│ │ │ ├── AspNetCoreMetadataReference.cs
│ │ │ ├── CoreMetadataReference.cs
│ │ │ ├── FrameworkMetadataReference.cs
│ │ │ ├── MetadataReferenceFacade.cs
│ │ │ ├── MetadataReferenceFactory.cs
│ │ │ ├── NuGetMetadataFactory.Package.cs
│ │ │ ├── NuGetMetadataFactory.cs
│ │ │ ├── NuGetMetadataReference.cs
│ │ │ ├── NugetPackageVersions.cs
│ │ │ └── WindowsDesktopMetadataReference.cs
│ │ ├── Packaging/
│ │ │ ├── RuleTypeMappingCS.cs
│ │ │ └── RuleTypeMappingVB.cs
│ │ ├── Properties/
│ │ │ └── AssemblyInfo.cs
│ │ ├── SonarAnalyzer.TestFramework.csproj
│ │ ├── Verification/
│ │ │ ├── CodeFixVerifier.cs
│ │ │ ├── DiagnosticVerifier.cs
│ │ │ ├── DiagnosticVerifierException.cs
│ │ │ ├── IssueValidation/
│ │ │ │ ├── CompilationIssues.cs
│ │ │ │ ├── FileContent.cs
│ │ │ │ ├── IssueLocation.cs
│ │ │ │ ├── IssueLocationCollector.cs
│ │ │ │ ├── IssueLocationPair.cs
│ │ │ │ └── VerificationMessage.cs
│ │ │ ├── SuppressionHandler.cs
│ │ │ ├── Verifier.cs
│ │ │ └── VerifierBuilder.cs
│ │ └── packages.lock.json
│ ├── SonarAnalyzer.TestFramework.Test/
│ │ ├── Analyzers/
│ │ │ └── TestGeneratedCodeRecognizerTest.cs
│ │ ├── Build/
│ │ │ ├── LanguageOptionsTest.cs
│ │ │ ├── ProjectBuilderTest.cs
│ │ │ └── SnippetCompilerTest.cs
│ │ ├── Common/
│ │ │ ├── EditorConfigGeneratorTest.cs
│ │ │ ├── EnvironmentVariableScopeTest.cs
│ │ │ ├── LogTesterTest.cs
│ │ │ ├── SdkPathProviderTest.cs
│ │ │ ├── TestCompilerTest.cs
│ │ │ └── TestFilesTest.cs
│ │ ├── Extensions/
│ │ │ └── CompilationExtensionsTest.cs
│ │ ├── MetadataReferences/
│ │ │ ├── NuGetMetadataFactoryTest.cs
│ │ │ └── NugetPackageVersionsTest.cs
│ │ ├── SonarAnalyzer.TestFramework.Test.csproj
│ │ ├── TestCases/
│ │ │ ├── DiagnosticVerifierException.Concurrent.cs
│ │ │ ├── DiagnosticVerifierException.File1.cs
│ │ │ ├── DiagnosticVerifierException.File2.cs
│ │ │ ├── DiagnosticsVerifier/
│ │ │ │ ├── ExpectedIssuesNotRaised.cs
│ │ │ │ └── ExpectedIssuesNotRaised2.cs
│ │ │ ├── Dummy.SecondaryLocation.CSharp10.razor
│ │ │ ├── Dummy.SecondaryLocation.cshtml
│ │ │ ├── Dummy.SecondaryLocation.razor
│ │ │ ├── Dummy.cshtml
│ │ │ ├── Dummy.razor
│ │ │ ├── DummyExpressions.CSharp10.razor
│ │ │ ├── DummyExpressions.cshtml
│ │ │ ├── DummyExpressions.razor
│ │ │ ├── ProjectBuilder.AddDocument.cs
│ │ │ ├── ProjectBuilder.AddDocument.cshtml
│ │ │ ├── ProjectBuilder.AddDocument.razor
│ │ │ ├── ProjectBuilder.AddDocument.vb
│ │ │ ├── ProjectBuilder.AddDocument.vbhtml
│ │ │ ├── Verifier/
│ │ │ │ └── Verifier.BasePath.cs
│ │ │ ├── Verifier.BasePathAssertFails.cs
│ │ │ └── VerifyCodeFix.Empty.cs
│ │ ├── Verification/
│ │ │ ├── CodeFixProviderTest.cs
│ │ │ ├── DiagnosticVerifierExceptionTest.cs
│ │ │ ├── DiagnosticVerifierTest.cs
│ │ │ ├── IssueValidation/
│ │ │ │ ├── CompilationIssuesTest.cs
│ │ │ │ ├── IssueLocationCollectorTest.ExpectedIssueLocations.cs
│ │ │ │ ├── IssueLocationCollectorTest.FindIssueLocations.cs
│ │ │ │ ├── IssueLocationCollectorTest.FindPreciseIssueLocations.cs
│ │ │ │ ├── IssueLocationCollectorTest.MergeLocations.cs
│ │ │ │ ├── IssueLocationCollectorTest.cs
│ │ │ │ ├── IssueLocationPairTest.cs
│ │ │ │ └── IssueLocationTest.cs
│ │ │ ├── VerifierBuilderTest.cs
│ │ │ └── VerifierTest.cs
│ │ └── packages.lock.json
│ └── SonarAnalyzer.VisualBasic.Core.Test/
│ ├── Extensions/
│ │ └── ISymbolExtensionsTest.cs
│ ├── Facade/
│ │ ├── Implementation/
│ │ │ └── VisualBasicSyntaxFacadeTest.cs
│ │ └── VisualBasicFacadeTest.cs
│ ├── SonarAnalyzer.VisualBasic.Core.Test.csproj
│ ├── Syntax/
│ │ ├── Extensions/
│ │ │ ├── ExpressionSyntaxExtensionsTest.cs
│ │ │ ├── InterpolatedStringExpressionSyntaxExtensionsTest.cs
│ │ │ ├── InvocationExpressionSyntaxExtensionsTest.cs
│ │ │ ├── ObjectCreationExpressionSyntaxExtensionsTest.cs
│ │ │ ├── SyntaxNodeExtensionsVisualBasicTest.cs
│ │ │ └── SyntaxTokenExtensionsTest.cs
│ │ └── Utilities/
│ │ └── SafeVisualBasicSyntaxWalkerTest.cs
│ ├── Trackers/
│ │ └── FieldAccessTrackerTest.cs
│ └── packages.lock.json
├── azure-pipelines.yml
├── docs/
│ ├── code-of-conduct.md
│ ├── coding-style.md
│ ├── contributing-analyzer.md
│ ├── contributing-plugin.md
│ ├── issues.md
│ ├── regenerate-lock-files.md
│ └── verifier-syntax.md
├── global.json
├── pom.xml
├── scripts/
│ ├── build/
│ │ ├── build-utils.ps1
│ │ └── store-azp-variables.ps1
│ ├── rspec/
│ │ ├── CopyTestCasesFromRspec.ps1
│ │ ├── README.md
│ │ ├── rspec-templates/
│ │ │ ├── Rule.Base.cs
│ │ │ ├── Rule.CS.cs
│ │ │ ├── Rule.VB.cs
│ │ │ ├── Test.CS.cs
│ │ │ ├── Test.VB.cs
│ │ │ ├── TestCase.CS.cs
│ │ │ ├── TestCase.VB.vb
│ │ │ └── TestMethod.VB.cs
│ │ ├── rspec.ps1
│ │ └── tests/
│ │ └── CopyTestCasesFromRspec.Tests.ps1
│ ├── set-version.ps1
│ └── utils.ps1
├── sonar-csharp-core/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ ├── sonar/
│ │ │ └── plugins/
│ │ │ └── csharpenterprise/
│ │ │ └── api/
│ │ │ ├── ProfileRegistrar.java
│ │ │ └── package-info.java
│ │ └── sonarsource/
│ │ └── csharp/
│ │ └── core/
│ │ ├── CSharpCoreExtensions.java
│ │ ├── CSharpCorePluginMetadata.java
│ │ ├── CSharpFileCacheSensor.java
│ │ ├── CSharpLanguageConfiguration.java
│ │ ├── CSharpPropertyDefinitions.java
│ │ ├── CSharpSonarWayProfile.java
│ │ └── package-info.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── sonarsource/
│ │ └── csharp/
│ │ └── core/
│ │ ├── CSharpCoreExtensionsTest.java
│ │ ├── CSharpCorePluginMetadataTest.java
│ │ ├── CSharpFileCacheSensorTest.java
│ │ ├── CSharpLanguageConfigurationTest.java
│ │ ├── CSharpPropertyDefinitionsTest.java
│ │ ├── CSharpSonarWayProfileTest.java
│ │ ├── CSharpTest.java
│ │ └── TestCSharpMetadata.java
│ └── resources/
│ └── CSharpSonarWayProfileTest/
│ └── Sonar_way_profile.json
├── sonar-csharp-plugin/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── sonar/
│ │ └── plugins/
│ │ └── csharp/
│ │ ├── CSharpPlugin.java
│ │ └── package-info.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── sonar/
│ │ └── plugins/
│ │ └── csharp/
│ │ ├── CSharpPluginTest.java
│ │ ├── CSharpRulesDefinitionTest.java
│ │ └── CSharpSonarWayProfileTest.java
│ ├── resources/
│ │ └── Program.cs
│ └── scripts/
│ ├── echo.bat
│ ├── echo.sh
│ ├── forever.bat
│ └── forever.sh
├── sonar-dotnet-core/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ ├── sonar/
│ │ │ │ └── plugins/
│ │ │ │ └── dotnet/
│ │ │ │ └── tests/
│ │ │ │ ├── FileService.java
│ │ │ │ ├── NUnitTestResults.java
│ │ │ │ ├── NUnitTestResultsParser.java
│ │ │ │ ├── ParseErrorException.java
│ │ │ │ ├── PathSuffixPredicate.java
│ │ │ │ ├── ScannerFileService.java
│ │ │ │ ├── UnitTestConfiguration.java
│ │ │ │ ├── UnitTestResultParser.java
│ │ │ │ ├── UnitTestResults.java
│ │ │ │ ├── UnitTestResultsAggregator.java
│ │ │ │ ├── UnitTestResultsImportSensor.java
│ │ │ │ ├── VisualStudioTestResultParser.java
│ │ │ │ ├── VisualStudioTestResults.java
│ │ │ │ ├── WildcardPatternFileProvider.java
│ │ │ │ ├── XUnitTestResults.java
│ │ │ │ ├── XUnitTestResultsParser.java
│ │ │ │ ├── XmlParserHelper.java
│ │ │ │ ├── XmlTestReportParser.java
│ │ │ │ ├── coverage/
│ │ │ │ │ ├── BranchCoverage.java
│ │ │ │ │ ├── CoberturaReportParser.java
│ │ │ │ │ ├── ConditionData.java
│ │ │ │ │ ├── Coverage.java
│ │ │ │ │ ├── CoverageAggregator.java
│ │ │ │ │ ├── CoverageCache.java
│ │ │ │ │ ├── CoverageConfiguration.java
│ │ │ │ │ ├── CoverageParser.java
│ │ │ │ │ ├── CoverageReportImportSensor.java
│ │ │ │ │ ├── DotCoverReportParser.java
│ │ │ │ │ ├── DotCoverReportsAggregator.java
│ │ │ │ │ ├── NCover3ReportParser.java
│ │ │ │ │ ├── OpenCoverReportParser.java
│ │ │ │ │ ├── SequencePoint.java
│ │ │ │ │ ├── VisualStudioCoverageXmlReportParser.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── package-info.java
│ │ │ └── sonarsource/
│ │ │ └── dotnet/
│ │ │ └── shared/
│ │ │ ├── CallableUtils.java
│ │ │ ├── LazyCallException.java
│ │ │ ├── PropertyUtils.java
│ │ │ ├── StringUtils.java
│ │ │ ├── package-info.java
│ │ │ ├── plugins/
│ │ │ │ ├── AbstractLanguageConfiguration.java
│ │ │ │ ├── AbstractPropertyDefinitions.java
│ │ │ │ ├── AbstractSonarWayProfile.java
│ │ │ │ ├── CodeCoverageProvider.java
│ │ │ │ ├── DotNetRulesDefinition.java
│ │ │ │ ├── EncodingPerFile.java
│ │ │ │ ├── GlobalProtobufFileProcessor.java
│ │ │ │ ├── HashProvider.java
│ │ │ │ ├── MethodDeclarationsCollector.java
│ │ │ │ ├── ModuleConfiguration.java
│ │ │ │ ├── PluginMetadata.java
│ │ │ │ ├── ProjectTypeCollector.java
│ │ │ │ ├── ProtobufDataImporter.java
│ │ │ │ ├── RealPathProvider.java
│ │ │ │ ├── ReportPathCollector.java
│ │ │ │ ├── RoslynDataImporter.java
│ │ │ │ ├── RoslynReport.java
│ │ │ │ ├── RoslynRules.java
│ │ │ │ ├── SarifParserCallbackImpl.java
│ │ │ │ ├── SensorContextUtils.java
│ │ │ │ ├── TelemetryCollector.java
│ │ │ │ ├── UnitTestResultsProvider.java
│ │ │ │ ├── filters/
│ │ │ │ │ ├── GeneratedFileFilter.java
│ │ │ │ │ ├── WrongEncodingFileFilter.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── package-info.java
│ │ │ │ ├── protobuf/
│ │ │ │ │ ├── CPDTokensImporter.java
│ │ │ │ │ ├── FileMetadataImporter.java
│ │ │ │ │ ├── HighlightImporter.java
│ │ │ │ │ ├── LogImporter.java
│ │ │ │ │ ├── MethodDeclarationsImporter.java
│ │ │ │ │ ├── MetricsImporter.java
│ │ │ │ │ ├── ProtobufImporter.java
│ │ │ │ │ ├── RawProtobufImporter.java
│ │ │ │ │ ├── SymbolRefsImporter.java
│ │ │ │ │ ├── TelemetryAggregator.java
│ │ │ │ │ ├── TelemetryImporter.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── sensors/
│ │ │ │ │ ├── AbstractFileCacheSensor.java
│ │ │ │ │ ├── AnalysisWarningsSensor.java
│ │ │ │ │ ├── DotNetSensor.java
│ │ │ │ │ ├── FileTypeSensor.java
│ │ │ │ │ ├── LogSensor.java
│ │ │ │ │ ├── MethodDeclarationsSensor.java
│ │ │ │ │ ├── PropertiesSensor.java
│ │ │ │ │ ├── TelemetryJsonProcessor.java
│ │ │ │ │ ├── TelemetryJsonProjectCollector.java
│ │ │ │ │ ├── TelemetryJsonSensor.java
│ │ │ │ │ ├── TelemetryProcessor.java
│ │ │ │ │ ├── TelemetrySensor.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── telemetryjson/
│ │ │ │ ├── TelemetryJsonAggregator.java
│ │ │ │ ├── TelemetryJsonCollector.java
│ │ │ │ ├── TelemetryJsonParser.java
│ │ │ │ ├── TelemetryUtils.java
│ │ │ │ └── package-info.java
│ │ │ └── sarif/
│ │ │ ├── Location.java
│ │ │ ├── SarifParser.java
│ │ │ ├── SarifParser01And04.java
│ │ │ ├── SarifParser10.java
│ │ │ ├── SarifParserCallback.java
│ │ │ ├── SarifParserFactory.java
│ │ │ └── package-info.java
│ │ └── protobuf/
│ │ └── .gitignore
│ └── test/
│ ├── java/
│ │ └── org/
│ │ ├── sonar/
│ │ │ └── plugins/
│ │ │ └── dotnet/
│ │ │ └── tests/
│ │ │ ├── NUnitTestResultsParserTest.java
│ │ │ ├── PathSuffixPredicateTest.java
│ │ │ ├── ScannerFileServiceTest.java
│ │ │ ├── UnitTestResultsAggregatorTest.java
│ │ │ ├── UnitTestResultsImportSensorTest.java
│ │ │ ├── VisualStudioTestResultParserTest.java
│ │ │ ├── VstsUtils.java
│ │ │ ├── WildcardPatternFileProviderTest.java
│ │ │ ├── XUnitTestResultParserTest.java
│ │ │ ├── XmlParserHelperTest.java
│ │ │ └── coverage/
│ │ │ ├── BranchCoverageTest.java
│ │ │ ├── CoberturaReportParserTest.java
│ │ │ ├── ConditionDataTest.java
│ │ │ ├── CoverageAggregatorTest.java
│ │ │ ├── CoverageCacheTest.java
│ │ │ ├── CoverageReportImportSensorTest.java
│ │ │ ├── CoverageTest.java
│ │ │ ├── DotCoverReportParserTest.java
│ │ │ ├── DotCoverReportsAggregatorTest.java
│ │ │ ├── NCover3ReportParserTest.java
│ │ │ ├── OpenCoverReportParserTest.java
│ │ │ └── VisualStudioCoverageXmlReportParserTest.java
│ │ └── sonarsource/
│ │ └── dotnet/
│ │ └── shared/
│ │ ├── CallableUtilsTests.java
│ │ ├── plugins/
│ │ │ ├── AbstractLanguageConfigurationTest.java
│ │ │ ├── AbstractPropertyDefinitionsTest.java
│ │ │ ├── AbstractSonarWayProfileTest.java
│ │ │ ├── CodeCoverageProviderTest.java
│ │ │ ├── DotNetRulesDefinitionTest.java
│ │ │ ├── EncodingPerFileTest.java
│ │ │ ├── GeneratedFileFilterTest.java
│ │ │ ├── GlobalProtobufFileProcessorTest.java
│ │ │ ├── HashProviderTest.java
│ │ │ ├── MethodDeclarationsSensorTest.java
│ │ │ ├── ModuleConfigurationTest.java
│ │ │ ├── ProjectTypeCollectorTest.java
│ │ │ ├── ProtobufDataImporterTest.java
│ │ │ ├── RealPathProviderTest.java
│ │ │ ├── ReportPathCollectorTest.java
│ │ │ ├── RoslynDataImporterTest.java
│ │ │ ├── RoslynRulesTest.java
│ │ │ ├── SensorContextUtilsTest.java
│ │ │ ├── UnitTestResultsProviderTest.java
│ │ │ ├── WrongEncodingFileFilterTest.java
│ │ │ ├── protobuf/
│ │ │ │ ├── CPDTokensImporterTest.java
│ │ │ │ ├── FileMetadataImporterTest.java
│ │ │ │ ├── HighlightImporterTest.java
│ │ │ │ ├── LogImporterTest.java
│ │ │ │ ├── MethodDeclarationsImporterTest.java
│ │ │ │ ├── MetricsImporterTest.java
│ │ │ │ ├── RazorImporterTestBase.java
│ │ │ │ ├── RazorMetricsImporterTest.java
│ │ │ │ ├── RazorSymbolRefsImporterTest.java
│ │ │ │ ├── SymbolRefsImporterTest.java
│ │ │ │ ├── TelemetryAggregatorLanguageVersionTest.java
│ │ │ │ └── TelemetryImporterTest.java
│ │ │ ├── sensors/
│ │ │ │ ├── AbstractFileCacheSensorTest.java
│ │ │ │ ├── AnalysisWarningsSensorTest.java
│ │ │ │ ├── DotNetSensorTest.java
│ │ │ │ ├── FileTypeSensorTest.java
│ │ │ │ ├── LogSensorTest.java
│ │ │ │ ├── PropertiesSensorTest.java
│ │ │ │ ├── TelemetryJsonProcessorTest.java
│ │ │ │ ├── TelemetryJsonProjectSensorTest.java
│ │ │ │ ├── TelemetryJsonSensorTest.java
│ │ │ │ ├── TelemetryProcessorTest.java
│ │ │ │ └── TelemetrySensorTest.java
│ │ │ ├── telemetryjson/
│ │ │ │ ├── TelemetryJsonAggregatorTest.java
│ │ │ │ ├── TelemetryJsonCollectorTest.java
│ │ │ │ ├── TelemetryJsonParserTest.java
│ │ │ │ └── TelemetryUtilsTest.java
│ │ │ └── testutils/
│ │ │ ├── AutoDeletingTempFile.java
│ │ │ ├── FileUtils.java
│ │ │ └── ProtobufFilterTool.java
│ │ └── sarif/
│ │ ├── SarifParser01And04Test.java
│ │ ├── SarifParser10Test.java
│ │ ├── SarifParserCallbackImplTest.java
│ │ └── SarifParserFactoryTest.java
│ └── resources/
│ ├── AbstractSonarWayProfile/
│ │ └── Sonar_way_profile.json
│ ├── Directory.Build.targets
│ ├── DotNetRulesDefinitionTest/
│ │ ├── Rules.json
│ │ ├── S100.html
│ │ ├── S100.json
│ │ ├── S1111.html
│ │ ├── S1111.json
│ │ ├── S1112.html
│ │ ├── S1112.json
│ │ ├── S1113.html
│ │ ├── S1113.json
│ │ ├── S1114.html
│ │ ├── S1114.json
│ │ ├── S1115.html
│ │ ├── S1115.json
│ │ ├── S1116.html
│ │ ├── S1116.json
│ │ ├── S1117.html
│ │ ├── S1117.json
│ │ ├── S2115.html
│ │ ├── S2115.json
│ │ ├── S4502.html
│ │ ├── S4502.json
│ │ └── Sonar_way_profile.json
│ ├── HashProvider/
│ │ ├── Ansi.cs
│ │ ├── CodeNoBom.cs
│ │ ├── CodeWithBom.cs
│ │ ├── EmptyNoBom.cs
│ │ ├── EmptyWithBom.cs
│ │ ├── Utf16.cs
│ │ └── Utf8.cs
│ ├── LogSensorTest/
│ │ └── log.pb
│ ├── MethodDeclarationsSensorTest/
│ │ ├── ReadMe.md
│ │ ├── TestMethodImport/
│ │ │ ├── TestMethodImport.Tests/
│ │ │ │ ├── TestBase.cs
│ │ │ │ ├── TestClass.cs
│ │ │ │ ├── TestMethodImport.Tests.csproj
│ │ │ │ └── packages.lock.json
│ │ │ └── TestMethodImport.sln
│ │ └── protobuf-files/
│ │ └── test-method-declarations.pb
│ ├── Program.cs
│ ├── ProtobufImporterTest/
│ │ ├── Program.cs
│ │ ├── README.md
│ │ ├── custom-log.pb
│ │ ├── file-metadata.pb
│ │ ├── invalid-encoding.pb
│ │ ├── metrics.pb
│ │ ├── symrefs.pb
│ │ ├── token-cpd.pb
│ │ ├── token-type.pb
│ │ └── unknown-log.pb
│ ├── RazorProtobufImporter/
│ │ ├── ReadMe.md
│ │ ├── Roslyn 4.10/
│ │ │ ├── file-metadata.pb
│ │ │ ├── global.json
│ │ │ ├── log.pb
│ │ │ ├── metrics.pb
│ │ │ ├── symrefs.pb
│ │ │ ├── telemetry.pb
│ │ │ ├── test-method-declarations.pb
│ │ │ ├── token-cpd.pb
│ │ │ └── token-type.pb
│ │ ├── Roslyn 4.9/
│ │ │ ├── file-metadata.pb
│ │ │ ├── global.json
│ │ │ ├── log.pb
│ │ │ ├── metrics.pb
│ │ │ ├── symrefs.pb
│ │ │ ├── telemetry.pb
│ │ │ ├── test-method-declarations.pb
│ │ │ ├── token-cpd.pb
│ │ │ └── token-type.pb
│ │ └── WebProject/
│ │ ├── BlazorWebAssembly.csproj
│ │ ├── Cases.razor
│ │ ├── OverlapSymbolReferences.razor
│ │ ├── Program.cs
│ │ ├── _Imports.razor
│ │ └── packages.lock.json
│ ├── RoslynDataImporterTest/
│ │ ├── roslyn-report-empty.json
│ │ ├── roslyn-report-invalid-location.json
│ │ └── roslyn-report.json
│ ├── RoslynProfileExporterTest/
│ │ ├── empty_string_value.xml
│ │ ├── mixed.xml
│ │ ├── no_rules.xml
│ │ └── only_sonarlint.xml
│ ├── RoslynRulesTest/
│ │ └── Rules.json
│ ├── SarifParserTest/
│ │ ├── v0_1.json
│ │ ├── v0_1_empty_issues.json
│ │ ├── v0_1_empty_no_issues.json
│ │ ├── v0_4.json
│ │ ├── v0_4_empty_no_results.json
│ │ ├── v0_4_empty_no_runLogs.json
│ │ ├── v0_4_empty_results.json
│ │ ├── v0_4_empty_runLogs.json
│ │ ├── v0_4_file_level_issue.json
│ │ ├── v0_4_secondary_locations.json
│ │ ├── v0_4_secondary_locations_no_messages.json
│ │ ├── v1_0.json
│ │ ├── v1_0_another.json
│ │ ├── v1_0_empty.json
│ │ ├── v1_0_empty_location.json
│ │ ├── v1_0_escaping.json
│ │ ├── v1_0_execution_flow.json
│ │ ├── v1_0_execution_flow_no_secondary_locations.json
│ │ ├── v1_0_file_level_issue.json
│ │ ├── v1_0_file_level_issue_with_execution_flow.json
│ │ ├── v1_0_file_name_with_illegal_char.json
│ │ ├── v1_0_invalid_execution_flow_value.json
│ │ ├── v1_0_no_execution_flow.json
│ │ ├── v1_0_no_location.json
│ │ ├── v1_0_no_message.json
│ │ ├── v1_0_region_with_length.json
│ │ ├── v1_0_relative_paths.json
│ │ ├── v1_0_same_start_end_location.json
│ │ ├── v1_0_secondary_locations.json
│ │ ├── v1_0_secondary_locations_messages.json
│ │ └── v1_0_suppressed.json
│ ├── TelemetryJsonSensorTest/
│ │ ├── 0/
│ │ │ └── Telemetry.json
│ │ ├── 1/
│ │ │ └── Telemetry.json
│ │ ├── Telemetry.Other.json
│ │ └── Telemetry.S4NET.json
│ ├── TelemetrySensorTest/
│ │ ├── 0/
│ │ │ └── telemetry.pb
│ │ ├── 1/
│ │ │ └── telemetry.pb
│ │ └── Readme.md
│ ├── analysisWarnings/
│ │ ├── AnalysisWarnings.AutoScan.json
│ │ └── AnalysisWarnings.Scanner.json
│ ├── cobertura/
│ │ ├── absolute_path_no_sources.xml
│ │ ├── absolute_path_with_sources.xml
│ │ ├── branch_jump_conditions.xml
│ │ ├── branch_malformed_condition_coverage.xml
│ │ ├── branch_mixed_conditions.xml
│ │ ├── branch_no_condition_coverage_attr.xml
│ │ ├── branch_no_conditions.xml
│ │ ├── branch_switch_condition.xml
│ │ ├── branch_unresolved_file.xml
│ │ ├── empty_source_tag.xml
│ │ ├── invalid_path.xml
│ │ ├── invalid_root.xml
│ │ ├── line_coverage.xml
│ │ ├── line_coverage_unresolved_file.xml
│ │ ├── multiple_classes_same_filename.xml
│ │ ├── relative_path_no_sources.xml
│ │ ├── relative_path_with_sources.xml
│ │ ├── source_with_nested_elements.xml
│ │ └── valid_empty.xml
│ ├── dotcover/
│ │ ├── invalid_path.html
│ │ ├── no_highlight.html
│ │ ├── no_script.html
│ │ ├── no_title.html
│ │ ├── no_title_end.html
│ │ ├── title_nested_tag.html
│ │ ├── title_swapped.html
│ │ ├── valid.html
│ │ ├── valid_big.html
│ │ └── valid_multiple_sequence_points_per_line.html
│ ├── dotcover_aggregator/
│ │ ├── empty_folder/
│ │ │ └── src/
│ │ │ └── .gitignore
│ │ ├── empty_folder.html
│ │ ├── foo.bar/
│ │ │ └── src/
│ │ │ ├── 1.html
│ │ │ ├── 2.html
│ │ │ └── nosource.html
│ │ ├── foo.bar.html
│ │ ├── no_extension
│ │ ├── no_sources.html
│ │ └── not_html.html
│ ├── ncover3/
│ │ ├── invalid_path.nccov
│ │ ├── invalid_root.nccov
│ │ ├── no_version.nccov
│ │ ├── one_file.nccov
│ │ ├── valid.nccov
│ │ └── wrong_version.nccov
│ ├── nunit/
│ │ ├── invalid_root.xml
│ │ ├── invalid_test_outcome.xml
│ │ ├── readme.md
│ │ ├── test_name_not_mapped.xml
│ │ ├── valid_comma_in_double.xml
│ │ ├── valid_inheritance.xml
│ │ ├── valid_no_execution_time.xml
│ │ ├── valid_nunit2.xml
│ │ └── valid_nunit3.xml
│ ├── opencover/
│ │ ├── code_tested_by_multiple_projects.xml
│ │ ├── coverage_branches.xml
│ │ ├── deterministic_source_paths.xml
│ │ ├── invalid_file_id.xml
│ │ ├── invalid_path.xml
│ │ ├── invalid_root.xml
│ │ ├── missing_start_line.xml
│ │ ├── one_class.xml
│ │ ├── switch_expression_multiple_test_projects_1.xml
│ │ ├── switch_expression_multiple_test_projects_2.xml
│ │ ├── valid.xml
│ │ ├── valid_case_multiple_sequence_points_per_line.xml
│ │ └── wrong_start_line.xml
│ ├── samples/
│ │ ├── ReadMe.md
│ │ ├── csharp/
│ │ │ └── Calculator/
│ │ │ ├── Calculator/
│ │ │ │ ├── Calculator.cs
│ │ │ │ ├── Calculator.csproj
│ │ │ │ └── packages.lock.json
│ │ │ ├── Calculator.MSTest/
│ │ │ │ ├── Calculator.MSTest.csproj
│ │ │ │ ├── CalculatorTests.cs
│ │ │ │ ├── MSTestSettings.cs
│ │ │ │ └── packages.lock.json
│ │ │ ├── Calculator.NUnit3/
│ │ │ │ ├── Calculator.NUnit3.csproj
│ │ │ │ ├── CalculatorTests.cs
│ │ │ │ └── packages.lock.json
│ │ │ ├── Calculator.NUnit4/
│ │ │ │ ├── Calculator.NUnit4.csproj
│ │ │ │ ├── CalculatorTests.cs
│ │ │ │ └── packages.lock.json
│ │ │ ├── Calculator.sln
│ │ │ └── Calculator.xUnit/
│ │ │ ├── Calculator.xUnit.csproj
│ │ │ ├── CalculatorTests.cs
│ │ │ └── packages.lock.json
│ │ └── vbnet/
│ │ └── Calculator/
│ │ ├── Calculator/
│ │ │ ├── Calculator.vb
│ │ │ └── Calculator.vbproj
│ │ ├── Calculator.MSTest/
│ │ │ ├── Calculator.MSTest.vbproj
│ │ │ ├── CalculatorTests.vb
│ │ │ └── MSTestSettings.vb
│ │ ├── Calculator.NUnit3/
│ │ │ ├── Calculator.NUnit3.vbproj
│ │ │ └── CalculatorTests.vb
│ │ ├── Calculator.NUnit4/
│ │ │ ├── Calculator.NUnit4.vbproj
│ │ │ └── CalculatorTests.vb
│ │ ├── Calculator.sln
│ │ └── Calculator.xUnit/
│ │ ├── Calculator.xUnit.vbproj
│ │ └── CalculatorTests.vb
│ ├── visualstudio_coverage_xml/
│ │ ├── deterministic_source_paths.coveragexml
│ │ ├── getter_setter.coveragexml
│ │ ├── getter_setter_multiple_per_line.coveragexml
│ │ ├── invalid_path.coveragexml
│ │ ├── invalid_root.coveragexml
│ │ ├── no_ranges.coveragexml
│ │ ├── valid.coveragexml
│ │ ├── valid_complex_case.coveragexml
│ │ └── wrong_covered.coveragexml
│ ├── visualstudio_test_results/
│ │ ├── invalid_character.trx
│ │ ├── invalid_dates.trx
│ │ ├── invalid_test_outcome.trx
│ │ ├── multiple_runs_same_test.trx
│ │ ├── nunitproject_with_vs_logger.trx
│ │ ├── projects/
│ │ │ └── TestReport/
│ │ │ ├── TestReport/
│ │ │ │ ├── Test1.cs
│ │ │ │ ├── TestReport.csproj
│ │ │ │ └── packages.lock.json
│ │ │ └── TestReport.sln
│ │ ├── readme.md
│ │ ├── test_name_not_mapped.trx
│ │ ├── test_result_no_test_method.trx
│ │ └── valid.trx
│ ├── xml_parser_helper/
│ │ ├── invalid_prolog.txt
│ │ └── valid.xml
│ └── xunit/
│ ├── ReadMe.md
│ ├── invalid_root.xml
│ ├── invalid_test_outcome.xml
│ ├── test_name_not_mapped.xml
│ ├── valid.xml
│ ├── valid_data_attribute.xml
│ ├── valid_generic_methods_csharp.xml
│ ├── valid_generic_methods_vbnet.xml
│ └── valid_no_execution_time.xml
├── sonar-vbnet-core/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ ├── sonar/
│ │ │ └── plugins/
│ │ │ └── vbnetenterprise/
│ │ │ └── api/
│ │ │ ├── ProfileRegistrar.java
│ │ │ └── package-info.java
│ │ └── sonarsource/
│ │ └── vbnet/
│ │ └── core/
│ │ ├── VbNetCoreExtensions.java
│ │ ├── VbNetCorePluginMetadata.java
│ │ ├── VbNetFileCacheSensor.java
│ │ ├── VbNetLanguageConfiguration.java
│ │ ├── VbNetPropertyDefinitions.java
│ │ ├── VbNetSonarWayProfile.java
│ │ └── package-info.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── sonarsource/
│ │ └── vbnet/
│ │ └── core/
│ │ ├── TestVbNetMetadata.java
│ │ ├── VbNetCoreExtensionsTest.java
│ │ ├── VbNetCorePluginMetadataTest.java
│ │ ├── VbNetFileCacheSensorTest.java
│ │ ├── VbNetLanguageConfigurationTest.java
│ │ ├── VbNetPropertyDefinitionsTest.java
│ │ ├── VbNetSonarWayProfileTest.java
│ │ └── VbNetTest.java
│ └── resources/
│ └── VbNetSonarWayProfileTest/
│ └── Sonar_way_profile.json
└── sonar-vbnet-plugin/
├── README.md
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── vbnet/
│ ├── VbNetPlugin.java
│ └── package-info.java
└── test/
├── java/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── vbnet/
│ ├── VbNetPluginTest.java
│ ├── VbNetRulesDefinitionTest.java
│ └── VbNetSonarWayProfileTest.java
├── resources/
│ └── Program.cs
└── scripts/
├── echo.bat
├── echo.sh
├── forever.bat
└── forever.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# Editor configuration, see http://editorconfig.org
# Visual studio supported code style syntax https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
# Visual studio supported naming convention syntax https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions
# Undocumented https://kent-boogaart.com/blog/editorconfig-reference-for-c-developers
# Undocumented CS options https://github.com/dotnet/roslyn/blob/master/src/Workspaces/CSharp/Portable/Formatting/CSharpFormattingOptions.cs
# Undocumented .NET options https://github.com/dotnet/roslyn/blob/master/src/Workspaces/Core/Portable/CodeStyle/CodeStyleOptions.cs
# top-most EditorConfig file, hierarchy search will stop in this file
root = true
# ----------------------------------------------------------------------------------------------------------------------
# General settings
# ----------------------------------------------------------------------------------------------------------------------
# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
[*.md]
trim_trailing_whitespace = false
end_of_line = lf
# Code files
[*.{cs,csx,vb,vbx}]
charset = utf-8-bom
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
max_line_length = 200
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON and YML files
[*.{json,yml}]
indent_size = 2
# Scripting files
[*.{ps1,bat,cmd}]
indent_size = 4
# Java code files
[*.java]
indent_size = 2
# Pom files
[pom.xml]
indent_size = 2
# ----------------------------------------------------------------------------------------------------------------------
# Coding styles
# ----------------------------------------------------------------------------------------------------------------------
# Dotnet code style settings:
[*.{cs,vb}]
tab_width = 4
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
# Suggest more modern language features when available
dotnet_style_coalesce_expression = true:warning
dotnet_style_collection_initializer = true:warning
dotnet_style_prefer_collection_expression = when_types_loosely_match:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true:warning # This is activated only on production code below via IDE0130 settings
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line:warning
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_return = false:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_interpolation = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_readonly_field = true:warning
# Parameter preferences
dotnet_code_quality_unused_parameters = non_public:warning
# Parentheses
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# CSharp code style settings:
[*.cs]
csharp_prefer_braces = true:warning
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = true:warning
# Prefer expression-body
csharp_style_expression_bodied_methods = true:warning
csharp_style_expression_bodied_constructors = true:warning
csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_lambdas = true:warning
csharp_style_expression_bodied_local_functions = true:warning
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_inlined_variable_declaration = true:warning
csharp_prefer_simple_default_expression = true:warning
csharp_style_deconstructed_variable_declaration = true:warning
csharp_style_throw_expression = true:warning
csharp_style_conditional_delegate_call = true:warning
# IDE0055 configuration
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indent
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents_when_block = true
# Spaces
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_before_comma = false
csharp_space_after_dot = false
csharp_space_before_dot = false
csharp_space_after_semicolon_in_for_statement = true
csharp_space_before_semicolon_in_for_statement = false
# Extra space before equals sign DOES MATTER https://github.com/dotnet/roslyn/issues/20355
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_open_square_brackets = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
# End of IDE0055 configuration
csharp_using_directive_placement = outside_namespace:warning
csharp_prefer_simple_using_statement = true:warning
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_prefer_method_group_conversion = true:warning
csharp_style_prefer_top_level_statements = true:warning
csharp_style_prefer_primary_constructors = false:suggestion
csharp_prefer_system_threading_lock = true:warning
csharp_style_prefer_null_check_over_type_check = true:warning
csharp_style_prefer_local_over_anonymous_function = true:warning
csharp_style_prefer_index_operator = false:silent
csharp_style_prefer_range_operator = false:silent
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_prefer_tuple_swap = false:silent
csharp_style_prefer_unbound_generic_type_in_nameof = true:warning
csharp_style_prefer_utf8_string_literals = true:warning
csharp_style_unused_value_assignment_preference = unused_local_variable:silent
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_prefer_static_local_function = true:warning
csharp_prefer_static_anonymous_function = true:warning
csharp_style_prefer_readonly_struct = true:warning
csharp_style_prefer_readonly_struct_member = true:warning
csharp_style_prefer_switch_expression = true:warning
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_extended_property_pattern = true:warning
csharp_style_prefer_implicitly_typed_lambda_expression = true:warning
# ----------------------------------------------------------------------------------------------------------------------
# Naming conventions
# ----------------------------------------------------------------------------------------------------------------------
# ORDERING DOES MATTER!!!
# Naming conventions should be ordered from most-specific to least-specific in the .editorconfig file.
# The first rule encountered that can be applied is the only rule that is applied.
[*.{cs,vb}]
# Naming rules
dotnet_naming_rule.interface_must_start_with_i.severity = warning
dotnet_naming_rule.interface_must_start_with_I.symbols = interface_types
dotnet_naming_rule.interface_must_start_with_i.style = I_style
dotnet_naming_rule.variables_must_be_camel_style.severity = warning
dotnet_naming_rule.variables_must_be_camel_style.symbols = parameter_types
dotnet_naming_rule.variables_must_be_camel_style.style = camel_style
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface_types.applicable_kinds = interface
dotnet_naming_symbols.interface_types.applicable_accessibilities = *
dotnet_naming_symbols.parameter_types.applicable_kinds = parameter
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
# Naming styles
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.camel_style.capitalization = camel_case
dotnet_naming_style.I_style.required_prefix = I
dotnet_naming_style.I_style.capitalization = pascal_case
# ----------------------------------------------------------------------------------------------------------------------
# Rules
# ----------------------------------------------------------------------------------------------------------------------
dotnet_diagnostic.CS7035.severity = none # CS7035: it expects the build number to fit in 16 bits, our build numbers are bigger https://github.com/dotnet/roslyn/issues/17024#issuecomment-1669503201
dotnet_diagnostic.CA1822.severity = warning # Increase visibility for Member 'xxx' does not access instance data and can be marked as static
dotnet_diagnostic.RS2008.severity = none # Enable analyzer release tracking - we don't use the release tracking analyzer
dotnet_diagnostic.RS1036.severity = none # A project containing analyzers or source generators should specify the property '
Shared naming conventions allow teams to collaborate efficiently.
This rule raises an issue when a method or a property name is not PascalCased.
For example, the method
public int doSomething() {...} // Noncompliant
should be renamed to
public int DoSomething() {...}
ComImportAttribute or InterfaceTypeAttribute.extern methods.MyXMethod is compliant, but
XM is not.'_' character.
void My_method_(){...} // Noncompliant, leading and trailing underscores are reported
void My_method(){...} // Compliant by exception
Microsoft Capitalization Conventions
================================================ FILE: analyzers/rspec/cs/S100.json ================================================ { "title": "Methods and properties should be named in PascalCase", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "IDENTIFIABLE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-100", "sqKey": "S100", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1006.html ================================================Default arguments are determined by the static type of the object.
class Base
{
public virtual void Run(int distance = 42) { /* ... */ }
}
class Derived : Base
{
public override void Run(int distance = 5) { /* ... */ }
}
Base x = new Base();
x.Run(); // Here the default value of distance is 42
Derived d = new Derived();
d.Run(); // Here the default value of distance is 5
Base b = new Derived();
b.Run(); // Here the default value of distance is 42, not 5
If a default argument is different for a parameter in an overriding method, the value used in the call will be different when calls are made via the base or derived object, which may be contrary to developer expectations.
Default parameter values in explicit interface implementations will be never used, because the static type of the object will always be the implemented interface. Thus, specifying default values in this case is confusing.
interface IRunner
{
void Run(int distance = 42) { /* ... */ }
}
class Runner : IRunner
{
void IRunner.Run(int distance = 5) { /* ... */ }
}
IRunner x = new Runner();
x.Run(); // Here the default value of distance is 42
Runner d = new Runner();
d.Run(); // This will not compile as the Run method is only visible through the specified interface
using System;
public class Base
{
public virtual void Write(int i = 42)
{
Console.WriteLine(i);
}
}
public class Derived : Base
{
public override void Write(int i = 5) // Noncompliant
{
Console.WriteLine(i);
}
}
public class Program
{
public static void Main()
{
var derived = new Derived();
derived.Write(); // writes 5
Print(derived); // writes 42; was that expected?
}
private static void Print(Base item)
{
item.Write();
}
}
using System;
public class Base
{
public virtual void Write(int i = 42)
{
Console.WriteLine(i);
}
}
public class Derived : Base
{
public override void Write(int i = 42)
{
Console.WriteLine(i);
}
}
public class Program
{
public static void Main()
{
var derived = new Derived();
derived.Write(); // writes 42
Print(derived); // writes 42
}
private static void Print(Base item)
{
item.Write();
}
}
Shared naming conventions allow teams to collaborate efficiently.
This rule raises an issue when a type name is not PascalCased.
For example, the classes
class my_class {...}
class SOMEName42 {...}
should be renamed to
class MyClass {...}
class SomeName42 {...}
ComImportAttribute or InterfaceTypeAttribute.MyXClass is compliant,
but XC is not.'_' characters in class names inside test projects: in that case, each word separated by '_'
should be PascalCased.
class Some_Name___42 {...} // Compliant in tests
class Some_name___42 {...} // Noncompliant
class Some_Name_XC {...} // Noncompliant because of XC, should be Some_Name_Xc
Scrolling horizontally to see a full line of code lowers the code readability.
================================================ FILE: analyzers/rspec/cs/S103.json ================================================ { "title": "Lines should not be too long", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "convention" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-103", "sqKey": "S103", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S104.html ================================================When a source file grows too much, it can accumulate numerous responsibilities and become challenging to understand and maintain.
Above a specific threshold, refactor the file into smaller files whose code focuses on well-defined tasks. Those smaller files will be easier to understand and test.
================================================ FILE: analyzers/rspec/cs/S104.json ================================================ { "title": "Files should not have too many lines of code", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1h" }, "tags": [ "brain-overload" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-104", "sqKey": "S104", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1048.html ================================================The finalizers are used to perform any necessary final clean-up when the garbage collector is collecting a class instance. The programmer has no control over when the finalizer is called; the garbage collector decides when to call it.
When creating a finalizer, it should never throw an exception, as there is a high risk of having the application terminated leaving unmanaged resources without a graceful cleanup.
The rule raises an issue on throw statements used in a finalizer.
class MyClass
{
~MyClass()
{
throw new NotImplementedException(); // Noncompliant: finalizer throws an exception
}
}
class MyClass
{
~MyClass()
{
// Compliant: finalizer does not throw an exception
}
}
In general object finalization can be a complex and error-prone operation and should not be implemented except within the dispose pattern.
When cleaning up unmanaged resources, it is
recommended to implement the dispose pattern or, to cover uncalled Dispose method by the consumer, implement SafeHandle.
SafeHandle
ClassIDisposable.Dispose
MethodThe tab width can differ from one development environment to another. Using tabs may require other developers to configure their environment (text editor, preferences, etc.) to read source code.
That is why using spaces is preferable.
================================================ FILE: analyzers/rspec/cs/S105.json ================================================ { "title": "Tabulation characters should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-105", "sqKey": "S105", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S106.html ================================================In software development, logs serve as a record of events within an application, providing crucial insights for debugging. When logging, it is essential to ensure that the logs are:
Those requirements are not met if a program directly writes to the standard outputs (e.g., Console). That is why defining and using a dedicated logger is highly recommended.
The rule doesn’t raise an issue for:
[Conditional ("DEBUG")]#if DEBUG)The following noncompliant code:
public class MyClass
{
private void DoSomething()
{
// ...
Console.WriteLine("My Message"); // Noncompliant
// ...
}
}
Could be replaced by:
public class MyClass
{
private readonly ILogger _logger;
// ...
private void DoSomething()
{
// ...
_logger.LogInformation("My Message");
// ...
}
}
Nested code - blocks of code inside blocks of code - is eventually necessary, but increases complexity. This is why keeping the code as flat as possible, by avoiding unnecessary nesting, is considered a good practice.
Merging if statements when possible will decrease the nesting of the code and improve its readability.
Code like
if (condition1)
{
if (condition2) // Noncompliant
{
// ...
}
}
Will be more readable as
if (condition1 && condition2) // Compliant
{
// ...
}
If merging the conditions seems to result in a more complex code, extracting the condition or part of it in a named function or variable is a better approach to fix readability.
if (file != null)
{
if (file.isFile() || file.isDirectory()) // Noncompliant
{
/* ... */
}
}
bool isFileOrDirectory(File file)
{
return file.isFile() || file.isDirectory();
}
/* ... */
if (file != null && isFileOrDirectory(file)) // Compliant
{
/* ... */
}
================================================
FILE: analyzers/rspec/cs/S1066.json
================================================
{
"title": "Mergeable \"if\" statements should be combined",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1066",
"sqKey": "S1066",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1067.html
================================================
The complexity of an expression is defined by the number of &&, || and condition ? ifTrue : ifFalse
operators it contains.
A single expression’s complexity should not become too high to keep the code readable.
With the default threshold value of 3
if (((condition1 && condition2) || (condition3 && condition4)) && condition5) { ... }
if ((MyFirstCondition() || MySecondCondition()) && MyLastCondition()) { ... }
================================================
FILE: analyzers/rspec/cs/S1067.json
================================================
{
"title": "Expressions should not be too complex",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "3min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-1067",
"sqKey": "S1067",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S107.html
================================================
Methods with a long parameter list are difficult to use because maintainers must figure out the role of each parameter and keep track of their position.
void SetCoordinates(int x1, int y1, int z1, int x2, int y2, int z2) // Noncompliant
{
// ...
}
The solution can be to:
// Each function does a part of what the original setCoordinates function was doing, so confusion risks are lower
void SetOrigin(int x, int y, int z)
{
// ...
}
void SetSize(int width, int height, int depth)
{
//
}
// In geometry, Point is a logical structure to group data
readonly record struct Point(int X, int Y, int Z);
void SetCoordinates(Point p1, Point p2)
{
// ...
}
This rule raises an issue when a method has more parameters than the provided threshold.
The rule does not count the parameters intended for a base class constructor.
With a maximum number of 4 parameters:
public class BaseClass
{
public BaseClass(int param1)
{
// ...
}
}
public class DerivedClass : BaseClass
{
public DerivedClass(int param1, int param2, int param3, string param4, long param5) : base(param1) // Compliant by exception
{
// ...
}
}
================================================
FILE: analyzers/rspec/cs/S107.json
================================================
{
"title": "Methods should not have too many parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-107",
"sqKey": "S107",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1075.html
================================================
Hard-coding a URI makes it difficult to test a program for a variety of reasons:
In addition, hard-coded URIs can contain sensitive information, like IP addresses, and they should not be stored in the code.
For all those reasons, a URI should never be hard coded. Instead, it should be replaced by a customizable parameter.
Further, even if the elements of a URI are obtained dynamically, portability can still be limited if the path delimiters are hard-coded.
This rule raises an issue when URIs or path delimiters are hard-coded.
This rule does not raise an issue when an ASP.NET virtual path is passed as an argument to one of the following:
System.Web.HttpServerUtilityBase.MapPath(), System.Web.HttpRequestBase.MapPath(),
System.Web.HttpResponseBase.ApplyAppPathModifier(), System.Web.Mvc.UrlHelper.Content()System.Web.VirtualPathUtilityMicrosoft.AspNetCore.Mvc.VirtualFileResult, Microsoft.AspNetCore.Routing.VirtualPathData
public class Foo {
public List<User> ListUsers() {
string userListPath = "/home/mylogin/Dev/users.txt"; // Noncompliant
return ParseUsers(userListPath);
}
}
public class Foo {
// Configuration is a class that returns customizable properties: it can be mocked to be injected during tests.
private Configuration config;
public Foo(Configuration myConfig) {
this.config = myConfig;
}
public List<User> ListUsers() {
// Find here the way to get the correct folder, in this case using the Configuration object
string listingFolder = config.GetProperty("myApplication.listingFolder");
// and use this parameter instead of the hard coded path
string userListPath = Path.Combine(listingFolder, "users.txt"); // Compliant
return ParseUsers(userListPath);
}
}
================================================
FILE: analyzers/rspec/cs/S1075.json
================================================
{
"title": "URIs should not be hardcoded",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1075",
"sqKey": "S1075",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S108.html
================================================
An empty code block is confusing. It will require some effort from maintainers to determine if it is intentional or indicates the implementation is incomplete.
for (int i = 0; i < 42; i++){} // Noncompliant: is the block empty on purpose, or is code missing?
Removing or filling the empty code blocks takes away ambiguity and generally results in a more straightforward and less surprising code.
The rule ignores code blocks that contain comments.
================================================ FILE: analyzers/rspec/cs/S108.json ================================================ { "title": "Nested blocks of code should not be left empty", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-108", "sqKey": "S108", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S109.html ================================================A magic number is a hard-coded numerical value that may lack context or meaning. They should not be used because they can make the code less readable and maintainable.
Magic numbers make the code more complex to understand as it requires the reader to have knowledge about the global context to understand the number itself. Their usage may seem obvious when writing the code, but it may not be the case for another developer or later once the context faded away. -1, 0, and 1 are not considered magic numbers.
This rule doesn’t raise an issue when the magic number is used as part of:
GetHashCode methodReplacing them with a constant allows us to provide a meaningful name associated with the value. Instead of adding complexity to the code, it brings clarity and helps to understand the context and the global meaning.
public void DoSomething()
{
for (int i = 0; i < 4; i++) // Noncompliant, 4 is a magic number
{
...
}
}
private const int NUMBER_OF_CYCLES = 4;
public void DoSomething()
{
for (int i = 0; i < NUMBER_OF_CYCLES; i++) // Compliant
{
...
}
}
================================================
FILE: analyzers/rspec/cs/S109.json
================================================
{
"title": "Magic numbers should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-109",
"sqKey": "S109",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S110.html
================================================
Inheritance is one of the most valuable concepts in object-oriented programming. It’s a way to categorize and reuse code by creating collections of attributes and behaviors called classes, which can be based on previously created classes.
But abusing this concept by creating a deep inheritance tree can lead to complex and unmaintainable source code. Often, an inheritance tree becoming too deep is the symptom of systematic use of "inheritance" when other approaches like "composition" would be better suited.
This rule raises an issue when the inheritance tree, starting from Object, has a greater depth than is allowed.
Public fields in public classes do not respect the encapsulation principle and have three main disadvantages:
To prevent unauthorized modifications, private attributes and accessor methods (set and get) should be used.
Note that due to optimizations on simple properties, public fields provide only very little performance gain.
Public fields can be modified by any part of the code and this can lead to unexpected changes and hard-to-trace bugs.
Public fields don’t hide the implementation details. As a consequence, it is no longer possible to change how the data is stored internally without impacting the client code of the class.
The code is harder to maintain.
Fields marked as readonly or const are ignored by this rule.
Fields inside classes or structs annotated with [StructLayout] are ignored by this rule.
Fields inside classes or structs annotated with [Serializable] are ignored by this rule unless they are annotated with
[NonSerialized].
Depending on your needs:
readonly or const.
public class Foo
{
public int InstanceData = 32; // Noncompliant
public int AnotherInstanceData = 32; // Noncompliant
}
public class Foo
{
// using auto-implemented properties
public int InstanceData { get; set; } = 32;
// using field encapsulation
private int _anotherInstanceData = 32;
public int AnotherInstanceData
{
get { return _anotherInstanceData; }
set
{
// perform validation
_anotherInstanceData = value;
}
}
}
Please be aware that changing a field by a property in a software that uses serialization could lead to binary incompatibility.
Shared coding conventions make it possible for a team to efficiently collaborate. This rule makes it mandatory to place a close curly brace at the beginning of a line.
if(condition)
{
doSomething();}
if(condition)
{
doSomething();
}
When blocks are inlined (open and close curly braces on the same line), no issue is triggered.
if(condition) {doSomething();}
================================================
FILE: analyzers/rspec/cs/S1109.json
================================================
{
"title": "A close curly brace should be located at the beginning of a line",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1109",
"sqKey": "S1109",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1110.html
================================================
Parentheses can disambiguate the order of operations in complex expressions and make the code easier to understand.
a = (b * c) + (d * e); // Compliant: the intent is clear.
Redundant parentheses are parenthesis that do not change the behavior of the code, and do not clarify the intent. They can mislead and complexify the code. They should be removed.
int x = ((y / 2 + 1)); // Noncompliant
if (a && ((x + y > 0))) { // Noncompliant
return ((x + 1)); // Noncompliant
}
int x = (y / 2 + 1);
if (a && (x + y > 0)) {
return (x + 1);
}
================================================
FILE: analyzers/rspec/cs/S1110.json
================================================
{
"title": "Redundant pairs of parentheses should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1110",
"sqKey": "S1110",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1116.html
================================================
Empty statements represented by a semicolon ; are statements that do not perform any operation. They are often the result of a typo or
a misunderstanding of the language syntax. It is a good practice to remove empty statements since they don’t add value and lead to confusion and
errors.
This rule does not raise when an empty statement is the only statement in a loop.
for (int i = 0; i < 3; Console.WriteLine(i), i++);
void DoSomething()
{
; // Noncompliant - was used as a kind of TODO marker
}
void DoSomethingElse()
{
Console.WriteLine("Hello, world!");; // Noncompliant - double ;
}
void DoSomething()
{
}
void DoSomethingElse()
{
Console.WriteLine("Hello, world!");
}
================================================
FILE: analyzers/rspec/cs/S1116.json
================================================
{
"title": "Empty statements should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1116",
"sqKey": "S1116",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1117.html
================================================
Shadowing occurs when a local variable has the same name as a variable, field, or property in an outer scope.
This can lead to three main problems:
To avoid these problems, rename the shadowing, shadowed, or both variables/fields/properties to accurately represent their purpose with unique and meaningful names. It improves clarity and allows reasoning locally about the code without considering other software parts.
This rule focuses on variables shadowing fields or properties.
class Foo
{
public int myField;
public int MyProperty { get; set; }
public void DoSomething()
{
int myField = 0; // Noncompliant
int MyProperty = 0; // Noncompliant
}
}
Whenever there are portions of code that are duplicated and do not depend on the state of their container class, they can be centralized inside a "utility class". A utility class is a class that only has static members, hence it should not be instantiated.
To prevent the class from being instantiated, you should define a non-public constructor. This will prevent the compiler from implicitly generating a public parameterless constructor.
Alternatively, adding the static keyword as class modifier will also prevent it from being instantiated.
public class StringUtils // Noncompliant: implicit public constructor
{
public static string Concatenate(string s1, string s2)
{
return s1 + s2;
}
}
or
public class StringUtils // Noncompliant: explicit public constructor
{
public StringUtils()
{
}
public static string Concatenate(string s1, string s2)
{
return s1 + s2;
}
}
public static class StringUtils // Compliant: the class is static
{
public static string Concatenate(string s1, string s2)
{
return s1 + s2;
}
}
or
public class StringUtils // Compliant: the constructor is not public
{
private StringUtils()
{
}
public static string Concatenate(string s1, string s2)
{
return s1 + s2;
}
}
================================================
FILE: analyzers/rspec/cs/S1118.json
================================================
{
"title": "Utility classes should not have public constructors",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1118",
"sqKey": "S1118",
"scope": "All",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/cs/S112.html
================================================
This rule raises an issue when a general or reserved exception is thrown.
Throwing general exceptions such as Exception, SystemException and ApplicationException will have a negative
impact on any code trying to catch these exceptions.
From a consumer perspective, it is generally a best practice to only catch exceptions you intend to handle. Other exceptions should ideally be let to propagate up the stack trace so that they can be dealt with appropriately. When a general exception is thrown, it forces consumers to catch exceptions they do not intend to handle, which they then have to re-throw.
Besides, when working with a general type of exception, the only way to distinguish between multiple exceptions is to check their message, which is error-prone and difficult to maintain. Legitimate exceptions may be unintentionally silenced and errors may be hidden.
For instance, if an exception such as StackOverflowException is caught and not re-thrown, it may prevent the program from terminating
gracefully.
When throwing an exception, it is therefore recommended to throw the most specific exception possible so that it can be handled intentionally by consumers.
Additionally, some reserved exceptions should not be thrown manually. Exceptions such as IndexOutOfRangeException,
NullReferenceException, OutOfMemoryException or ExecutionEngineException will be thrown automatically by the
runtime when the corresponding error occurs. Many of them indicate serious errors, which the application may not be able to recover from. It is
therefore recommended to avoid throwing them as well as using them as base classes.
To fix this issue, make sure to throw specific exceptions that are relevant to the context in which they arise. It is recommended to either:
Exception when one matches. For instance ArgumentException could be raised when an unexpected
argument is provided to a function.Exception or one of its subclasses.
public void DoSomething(object obj)
{
if (obj == null)
{
throw new NullReferenceException("obj"); // Noncompliant: This reserved exception should not be thrown manually
}
// ...
}
public void DoSomething(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj"); // Compliant: this is a specific and non-reserved exception type
}
// ...
}
A common code smell that can hinder the clarity of source code is making assignments within sub-expressions. This practice involves assigning a value to a variable inside a larger expression, such as within a loop or a conditional statement.
This practice essentially gives a side-effect to a larger expression, thus making it less readable. This often leads to confusion and potential errors.
Assignments inside lambda and delegate expressions are allowed.
var result = Foo(() =>
{
int x = 100; // dead store, but ignored
x = 200;
return x;
}
The rule also ignores the following patterns:
var a = b = c = 10;
if statement or a loop
while ((val = GetNewValue()) > 0)
{
...
}
private MyClass instance; public MyClass Instance => instance ?? (instance = new MyClass());
Making assignments within sub-expressions can hinder the clarity of source code.
This practice essentially gives a side-effect to a larger expression, thus making it less readable. This often leads to confusion and potential errors.
Extracting assignments into separate statements is encouraged to keep the code clear and straightforward.
if (string.IsNullOrEmpty(result = str.Substring(index, length))) // Noncompliant
{
// do something with "result"
}
var result = str.Substring(index, length);
if (string.IsNullOrEmpty(result))
{
// do something with "result"
}
The Obsolete attribute can be applied with or without a message argument. Marking something Obsolete without including
advice on why it’s obsolete or what to use instead will lead maintainers to waste time trying to figure those things out.
public class Car
{
[Obsolete] // Noncompliant
public void CrankEngine(int turnsOfCrank)
{ ... }
}
public class Car
{
[Obsolete("Replaced by the automatic starter")]
public void CrankEngine(int turnsOfCrank)
{ ... }
}
================================================
FILE: analyzers/rspec/cs/S1123.json
================================================
{
"title": "\"Obsolete\" attributes should include explanations",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"obsolete",
"bad-practice"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1123",
"sqKey": "S1123",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1125.html
================================================
A boolean literal can be represented in two different ways: true or false. They can be combined with logical operators
(!, &&, ||, ==, !=) to produce logical expressions that represent truth values. However, comparing a boolean literal to a
variable or expression that evaluates to a boolean value is unnecessary and can make the code harder to read and understand. The more complex a
boolean expression is, the harder it will be for developers to understand its meaning and expected behavior, and it will favour the introduction of
new bugs.
Remove redundant boolean literals from expressions to improve readability and make the code more maintainable.
if (booleanMethod() == true) { /* ... */ }
if (booleanMethod() == false) { /* ... */ }
if (booleanMethod() || false) { /* ... */ }
doSomething(!false);
doSomething(booleanMethod() == true);
booleanVariable = booleanMethod() ? true : false;
booleanVariable = booleanMethod() ? true : exp;
booleanVariable = booleanMethod() ? false : exp;
booleanVariable = booleanMethod() ? exp : true;
booleanVariable = booleanMethod() ? exp : false;
for (var x = 0; true; x++)
{
...
}
if (booleanMethod()) { /* ... */ }
if (!booleanMethod()) { /* ... */ }
if (booleanMethod()) { /* ... */ }
doSomething(true);
doSomething(booleanMethod());
booleanVariable = booleanMethod();
booleanVariable = booleanMethod() || exp;
booleanVariable = !booleanMethod() && exp;
booleanVariable = !booleanMethod() || exp;
booleanVariable = booleanMethod() && exp;
for (var x = 0; ; x++)
{
...
}
================================================
FILE: analyzers/rspec/cs/S1125.json
================================================
{
"title": "Boolean literals should not be redundant",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1125",
"sqKey": "S1125",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1128.html
================================================
Unnecessary using directives refer to importing namespaces, types or creating aliases that are not used or referenced anywhere in the
code.
Although they don’t affect the runtime behavior of the application after compilation, removing them will:
Starting with C# 10, it’s possible to define global usings for an entire project. They reduce the need for repetitive namespace inclusions, but can also mask which namespaces are truly necessary for the code at hand. Over-relying on them can lead to less transparent code dependencies, especially for newcomers to the project.
The rule will not raise a warning for global using directives, even if none of the types of that namespace are used in the
project:
global using System.Net.Sockets; // Compliant by exception
Unnecessary using directives are ignored in ASP.NET Core projects in the following files:
_Imports.razor_ViewImports.cshtmlWhile it’s not difficult to remove these unneeded lines manually, modern code editors support the removal of every unnecessary using
directive with a single click from every file of the project.
using System.IO;
using System.Linq;
using System.Collections.Generic; // Noncompliant - no types are used from this namespace
using MyApp.Helpers; // Noncompliant - FileHelper is in the same namespace
using MyCustomNamespace; // Noncompliant - no types are used from this namespace
namespace MyApp.Helpers
{
public class FileHelper
{
public static string ReadFirstLine(string filePath) =>
File.ReadAllLines(filePath).First();
}
}
using System.IO;
using System.Linq;
namespace MyApp.Helpers
{
public class FileHelper
{
public static string ReadFirstLine(string filePath) =>
File.ReadAllLines(filePath).First();
}
}
Some tools work better when files end with an empty line.
This rule simply generates an issue if it is missing.
For example, a Git diff looks like this if the empty line is missing at the end of the file:
+class Test
+{
+}
\ No newline at end of file
================================================
FILE: analyzers/rspec/cs/S113.json
================================================
{
"title": "Files should end with a newline",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-113",
"sqKey": "S113",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1133.html
================================================
This rule is meant to be used as a way to track code which is marked as being deprecated. Deprecated code should eventually be removed.
[Obsolete] // Noncompliant
void Method()
{
// ..
}
================================================
FILE: analyzers/rspec/cs/S1133.json
================================================
{
"title": "Deprecated code should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "INFO"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"obsolete"
],
"defaultSeverity": "Info",
"ruleSpecification": "RSPEC-1133",
"sqKey": "S1133",
"scope": "All",
"quickfix": "infeasible"
}
================================================
FILE: analyzers/rspec/cs/S1134.html
================================================
FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to deal with later.
Sometimes the developer will not have the time or will simply forget to get back to that tag.
This rule is meant to track those tags and to ensure that they do not go unnoticed.
private int Divide(int numerator, int denominator)
{
return numerator / denominator; // FIXME denominator value might be 0
}
Developers often use TODO tags to mark areas in the code where additional work or improvements are needed but are not implemented
immediately. However, these TODO tags sometimes get overlooked or forgotten, leading to incomplete or unfinished code. This rule aims to
identify and address unattended TODO tags to ensure a clean and maintainable codebase. This description explores why this is a problem
and how it can be fixed to improve the overall code quality.
Unattended TODO tags in code can have significant implications for the development process and the overall codebase.
Incomplete Functionality: When developers leave TODO tags without implementing the corresponding code, it results in incomplete
functionality within the software. This can lead to unexpected behavior or missing features, adversely affecting the end-user experience.
Missed Bug Fixes: If developers do not promptly address TODO tags, they might overlook critical bug fixes and security updates.
Delayed bug fixes can result in more severe issues and increase the effort required to resolve them later.
Impact on Collaboration: In team-based development environments, unattended TODO tags can hinder collaboration. Other team members
might not be aware of the intended changes, leading to conflicts or redundant efforts in the codebase.
Codebase Bloat: The accumulation of unattended TODO tags over time can clutter the codebase and make it difficult to distinguish
between work in progress and completed code. This bloat can make it challenging to maintain an organized and efficient codebase.
Addressing this code smell is essential to ensure a maintainable, readable, reliable codebase and promote effective collaboration among developers.
private void DoSomething()
{
// TODO
}
This rule raises an issue when a private/internal type or member is never referenced in the code.
A type or member that is never called is dead code, and should be removed. Cleaning out dead code decreases the size of the maintained codebase, making it easier to understand the program and preventing bugs from being introduced.
This rule detects type or members that are never referenced from inside a translation unit, and cannot be referenced from the outside.
This rule doesn’t raise issues on:
Main method of the applicationvoid methods with two parameters when the second parameter type derives from EventArgsDynamicallyAccessedMembersAttribute.
public class Foo
{
private void UnusedPrivateMethod(){...} // Noncompliant, this private method is unused and can be removed.
private class UnusedClass {...} // Noncompliant, unused private class that can be removed.
}
public class Foo
{
public Foo()
{
UsedPrivateMethod();
}
private void UsedPrivateMethod()
{
var c = new UsedClass();
}
private class UsedClass {...}
}
Calling Environment.Exit(exitCode) or Application.Exit() terminates the process and returns an exit code to the operating
system..
Each of these methods should be used with extreme care, and only when the intent is to stop the whole application.
Environment.Exit(0); Application.Exit();
These methods are ignored inside Main.
The switch statement should be used only to clearly define some new branches in the control flow. As soon as a case
clause contains too many statements this highly decreases the readability of the overall control flow statement. In such case, the content of the
case clause should be extracted into a dedicated method.
With the default threshold of 8:
switch (myVariable)
{
case 0: // Noncompliant: 9 statements in the case
methodCall1("");
methodCall2("");
methodCall3("");
methodCall4("");
methodCall5("");
methodCall6("");
methodCall7("");
methodCall8("");
methodCall9("");
break;
case 1:
...
}
switch (myVariable)
{
case 0:
DoSomething()
break;
case 1:
...
}
...
private void DoSomething()
{
methodCall1("");
methodCall2("");
methodCall3("");
methodCall4("");
methodCall5("");
methodCall6("");
methodCall7("");
methodCall8("");
methodCall9("");
}
================================================
FILE: analyzers/rspec/cs/S1151.json
================================================
{
"title": "\"switch case\" clauses should not have too many lines of code",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1151",
"sqKey": "S1151",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1155.html
================================================
When you call Any(), it clearly communicates the code’s intention, which is to check if the collection is empty. Using Count()
== 0 for this purpose is less direct and makes the code slightly more complex. However, there are some cases where special attention should be
paid:
EntityFramework or other ORM query, calling Count() will cause executing a potentially massive
SQL query and could put a large overhead on the application database. Calling Any() will also connect to the database, but will
generate much more efficient SQL.Select() statements that create objects, a large amount of memory could be
unnecessarily allocated. Calling Any() will be much more efficient because it will execute fewer iterations of the enumerable.Prefer using Any() to test for emptiness over Count().
private static bool HasContent(IEnumerable<string> strings)
{
return strings.Count() > 0; // Noncompliant
}
private static bool HasContent2(IEnumerable<string> strings)
{
return strings.Count() >= 1; // Noncompliant
}
private static bool IsEmpty(IEnumerable<string> strings)
{
return strings.Count() == 0; // Noncompliant
}
private static bool HasContent(IEnumerable<string> strings)
{
return strings.Any();
}
private static bool HasContent2(IEnumerable<string> strings)
{
return strings.Any();
}
private static bool IsEmpty(IEnumerable<string> strings)
{
return !strings.Any();
}
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
Count |
.NET 9.0 |
2,841.003 ns |
266.0238 ns |
|
Any |
.NET 9.0 |
1.749 ns |
0.1242 ns |
|
Count |
.NET Framework 4.8.1 |
71,125.275 ns |
731.0382 ns |
|
Any |
.NET Framework 4.8.1 |
31.774 ns |
0.3196 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private IEnumerable<int> collection;
public const int N = 10_000;
[GlobalSetup]
public void GlobalSetup()
{
collection = Enumerable.Range(0, N).Select(x => N - x);
}
[Benchmark(Baseline = true)]
public bool Count() =>
collection.Count() > 0;
[Benchmark]
public bool Any() =>
collection.Any();
Hardware Configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S1155.json ================================================ { "title": "\"Any()\" should be used to test for emptiness", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1155", "sqKey": "S1155", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S1163.html ================================================
If an exception is already being thrown within the try block or caught in a catch block, throwing another exception in
the finally block will override the original exception. This means that the original exception’s message and stack trace will be lost,
potentially making it challenging to diagnose and troubleshoot the root cause of the problem.
try
{
// Some work which end up throwing an exception
throw new ArgumentException();
}
finally
{
// Cleanup
throw new InvalidOperationException(); // Noncompliant: will mask the ArgumentException
}
try
{
// Some work which end up throwing an exception
throw new ArgumentException();
}
finally
{
// Cleanup without throwing
}
Returning null or default instead of an actual collection forces the method callers to explicitly test for null, making
the code more complex and less readable.
Moreover, in many cases, null or default is used as a synonym for empty.
public Result[] GetResults()
{
return null; // Noncompliant
}
public IEnumerable<Result> GetResults(bool condition)
{
var results = GenerateResults();
return condition
? results
: null; // Noncompliant
}
public IEnumerable<Result> GetResults() => null; // Noncompliant
public IEnumerable<Result> Results
{
get
{
return default(IEnumerable<Result>); // Noncompliant
}
}
public IEnumerable<Result> Results => default; // Noncompliant
public Result[] GetResults()
{
return new Result[0];
}
public IEnumerable<Result> GetResults(bool condition)
{
var results = GenerateResults();
return condition
? results
: Enumerable.Empty<Result>();
}
public IEnumerable<Result> GetResults() => Enumerable.Empty<Result>();
public IEnumerable<Result> Results
{
get
{
return Enumerable.Empty<Result>();
}
}
public IEnumerable<Result> Results => Enumerable.Empty<Result>();
Although string is a collection, the rule won’t report on it.
A typical code smell known as unused function parameters refers to parameters declared in a function but not used anywhere within the function’s body. While this might seem harmless at first glance, it can lead to confusion and potential errors in your code. Disregarding the values passed to such parameters, the function’s behavior will be the same, but the programmer’s intention won’t be clearly expressed anymore. Therefore, removing function parameters that are not being utilized is considered best practice.
This rule raises an issue when a private method or constructor of a class/struct takes a parameter without using it.
This rule doesn’t raise any issue in the following contexts:
this parameter of extension methods.NotImplementedException.virtual, override methods.Having unused function parameters in your code can lead to confusion and misunderstanding of a developer’s intention. They reduce code readability and introduce the potential for errors. To avoid these problems, developers should remove unused parameters from function declarations.
private void DoSomething(int a, int b) // Noncompliant, "b" is unused
{
Compute(a);
}
private void DoSomething2(int a) // Noncompliant, the value of "a" is unused
{
a = 10;
Compute(a);
}
private void DoSomething(int a)
{
Compute(a);
}
private void DoSomething2()
{
var a = 10;
Compute(a);
}
================================================
FILE: analyzers/rspec/cs/S1172.json
================================================
{
"title": "Unused method parameters should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1172",
"sqKey": "S1172",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1185.html
================================================
Overriding a method just to call the same method from the base class without performing any other actions is useless and misleading. The only time
this is justified is in sealed overriding methods, where the effect is to lock in the parent class behavior. This rule ignores overrides
of Equals and GetHashCode.
NOTE: In some cases it might be dangerous to add or remove empty overrides, as they might be breaking changes.
public override void Method() // Noncompliant
{
base.Method();
}
public override void Method()
{
//do something else
}
If there is an attribute in any level of the overriding chain, then the overridden member is ignored.
public class Base
{
[Required]
public virtual string Name { get; set; }
}
public class Derived : Base
{
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
}
If there is a documentation comment on the overriding method, it will be ignored:
public class Foo : Bar
{
/// <summary>
/// Keep this method for backwards compatibility.
/// </summary>
public override void DoSomething()
{
base.DoSomething();
}
}
================================================
FILE: analyzers/rspec/cs/S1185.json
================================================
{
"title": "Overriding members should do more than simply call the same member in the base class",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"redundant",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1185",
"sqKey": "S1185",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1186.html
================================================
An empty method is generally considered bad practice and can lead to confusion, readability, and maintenance issues. Empty methods bring no functionality and are misleading to others as they might think the method implementation fulfills a specific and identified requirement.
There are several reasons for a method not to have a body:
The following empty methods are considered compliant:
virtual methods as the implementation might not be required in the base classabstract method as the implementation is mandatory for child class
public void ShouldNotBeEmpty() { // Noncompliant - method is empty
}
public void NotImplementedYet() { // Noncompliant - method is empty
}
public void WillNeverBeImplemented() { // Noncompliant - method is empty
}
public void EmptyOnPurpose() { // Noncompliant - method is empty
}
public void ShouldNotBeEmpty() {
DoSomething();
}
public void NotImplementedYet() {
throw new NotImplementedException();
}
public void WillNeverBeImplemented() {
throw new NotSupportedException();
}
public void EmptyOnPurpose() {
// comment explaining why the method is empty
}
Duplicated string literals make the process of refactoring complex and error-prone, as any change would need to be propagated on all occurrences.
The following are ignored:
Use constants to replace the duplicated string literals. Constants can be referenced from many places, but only need to be updated in a single place.
public class Foo
{
private string name = "foobar"; // Noncompliant
public string DefaultName { get; } = "foobar"; // Noncompliant
public Foo(string value = "foobar") // Noncompliant
{
var something = value ?? "foobar"; // Noncompliant
}
}
public class Foo
{
private const string Foobar = "foobar";
private string name = Foobar;
public string DefaultName { get; } = Foobar;
public Foo(string value = Foobar)
{
var something = value ?? Foobar;
}
}
================================================
FILE: analyzers/rspec/cs/S1192.json
================================================
{
"title": "String literals should not be duplicated",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "DISTINCT"
},
"status": "ready",
"remediation": {
"func": "Linear with offset",
"linearDesc": "per duplicate instance",
"linearOffset": "2min",
"linearFactor": "2min"
},
"tags": [
"design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1192",
"sqKey": "S1192",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1199.html
================================================
Nested code blocks create new scopes where variables declared within are inaccessible from the outside, and their lifespan ends with the block.
Although this may appear beneficial, their usage within a function often suggests that the function is overloaded. Thus, it may violate the Single Responsibility Principle, and the function needs to be broken down into smaller functions.
The presence of nested blocks that don’t affect the control flow might suggest possible mistakes in the code.
The usage of a code block after a case is allowed.
The nested code blocks should be extracted into separate methods.
public void Evaluate()
{
/* ... */
{ // Noncompliant - nested code block '{' ... '}'
int a = stack.pop();
int b = stack.pop();
int result = a + b;
stack.push(result);
}
/* ... */
}
public void Evaluate()
{
/* ... */
StackAdd();
/* ... */
}
private void StackAdd()
{
int a = stack.pop();
int b = stack.pop();
int result = a + b;
stack.push(result);
}
According to the Single Responsibility Principle, introduced by Robert C. Martin in his book "Principles of Object Oriented Design", a class should have only one responsibility:
If a class has more than one responsibility, then the responsibilities become coupled.
Changes to one responsibility may impair or inhibit the class' ability to meet the others.
This kind of coupling leads to fragile designs that break in unexpected ways when changed.
Classes which rely on many other classes tend to aggregate too many responsibilities and should be split into several smaller ones.
Nested classes dependencies are not counted as dependencies of the outer class.
With a threshold of 5:
public class Foo // Noncompliant - Foo depends on too many classes: T1, T2, T3, T4, T5, T6 and T7
{
private T1 a1; // Foo is coupled to T1
private T2 a2; // Foo is coupled to T2
private T3 a3; // Foo is coupled to T3
public T4 Compute(T5 a, T6 b) // Foo is coupled to T4, T5 and T6
{
T7 result = a.Process(b); // Foo is coupled to T7
return result;
}
public static class Bar // Compliant - Bar depends on 2 classes: T8 and T9
{
public T8 a8;
public T9 a9;
}
}
================================================
FILE: analyzers/rspec/cs/S1200.json
================================================
{
"title": "Classes should not be coupled to too many other classes",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2h"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1200",
"sqKey": "S1200",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1206.html
================================================
Suppose you override Object.Equals in a type, you must also
override Object.GetHashCode. If two objects are equal according
to the Equals method, then calling GetHashCode on each of them must yield the same integer. If this is not the case, many
collections, such as a Hashtable or a Dictionary won’t handle class instances correctly.
In order to not have unpredictable behavior, Equals and GetHashCode should be either both inherited, or both
overridden.
When you override Equals then you have to also override GetHashCode. You have to override both of them, or simply inherit
them.
class MyClass // Noncompliant: should also override GetHashCode
{
public override bool Equals(object obj)
{
// ...
}
}
class MyClass
{
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
// ...
}
}
Control structures are code statements that impact the program’s control flow (e.g., if statements, for loops, etc.)
While not technically incorrect, the omission of curly braces can be misleading and may lead to the introduction of errors during maintenance.
In the following example, the two calls seem to be attached to the if statement, but only the first one is, and
CheckSomething will always be executed:
if (condition) // Noncompliant ExecuteSomething(); CheckSomething();
Adding curly braces improves the code readability and its robustness:
if (condition)
{
ExecuteSomething();
CheckSomething();
}
The rule raises an issue when a control structure has no curly braces.
================================================ FILE: analyzers/rspec/cs/S121.json ================================================ { "title": "Control structures should use curly braces", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "pitfall" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-121", "sqKey": "S121", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1210.html ================================================When you implement IComparable or IComparable<T> on a class you should also override Equals(object)
and overload the comparison operators (==, !=, <, <=, >,
>=). That’s because the CLR cannot automatically call your CompareTo implementation from Equals(object) or
from the base comparison operator implementations. Additionally, it is best practice to override GetHashCode along with
Equals.
This rule raises an issue when a class implements IComparable without also overriding Equals(object) and the comparison
operators.
public class Foo: IComparable // Noncompliant
{
public int CompareTo(object obj) { /* ... */ }
}
public class Foo: IComparable
{
public int CompareTo(object obj) { /* ... */ }
public override bool Equals(object obj)
{
var other = obj as Foo;
if (object.ReferenceEquals(other, null))
{
return false;
}
return this.CompareTo(other) == 0;
}
public int GetHashCode() { /* ... */ }
public static bool operator == (Foo left, Foo right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator > (Foo left, Foo right)
{
return Compare(left, right) > 0;
}
public static bool operator < (Foo left, Foo right)
{
return Compare(left, right) < 0;
}
public static bool operator != (Foo left, Foo right)
{
return !(left == right);
}
}
================================================
FILE: analyzers/rspec/cs/S1210.json
================================================
{
"title": "\"Equals\" and the comparison operators should be overridden when implementing \"IComparable\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1210",
"sqKey": "S1210",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1215.html
================================================
GC.Collect is a method that forces or suggests to the garbage collector to run a collection of objects in the managed heap that are no longer being used and free their memory.
Calling GC.Collect is rarely necessary and can significantly affect application performance. That’s because it is a tracing garbage collector and needs to examine every object in memory for
cleanup and analyze all reachable objects from every application’s root (static fields, local variables on thread stacks, etc.).
To perform tracing and memory releasing correctly, the garbage collection may need to block all threads currently in execution. That is
why, as a general rule, the performance implications
of calling GC.Collect far outweigh the benefits.
This rule raises an issue when any overload of Collect is invoked.
static void Main(string[] args)
{
// ...
GC.Collect(); // Noncompliant
GC.Collect(2, GCCollectionMode.Optimized); // Noncompliant
}
There may be exceptions to this rule: for example, you’ve just triggered some event that is unique in the run of your program that caused a lot of long-lived objects to die, and you want to release their memory.
This rule also raises on GC.GetTotalMemory when forceFullCollection is true as it directly invokes
GC.Collect.
Each .NET runtime features distinct implementations, modes, and configurations for its garbage collector. The benchmark below illustrates how
invoking GC.Collect() can have opposite effects across different runtimes.
| Runtime | Collect | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
.NET 9.0 |
False |
659.2 ms |
15.69 ms |
205.95 MB |
|
.NET 9.0 |
True |
888.8 ms |
15.34 ms |
205.95 MB |
|
.NET Framework 4.8.1 |
False |
545.7 ms |
19.49 ms |
228.8 MB |
|
.NET Framework 4.8.1 |
True |
484.8 ms |
11.79 ms |
228.8 MB |
True, GC.Collect() is called in the middle of the allocation heavy Benchmark() methodThe results were generated by running the following snippet with BenchmarkDotNet:
class Tree
{
public List<Tree> Children = new();
}
private void AppendToTree(Tree tree, int childsPerTree, int depth)
{
if (depth == 0)
{
return;
}
for (int i = 0; i < childsPerTree; i++)
{
var child = new Tree();
tree.Children.Add(child);
AppendToTree(child, childsPerTree, depth - 1);
}
}
[Benchmark]
[Arguments(true)]
[Arguments(false)]
public void Benchmark(bool collect)
{
var tree = new Tree();
AppendToTree(tree, 8, 7); // Create 8^7 Tree objects (2.097.152 objects) linked via List<Tree> Children
GC.Collect();
GC.Collect(); // Move the objects to generation 2
AppendToTree(new Tree(), 8, 6); // Add some more memory preasure (8^6 262.144 objects) which can be collected right after this call
tree = null; // Remove all references to the tree and its content. This freees up 8^7 Tree objects (2.097.152 objects)
if (collect)
{
GC.Collect(); // Force GC to run and block until it finishes
}
AppendToTree(new Tree(), 3, 10); // Do some more allocations (3^10 = 59.049)
AppendToTree(new Tree(), 4, 7); // 4^10 = 1.048.576
AppendToTree(new Tree(), 5, 7); // 5^7 = 78.125
GC.Collect(); // Collect all the memory allocated in this method
}
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) Intel Core Ultra 7 165H, 1 CPU, 22 logical and 16 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S1215.json ================================================ { "title": "\"GC.Collect\" should not be called", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "30min" }, "tags": [ "performance", "unpredictable", "bad-practice" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-1215", "sqKey": "S1215", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S122.html ================================================
Putting multiple statements on a single line lowers the code readability and makes debugging the code more complex.
if (someCondition) { DoSomething(); } // Noncompliant
DoSomething(); DoSomethingElse(); // Noncompliant
Write one statement per line to improve readability.
if (someCondition)
{
DoSomething();
}
DoSomething();
DoSomethingElse();
The rule ignores:
Func<object, bool> item1 = o => { return true; }; // Compliant by exception
Func<object, bool> item1 = o => { var r = false; return r; }; // Noncompliant
While it is technically correct to assign to parameters from within method bodies, doing so before the parameter value is read is likely a bug.
Instead, initial values of parameters, caught exceptions, and foreach parameters should be, if not treated as final, then at least read
before reassignment.
public void DoTheThing(string str, int i, List<string> strings)
{
str = i.ToString(i); // Noncompliant
foreach (var s in strings)
{
s = "hello world"; // Noncompliant
}
}
================================================
FILE: analyzers/rspec/cs/S1226.json
================================================
{
"title": "Method parameters, caught exceptions and foreach variables\u0027 initial values should not be ignored",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1226",
"sqKey": "S1226",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1227.html
================================================
This rule is deprecated, and will eventually be removed.
break; is an unstructured control flow statement which makes code harder to read.
Ideally, every loop should have a single termination condition.
int i = 0;
while (true)
{
if (i == 10)
{
break; // Non-Compliant
}
Console.WriteLine(i);
i++;
}
int i = 0;
while (i != 10) // Compliant
{
Console.WriteLine(i);
i++;
}
================================================
FILE: analyzers/rspec/cs/S1227.json
================================================
{
"title": "break statements should not be used except for switch cases",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "deprecated",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1227",
"sqKey": "S1227",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1244.html
================================================
Floating point numbers in C# (and in most other programming languages) are not precise. They are a binary approximation of the actual value. This means that even if two floating point numbers appear to be equal, they might not be due to the tiny differences in their binary representation.
Even simple floating point assignments are not simple:
float f = 0.100000001f; // 0.1 double d = 0.10000000000000001; // 0.1
(Note: Results may vary based on the compiler and its settings)
This issue is further compounded by the non-associative nature of floating point arithmetic. The order in which operations are performed can affect the outcome due to the rounding that occurs at each step. Consequently, the outcome of a series of mathematical operations can vary based on the order of operations.
As a result, using the equality (==) or inequality (!=) operators with float or double values
is typically a mistake, as it can lead to unexpected behavior.
Consider using a small tolerance value to check if the numbers are "close enough" to be considered equal. This tolerance value, often called
epsilon, should be chosen based on the specifics of your program.
float myNumber = 3.146f;
if (myNumber == 3.146f) // Noncompliant: due to floating point imprecision, this will likely be false
{
// ...
}
if (myNumber < 4 || myNumber > 4) // Noncompliant: indirect inequality test
{
// ...
}
float myNumber = 3.146f;
float epsilon = 0.0001f; // or some other small value
if (Math.Abs(myNumber - 3.146f) < epsilon)
{
// ...
}
if (myNumber <= 4 - epsilon || myNumber >= 4 + epsilon)
{
// ...
}
Commented-out code distracts the focus from the actual executed code. It creates a noise that increases maintenance code. And because it is never executed, it quickly becomes out of date and invalid.
Commented-out code should be deleted and can be retrieved from source control history if required.
Delete the commented out code.
void Method(string s)
{
// if (s.StartsWith('A'))
// {
// s = s.Substring(1);
// }
// Do something...
}
void Method(string s)
{
// Do something...
}
================================================
FILE: analyzers/rspec/cs/S125.json
================================================
{
"title": "Sections of code should not be commented out",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-125",
"sqKey": "S125",
"scope": "All",
"quickfix": "partial"
}
================================================
FILE: analyzers/rspec/cs/S126.html
================================================
This rule applies whenever an if statement is followed by one or more else if statements; the final else if
should be followed by an else statement.
The requirement for a final else statement is defensive programming.
The else statement should either take appropriate action or contain a suitable comment as to why no action is taken. This is
consistent with the requirement to have a final default clause in a switch statement.
if (x == 0)
{
DoSomething();
}
else if (x == 1)
{
DoSomethingElse();
}
if (x == 0)
{
DoSomething();
}
else if (x == 1)
{
DoSomethingElse();
}
else
{
throw new InvalidOperationException();
}
None
================================================ FILE: analyzers/rspec/cs/S126.json ================================================ { "title": "\"if ... else if\" constructs should end with \"else\" clauses", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-126", "sqKey": "S126", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1264.html ================================================Using a for loop without its typical structure (initialization, condition, increment) can be confusing. In those cases, it is better
to use a while loop as it is more readable.
The initializer section should contain a variable declaration to be considered as a valid initialization.
Replace the for loop with a while loop.
for (;condition;) // Noncompliant; both the initializer and increment sections are missing
{
// Do something
}
while (condition)
{
// Do something
}
int i;
for (i = 0; i < 10;) // Noncompliant; the initializer section should contain a variable declaration
{
// Do something
i++;
}
int i = 0;
while (i < 10)
{
// Do something
i++;
}
for
statementA for loop stop condition should test the loop counter against an invariant value, one that is true at both the beginning and ending
of every loop iteration. Ideally, this means that the stop condition is set to a local variable just before the loop begins.
This rule tracks when incremented counters used in the stop condition are updated in the body of the for loop.
Non-invariant stop conditions can lead to unexpected loop behavior, making the code harder to debug and maintain. If the stop condition changes unexpectedly during iteration, it may cause:
It is generally recommended to only update the loop counter in the loop declaration. If skipping elements or iterating at a different pace based on a condition is needed, consider using a while loop or a different structure that better fits the needs.
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
if (condition)
{
i = 20;
}
}
int i = 1;
while (i <= 5)
{
Console.WriteLine(i);
if (condition)
{
i = 20;
}
else
{
i++;
}
}
A while loop signals that the iteration logic may be more complex, so readers will naturally look for control flow changes within the
loop body. This makes the code’s intent clearer and easier to reason about.
switch statements and expressions are useful when there are many different cases depending on the value of the same expression.
When a switch statement or expression is simple enough, the code will be more readable with a single if,
if-else or ternary conditional operator.
switch (variable)
{
case 0:
doSomething();
break;
default:
doSomethingElse();
break;
}
var foo = variable switch
{
0 => doSomething(),
_ => doSomethingElse(),
}
if (variable == 0)
{
doSomething();
}
else
{
doSomethingElse();
}
var foo = variable == 0
? doSomething()
: doSomethingElse();
================================================
FILE: analyzers/rspec/cs/S1301.json
================================================
{
"title": "\"switch\" statements should have at least 3 \"case\" clauses",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"bad-practice"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1301",
"sqKey": "S1301",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1309.html
================================================
This rule allows you to track the usage of the SuppressMessage attributes and #pragma warning disable mechanism.
[SuppressMessage("", "S100")]
...
#pragma warning disable S100
...
#pragma warning restore S100
================================================
FILE: analyzers/rspec/cs/S1309.json
================================================
{
"title": "Track uses of in-source issue suppressions",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "INFO"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Info",
"ruleSpecification": "RSPEC-1309",
"sqKey": "S1309",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S131.html
================================================
The requirement for a final default clause is defensive programming. The clause should either take appropriate action, or contain a
suitable comment as to why no action is taken. Even when the switch covers all current values of an enum, a
default case should still be used because there is no guarantee that the enum won’t be extended.
int foo = 42;
switch (foo) // Noncompliant
{
case 0:
Console.WriteLine("foo = 0");
break;
case 42:
Console.WriteLine("foo = 42");
break;
}
int foo = 42;
switch (foo) // Compliant
{
case 0:
Console.WriteLine("foo = 0");
break;
case 42:
Console.WriteLine("foo = 42");
break;
default:
throw new InvalidOperationException("Unexpected value foo = " + foo);
}
Regardless of the logging framework in use (Microsoft.Extension.Logging, Serilog, Log4net, NLog, …), logger fields should be:
static will ensure that the lifetime of the object doesn’t depend on the lifetime
of the instance of the enclosing type.readonly will prevent modifications to the reference of the logger. This ensures
that the reference to the logger remains consistent and doesn’t get accidentally reassigned during the lifetime of the enclosing type.This rule should be activated when Service Locator Design pattern is followed in place of Dependency Injection for logging.
The rule supports the most popular logging frameworks:
Make the logging field {private static readonly}.
public Logger logger;
private static readonly Logger logger;
static class membersreadonly
keywordHardcoding IP addresses is security-sensitive. It has led in the past to the following vulnerabilities:
Today’s services have an ever-changing architecture due to their scaling and redundancy needs. It is a mistake to think that a service will always have the same IP address. When it does change, the hardcoded IP will have to be modified too. This will have an impact on the product development, delivery, and deployment:
Last but not least it has an effect on application security. Attackers might be able to decompile the code and thereby discover a potentially sensitive address. They can perform a Denial of Service attack on the service, try to get access to the system, or try to spoof the IP address to bypass security checks. Such attacks can always be possible, but in the case of a hardcoded IP address solving the issue will take more time, which will increase an attack’s impact.
The disclosed IP address is sensitive, e.g.:
There is a risk if you answered yes to any of these questions.
Don’t hard-code the IP address in the source code, instead make it configurable with environment variables, configuration files, or a similar approach. Alternatively, if confidentially is not required a domain name can be used since it allows to change the destination quickly without having to rebuild the software.
var ip = "192.168.12.42"; var address = IPAddress.Parse(ip);
var ip = ConfigurationManager.AppSettings["myapplication.ip"]; var address = IPAddress.Parse(ip);
No issue is reported for the following cases because they are not considered sensitive:
2.5.<number>.<number> as they often match
Object Identifiers (OID)Nested control flow statements if, switch, for, foreach, while, do,
and try are often key ingredients in creating what’s known as "Spaghetti code". This code smell can make your program difficult to
understand and maintain.
When numerous control structures are placed inside one another, the code becomes a tangled, complex web. This significantly reduces the code’s readability and maintainability, and it also complicates the testing process.
The following example demonstrates the behavior of the rule with the default threshold of 3 levels of nesting and one of the potential ways to fix the code smell by introducing guard clauses:
if (condition1) // Compliant - depth = 1
{
/* ... */
if (condition2) // Compliant - depth = 2
{
/* ... */
for (int i = 0; i < 10; i++) // Compliant - depth = 3
{
/* ... */
if (condition4) // Noncompliant - depth = 4, which exceeds the limit
{
if (condition5) // Depth = 5, exceeding the limit, but issues are only reported on depth = 4
{
/* ... */
}
return;
}
}
}
}
if (!condition1)
{
return;
}
/* ... */
if (!condition2)
{
return;
}
for (int i = 0; i < 10; i++)
{
/* ... */
if (condition4)
{
if (condition5)
{
/* ... */
}
return;
}
}
A function that grows too large tends to aggregate too many responsibilities.
Such functions inevitably become harder to understand and therefore harder to maintain.
Above a specific threshold, it is strongly advised to refactor into smaller functions which focus on well-defined tasks.
Those smaller functions will not only be easier to understand, but also probably easier to test.
================================================ FILE: analyzers/rspec/cs/S138.json ================================================ { "title": "Functions should not have too many lines of code", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "20min" }, "tags": [ "brain-overload" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-138", "sqKey": "S138", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1449.html ================================================string.ToLower(), ToUpper, IndexOf, LastIndexOf, and Compare are all
culture-dependent, as are some (floating point number and DateTime-related) calls to ToString. Fortunately, all have
variants which accept an argument specifying the culture or formatter to use. Leave that argument off and the call will use the system default
culture, possibly creating problems with international characters.
string.CompareTo() is also culture specific, but has no overload that takes a culture information, so instead it’s better to use
CompareOrdinal, or Compare with culture.
Calls without a culture may work fine in the system’s "home" environment, but break in ways that are extremely difficult to diagnose for customers who use different encodings. Such bugs can be nearly, if not completely, impossible to reproduce when it’s time to fix them.
var lowered = someString.ToLower(); //Noncompliant
var lowered = someString.ToLower(CultureInfo.InvariantCulture);
or
var lowered = someString.ToLowerInvariant();================================================ FILE: analyzers/rspec/cs/S1449.json ================================================ { "title": "Culture should be specified for \"string\" operations", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "COMPLETE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "unpredictable" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1449", "sqKey": "S1449", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1450.html ================================================
When the value of a private field is always assigned to in a class' methods before being read, then it is not being used to store class information. Therefore, it should become a local variable in the relevant methods to prevent any misunderstanding.
public class Foo
{
private int singularField;
public void DoSomething(int x)
{
singularField = x + 5;
if (singularField == 0) { /* ... */ }
}
}
public class Foo
{
public void DoSomething(int x)
{
int localVariable = x + 5;
if (localVariable == 0) { /* ... */ }
}
}
================================================
FILE: analyzers/rspec/cs/S1450.json
================================================
{
"title": "Private fields only used as local variables in methods should become local variables",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1450",
"sqKey": "S1450",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1451.html
================================================
Each source file should start with a header stating file ownership and the license which must be used to distribute the application.
This rule must be fed with the header text that is expected at the beginning of every file.
The headerFormat must end with an empty line if you want to have an empty line between the file header and the first line for your
source file (using, namespace…).
For example, if you want the source file to look like this
// Copyright (c) SonarSource. All Rights Reserved. Licensed under the LGPL License. See License.txt in the project root for license information.
namespace Foo
{
}
then the headerFormat parameter should end with an empty line like this
// Copyright (c) SonarSource. All Rights Reserved. Licensed under the LGPL License. See License.txt in the project root for license information.
/* * SonarQube, open source software quality management tool. * Copyright (C) 2008-2013 SonarSource * mailto:contact AT sonarsource DOT com * * SonarQube is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * SonarQube is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */================================================ FILE: analyzers/rspec/cs/S1451.json ================================================ { "title": "Track lack of copyright and license headers", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "BLOCKER" }, "attribute": "LAWFUL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "convention" ], "defaultSeverity": "Blocker", "ruleSpecification": "RSPEC-1451", "sqKey": "S1451", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S1479.html ================================================
When switch
statements have large sets of multi-line case clauses, the code becomes hard to read and maintain.
For example, the Cognitive Complexity is going to be particularly high.
In such scenarios, it’s better to refactor the switch to only have single-line case clauses.
When all the case clauses of a switch statement are single-line, the readability of the code is not affected. Moreover,
switch statements with single-line case clauses can easily be converted into switch expressions, which are
more concise for assignment and avoid the need for break statements.
This rule ignores:
switch statements over Enum argumentsreturn, break and throw statements in case clausesExtract the logic of multi-line case clauses into separate methods.
The examples below use the "Maximum number of case" property set to 4.
Note that from C# 8, you can use switch expression.
public int MapChar(char ch, int value)
{
switch(ch) // Noncompliant
{
case 'a':
return 1;
case 'b':
return 2;
case 'c':
return 3;
// ...
case '-':
if (value > 10)
{
return 42;
}
else if (value < 5 && value > 1)
{
return 21;
}
return 99;
default:
return 1000;
}
}
public int MapChar(char ch, int value)
{
switch(ch) // Compliant: All 5 cases are single line statements
{
case 'a':
return 1;
case 'b':
return 2;
case 'c':
return 3;
// ...
case '-':
return HandleDash(value);
default:
return 1000;
}
}
private int HandleDash(int value)
{
if (value > 10)
{
return 42;
}
else if (value < 5 && value > 1)
{
return 21;
}
return 99;
}
For this example, a switch expression is more concise and clear:
public int MapChar(char ch, int value) =>
ch switch // Compliant
{
'a' => 1,
'b' => 2,
'c' => 3,
// ...
'-' => HandleDash(value),
_ => 1000,
};
private int HandleDash(int value)
{
if (value > 10)
{
return 42;
}
else if (value < 5 && value > 1)
{
return 21;
}
return 99;
}
switch statementAn unused local variable is a variable that has been declared but is not used anywhere in the block of code where it is defined. It is dead code, contributing to unnecessary complexity and leading to confusion when reading the code. Therefore, it should be removed from your code to maintain clarity and efficiency.
Having unused local variables in your code can lead to several issues:
In summary, unused local variables can make your code less readable, more confusing, and harder to maintain, and they can potentially lead to bugs or inefficient memory use. Therefore, it is best to remove them.
Unused locally created resources in a using statement are not reported.
using(var t = new TestTimer()) // t never used, but compliant.
{
//...
}
The fix for this issue is straightforward. Once you ensure the unused variable is not part of an incomplete implementation leading to bugs, you just need to remove it.
public int NumberOfMinutes(int hours)
{
int seconds = 0; // Noncompliant - seconds is unused
return hours * 60;
}
public int NumberOfMinutes(int hours)
{
return hours * 60;
}
================================================
FILE: analyzers/rspec/cs/S1481.json
================================================
{
"title": "Unused local variables should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1481",
"sqKey": "S1481",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S1541.html
================================================
The cyclomatic complexity of methods and properties should not exceed a defined threshold. Complex code can perform poorly and will in any case be difficult to understand and therefore to maintain.
================================================ FILE: analyzers/rspec/cs/S1541.json ================================================ { "title": "Methods and properties should not be too complex", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "brain-overload" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-1541", "sqKey": "S1541", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1607.html ================================================When a test fails due, for example, to infrastructure issues, you might want to ignore it temporarily. But without some kind of notation about why the test is being ignored, it may never be reactivated. Such tests are difficult to address without comprehensive knowledge of the project, and end up polluting their projects.
This rule raises an issue for each ignored test that does not have a WorkItem attribute nor a comment about why it is being skipped on
the right side of the Ignore attribute.
[TestMethod]
[Ignore]
public void Test_DoTheThing()
{
// ...
}
[TestMethod]
[Ignore] // renable when TCKT-1234 is fixed
public void Test_DoTheThing()
{
// ...
}
or
[TestMethod]
[Ignore]
[WorkItem(1234)]
public void Test_DoTheThing()
{
// ...
}
The rule doesn’t raise an issue if:
WorkItem attributeIgnore attributeConcatenating multiple string literals or strings using the + operator creates a new string object for each concatenation. This can
lead to a large number of intermediate string objects and can be inefficient. The StringBuilder class is more efficient than string
concatenation, especially when the operator is repeated over and over as in loops.
Replace string concatenation with StringBuilder.
string str = "";
for (int i = 0; i < arrayOfStrings.Length ; ++i)
{
str = str + arrayOfStrings[i];
}
StringBuilder bld = new StringBuilder();
for (int i = 0; i < arrayOfStrings.Length; ++i)
{
bld.Append(arrayOfStrings[i]);
}
string str = bld.ToString();
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
StringConcatenation |
.NET 9.0 |
50,530.75 us |
2,699.189 us |
586280.70 KB |
|
StringBuilder |
.NET 9.0 |
82.31 us |
3.387 us |
243.79 KB |
|
StringConcatenation |
.NET Framework 4.8.1 |
37,453.72 us |
1,543.051 us |
586450.38 KB |
|
StringBuilder |
.NET Framework 4.8.1 |
178.32 us |
6.148 us |
244.15 KB |
The results were generated by running the following snippet with BenchmarkDotNet:
[Params(10_000)]
public int Iterations;
[Benchmark]
public void StringConcatenation()
{
string str = "";
for (int i = 0; i < Iterations; i++)
{
str = str + "append";
}
}
[Benchmark]
public void StringBuilder()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < Iterations; i++)
{
builder.Append("append");
}
_ = builder.ToString();
}
Hardware Configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S1643.json ================================================ { "title": "Strings should not be concatenated using \u0027+\u0027 in a loop", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1643", "sqKey": "S1643", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S1656.html ================================================
Re-assigning a variable to itself is a defect as it has no actual effect and indicates meaning to do something else. It usually means that:
public class Choice {
private bool selected;
public void MakeChoice(bool selected)
{
selected = selected; // Noncompliant
}
}
public class Choice {
private bool selected;
public void MakeChoice(bool selected)
{
this.selected = selected; // Compliant
}
}
Declaring multiple variable on one line is difficult to read.
class MyClass
{
private int a, b; // Noncompliant
public void Method()
{
int c, d; // Noncompliant
}
}
class MyClass
{
private int a;
private int b;
public void Method()
{
int c;
int d;
}
}
================================================
FILE: analyzers/rspec/cs/S1659.json
================================================
{
"title": "Multiple variables should not be declared on the same line",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1659",
"sqKey": "S1659",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1694.html
================================================
A class with only abstract methods and no inheritable behavior should be converted to an interface.
The purpose of an abstract
class is to provide some overridable behaviors while also defining methods that are required to be implemented by sub-classes.
A class that contains only abstract methods, often called pure abstract class, is effectively an interface, but with the disadvantage
of not being able to be implemented by multiple classes.
Using interfaces over pure abstract classes presents multiple advantages:
abstract classes that contain non-abstract methods, in addition to abstract ones, cannot easily be converted to
interfaces, and are not the subject of this rule:
public abstract class Lamp // Compliant: Glow is abstract, but FlipSwitch is not
{
private bool switchLamp = false;
public abstract void Glow();
public void FlipSwitch()
{
switchLamp = !switchLamp;
if (switchLamp)
{
Glow();
}
}
}
Notice that, since C# 8.0, you can also define default implementations for interface methods, which is yet another reason to prefer interfaces over abstract classes when you don’t need to provide any inheritable behavior.
However, interfaces cannot have fields (such as switchLamp in the example above), and that remains true even in C# 8.0 and upwards.
This can be a valid reason to still prefer an abstract class over an interface.
Convert the abstract class to an interface with the same methods.
public abstract class Animal // Noncompliant: should be an interface
{
public abstract void Move();
public abstract void Feed();
}
public interface Animal
{
void Move();
void Feed();
}
Catching NullReferenceException is generally considered a bad practice because it can hide bugs in your code. Instead of catching this
exception, you should aim to prevent it. This makes your code more robust and easier to understand. In addition, constantly catching and handling
NullReferenceException can lead to performance issues. Exceptions are expensive in terms of system resources, so they should be used
cautiously and only for exceptional conditions, not for regular control flow.
Instead of catching NullReferenceException, it’s better to prevent it from happening in the first place. You can do this by using null checks or
null conditional operators (?.) before accessing members of an object.
public int GetLengthPlusTwo(string str)
{
try
{
return str.Length + 2;
}
catch (NullReferenceException e)
{
return 2;
}
}
public int GetLengthPlusTwo(string str)
{
if (str is null)
{
return 2;
}
return str.Length + 2;
}
Using the equality == and inequality != operators to compare two objects generally works. The operators can be
overloaded, and therefore the comparison can resolve to the appropriate method. However, when the operators are used on interface instances, then
== resolves to reference equality, which may result in unexpected behavior if implementing classes override Equals.
Similarly, when a class overrides Equals, but instances are compared with non-overloaded ==, there is a high chance that
value comparison was meant instead of the reference one.
public interface IMyInterface
{
}
public class MyClass : IMyInterface
{
public override bool Equals(object obj)
{
//...
}
}
public class Program
{
public static void Method(IMyInterface instance1, IMyInterface instance2)
{
if (instance1 == instance2) // Noncompliant, will do reference equality check, but was that intended? MyClass overrides Equals.
{
Console.WriteLine("Equal");
}
}
}
public interface IMyInterface
{
}
public class MyClass : IMyInterface
{
public override bool Equals(object obj)
{
//...
}
}
public class Program
{
public static void Method(IMyInterface instance1, IMyInterface instance2)
{
if (object.Equals(instance1, instance2)) // object.Equals checks for null and then calls the instance based Equals, so MyClass.Equals
{
Console.WriteLine("Equal");
}
}
}
The rule does not report on comparisons of System.Type instances and on comparisons inside Equals overrides.
It also does not raise an issue when one of the operands is null nor when one of the operand is cast to object (because
in this case we want to ensure reference equality even if some == overload is present).
Calling an overridable method from a constructor could result in failures or strange behaviors when instantiating a subclass which overrides the method.
When constructing an object of a derived class, the constructor of the parent class is invoked first, and only then the constructor of the derived class is called. This sequential construction process applies to multiple levels of inheritance as well, starting from the base class and progressing to the most derived class.
If an overridable method is called within the constructor of the parent class, it may inadvertently invoke an overridden implementation in the derived class. This can lead to unexpected failures or strange behaviors because the object’s construction is still in progress and may not have reached a fully initialized state. Consequently, the overridden method may rely on uninitialized members or have assumptions about the object’s state that are not yet valid.
For example:
public class Parent
{
public Parent()
{
DoSomething(); // Noncompliant
}
public virtual void DoSomething() // can be overridden
{
// ...
}
}
public class Child : Parent
{
private string foo;
public Child(string foo) // leads to call DoSomething() in Parent constructor which triggers a NullReferenceException as foo has not yet been initialized
{
this.foo = foo;
}
public override void DoSomething()
{
Console.WriteLine(this.foo.Length);
}
}
Child class constructor starts by calling the Parent class constructor.Parent class constructor calls the method DoSomething, which has been overridden in the Child
class.Child class overridden DoSomething method depends on fields that are initialized in the
Child class constructor, unexpected behavior (such as a NullReferenceException) can result, because the fields aren’t
initialized yet.Depending on the context, you can either:
sealed in the current
classsealed
class Parent
{
public virtual void DoSomething() { }
}
class Child : Parent
{
public Child()
{
DoSomething(); // Noncompliant
}
}
class Parent
{
public virtual void DoSomething() { }
}
class Child : Parent
{
public Child()
{
DoSomething(); // Noncompliant
}
}
class Parent
{
public virtual void DoSomething() { }
}
class Child : Parent
{
public Child()
{
DoSomething(); // Noncompliant
}
}
class Parent
{
public virtual void DoSomething() { }
}
class Child : Parent
{
public Child()
{
// Call removed
}
}
class Parent
{
public virtual void DoSomething() { }
}
class Child : Parent
{
public Child()
{
DoSomething();
}
// Method sealed to prevent overriding
public sealed override void DoSomething()
{
base.DoSomething();
}
}
class Parent
{
public virtual void DoSomething() { }
}
// Class sealed to prevent inheritance
sealed class Child : Parent
{
public Child()
{
DoSomething();
}
}
A loop statement with at most one
iteration is equivalent to an if statement; the following block is executed only once.
If the initial intention was to conditionally execute the block only once, an if statement should be used instead. If that was not the
initial intention, the block of the loop should be fixed so the block is executed multiple times.
A loop statement with at most one iteration can happen when a statement unconditionally transfers control, such as a jump statement or a throw statement, is misplaced inside the loop block.
This rule raises when the following statements are misplaced:
public object Method(IEnumerable<object> items)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
break; // Noncompliant: loop only executes once
}
foreach (object item in items)
{
return item; // Noncompliant: loop only executes once
}
return null;
}
public object Method(IEnumerable<object> items)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
var item = items.FirstOrDefault();
if (item != null)
{
return item;
}
return null;
}
for, foreach, do, and whilebreak, continue, return, and gotothrow statementUsing the same value on both sides of certain operators is a code defect. In the case of logical operators, it is either a copy/paste error and, therefore, a bug, or it is simply duplicated code and should be simplified. For bitwise operators and most binary mathematical operators, having the same value on both sides of an operator yields predictable results and should be simplified as well to avoid further code defects.
This rule raises for the following operators.
== and !=)<
=, <, >, >=)| )||)&)&&)^)-)\)%)-=)\=)This rule ignores the following operators:
if ( a == a ) // always true
{
doZ();
}
if ( a != a ) // always false
{
doY();
}
if ( a == b && a == b ) // if the first one is true, the second one is too
{
doX();
}
if ( a == b || a == b ) // if the first one is true, the second one is too
{
doW();
}
int j = 5 / 5; // always 1
int k = 5 - 5; // always 0
c.Equals(c); // always true
Object.Equals(c, c); // always true
Nested switch structures are difficult to understand because you can easily confuse the cases of an inner switch as
belonging to an outer statement. Therefore nested switch statements should be avoided.
Specifically, you should structure your code to avoid the need for nested switch statements, but if you cannot, then consider moving
the inner switch to another function.
Creating objects that are not used is a vulnerability that can lead to unexpected behavior.
If this was done intentionally due to side effects in the object’s constructor, the code should be moved to a dedicated method.
public void Method(MyObject myObject)
{
if (myObject is null)
{
new MyObject(); // Noncompliant
}
if (myObject.IsCorrupted)
{
new ArgumentException($"{nameof(myObject)} is corrupted"); // Noncompliant
}
// ...
}
public void Method(MyObject myObject)
{
if (myObject is null)
{
myObject = new MyObject(); // Compliant
}
if (myObject.IsCorrupted)
{
throw new ArgumentException($"{nameof(myObject)} is corrupted"); // Compliant
}
// ...
}
================================================
FILE: analyzers/rspec/cs/S1848.json
================================================
{
"title": "Objects should not be created to be dropped immediately without being used",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1848",
"sqKey": "S1848",
"scope": "Main",
"quickfix": "infeasible"
}
================================================
FILE: analyzers/rspec/cs/S1854.html
================================================
Dead stores refer to assignments made to local variables that are subsequently never used or immediately overwritten. Such assignments are unnecessary and don’t contribute to the functionality or clarity of the code. They may even negatively impact performance. Removing them enhances code cleanliness and readability. Even if the unnecessary operations do not do any harm in terms of the program’s correctness, they are - at best - a waste of computing resources.
No issue is reported when
try blocks-1, 0, 1, null, true, false,
"" or string.EmptyRemove the unnecessary assignment, then test the code to make sure that the right-hand side of a given assignment had no side effects (e.g. a method that writes certain data to a file and returns the number of written bytes).
You can also use discards (rather than a variable) to express that result of a method call is ignored on purpose.
int Foo(int y)
{
int x = 100; // Noncompliant: dead store
x = 150; // Noncompliant: dead store
x = 200;
return x + y;
}
int Foo(int y)
{
int x = 200; // Compliant: no unnecessary assignment
return x + y;
}
Invoking a method designed to return a string representation of an object which is already a string is a waste of keystrokes. Similarly, explicitly
invoking ToString() when the compiler would do it implicitly is also needless code-bloat.
This rule raises an issue when ToString() is invoked:
stringstring operand to concatenationstring.Format
var s = "foo";
var t = "fee fie foe " + s.ToString(); // Noncompliant
var someObject = new object();
var u = "" + someObject.ToString(); // Noncompliant
var v = string.Format("{0}", someObject.ToString()); // Noncompliant
var s = "foo";
var t = "fee fie foe " + s;
var someObject = new object();
var u = "" + someObject;
var v = string.Format("{0}", someObject);
The rule does not report on value types, where leaving off the ToString() call would result in automatic boxing.
var v = string.Format("{0}", 1.ToString());
================================================
FILE: analyzers/rspec/cs/S1858.json
================================================
{
"title": "\"ToString()\" calls should not be redundant",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1858",
"sqKey": "S1858",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1862.html
================================================
A chain of if/else
if statements is evaluated from top to bottom. At most, only one branch will be executed: the first statement with a condition that evaluates to
true. Therefore, duplicating a condition leads to unreachable code inside the duplicated condition block. Usually, this is due to a
copy/paste error.
The result of such duplication can lead to unreachable code or even to unexpected behavior.
if (param == 1)
{
OpenWindow();
}
else if (param == 2)
{
CloseWindow();
}
else if (param == 1) // Noncompliant: condition has already been checked
{
MoveWindowToTheBackground(); // unreachable code
}
if (param == 1)
{
OpenWindow();
}
else if (param == 2)
{
CloseWindow();
}
else if (param == 3)
{
MoveWindowToTheBackground();
}
When the same code is duplicated in two or more separate branches of a conditional, it can make the code harder to understand, maintain, and can potentially introduce bugs if one instance of the code is changed but others are not.
Having two cases in a switch statement or two branches in an if chain with the same implementation is at
best duplicate code, and at worst a coding error.
if (a >= 0 && a < 10)
{
DoFirst();
DoTheThing();
}
else if (a >= 10 && a < 20)
{
DoTheOtherThing();
}
else if (a >= 20 && a < 50) // Noncompliant; duplicates first condition
{
DoFirst();
DoTheThing();
}
switch (i)
{
case 1:
DoFirst();
DoSomething();
break;
case 2:
DoSomethingDifferent();
break;
case 3: // Noncompliant; duplicates case 1's implementation
DoFirst();
DoSomething();
break;
default:
DoTheRest();
}
If the same logic is truly needed for both instances, then:
if chain they should be combined
if ((a >= 0 && a < 10) || (a >= 20 && a < 50))
{
DoFirst();
DoTheThing();
}
else if (a >= 10 && a < 20)
{
DoTheOtherThing();
}
switch, one should fall through to the other
switch (i)
{
case 1:
case 3:
DoFirst();
DoSomething();
break;
case 2:
DoSomethingDifferent();
break;
default:
DoTheRest();
}
The rule does not raise an issue for blocks in an if chain that contain a single line of code. The same applies to blocks in a
switch statement that contain a single line of code with or without a following break.
if (a >= 0 && a < 10)
{
DoTheThing();
}
else if (a >= 10 && a < 20)
{
DoTheOtherThing();
}
else if (a >= 20 && a < 50) //no issue, usually this is done on purpose to increase the readability
{
DoTheThing();
}
However, this exception does not apply to if chains without an else statement or to a switch statement
without a default clause when all branches have the same single line of code.
if (a == 1)
{
DoSomething(); // Noncompliant, this might have been done on purpose but probably not
}
else if (a == 2)
{
DoSomething();
}
Casting expressions are utilized to convert one data type to another, such as transforming an integer into a string. This is especially crucial in strongly typed languages like C, C++, C#, Java, Python, and others.
However, there are instances where casting expressions are not needed. These include situations like:
These scenarios are considered unnecessary casting expressions. They can complicate the code and make it more difficult to understand, without offering any advantages.
As a result, it’s generally advised to avoid unnecessary casting expressions. Instead, rely on the language’s type system to ensure type safety and code clarity.
Issues are not raised against the default literal.
To fix your code remove the unnecessary casting expression.
public int Example(int i)
{
return (int) (i + 42); // Noncompliant
}
public IEnumerable<int> ExampleCollection(IEnumerable<int> coll)
{
return coll.Reverse().OfType<int>(); // Noncompliant
}
public int Example(int i)
{
return i + 42;
}
public IEnumerable<int> ExampleCollection(IEnumerable<int> coll)
{
return coll.Reverse();
}
bool b = (bool)default; // Doesn't raise an issue
An inheritance list entry is redundant if:
Object - all classes extend Object implicitly.int for an enumSuch redundant declarations should be removed because they needlessly clutter the code and can be confusing.
public class MyClass : Object // Noncompliant enum MyEnum : int // Noncompliant
public class MyClass enum MyEnum================================================ FILE: analyzers/rspec/cs/S1939.json ================================================ { "title": "Inheritance list should not be redundant", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1939", "sqKey": "S1939", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S1940.html ================================================
It is needlessly complex to invert the result of a boolean comparison. The opposite comparison should be made instead.
if ( !(a == 2)) { ...} // Noncompliant
bool b = !(i < 10); // Noncompliant
if (a != 2) { ...}
bool b = (i >= 10);
================================================
FILE: analyzers/rspec/cs/S1940.json
================================================
{
"title": "Boolean checks should not be inverted",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1940",
"sqKey": "S1940",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S1944.html
================================================
A cast is an explicit conversion, which is a way to tell the compiler the intent to convert from one type to another.
void Method(object value)
{
int i;
i = (int)value; // Casting (explicit conversion) from float to int
}
In most cases, the compiler will be able to catch invalid casts between incompatible value types or reference types.
However, the compiler will not be able to detect invalid casts to interfaces.
Invalid casts will lead to unexpected behaviors or runtime errors such as InvalidCastException.
No issue is reported if the interface has no implementing class in the assembly.
To prevent an InvalidCastException from raising during an explicit conversion, it is recommended to use the as operator.
When the conversion is not possible, the as operator returns null and will never raise an exception.
public interface IMyInterface
{ /* ... */ }
public class Implementer : IMyInterface
{ /* ... */ }
public class AnotherClass
{ /* ... */ }
public static class Program
{
public static void Main()
{
var another = new AnotherClass();
var x = (IMyInterface)another; // Noncompliant: InvalidCastException is being thrown
}
}
public interface IMyInterface
{ /* ... */ }
public class Implementer : IMyInterface
{ /* ... */ }
public class AnotherClass
{ /* ... */ }
public static class Program
{
public static void Main()
{
var another = new AnotherClass();
var x = another as IMyInterface; // Compliant: but will always be null
}
}
The for loop is designed to iterate over a range using a counter variable, with the counter being updated in the loop’s increment
section. Misusing this structure can lead to issues such as infinite loops if the counter is not updated correctly. If this is intentional, use a
while or do while loop instead of a for loop.
Using a for loop for purposes other than its intended use can lead to confusion and potential bugs. If the for loop structure does not
fit your needs, consider using an alternative iteration statement.
Move the counter variable update to the loop’s increment section. If this is impossible, consider using another iteration statement instead.
int sum = 0;
for (int i = 0; i < 10; sum++) // Noncompliant: `i` is not updated in the increment section
{
// ...
i++;
}
for (int i = 0;; i++) // Noncompliant: the loop condition is empty although incrementing `i`
{
// ...
}
int sum = 0;
for (int i = 0; i < 10; i++)
{
// ...
sum++;
}
int i = 0;
while (true)
{
// ...
i++;
}
for
statementfor, foreach, do, and whileThis vulnerability increases the likelihood that attackers are able to compute the cleartext of password hashes.
During the process of password hashing, an additional component, known as a "salt," is often integrated to bolster the overall security. This salt, acting as a defensive measure, primarily wards off certain types of attacks that leverage pre-computed tables to crack passwords.
However, potential risks emerge when the salt is deemed insecure. This can occur when the salt is consistently the same across all users or when it is too short or predictable. In scenarios where users share the same password and salt, their password hashes will inevitably mirror each other. Similarly, a short salt heightens the probability of multiple users unintentionally having identical salts, which can potentially lead to identical password hashes. These identical hashes streamline the process for potential attackers to recover clear-text passwords. Thus, the emphasis on implementing secure, unique, and sufficiently lengthy salts in password-hashing functions is vital.
Despite best efforts, even well-guarded systems might have vulnerabilities that could allow an attacker to gain access to the hashed passwords. This could be due to software vulnerabilities, insider threats, or even successful phishing attempts that give attackers the access they need.
Once the attacker has these hashes, they will likely attempt to crack them using a couple of methods. One is brute force, which entails trying every possible combination until the correct password is found. While this can be time-consuming, having the same salt for all users or a short salt can make the task significantly easier and faster.
If multiple users have the same password and the same salt, their password hashes would be identical. This means that if an attacker successfully cracks one hash, they have effectively cracked all identical ones, granting them access to multiple accounts at once.
A short salt, while less critical than a shared one, still increases the odds of different users having the same salt. This might create clusters of password hashes with identical salt that can then be attacked as explained before.
With short salts, the probability of a collision between two users' passwords and salts couple might be low depending on the salt size. The shorter the salt, the higher the collision probability. In any case, using longer, cryptographically secure salt should be preferred.
To securely store password hashes, it is a recommended to rely on key derivation functions that are computationally intensive. Examples of such functions are:
When they are used for password storage, using a secure, random salt is required.
However, those functions can also be used for other purposes such as master key derivation or password-based pre-shared key generation. In those cases, the implemented cryptographic protocol might require using a fixed salt to derive keys in a deterministic way. In such cases, using a fixed salt is safe and accepted.
The following code contains examples of hard-coded salts.
using System.Security.Cryptography;
public static void hash(string password)
{
var salt = Encoding.UTF8.GetBytes("salty");
var hashed = new Rfc2898DeriveBytes(password, salt); // Noncompliant
}
using System.Security.Cryptography;
public static void hash(string password)
{
var saltSize = 16;
var iterations = 100_000;
var hashed = new Rfc2898DeriveBytes(password, saltSize, iterations, HashAlgorithmName.SHA512);
}
This code ensures that each user’s password has a unique salt value associated with it. It generates a salt randomly and with a length that provides the required security level. It uses a salt length of at least 16 bytes (128 bits), as recommended by industry standards.
In the case of the code sample, the class automatically takes care of generating a secure salt if none is specified.
Hard-coding credentials in source code or binaries makes it easy for attackers to extract sensitive information, especially in distributed or open-source applications. This practice exposes your application to significant security risks.
This rule flags instances of hard-coded credentials used in database and LDAP connections. It looks for hard-coded credentials in connection strings, and for variable names that match any of the patterns from the provided list.
In the past, it has led to the following vulnerabilities:
Credentials should be stored in a configuration file that is not committed to the code repository, in a database, or managed by your cloud provider’s secrets management service. If a password is exposed in the source code, it must be changed immediately.
string username = "admin"; string password = "Admin123"; // Noncompliant string usernamePassword = "user=admin&password=Admin123"; // Noncompliant string url = "scheme://user:Admin123@domain.com"; // Noncompliant
string username = "admin";
string password = GetEncryptedPassword();
string usernamePassword = string.Format("user={0}&password={1}", GetEncryptedUsername(), GetEncryptedPassword());
string url = $"scheme://{username}:{password}@domain.com";
string url2 = "http://guest:guest@domain.com"; // Compliant
const string Password_Property = "custom.password"; // Compliant
Formatted SQL queries can be difficult to maintain, debug and can increase the risk of SQL injection when concatenating untrusted values into the query. However, this rule doesn’t detect SQL injections (unlike rule {rule:csharpsquid:S3649}), the goal is only to highlight complex/formatted queries.
There is a risk if you answered yes to any of those questions.
public void Foo(DbContext context, string query, string param)
{
string sensitiveQuery = string.Concat(query, param);
context.Database.ExecuteSqlCommand(sensitiveQuery); // Sensitive
context.Query<User>().FromSql(sensitiveQuery); // Sensitive
context.Database.ExecuteSqlCommand($"SELECT * FROM mytable WHERE mycol={value}", param); // Sensitive, the FormattableString is evaluated and converted to RawSqlString
string query = $"SELECT * FROM mytable WHERE mycol={param}";
context.Database.ExecuteSqlCommand(query); // Sensitive, the FormattableString has already been evaluated, it won't be converted to a parametrized query.
}
public void Bar(SqlConnection connection, string param)
{
SqlCommand command;
string sensitiveQuery = string.Format("INSERT INTO Users (name) VALUES (\"{0}\")", param);
command = new SqlCommand(sensitiveQuery); // Sensitive
command.CommandText = sensitiveQuery; // Sensitive
SqlDataAdapter adapter;
adapter = new SqlDataAdapter(sensitiveQuery, connection); // Sensitive
}
public void Foo(DbContext context, string query, string param)
{
context.Database.ExecuteSqlCommand("SELECT * FROM mytable WHERE mycol=@p0", param); // Compliant, it's a parametrized safe query
}
When a cookie is protected with the secure attribute set to true it will not be send by the browser over an unencrypted HTTP
request and thus cannot be observed by an unauthorized person during a man-in-the-middle attack.
There is a risk if you answered yes to any of those questions.
HTTPs everywhere so setting the secure flag to true should be the default behaviour
when creating cookies.secure flag to true for session-cookies.When the HttpCookie.Secure property is set to false then the cookie will be send during an unencrypted HTTP request:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
myCookie.Secure = false; // Sensitive: a security-sensitive cookie is created with the secure flag set to false
The default value of
Secure flag is false, unless overwritten by an application’s configuration file:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
// Sensitive: a security-sensitive cookie is created with the secure flag not defined (by default set to false)
Set the HttpCookie.Secure property to true:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
myCookie.Secure = true; // Compliant
Or change the default flag values for the whole application by editing the Web.config configuration file:
<httpCookies httpOnlyCookies="true" requireSSL="true" />
requireSSL attribute corresponds programmatically to the Secure field.httpOnlyCookies attribute corresponds programmatically to the httpOnly field.There is no good excuse for an empty class. If it’s being used simply as a common extension point, it should be replaced with an
interface. If it was stubbed in as a placeholder for future development it should be fleshed-out. In any other case, it should be
eliminated.
public class Empty // Noncompliant
{
}
public interface IEmpty
{
}
Command, Message, Event, or Query are ignored as messaging
libraries often use them.System.Exception are ignored; even an empty Exception class can provide helpful information by its type name
alone.System.Attribute and classes annotated with attributes are ignored.PageModel class used in ASP.NET Core Razor Pages — are ignored.
using Microsoft.AspNetCore.Mvc.RazorPages;
public class EmptyPageModel: PageModel // Compliant - an empty PageModel can be fully functional, the C# code can be in the cshtml file
{
}
================================================
FILE: analyzers/rspec/cs/S2094.json
================================================
{
"title": "Classes should not be empty",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2094",
"sqKey": "S2094",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2114.html
================================================
Passing a collection as an argument to the collection’s own method is a code defect. Doing so might either have unexpected side effects or always have the same result.
Another case is using set-like operations. For example, using Union between a list and itself will always return the same list. Conversely, using Except between a list and itself will always return an empty list.
Exceptions to this rule are the methods AddRange and Concat, since the developer can use them to
multiply the list elements or the list itself respectively.
var list = new List<int>(); list.Union(list); // Noncompliant: always returns list list.Intersect(list); // Noncompliant: always returns list list.Except(list); // Noncompliant: always returns empty list.SequenceEqual(list); // Noncompliant: always returns true var set = new HashSet<int>(); set.UnionWith(set); // Noncompliant: no changes set.IntersectWith(set); // Noncompliant: no changes set.ExceptWith(set); // Noncompliant: always returns empty set.SymmetricExceptWith(set); // Noncompliant: always returns empty set.IsProperSubsetOf(set); // Noncompliant: always returns false set.IsProperSupersetOf(set); // Noncompliant: always returns false set.IsSubsetOf(set); // Noncompliant: always returns true set.IsSupersetOf(set); // Noncompliant: always returns true set.Overlaps(set); // Noncompliant: always returns true set.SetEquals(set); // Noncompliant: always returns true list.AddRange(list); // Compliant list.Concat(list); // Compliant
When accessing a database, an empty password should be avoided as it introduces a weakness.
When a database does not require a password for authentication, it allows anyone to access and manipulate the data stored within it. Exploiting this vulnerability typically involves identifying the target database and establishing a connection to it without the need for any authentication credentials.
Once connected, an attacker can perform various malicious actions, such as viewing, modifying, or deleting sensitive information, potentially leading to data breaches or unauthorized access to critical systems. It is crucial to address this vulnerability promptly to ensure the security and integrity of the database and the data it contains.
When a database lacks a password for authentication, it opens the door for unauthorized individuals to gain access to sensitive data. This can include personally identifiable information (PII), financial records, intellectual property, or any other confidential information stored in the database. Without proper access controls in place, malicious actors can exploit this vulnerability to retrieve sensitive data, potentially leading to identity theft, financial loss, or reputational damage.
Without a password requirement, unauthorized individuals can gain unrestricted access to a database, potentially compromising the integrity of the entire system. Attackers can inject malicious code, alter configurations, or manipulate data within the database, leading to system malfunctions, unauthorized system access, or even complete system compromise. This can disrupt business operations, cause financial losses, and expose the organization to further security risks.
The absence of a password for database access allows anyone to make modifications or deletions to the data stored within it. This poses a significant risk, as unauthorized changes can lead to data corruption, loss of critical information, or the introduction of malicious content. For example, an attacker could modify financial records, tamper with customer orders, or delete important files, causing severe disruptions to business processes and potentially leading to financial and legal consequences.
Overall, the lack of a password configured to access a database poses a serious security risk, enabling unauthorized access, data breaches, system compromise, and unwanted modifications or deletions. It is essential to address this vulnerability promptly to safeguard sensitive data, maintain system integrity, and protect the organization from potential harm.
The following code uses an empty password to connect to a SQL Server database.
The vulnerability can be fixed by using Windows authentication (sometimes referred to as integrated security).
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password="); // Noncompliant
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=myServerAddress;Database=myDataBase;Integrated Security=True");
}
When the connection string includes the Integrated Security=true parameter, it enables Windows authentication (sometimes called
integrated security) for the database connection. With integrated security, the user’s Windows credentials are used to authenticate and authorize
access to the database. It eliminates the need for a separate username and password for the database connection. Integrated security simplifies
authentication and leverages the existing Windows authentication infrastructure for secure database access in your C# application.
It’s important to note that when using integrated security, the user running the application must have the necessary permissions to access the database. Ensure that the user account running the application has the appropriate privileges and is granted access to the database.
The syntax employed in connection strings varies by provider:
|
Syntax |
Supported by |
|
|
SQL Server, Oracle, Postgres |
|
|
SQL Server, OLE DB |
|
|
MySQL |
|
|
SQL Server |
|
|
ODBC |
Note: Some providers such as MySQL do not support Windows authentication with .NET Core.
It could be tempting to replace the empty password with a hard-coded one. Hard-coding passwords in the code can pose significant security risks. Here are a few reasons why it is not recommended:
To mitigate these risks, it is recommended to use secure methods for storing and retrieving passwords, such as using environment variables, configuration files, or secure key management systems. These methods allow for better security, flexibility, and separation of sensitive information from the codebase.
The following configuration file uses an empty password to connect to a database.
The vulnerability can be fixed by using Windows authentication (sometimes referred to as integrated security)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="myConnection" connectionString="Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=" /> <!-- Noncompliant -->
</connectionStrings>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="myConnection" connectionString="Server=myServerAddress;Database=myDataBase;Integrated Security=True" />
</connectionStrings>
</configuration>
When the connection string includes the Integrated Security=true parameter, it enables Windows authentication (sometimes called
integrated security) for the database connection. With integrated security, the user’s Windows credentials are used to authenticate and authorize
access to the database. It eliminates the need for a separate username and password for the database connection. Integrated security simplifies
authentication and leverages the existing Windows authentication infrastructure for secure database access in your C# application.
It’s important to note that when using integrated security, the user running the application must have the necessary permissions to access the database. Ensure that the user account running the application has the appropriate privileges and is granted access to the database.
The syntax employed in connection strings varies by provider:
|
Syntax |
Supported by |
|
|
SQL Server, Oracle, Postgres |
|
|
SQL Server, OLE DB |
|
|
MySQL |
|
|
SQL Server |
|
|
ODBC |
Note: Some providers such as MySQL do not support Windows authentication with .NET Core.
It could be tempting to replace the empty password with a hard-coded one. Hard-coding passwords in the code can pose significant security risks. Here are a few reasons why it is not recommended:
To mitigate these risks, it is recommended to use secure methods for storing and retrieving passwords, such as using environment variables, configuration files, or secure key management systems. These methods allow for better security, flexibility, and separation of sensitive information from the codebase.
When using the postfix
increment operator, it is important to know that the result of the expression x++ is the value before the operation
x.
This means that in some cases, the result might not be what you expect:
x++ to x, it’s the same as assigning x to itself, since the value is assigned before the
increment takes placex++, the returning value is x, not x+1The same applies to the postfix and prefix decrement operators.
To solve the issue in assignments, eliminate the assignment, since x\++ mutates x anyways.
To solve the issue in return statements, consider using the prefix
increment operator, since it works in reverse: the result of the expression ++x is the value after the operation,
which is x+1, as one might expect.
The same applies to the postfix and prefix decrement operators.
int PickNumber()
{
int i = 0;
int j = 0;
i = i++; // Noncompliant: i is still 0
return j--; // Noncompliant: returns 0
}
int PickNumber()
{
int i = 0;
int j = 0;
i++; // Compliant: i is incremented to 1
return --j; // Compliant: returns -1
}
When an exception is logged and rethrown, the upstream code may not be aware that the exception has already been logged. As a result, the same exception gets logged multiple times, making it difficult to identify the root cause of the issue. This can be particularly problematic in multi-threaded applications where messages from other threads can be interwoven with the repeated log entries.
This rule will not generate issues if, within the catch block, one of the following conditions are met:
To address this issue, it is recommended to modify the code to log exceptions only when they are handled locally. In all other cases, simply rethrow the exception and allow the higher-level layers of the application to handle the logging and appropriate actions.
try {}
catch (Exception ex)
{
logger.LogError(ex.Message);
throw;
}
try {}
catch (Exception ex)
{
logger.LogError(ex.Message);
// Handle exception
}
or
try {}
catch (Exception ex)
{
// ...
throw;
}
Beginning with C# 7, it is possible to add underscores ('_') to numeric literals to enhance readability. The addition of underscores in this manner has no semantic meaning, but makes it easier for maintainers to understand the code.
The number of digits to the left of a decimal point needed to trigger this rule varies by base.
| Base | Minimum digits |
|---|---|
|
binary |
9 |
|
decimal |
6 |
|
hexadecimal |
9 |
It is only the presence of underscores, not their spacing that is scrutinized by this rule.
Note that this rule is automatically disabled when the project’s C# version is lower than 7.
int i = 10000000; // Noncompliant; is this 10 million or 100 million? int j = 0b01101001010011011110010101011110; // Noncompliant long l = 0x7fffffffffffffffL; // Noncompliant
int i = 10_000_000; int j = 0b01101001_01001101_11100101_01011110; long l = 0x7fff_ffff_ffff_ffffL;================================================ FILE: analyzers/rspec/cs/S2148.json ================================================ { "title": "Underscores should be used to make large numbers readable", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-2148", "sqKey": "S2148", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S2156.html ================================================
The difference between private and protected visibility is that child classes can see and use protected
members, but they cannot see private ones. Since a sealed class cannot have children, marking its members
protected is confusingly pointless.
public sealed class MySealedClass
{
protected string name = "Fred"; // Noncompliant
protected void SetName(string name) // Noncompliant
{
// ...
}
}
public sealed class MySealedClass
{
private string name = "Fred";
public void SetName(string name)
{
// ...
}
}
================================================
FILE: analyzers/rspec/cs/S2156.json
================================================
{
"title": "\"sealed\" classes should not have \"protected\" members",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2156",
"sqKey": "S2156",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2166.html
================================================
Clear, communicative naming is important in code. It helps maintainers and API users understand the intentions for and uses of a unit of code.
Using "exception" in the name of a class that does not extend Exception or one of its subclasses is a clear violation of the expectation
that a class' name will indicate what it is and/or does.
public class FruitException // Noncompliant - this has nothing to do with Exception
{
private Fruit expected;
private string unusualCharacteristics;
private bool appropriateForCommercialExploitation;
// ...
}
public class CarException // Noncompliant - does not derive from any Exception-based class
{
public CarException(string message, Exception inner)
{
// ...
}
}
public class FruitSport // Compliant - class name does not end with 'Exception'
{
private Fruit expected;
private string unusualCharacteristics;
private bool appropriateForCommercialExploitation;
// ...
}
public class CarException: Exception // Compliant - correctly extends System.Exception
{
public CarException(string message, Exception inner): base(message, inner)
{
// ...
}
}
================================================
FILE: analyzers/rspec/cs/S2166.json
================================================
{
"title": "Classes named like \"Exception\" should extend \"Exception\" or a subclass",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention",
"error-handling",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2166",
"sqKey": "S2166",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2178.html
================================================
Short-circuit evaluation is an evaluation strategy for Boolean operators, that doesn’t evaluates the second argument of the operator if it is not needed to determine the result of the operation.
C# provides logical operators that implement short-circuit evaluation: && and ||, as well as non-short-circuit
versions: & and |. Unlike short-circuit operators, non-short-circuit ones evaluate both operands and afterwards perform
the logical operation.
For example false && FunctionCall() always results in false, even when FunctionCall invocation would
raise an exception. Instead, false & FunctionCall() also evaluates FunctionCall(), and results in an exception if
FunctionCall() invocation raises an exception.
Similarly, true || FunctionCall() always results in true, no matter what the return value of FunctionCall()
would be.
The use of non-short-circuit logic in a boolean context is likely a mistake - one that could cause serious program errors as conditions are evaluated under the wrong circumstances.
if (GetTrue() | GetFalse()) // Noncompliant: both sides evaluated
{
}
if (GetTrue() || GetFalse()) // Compliant: short-circuit logic used
{
}
The shifting operators are used to do an arithmetic shift to the bits of an integral numeric value, either to the left or the right.
var number = 14; // ...01110 (14) var left = number << 1; // ...11100 (28) var right = number >> 1; // ...00111 (7)
Therefore, shifting an integral number by 0 is equivalent to doing nothing, since the bits do not move any positions to the left or the right.
On the other hand, shifting an integral number by a value greater than their count of bits minus one (n_bits-1) is equivalent to
shifting by the value modulo the bit count of the number (value % n_bits).
In the case of int and uint, which take 32 bits in the memory, the shift count is given by the five low-order bits of the
second operand, which can represent numbers from 0 to 31. This means that numbers having the same five low-order bits are treated the same by the
shift operators.
var one = 0b0_00001; var thirtyThree = 0b1_00001; // Same five low-order bits, 33 % 32 = 1 var shifted1 = 42 << one; // Results in 84 var shifted2 = 42 << thirtyThree; // Results in 84
Note that integral number with a less than 32-bit quantity (e.g. short, ushort) are implicitly converted to
int before the shifting operation and so the rule for int/uint applies.
If the first operand is a long or ulong (64-bit quantity), the shift count is given by the six low-order bits of the
second operand. That is, the actual shift count is 0 to 63 bits.
This rule doesn’t raise an issue when the shift by zero is obviously for cosmetic reasons:
bytes[loc+0] = (byte)(value >> 8); bytes[loc+1] = (byte)(value >> 0);
short s = 1; short shortShift1 = (short)(s << 0); // Noncompliant: the value does not change short shortShift2 = (short)(s << 33); // Noncompliant: this is equivalent to shifting by 1 int i = 1; int intShift = i << 33; // Noncompliant: this is equivalent to shifting by 1 long lg = 1; long longShift1 = lg << 0; // Noncompliant: the value does not change long longShift2 = lg << 65; // Noncompliant: this is equivalent to shifting by 1
short s = 1; short shortShift1 = s; short shortShift2 = (short)(s << 1); int i = 1; var intShift = i << 1; long lg = 1; var longShift1 = lg; var longShift2 = lg << 1;
When division is performed on ints, the result will always be an int. You can assign that result to a
double, float or decimal with automatic type conversion, but having started as an int, the result
will likely not be what you expect. If the result of int division is assigned to a floating-point variable, precision will have been lost
before the assignment. Instead, at least one operand should be cast or promoted to the final type before the operation takes place.
static void Main()
{
decimal dec = 3/2; // Noncompliant
Method(3/2); // Noncompliant
}
static void Method(float f) { }
static void Main()
{
decimal dec = (decimal)3/2;
Method(3.0F/2);
}
static void Method(float f) { }
To ensure proper testing, it is important to include test cases in a test class. If a test class does not have any test cases, it can give the wrong impression that the class being tested has been thoroughly tested, when in reality, it has not.
This rule will raise an issue when any of these conditions are met:
NUnit, a class is marked with TestFixture but does not contain any method marked with Test,
TestCase, TestCaseSource, or Theory.MSTest, a class is marked with TestClass but does not contain any method marked with TestMethod or
DataTestMethod.It does not apply to xUnit since xUnit does not require a test
class attribute.
There are scenarios where not having any test cases within a test class is perfectly acceptable and not seen as a problem.
To facilitate the creation of common test cases, test logic, or test infrastructure, it is advisable to use a base class.
Additionally, in both NUnit and MSTest, abstract classes that are annotated with their respective attributes
(TestFixture in NUnit and TestClass in MSTest) are automatically ignored.
Therefore, there is no need to raise an issue in this particular scenario.
More information here:
A base class containing one or more test cases to provide generic test cases is also considered a compliant scenario.
AssemblyInitialize or AssemblyCleanup methodsThis particular exception scenario only applies to the MSTest test framework.
The AssemblyInitialize and AssemblyCleanup attributes are used to annotate methods that are executed only once at the
beginning and at the end of a test run. These attributes can only be applied once per assembly.
It is logical to have a dedicated class for these methods, and this scenario is also considered compliant.
Furthermore, it is important to note that the test engine will execute a method annotated with either the AssemblyInitialize or
AssemblyCleanup attribute only if that method is part of a class annotated with the TestClass attribute.
More information here:
To fix this issue in MSTest, it is important that all test classes annotated with the [TestClass] attribute contain at
least one test case.
To achieve this, at least one method needs to be annotated with one of the following method attributes:
TestMethodDataTestMethod
[TestClass]
public class SomeOtherClassTest { } // Noncompliant: no test
[TestClass]
public class SomeOtherClassTest
{
[TestMethod]
public void SomeMethodShouldReturnTrue() { }
}
To fix this issue in NUnit, it is important that all test classes annotated with the [TestFixture] attribute contain at
least one test case.
To achieve this, at least one method needs to be annotated with one of the following method attributes:
TestTestCaseTestCaseSourceTheory
[TestFixture]
public class SomeClassTest { } // Noncompliant: no test
[TestFixture]
public class SomeClassTest
{
[Test]
public void SomeMethodShouldReturnTrue() { }
}
NUnit documentation
MSTest documentationxUnit to other frameworks - AttributesHaving an infinite loop or recursion will lead to a program failure or a program never finishing the execution.
public int Sum()
{
var i = 0;
var result = 0;
while (true) // Noncompliant: the program will never stop
{
result += i;
i++;
}
return result;
}
This can happen in multiple scenarios.
while and for loops with no break or return statements that have exit conditions which are
always false will be indefinitely executed.
goto statement with nothing that stops it from being executed over and over again will prevent the program from the completion.
When a recursive method call chain lacks an exit condition, the call stack will reach its limit and the program will crash due to a StackOverflowException.
int Pow(int num, int exponent)
{
return num * Pow(num, exponent - 1); // Noncompliant: no condition under which Pow isn't re-called
}
In this example, Pow will keep calling Pow with exponent - 1 forever, until the program crashes with a
StackOverflowException.
Recursion provides some benefits.
However, it has disadvantages as well.
The program’s logic should incorporate a mechanism to break out of the control flow loop. Here are some examples.
false
public int Sum()
{
var i = 0;
var result = 0;
while (true) // Noncompliant: the program will never stop
{
result += i;
i++;
}
return result;
}
public int Sum()
{
var i = 0;
var result = 0;
while (result < 1000)
{
result += i;
i++;
}
return result;
}
goto statements. Instead, you can use a loop statement or explicit
recursion.
public int Sum()
{
var result = 0;
var i = 0;
iteration:
// Noncompliant: program never ends
result += i;
i++;
goto iteration;
return result;
}
public int Sum()
{
var i = 0;
var result = 0;
while (result < 1000)
{
result += i;
i++;
}
return result;
}
int Pow(int num, int exponent)
{
return num * Pow(num, exponent - 1); // Noncompliant: no condition under which Pow isn't re-called
}
int Pow(int num, int exponent)
{
if (exponent > 1) // recursion is now conditional and stoppable
{
num = num * Pow(num, exponent - 1);
}
return num;
}
When the modulus of a negative number is calculated, the result will either be negative or zero. Thus, comparing the modulus of a variable for equality with a positive number (or a negative one) could result in unexpected results.
public bool IsOdd(int x)
{
return x % 2 == 1; // Noncompliant; if x is an odd negative, x % 2 == -1
}
public bool IsOdd(int x)
{
return x % 2 != 0;
}
or
public bool IsOdd(uint x)
{
return x % 2 == 1;
}
================================================
FILE: analyzers/rspec/cs/S2197.json
================================================
{
"title": "Modulus results should not be checked for direct equality",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"suspicious"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2197",
"sqKey": "S2197",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2198.html
================================================
Certain mathematical comparisons will always return the same value, and should not be performed.
Specifically, the following comparisons will return either always true or always false depending on the kind of
comparison:
char with a numeric constant that is outside of the range of charfloat with a numeric constant that is outside of the range of floatlong with a numeric constant that is outside of the range of longulong with a numeric constant that is outside of the range of ulong
float f = 42.0f;
if (f <= double.MaxValue) { } // Noncompliant: always true
if (f > double.MaxValue) { } // Noncompliant: always false
When you do not use the return value of a method with no side effects, it indicates that something is wrong. Either this method is unnecessary, or the source code does not behave as expected and could lead to code defects. For example, there are methods, such as DateTime.AddYears, that don’t change the value of the input object, but instead, they return a new object whose value is the result of this operation, and as a result that you will have unexpected effects if you do not use the return value.
This rule raises an issue when the results of the following methods are ignored:
Pure methodsSpecial cases:
string.Intern has a side effect, ignoring
its return value is still suspicious as it is the only reference ensured to point to the intern pool.
data.All(x =>
{
x.Property = "foo";
return true;
});
Such code should be rewritten as a loop because Enumerable.All<TSource> method should be used to
determine if all elements satisfy a condition and not to change their state.
This rule doesn’t report issues on invocations with out or ref arguments.
data.Where(x => x > 5).Select(x => x * x); // Noncompliant
"this string".Equals("other string"); // Noncompliant
data.All(x => // Noncompliant
{
x.Property = "foo";
return true;
});
var res = data.Where(x => x > 5).Select(x => x * x);
var isEqual = "this string".Equals("other string");
foreach (var x in data)
{
x.Property = "foo";
}
PureAttribute
Classout
parameter modifierref keywordString.Intern(String) MethodTo check the type of an object there are several options:
expr is SomeType or expr.GetType() == typeof(SomeType) if the type is known at compile time,typeInstance.IsInstanceOfType(expr) if the type is calculated during runtime.If runtime calculated Types need to be compared:
typeInstance1.IsAssignableFrom(typeInstance2).Depending on whether the type is returned by a GetType() or typeof() call, the IsAssignableFrom() and
IsInstanceOfType() might be simplified. Similarly, if the type is sealed, the type comparison with == can be
converted to an is call. Simplifying the calls also make null checking unnecessary because both is and
IsInstanceOfType performs it already.
Finally, utilizing the most concise language constructs for type checking makes the code more readable, so
expr as T != null checks should be simplified to expr is T, andexpr is T should be converted to expr != null, when expr is of type T.
class Fruit { }
sealed class Apple : Fruit { }
class Program
{
static void Main()
{
var apple = new Apple();
var b = apple != null && apple.GetType() == typeof (Apple); // Noncompliant
b = typeof(Apple).IsInstanceOfType(apple); // Noncompliant
if (apple != null)
{
b = typeof(Apple).IsAssignableFrom(apple.GetType()); // Noncompliant
}
var appleType = typeof (Apple);
if (apple != null)
{
b = appleType.IsAssignableFrom(apple.GetType()); // Noncompliant
}
Fruit f = apple;
if (f as Apple != null) // Noncompliant
{
}
if (apple is Apple) // Noncompliant
{
}
}
}
class Fruit { }
sealed class Apple : Fruit { }
class Program
{
static void Main()
{
var apple = new Apple();
var b = apple is Apple;
b = apple is Apple;
b = apple is Apple;
var appleType = typeof(Apple);
b = appleType.IsInstanceOfType(apple);
Fruit f = apple;
if (f is Apple)
{
}
if (apple != null)
{
}
}
}
Calling GetType on an object of Nullable<T> type returns the underlying generic type parameter T, thus
a comparison with typeof(Nullable<T>) can’t be simplified to use the is operator, which doesn’t make difference
between T and T?.
int? i = 42; bool condition = i.GetType() == typeof(int?); // false; condition = i is int?; // true
No issue is reported on the following expressions:
expr is T when either operand of the is operator is a value type. In that case CS0183 or CS0184 reportsexpr is object, as this is a common and efficient pattern to do null checksCatching System.Exception seems like an efficient way to handle multiple possible exceptions. Unfortunately, it traps all exception
types, including the ones that were not intended to be caught. To prevent any misunderstandings, exception filters should be used. Alternatively, each
exception type should be in a separate catch block.
try
{
// do something that might throw a FileNotFoundException or IOException
}
catch (Exception e) // Noncompliant
{
// log exception ...
}
try
{
// do something
}
catch (Exception e) when (e is FileNotFoundException or IOException)
{
// do something
}
The final option is to catch System.Exception and throw it in the last statement in the catch block. This is
the least-preferred option, as it is an old-style code, which also suffers from performance penalties compared to exception filters.
try
{
// do something
}
catch (Exception e)
{
if (e is FileNotFoundException or IOException)
{
// do something
}
else
{
throw;
}
}
To prevent potential deadlocks in an application, it is crucial to release any locks that are acquired within a method along all possible execution paths.
Failing to release locks properly can lead to potential deadlocks, where the lock might not be released, causing issues in the application.
This rule specifically focuses on tracking the following types from the System.Threading namespace:
An issue is reported when a lock is acquired within a method but not released on all paths.
If the lock is never released within the method, no issue is raised, assuming that the callers will handle the release.
To make sure that a lock is always released correctly, you can follow one of these two methods:
lock statement with your
lock object.try-finally statement and put the release of your lock object within the finally block.
class MyClass
{
private object obj = new object();
public void DoSomethingWithMonitor()
{
Monitor.Enter(obj); // Noncompliant: not all paths release the lock
if (IsInitialized())
{
// ...
Monitor.Exit(obj);
}
}
}
class MyClass
{
private ReaderWriterLockSlim lockObj = new ReaderWriterLockSlim();
public void DoSomethingWithReaderWriteLockSlim()
{
lockObj.EnterReadLock(); // Noncompliant: not all paths release the lock
if (IsInitialized())
{
// ...
lockObj.ExitReadLock();
}
}
}
class MyClass
{
private object obj = new object();
public void DoSomethingWithMonitor()
{
lock(obj) // Compliant: the lock will be released at the end of the lock block
{
if (IsInitialized())
{
// ...
}
}
}
}
class MyClass
{
private ReaderWriterLockSlim lockObj = new ReaderWriterLockSlim();
public void DoSomethingWithReaderWriteLockSlim()
{
lockObj.EnterReadLock(); // Compliant: the lock will be released in the finally block
try
{
if (IsInitialized())
{
// ...
}
}
finally
{
lockObj.ExitReadLock();
}
}
}
lock statementtry-finally statementUnlike instance fields, which can only be accessed by code having a hold on the instance, static fields can be accessed by any code
having visibility of the field and its type.
public class Math
{
public static double Pi = 3.14; // Noncompliant
}
// Somewhere else, where Math and Math.Pi are visible
var pi = Math.Pi; // Reading the value
Math.Pi = 3.1416; // Mutating the value
Another typical scenario of the use of a non-private mutable static field is the following:
public class Shape
{
public static Shape Empty = new EmptyShape(); // Noncompliant
private class EmptyShape : Shape
{
}
}
Non-private static fields that are neither const nor readonly, like the ones in the examples above, can lead
to errors and unpredictable behavior.
This can happen because:
class Counters
{
public static int ErrorCounter = 0;
}
class Program
{
public static void Thread1()
{
// ...
Counters.ErrorCounter = 0; // Error counter reset
// ...
}
public static void Thread2()
{
// ...
if (Counters.ErrorCounter > 0)
{
Trace.TraceError($"There are {Counters.ErrorCounter} errors"); // It may print "There are 0 errors"
}
// ...
}
}
lock or equivalent mechanisms. Improper
synchronization may lead to unexpected results.
class Counters
{
public static volatile int ErrorCounter;
}
class Program
{
public static void ImproperSynchronization()
{
Counters.ErrorCounter = 0;
Parallel.ForEach(Enumerable.Range(0, 1000), _ => Counters.ErrorCounter++); // Volatile is not enough
Console.WriteLine(Counters.ErrorCounter); // May print less than 1000
}
public static void ProperSynchronization()
{
Counters.ErrorCounter = 0;
Parallel.ForEach(Enumerable.Range(0, 1000), _ => Interlocked.Increment(ref Counters.ErrorCounter));
Console.WriteLine(Counters.ErrorCounter); // Always prints 1000
}
}
Publicly visible static fields should only be used to store shared data that does not change. To enforce this intent, these fields
should be marked readonly or converted to const.
public class Math
{
public const double Pi = 3.14;
}
public class Shape
{
public static readonly Shape Empty = new EmptyShape();
private class EmptyShape : Shape
{
}
}
Calling ToString() on an object should always return a
string. Thus, overriding the
ToString method should never return null, as it breaks the method’s implicit contract, and as a result the consumer’s
expectations.
public override string ToString ()
{
if (this.collection.Count == 0)
{
return null; // Noncompliant
}
else
{
// ...
}
}
A better alternative is to use the String.Empty built-in field.
public override string ToString ()
{
if (this.collection.Count == 0)
{
return string.Empty;
}
else
{
// ...
}
}
Calling a method with argument variables whose names match the method parameter names but in a different order can cause confusion. It could indicate a mistake in the arguments' order, leading to unexpected results.
public double Divide(int divisor, int dividend)
{
return divisor / dividend;
}
public void DoTheThing()
{
int divisor = 15;
int dividend = 5;
double result = Divide(dividend, divisor); // Noncompliant: arguments' order doesn't match their respective parameter names
// ...
}
However, matching the method parameters' order contributes to clearer and more readable code:
public double Divide(int divisor, int dividend)
{
return divisor / dividend;
}
public void DoTheThing()
{
int divisor = 15;
int dividend = 5;
double result = Divide(divisor, dividend); // Compliant
// ...
}
================================================
FILE: analyzers/rspec/cs/S2234.json
================================================
{
"title": "Arguments should be passed in the same order as the method parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2234",
"sqKey": "S2234",
"scope": "All",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/cs/S2245.html
================================================
PRNGs are algorithms that produce sequences of numbers that only approximate true randomness. While they are suitable for applications like simulations or modeling, they are not appropriate for security-sensitive contexts because their outputs can be predictable if the internal state is known.
In contrast, cryptographically secure pseudorandom number generators (CSPRNGs) are designed to be secure against prediction attacks. CSPRNGs use cryptographic algorithms to ensure that the generated sequences are not only random but also unpredictable, even if part of the sequence or the internal state becomes known. This unpredictability is crucial for security-related tasks such as generating encryption keys, tokens, or any other values that must remain confidential and resistant to guessing attacks.
For example, the use of non-cryptographic PRNGs has led to vulnerabilities such as:
When software generates predictable values in a context requiring unpredictability, it may be possible for an attacker to guess the next value that will be generated, and use this guess to impersonate another user or access sensitive information. Therefore, it is critical to use CSPRNGs in any security-sensitive application to ensure the robustness and security of the system.
As the System.Random class relies on a non-cryptographic pseudorandom number generator, it should not be used for security-critical
applications or for protecting sensitive data. In such context, the System.Cryptography.RandomNumberGenerator class which relies on a
CSPRNG should be used in place.
There is a risk if you answered yes to any of those questions.
var random = new Random(); // Sensitive use of Random byte[] data = new byte[16]; random.NextBytes(data); return BitConverter.ToString(data); // Check if this value is used for hashing or encryption
using System.Security.Cryptography; ... var randomGenerator = RandomNumberGenerator.Create(); byte[] data = new byte[16]; randomGenerator.GetBytes(data); return BitConverter.ToString(data);
A for loop with a counter that moves in the wrong direction, away from the stop condition, is not an infinite loop. Because of wraparound,
the loop will eventually reach its stop condition, but in doing so, it will probably run more times than anticipated, potentially causing unexpected
behavior.
If your stop condition indicates a maximum value, the iterator should increase towards it. Conversely, if your stop condition indicates a minimum value, the iterator should decrease towards it.
for (int i = 0; i < maximum; i--) // Noncompliant: runs until it underflows to int.MaxValue
{
// ...
}
for (int i = maximum; i >= maximum; i++) // Noncompliant: runs until it overflows to int.MinValue
{
// ...
}
for (int i = 0; i < maximum; i++) // Compliant: Increment towards the maximum value
{
}
for (int i = maximum; i >= 0; i--) // Compliant: Decrement towards the minimum value
{
// ...
}
A for loop
is a fundamental programming construct used to execute a block of code repeatedly. However, if the loop’s condition is false before the first
iteration, the loop will never execute.
for (int i = 0; i < 0; i++) // Noncompliant: the condition is always false, the loop will never execute
{
// ...
}
Rewrite the loop to ensure the condition evaluates to true at least once.
for (int i = 0; i < 10; i++) // Compliant: the condition is true at least once, the loop will execute
{
// ...
}
This bug has the potential to cause unexpected outcomes as the loop might contain critical code that needs to be executed.
for
statementThe use of a non-standard algorithm is dangerous because a determined attacker may be able to break the algorithm and compromise whatever data has
been protected. Standard algorithms like AES, RSA, SHA, … should be used instead.
This rule tracks custom implementation of these types from System.Security.Cryptography namespace:
AsymmetricAlgorithmAsymmetricKeyExchangeDeformatterAsymmetricKeyExchangeFormatterAsymmetricSignatureDeformatterAsymmetricSignatureFormatterDeriveBytesHashAlgorithmICryptoTransformSymmetricAlgorithm
public class CustomHash : HashAlgorithm // Noncompliant
{
private byte[] result;
public override void Initialize() => result = null;
protected override byte[] HashFinal() => result;
protected override void HashCore(byte[] array, int ibStart, int cbSize) =>
result ??= array.Take(8).ToArray();
}
SHA256 mySHA256 = SHA256.Create()
Accessing a null value will always throw a NullReferenceException most likely causing an abrupt program termination.
Such termination might expose sensitive information that a malicious third party could exploit to, for instance, bypass security measures.
In the following cases, the rule does not raise:
Calls to extension methods can still operate on null values.
using System;
using System.Text.RegularExpressions;
public static class Program
{
public static string RemoveVowels(this string value)
{
if (value == null)
{
return null;
}
return Regex.Replace(value, "[aeoui]*","", RegexOptions.IgnoreCase);
}
public static void Main()
{
Console.WriteLine(((string?)null).RemoveVowels()); // Compliant: 'RemoveVowels' is an extension method
}
}
Unreachable code is not executed, thus null values will never be accessed.
public void Method()
{
object o = null;
if (false)
{
o.ToString(); // Compliant: code is unreachable
}
}
Nullable analysis attributes enable
the developer to annotate methods with information about the null-state of its arguments. Thus, potential null values validated by one of
the following attributes will not raise:
It is important to note those attributes are only available starting .NET Core 3. As a workaround, it is possible to define those attributes manually in a custom class:
using System;
public sealed class NotNullAttribute : Attribute { } // The alternative name 'ValidatedNotNullAttribute' is also supported
public static class Guard
{
public static void NotNull<T>([NotNull] T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}
}
public static class Utils
{
public static string Normalize(string value)
{
Guard.NotNull(value, nameof(value)); // Will throw if value is null
return value.ToUpper(); // Compliant: value is known to be not null here.
}
}
A value validated with Debug.Assert to not be
null is safe to access.
using System.Diagnostics;
public void Method(object myObject)
{
Debug.Assert(myObject != null);
myObject.ToString(); // Compliant: 'myObject' is known to be not null here.
}
Like with null-analysis-attribute, potential null values validated by one of the following IDE-specific attributes will not raise
using System;
using JetBrains.Annotations;
public class Utils
{
[TerminatesProgram]
public void TerminateProgram()
{
Environment.FailFast("A catastrophic failure has occurred.")
}
public void TerminatesProgramIsRespected()
{
object myObject = null;
TerminateProgram();
myObject.ToString(); // Compliant: unreachable
}
}
Expression marked with the null forgiving operator
public void Method()
{
object o = null;
o!.ToString(); // Compliant: the null forgiving operator suppresses the nullable warning
}
To fix the issue, the access of the null value needs to be prevented by either:
nullThe variable myObject is equal to null, meaning it has no value:
public void Method()
{
object myObject = null;
Console.WriteLine(o.ToString()); // Noncompliant: 'myObject' is always null
}
The parameter input might be null as suggested by the if condition:
public void Method(object input)
{
if (input is null)
{
// ...
}
Console.WriteLine(input.ToString()); // Noncompliant: the if condition suggests 'input' might be null
}
Ensuring the variable myObject has a value resolves the issue:
public void Method()
{
var myObject = new object();
Console.WriteLine(myObject.ToString()); // Compliant: 'myObject' is not null
}
Preventing the non-compliant code to be executed by returning early:
public void Method(object input)
{
if (input is null)
{
return;
}
Console.WriteLine(input.ToString()); // Compliant: if 'input' is null, this is unreachable
}
Composite format strings in C# are evaluated at runtime, which means they are not verified by the compiler. Introducing an ill-formed format item, or indexing mismatch can lead to unexpected behaviors or runtime errors. The purpose of this rule is to perform static validation on composite format strings used in various string formatting functions to ensure their correct usage. This rule validates the proper behavior of composite formats when invoking the following methods:
String.FormatStringBuilder.AppendFormatConsole.WriteConsole.WriteLineTextWriter.WriteTextWriter.WriteLineDebug.WriteLine(String, Object[])Trace.TraceError(String, Object[])Trace.TraceInformation(String, Object[])Trace.TraceWarning(String, Object[])TraceSource.TraceInformation(String, Object[])
s = string.Format("[0}", arg0); // Noncompliant: square bracket '[' instead of curly bracket '{'
s = string.Format("{{0}", arg0); // Noncompliant: double starting curly brackets '{{'
s = string.Format("{0}}", arg0); // Noncompliant: double ending curly brackets '}}'
s = string.Format("{-1}", arg0); // Noncompliant: invalid index for the format item, must be >= 0
s = string.Format("{0} {1}", arg0); // Noncompliant: two format items in the string but only one argument provided
s = string.Format("{0}", 42); // Compliant
s = string.Format("{0,10}", 42); // Compliant
s = string.Format("{0,-10}", 42); // Compliant
s = string.Format("{0:0000}", 42); // Compliant
s = string.Format("{2}-{0}-{1}", 1, 2, 3); // Compliant
s = string.Format("no format"); // Compliant
The rule does not perform any checks on the format specifier, if present (defined after the :). Moreover, no issues are raised in the
following cases:
const.
var pattern = "{0} {1} {2}";
var res = string.Format(pattern, 1, 2); // Compliant, non-constant string are not recognized
var array = new int[] {};
var res = string.Format("{0} {1}", array); // Compliant the rule does not check the size of the array
Field-like events are events that do
not have explicit add and remove accessors.
public event EventHandler MyEvent; // No add and remove accessors
The compiler generates a private delegate field to back the event, as well as generating the implicit add
and remove accessors.
When a virtual field-like event is overridden by another field-like event, the behavior of the C# compiler
is to generate a new private delegate field in the derived class, separate from the parent’s field. This results in multiple
and separate events being created, which is rarely what’s actually intended.
abstract class Car
{
public virtual event EventHandler OnRefuel; // Noncompliant
public void Refuel()
{
// This OnRefuel will always be null
if (OnRefuel != null)
{
OnRefuel(this, EventArgs.Empty);
}
}
}
class R2 : Car
{
public override event EventHandler OnRefuel;
}
class Program
{
static void Main(string[] args)
{
var r2 = new R2();
r2.OnRefuel += (o, a) =>
{
Console.WriteLine("This event will be called");
};
r2.Refuel();
}
}
To prevent this, remove the virtual designation from the parent class event.
abstract class Car
{
public event EventHandler OnRefuel; // Compliant
public void Refuel()
{
if (OnRefuel != null)
{
OnRefuel(this, EventArgs.Empty);
}
}
}
class R2 : Car
{
}
class Program
{
static void Main(string[] args)
{
var r2 = new R2();
r2.OnRefuel += (o, a) =>
{
Console.WriteLine("This event will be called");
};
r2.Refuel();
}
}
Enumerable.Sum() always executes addition in a
checked context, so an OverflowException will be
thrown if the value exceeds MaxValue, even if an unchecked context was specified. Therefore, using this method inside an
unchecked context will only make the code more confusing, since the behavior will still be checked.
This rule raises an issue when an unchecked context is specified for a Sum on integer types.
When the Sum call is inside a try-catch block,
no issues are reported, since the exception is properly handled.
void Add(List<int> list)
{
unchecked
{
try
{
int total = list.Sum();
}
catch (System.OverflowException e)
{
// Exception handling
}
}
}
Remove the unchecked operator/statement, and optionally add some exception handling for the OverflowException.
void Add(List<int> list)
{
int total1 = unchecked(list.Sum()); // Noncompliant
unchecked
{
int total2 = list.Sum(); // Noncompliant
}
}
void Add(List<int> list)
{
int total1 = list.Sum();
try
{
int total2 = list.Sum();
}
catch (System.OverflowException e)
{
// Exception handling
}
}
Enumerable.Sum MethodEnumerable.Sum implementationchecked and
unchecked statementschecked and unchecked operatorsOverflowException ClassTrivial properties, which include no logic but setting and getting a backing field should be converted to auto-implemented properties, yielding cleaner and more readable code.
public class Car
{
private string _make;
public string Make // Noncompliant
{
get { return _make; }
set { _make = value; }
}
}
public class Car
{
public string Make { get; set; }
}
================================================
FILE: analyzers/rspec/cs/S2292.json
================================================
{
"title": "Trivial properties should be auto-implemented",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2292",
"sqKey": "S2292",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2302.html
================================================
Because parameter names could be changed during refactoring, they should not be spelled out literally in strings. Instead, use
nameof(), and the string that’s output will always be correct.
This rule raises an issue when a string in the throw statement contains the name of one of the method parameters.
void DoSomething(int someParameter, string anotherParam)
{
if (someParameter < 0)
{
throw new ArgumentException("Bad argument", "someParameter"); // Noncompliant
}
if (anotherParam == null)
{
throw new Exception("anotherParam should not be null"); // Noncompliant
}
}
void DoSomething(int someParameter)
{
if (someParameter < 0)
{
throw new ArgumentException("Bad argument", nameof(someParameter));
}
if (anotherParam == null)
{
throw new Exception($"{nameof(anotherParam)} should not be null");
}
}
throw statement string, the rule will raise an issue only if the
parameter name is at least 5 characters long. This is to avoid false positives.Since C# 5.0, async and await are contextual keywords. Contextual keywords
do have a particular meaning in some contexts, but are not reserved and therefore can be used as variable names.
int await = 42; // Noncompliant, but compiles int async = 42; // Noncompliant, but compiles
Keywords, on the other hand, are always reserved and therefore are not valid variable names.
int abstract = 42; // Error CS1585: Member modifier 'abstract' must precede the member type and name int foreach = 42; // Error CS1519: Invalid token 'foreach' in class, struct, or interface member declaration
To avoid any confusion, it is best to not use async and await as identifiers.
int someVariableName = 42; int someOtherVariableName = 42;
Methods and properties that don’t access instance data should be marked as static for the following reasons:
Methods with the following names are excluded because they can’t be made static:
Event handler methods part of a Windows Forms or Windows Presentation Foundation class are excluded because they can’t be made
static.
public class Utilities
{
public int MagicNum // Noncompliant - only returns a constant value
{
get
{
return 42;
}
}
private static string magicWord = "please";
public string MagicWord // Noncompliant - only accesses a static field
{
get
{
return magicWord;
}
set
{
magicWord = value;
}
}
public int Sum(int a, int b) // Noncompliant - doesn't access instance data, only the method parameters
{
return a + b;
}
}
public class Utilities
{
public static int MagicNum
{
get
{
return 42;
}
}
private static string magicWord = "please";
public static string MagicWord
{
get
{
return magicWord;
}
set
{
magicWord = value;
}
}
public static int Sum(int a, int b)
{
return a + b;
}
}
Type parameters that aren’t used are dead code, which can only distract and possibly confuse developers during maintenance. Therefore, unused type parameters should be removed.
public class MoreMath<T> // Noncompliant; <T> is ignored
{
public int Add<T>(int a, int b) // Noncompliant; <T> is ignored
{
return a + b;
}
}
public class MoreMath
{
public int Add (int a, int b)
{
return a + b;
}
}
================================================
FILE: analyzers/rspec/cs/S2326.json
================================================
{
"title": "Unused type parameters should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2326",
"sqKey": "S2326",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2327.html
================================================
When multiple, adjacent try statements have duplicate catch and/or finally blocks, they should be merged to
consolidate the catch/finally logic for cleaner, more readable code. Note that this applies even when there is intervening code outside
any try block.
try
{
DoTheFirstThing(a, b);
}
catch (InvalidOperationException ex)
{
HandleException(ex);
}
DoSomeOtherStuff();
try // Noncompliant; catch is identical to previous
{
DoTheSecondThing();
}
catch (InvalidOperationException ex)
{
HandleException(ex);
}
try // Compliant; catch handles exception differently
{
DoTheThirdThing(a);
}
catch (InvalidOperationException ex)
{
LogAndDie(ex);
}
try
{
DoTheFirstThing(a, b);
DoSomeOtherStuff();
DoTheSecondThing();
}
catch (InvalidOperationException ex)
{
HandleException(ex);
}
try // Compliant; catch handles exception differently
{
DoTheThirdThing(a);
}
catch (InvalidOperationException ex)
{
LogAndDie(ex);
}
================================================
FILE: analyzers/rspec/cs/S2327.json
================================================
{
"title": "\"try\" statements with identical \"catch\" and\/or \"finally\" blocks should be merged",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "DISTINCT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2327",
"sqKey": "S2327",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2328.html
================================================
GetHashCode is used to file an object in a Dictionary or Hashtable. If GetHashCode uses
non-readonly fields and those fields change after the object is stored, the object immediately becomes mis-filed in the
Hashtable. Any subsequent test to see if the object is in the Hashtable will return a false negative.
This rule does not raise if the type implementing GetHashCode is a value type, for example a struct or a record
struct, since when a value type is stored in a Dictionary or Hashtable, a copy of the value is stored, not a
reference to the value.
public class Person
{
public int age;
public string name;
public override int GetHashCode()
{
int hash = 12;
hash += this.age.GetHashCode(); // Noncompliant
hash += this.name.GetHashCode(); // Noncompliant
return hash;
}
public class Person
{
public readonly DateTime birthday;
public string name;
public override int GetHashCode()
{
int hash = 12;
hash += this.birthday.GetHashCode();
return hash;
}
================================================
FILE: analyzers/rspec/cs/S2328.json
================================================
{
"title": "\"GetHashCode\" should not reference mutable fields",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2328",
"sqKey": "S2328",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S2330.html
================================================
Array covariance is the principle that if an implicit or explicit reference conversion exits from type A to B, then the
same conversion exists from the array type A[] to B[].
While this array conversion can be useful in readonly situations to pass instances of A[] where B[] is expected, it must
be used with care, since assigning an instance of B into an array of A will cause an ArrayTypeMismatchException
to be thrown at runtime.
abstract class Fruit { }
class Apple : Fruit { }
class Orange : Fruit { }
class Program
{
static void Main(string[] args)
{
Fruit[] fruits = new Apple[1]; // Noncompliant - array covariance is used
FillWithOranges(fruits);
}
// Just looking at the code doesn't reveal anything suspicious
static void FillWithOranges(Fruit[] fruits)
{
for (int i = 0; i < fruits.Length; i++)
{
fruits[i] = new Orange(); // Will throw an ArrayTypeMismatchException
}
}
}
abstract class Fruit { }
class Apple : Fruit { }
class Orange : Fruit { }
class Program
{
static void Main(string[] args)
{
Orange[] fruits = new Orange[1]; // Compliant
FillWithOranges(fruits);
}
static void FillWithOranges(Orange[] fruits)
{
for (int i = 0; i < fruits.Length; i++)
{
fruits[i] = new Orange();
}
}
}
================================================
FILE: analyzers/rspec/cs/S2330.json
================================================
{
"title": "Array covariance should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2330",
"sqKey": "S2330",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2333.html
================================================
Unnecessary keywords simply clutter the code and should be removed. Specifically:
partial on type declarations that are completely defined in one placesealed on members of sealed classesunsafe method or block inside construct already marked with unsafe, or when there are no unsafe
constructs in the blockchecked and unchecked blocks with no integral-type arithmetic operations
public partial class MyClass // Noncompliant
{
public virtual void Method()
{
}
}
public sealed class MyOtherClass : MyClass
{
public sealed override void Method() // Noncompliant
{
}
}
public class MyClass
{
public virtual void Method()
{
}
}
public sealed class MyOtherClass : MyClass
{
public override void Method()
{
}
}
================================================
FILE: analyzers/rspec/cs/S2333.json
================================================
{
"title": "Redundant modifiers should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused",
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2333",
"sqKey": "S2333",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S2339.html
================================================
Constant members are copied at compile time to the call sites, instead of being fetched at runtime.
As an example, say you have a library with a constant Version member set to 1.0, and a client application linked to it.
This library is then updated and Version is set to 2.0. Unfortunately, even after the old DLL is replaced by the new one,
Version will still be 1.0 for the client application. In order to see 2.0, the client application would need to
be rebuilt against the new version of the library.
This means that you should use constants to hold values that by definition will never change, such as Zero. In practice, those cases
are uncommon, and therefore it is generally better to avoid constant members.
This rule only reports issues on public constant fields, which can be reached from outside the defining assembly.
public class Foo
{
public const double Version = 1.0; // Noncompliant
}
public class Foo
{
public static double Version
{
get { return 1.0; }
}
}
================================================
FILE: analyzers/rspec/cs/S2339.json
================================================
{
"title": "Public constant members should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2339",
"sqKey": "S2339",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2342.html
================================================
Shared naming conventions allow teams to collaborate efficiently. This rule checks that all enum names match a provided regular
expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression for non-flags enums: ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
public enum foo // Noncompliant
{
FooValue = 0
}
With the default regular expression for flags enums: ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?s$
[Flags]
public enum Option // Noncompliant
{
None = 0,
Option1 = 1,
Option2 = 2
}
public enum Foo
{
FooValue = 0
}
[Flags]
public enum Options
{
None = 0,
Option1 = 1,
Option2 = 2
}
================================================
FILE: analyzers/rspec/cs/S2342.json
================================================
{
"title": "Enumeration types should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2342",
"sqKey": "S2342",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2344.html
================================================
The information that an enumeration type is actually an enumeration or a set of flags should not be duplicated in its name.
enum FooFlags // Noncompliant
{
Foo = 1
Bar = 2
Baz = 4
}
enum Foo
{
Foo = 1
Bar = 2
Baz = 4
}
================================================
FILE: analyzers/rspec/cs/S2344.json
================================================
{
"title": "Enumeration type names should not have \"Flags\" or \"Enum\" suffixes",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2344",
"sqKey": "S2344",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2345.html
================================================
When you annotate an Enum with the Flags attribute, you must not rely on the values that are automatically
set by the language to the Enum members, but you should define the enumeration constants in powers of two (1, 2, 4, 8, and so on).
Automatic value initialization will set the first member to zero and increment the value by one for each subsequent member. As a result, you won’t be
able to use the enum members with bitwise operators.
The default initialization of 0, 1, 2, 3, 4, … matches 0, 1, 2, 4, 8 … in the first three values, so no issue is
reported if the first three members of the enumeration are not initialized.
Define enumeration constants in powers of two, that is, 1, 2, 4, 8, and so on.
var bananaAndStrawberry = FruitType.Banana | FruitType.Strawberry;
Console.WriteLine(bananaAndStrawberry.ToString()); // Will display only "Strawberry"
[Flags]
enum FruitType // Noncompliant
{
None,
Banana,
Orange,
Strawberry
}
var bananaAndStrawberry = FruitType.Banana | FruitType.Strawberry;
Console.WriteLine(bananaAndStrawberry.ToString()); // Will display "Banana, Strawberry"
[Flags]
enum FruitType
{
None = 0,
Banana = 1,
Orange = 2,
Strawberry = 4
}
An enumeration can be decorated with the FlagsAttribute to indicate that it can be used as a bit field: a set of flags, that can be independently set and reset.
For example, the following definition of the day of the week:
[Flags]
enum Days
{
Monday = 1, // 0b00000001
Tuesday = 2, // 0b00000010
Wednesday = 4, // 0b00000100
Thursday = 8, // 0b00001000
Friday = 16, // 0b00010000
Saturday = 32, // 0b00100000
Sunday = 64 // 0b01000000
}
allows to define special set of days, such as WeekDays and Weekend using the | operator:
[Flags]
enum Days
{
// ...
None = 0, // 0b00000000
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday, // 0b00011111
Weekend = Saturday | Sunday, // 0b01100000
All = Weekdays | Weekend // 0b01111111
}
These can be used to write more expressive conditions, taking advantage of bitwise operators and Enum.HasFlag:
var someDays = Days.Wednesday | Days.Weekend; // 0b01100100 someDays.HasFlag(Days.Wednesday); // someDays contains Wednesday var mondayAndWednesday = Days.Monday | Days.Wednesday; someDays.HasFlag(mondayAndWednesday); // someDays contains Monday and Wednesday someDays.HasFlag(Days.Monday) || someDays.HasFlag(Days.Wednesday); // someDays contains Monday or Wednesday someDays & Days.Weekend != Days.None; // someDays overlaps with the weekend someDays & Days.Weekdays == Days.Weekdays; // someDays is only made of weekdays
Consistent use of None in flag enumerations indicates that all flag values are cleared. The value 0 should not be used to indicate any
other state since there is no way to check that the bit 0 is set.
[Flags]
enum Days
{
Monday = 0, // 0 is used to indicate Monday
Tuesday = 1,
Wednesday = 2,
Thursday = 4,
Friday = 8,
Saturday = 16,
Sunday = 32,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday,
Weekend = Saturday | Sunday,
All = Weekdays | Weekend
}
var someDays = Days.Wednesday | Days.Thursday;
someDays & Days.Tuesday == Days.Tuesday // False, because someDays doesn't contains Tuesday
someDays & Days.Monday == Days.Monday // True, even though someDays doesn't contains Monday!
someDays.HasFlag(Days.Monday) // Same issue as above
[Flags]
enum FruitType
{
Void = 0, // Non-Compliant
Banana = 1,
Orange = 2,
Strawberry = 4
}
[Flags]
enum FruitType
{
None = 0, // Compliant
Banana = 1,
Orange = 2,
Strawberry = 4
}
Fields should not be part of an API, and therefore should always be private. Indeed, they cannot be added to an interface for instance, and validation cannot be added later on without breaking backward compatibility. Instead, developers should encapsulate their fields into properties. Explicit property getters and setters can be introduced for validation purposes or to smooth the transition to a newer system.
public class Foo
{
public int MagicNumber = 42;
}
public class Foo
{
public int MagicNumber
{
get { return 42; }
}
}
or
public class Foo
{
private int MagicNumber = 42;
}
structs are ignored, as are static and const fields in classes.
Further, an issue is only raised when the real accessibility is public, taking into account the class accessibility.
The overloading mechanism should be used in place of optional parameters for several reasons:
void Notify(string company, string office = "QJZ") // Noncompliant
{
}
void Notify(string company)
{
Notify(company, "QJZ");
}
void Notify(string company, string office)
{
}
The rule ignores non externally visible methods.
================================================ FILE: analyzers/rspec/cs/S2360.json ================================================ { "title": "Optional parameters should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "pitfall" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-2360", "sqKey": "S2360", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S2365.html ================================================Most developers expect property access to be as efficient as field access. However, if a property returns a copy of an array or collection, it will be much slower than a simple field access, contrary to the caller’s likely expectations. Therefore, such properties should be refactored into methods so that callers are not surprised by the unexpectedly poor performance.
This rule tracks calls to the following methods inside properties:
private List<string> foo = new List<string> { "a", "b", "c" };
private string[] bar = new string[] { "a", "b", "c" };
public IEnumerable<string> Foo => foo.ToList(); // Noncompliant: collection foo is copied
public IEnumerable<string> Bar => (string[])bar.Clone(); // Noncompliant: array bar is copied
private List<string> foo = new List<string> { "a", "b", "c" };
private string[] bar = new string[] { "a", "b", "c" };
public IEnumerable<string> GetFoo() => foo.ToList();
public IEnumerable<string> GetBar() => (string[])bar.Clone();
Using multidimensional and jagged arrays as method parameters in C# can be challenging for developers.
When these methods are exposed to external users, it requires advanced language knowledge for effective usage.
Determining the appropriate data to pass to these parameters may not be intuitive.
public class Program
{
public void WriteMatrix(int[][] matrix) // Noncompliant: data type is not intuitive
{
// ...
}
}
In this example, it cannot be inferred easily what the matrix should look like. Is it a 2x2 Matrix or even a triangular Matrix?
Using a collection, data structure, or class that provides a more suitable representation of the required data is recommended instead of a multidimensional array or jagged array to enhance code readability.
public class Matrix2x2
{
// ...
}
public class Program
{
public void WriteMatrix(Matrix2x2 matrix) // Compliant: data type is intuitive
{
// ...
}
}
As a result, avoiding exposing such methods to external users is recommended.
However, using multidimensional and jagged array method parameters internally, such as in private or internal methods or
within internal classes, is compliant since they are not publicly exposed.
public class FirstClass
{
private void UpdateMatrix(int[][] matrix) // Compliant: method is private
{
// ...
}
}
internal class SecondClass
{
public void UpdateMatrix(int[][] matrix) // Compliant: class is internal
{
// ...
}
}
Property getters should be simple operations that are always safe to call. If exceptions need to be thrown, it is best to convert the property to a method.
It is valid to throw exceptions from indexed property getters and from property setters, which are not detected by this rule.
public int Foo
{
get
{
throw new Exception(); // Noncompliant
}
}
public int Foo
{
get
{
return 42;
}
}
No issue is raised when the thrown exception derives from or is of type NotImplementedException, NotSupportedException or
InvalidOperationException.
Properties with only setters are confusing and counterintuitive. Instead, a property getter should be added if possible, or the property should be replaced with a setter method.
class Program
{
public int Foo //Non-Compliant
{
set
{
// ... some code ...
}
}
}
class Program
{
private int foo;
public void SetFoo(int value)
{
// ... some code ...
foo = value;
}
}
or
class Program
{
public int Foo { get; set; } // Compliant
}
================================================
FILE: analyzers/rspec/cs/S2376.json
================================================
{
"title": "Write-only properties should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2376",
"sqKey": "S2376",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2386.html
================================================
public static mutable fields of classes which are accessed directly should be protected to the degree possible. This can be done by
reducing the accessibility of the field or by changing the return type to an immutable type.
This rule raises issues for public static fields with a type inheriting/implementing System.Array or
System.Collections.Generic.ICollection<T>.
public class A
{
public static string[] strings1 = {"first","second"}; // Noncompliant
public static List<String> strings3 = new List<String>(); // Noncompliant
}
public class A
{
protected static string[] strings1 = {"first","second"};
protected static List<String> strings3 = new List<String>();
}
No issue is reported:
System.Collections.ObjectModel.ReadOnlyCollection<T>System.Collections.ObjectModel.ReadOnlyDictionary<TKey, TValue>System.Collections.Frozen.FrozenDictionary<TKey, TValue>System.Collections.Frozen.FrozenSet<T>System.Collections.Immutable.ImmutableArray<T>System.Collections.Immutable.IImmutableDictionary<TKey, TValue>System.Collections.Immutable.IImmutableList<T>System.Collections.Immutable.IImmutableSet<T>System.Collections.Immutable.IImmutableStack<T>System.Collections.Immutable.IImmutableQueue<T>readonly and is initialized inline with an immutable type (i.e. inherits/implements one of the types in the
previous list) or null.This rule is deprecated, and will eventually be removed.
Having a variable with the same name in two unrelated classes is fine, but do the same thing within a class hierarchy and you’ll get confusion at best, chaos at worst.
public class Fruit
{
protected Season ripe;
protected Color flesh;
// ...
}
public class Raspberry : Fruit
{
private bool ripe; // Noncompliant
private static Color FLESH; // Noncompliant
}
public class Fruit
{
protected Season ripe;
protected Color flesh;
// ...
}
public class Raspberry : Fruit
{
private bool ripened;
private static Color FLESH_COLOR;
}
This rule ignores same-name fields that are static in both the parent and child classes. It also ignores private parent
class fields, but in all other such cases, the child class field should be renamed.
public class Fruit
{
private Season ripe;
// ...
}
public class Raspberry : Fruit
{
private Season ripe; // Compliant as parent field 'ripe' is anyway not visible from Raspberry
// ...
}
================================================
FILE: analyzers/rspec/cs/S2387.json
================================================
{
"title": "Child class fields should not shadow parent class fields",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
},
"status": "deprecated",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-2387",
"sqKey": "S2387",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2436.html
================================================
A method or class with too many type parameters has likely aggregated too many responsibilities and should be split.
With the default parameter value of 2:
<S, T, U, V> void foo() {} // Noncompliant; not really readable
<String, Integer, Object, String>foo(); // especially on invocations
================================================
FILE: analyzers/rspec/cs/S2436.json
================================================
{
"title": "Types and methods should not have too many generic parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2436",
"sqKey": "S2436",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2437.html
================================================
Certain bitwise operations are not needed and should not be performed because their results are predictable.
Specifically, using & -1 with any value always results in the original value.
That is because the binary representation of -1 on a integral numeric type supporting
negative numbers, such as int or long, is based on two’s
complement and made of all 1s: 0b111…111.
Performing & between a value and 0b111…111 means applying the & operator to each bit of the value
and the bit 1, resulting in a value equal to the provided one, bit by bit.
anyValue & -1 // Noncompliant anyValue // Compliant
Similarly, anyValue | 0 always results in anyValue, because the binary representation of 0 is always
0b000…000 and the | operator returns its first input when the second is 0.
anyValue | 0 // Noncompliant anyValue // Compliant
The same applies to anyValue ^ 0: the ^ operator returns 1 when its two input bits are different
(1 and 0 or 0 and 1) and returns 0 when its two input bits are the same (both
0 or both 1). When ^ is applied with 0, the result would be 1 if the other input is
1, because the two input bits are different, and 0 if the other input bit is 0, because the two input are the
same. That results in returning anyValue.
anyValue ^ 0 // Noncompliant anyValue // Compliant
Locking on a class field synchronizes not on the field itself, but on the object assigned to it. Thus, there are some good practices to follow to avoid problems related to thread synchronization.
readonly field makes it possible for the field’s value to change while a thread is in the code block, locked on
the old value. This allows another thread to lock on the new value and access the same block concurrently.
private Color color = new Color("red");
private void DoSomething()
{
// Synchronizing access via "color"
lock (color) // Noncompliant: lock is actually on object instance "red" referred to by the "color" field
{
//...
color = new Color("green"); // other threads now allowed into this block
// ...
}
}
private void DoSomething()
{
lock (new object()) // Noncompliant: every thread locks on a different new instance
{
// ...
}
}
private readonly string colorString = "red";
private void DoSomething()
{
lock (colorString) // Noncompliant: strings can be interned
{
// ...
}
}
private Color color = new Color("red");
private void DoSomething()
{
// Synchronizing access via "color"
lock (color) // Noncompliant: lock is actually on object instance "red" referred to by the "color" field
{
//...
color = new Color("green"); // other threads now allowed into this block
// ...
}
}
private Color color = new Color("red");
private readonly object lockObj = new object();
private void DoSomething()
{
lock (lockObj)
{
//...
color = new Color("green");
// ...
}
}
String.Intern(String) MethodNon-encoded control characters and whitespace characters are often injected in the source code because of a bad manipulation. They are either invisible or difficult to recognize, which can result in bugs when the string is not what the developer expects. If you actually need to use a control character use their encoded version:
This rule raises an issue when the following characters are seen in a string literal:
U+200B, U+200C, U+200D, U+2060, U+FEFF, U+2028, U+2029U+0020, ASCII 32string tabInside = "A B"; // Noncompliant: contains a tabulation string zeroWidthSpaceInside = "foobar"; // Noncompliant: contains a U+200B character inside Console.WriteLine(zeroWidthSpaceInside); // Prints "foo?bar"
string tabInside = "A\tB"; // Compliant: escaped value string zeroWidthSpaceInside = "foo\u200Bbar"; // Compliant: escaped value Console.WriteLine(zeroWidthSpaceInside); // Prints "foo?bar"
When exceptions occur, it is usually a bad idea to simply ignore them. Instead, it is better to handle them properly, or at least to log them.
This rule only reports on empty catch clauses that catch generic Exceptions.
string text = "";
try
{
text = File.ReadAllText(fileName);
}
catch (Exception exc) // Noncompliant
{
}
string text = "";
try
{
text = File.ReadAllText(fileName);
}
catch (Exception exc)
{
logger.Log(exc);
}
When a block contains a comment, it is not considered to be empty.
The instance passed to the lock
statement should be a dedicated private field.
If the instance representing an exclusively acquired lock is publicly accessible, another thread in another part of the program could accidentally attempt to acquire the same lock. This increases the likelihood of deadlocks.
For example, a string should never be used for locking. When a string is interned by the runtime, it can be shared by multiple threads, breaking the
locking mechanism.
Instead, a dedicated private Lock object
instance (or object instance, for frameworks before .Net 9) should be used for locking. This minimizes access to the lock instance and
therefore prevents accidential lock sharing.
The following objects are considered potentially prone to accidental lock sharing:
void MyLockingMethod()
{
lock (this) // Noncompliant
{
// ...
}
}
#if NET9_0_OR_GREATER
private readonly Lock lockObj = new();
#else
private readonly object lockObj = new();
#endif
void MyLockingMethod()
{
lock (lockObj)
{
// ...
}
}
Conditional expressions which are always true or false can lead to unreachable code.
In the case below, the call of Dispose() never happens.
var a = false;
if (a)
{
Dispose(); // Never reached
}
This rule will not raise an issue in either of these cases:
const bool
const bool debug = false;
//...
if (debug)
{
// Print something
}
true or false.In these cases, it is obvious the code is as intended.
The conditions should be reviewed to decide whether:
public void Sample(bool b)
{
bool a = false;
if (a) // Noncompliant: The true branch is never reached
{
DoSomething(); // Never reached
}
if (!a || b) // Noncompliant: "!a" is always "true" and the false branch is never reached
{
DoSomething();
}
else
{
DoSomethingElse(); // Never reached
}
var c = "xxx";
var res = c ?? "value"; // Noncompliant: c is always not null, "value" is never used
}
public void Sample(bool b)
{
bool a = false;
if (Foo(a)) // Condition was updated
{
DoSomething();
}
if (b) // Parts of the condition were removed.
{
DoSomething();
}
else
{
DoSomethingElse();
}
var c = "xxx";
var res = c; // ?? "value" was removed
}
Gratuitous boolean expressions are conditions that do not change the evaluation of a program. This issue can indicate logical errors and affect the correctness of an application, as well as its maintainability.
Control flow constructs like if-statements allow the programmer to direct the flow of a program depending on a boolean expression.
However, if the condition is always true or always false, only one of the branches will ever be executed. In that case, the control flow construct and
the condition no longer serve a purpose; they become gratuitous.
The presence of gratuitous conditions can indicate a logical error. For example, the programmer intended to have the program branch into different paths but made a mistake when formulating the branching condition. In this case, this issue might result in a bug and thus affect the reliability of the application. For instance, it might lead to the computation of incorrect results.
Additionally, gratuitous conditions and control flow constructs introduce unnecessary complexity. The source code becomes harder to understand, and thus, the application becomes more difficult to maintain.
This rule looks for operands of a boolean expression never changing the result of the expression. It also applies to the null coalescing operator when one of
the operands always evaluates to null.
string d = null; var v1 = d ?? "value"; // Noncompliant
This rule will not raise an issue in either of these cases:
const bool
const bool debug = false;
//...
if (debug) // Compliant
{
// Print something
}
true or false.In these cases, it is obvious the code is as intended.
Gratuitous boolean expressions are suspicious and should be carefully removed from the code.
First, the boolean expression in question should be closely inspected for logical errors. If a mistake was made, it can be corrected so the condition is no longer gratuitous.
If it becomes apparent that the condition is actually unnecessary, it can be removed. The associated control flow construct (e.g., the
if-statement containing the condition) will be adapted or even removed, leaving only the necessary branches.
public void Sample(bool b, bool c)
{
var a = true;
if (a) // Noncompliant: "a" is always "true"
{
DoSomething();
}
if (b && a) // Noncompliant: "a" is always "true"
{
DoSomething();
}
if (c || !a) // Noncompliant: "!a" is always "false"
{
DoSomething();
}
string d = null;
var v1 = d ?? "value"; // Noncompliant: "d" is always null and v1 is always "value".
var v2 = s ?? d; // Noncompliant: "d" is always null and v2 is always equal to s.
}
The unnecessary operand is updated:
public void Sample(bool b, bool c, string s)
{
var a = IsAllowed();
if (a) // Compliant
{
DoSomething();
}
if (b && a) // Compliant
{
DoSomething();
}
if (c || !a) // Compliant
{
DoSomething();
}
string d = GetStringData();
var v1 = d ?? "value"; // Compliant
var v2 = s ?? d; // Compliant
}
The unnecessary operand is removed:
public void Sample(bool b, bool c, string s)
{
DoSomething();
if (b) // Compliant
{
DoSomething();
}
if (c) // Compliant
{
DoSomething();
}
var v1 = "value"; // Compliant
var v2 = s; // Compliant
}
In Windows, "Everyone" group is similar and includes all members of the Authenticated Users group as well as the built-in Guest account, and several other built-in security accounts.
Granting permissions to this category can lead to unintended access to files or directories that could allow attackers to obtain sensitive information, disrupt services or elevate privileges.
When file or directory permissions grant access to all users on a system (often represented as "others" or "everyone" in permission models), attackers who gain access to any user account can read sensitive files containing credentials, configuration data, API keys, database passwords, personal information, or proprietary business data. This exposure can lead to data breaches, identity theft, compliance violations, and competitive disadvantage.
Granting write permissions to broad user categories allows any user on the system to modify or delete critical files and directories. Attackers or compromised low-privileged accounts can corrupt application data, modify configuration files to alter system behavior or disrupt services, or delete important resources, leading to service outages, system instability, data loss, and denial of service.
When executable files or scripts have overly permissive permissions, especially when combined with special permission bits that allow programs to execute with the permissions of the file owner or group rather than the executing user, attackers can replace legitimate executables with malicious code. When these modified files are executed by privileged users or processes, the attacker’s code runs with elevated privileges, potentially enabling them to escalate from a low-privileged account to root or administrator access, install backdoors, or pivot to other systems in the network.
Instead of granting access to "Everyone", explicitly deny access to this group or grant permissions only to specific users or groups that require
access. Use AccessControlType.Deny to prevent the "Everyone" group from accessing the file.
var accessRule = new FileSystemAccessRule(
"Everyone",
FileSystemRights.FullControl,
AccessControlType.Allow);
var fileSecurity = File.GetAccessControl("path");
fileSecurity.AddAccessRule(accessRule); // Noncompliant
File.SetAccessControl("fileName", fileSecurity);
var accessRule = new FileSystemAccessRule(
"Everyone",
FileSystemRights.FullControl,
AccessControlType.Deny);
var fileSecurity = File.GetAccessControl("path");
fileSecurity.AddAccessRule(accessRule);
File.SetAccessControl("path", fileSecurity);
Use AccessControlType.Deny instead of AccessControlType.Allow when setting access rules for the "Everyone" group. This
prevents the broad group from having write or full control access to files.
var accessRule = new FileSystemAccessRule("Everyone", FileSystemRights.Write, AccessControlType.Allow);
var fileInfo = new FileInfo("path");
var fileSecurity = fileInfo.GetAccessControl();
fileSecurity.SetAccessRule(accessRule); // Noncompliant
fileInfo.SetAccessControl(fileSecurity);
var accessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Deny);
var fileInfo = new FileInfo("path");
var fileSecurity = fileInfo.GetAccessControl();
fileSecurity.SetAccessRule(accessRule);
fileInfo.SetAccessControl(fileSecurity);
Avoid setting permissions that grant read, write, or execute access to "others" (all users). Instead, restrict permissions to the file owner or
specific groups. Use FileAccessPermissions.UserExecute or other restrictive permission flags that limit access to the owner only.
var fsEntry = UnixFileSystemInfo.GetFileSystemEntry("path");
fsEntry.FileAccessPermissions = FileAccessPermissions.OtherReadWriteExecute; // Noncompliant
var fsEntry = UnixFileSystemInfo.GetFileSystemEntry("path");
fsEntry.FileAccessPermissions = FileAccessPermissions.UserExecute;
Logging arguments should not require evaluation in order to avoid unnecessary performance overhead. When passing concatenated strings or string interpolations directly into a logging method, the evaluation of these expressions occurs every time the logging method is called, regardless of the log level. This can lead to inefficient code execution and increased resource consumption.
Instead, it is recommended to use the overload of the logger that accepts a log format and its arguments as separate parameters. By separating the log format from the arguments, the evaluation of expressions can be deferred until it is necessary, based on the log level. This approach improves performance by reducing unnecessary evaluations and ensures that logging statements are only evaluated when needed.
Furthermore, using a constant log format enhances observability and facilitates searchability in log aggregation and monitoring software.
The rule covers the following logging frameworks:
Use an overload that takes the log format and the parameters as separate arguments. The log format should be a constant string.
logger.DebugFormat($"The value of the parameter is: {parameter}.");
logger.DebugFormat("The value of the parameter is: {Parameter}.", parameter);
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
CastleCoreLoggingTemplateNotConstant |
.NET 9.0 |
230.306 us |
2.7116 us |
479200 B |
|
CastleCoreLoggingTemplateConstant |
.NET 9.0 |
46.827 us |
1.4743 us |
560000 B |
|
CastleCoreLoggingTemplateNotConstant |
.NET Framework 4.8.1 |
1,060.413 us |
32.3559 us |
1115276 B |
|
CastleCoreLoggingTemplateConstant |
.NET Framework 4.8.1 |
93.697 us |
1.8201 us |
561650 B |
|
MSLoggingTemplateNotConstant |
.NET 9.0 |
333.246 us |
12.9214 us |
479200 B |
|
MSLoggingTemplateConstant |
.NET 9.0 |
441.118 us |
68.7999 us |
560000 B |
|
MSLoggingTemplateNotConstant |
.NET Framework 4.8.1 |
1,542.076 us |
99.3423 us |
1115276 B |
|
MSLoggingTemplateConstant |
.NET Framework 4.8.1 |
698.071 us |
18.6319 us |
561653 B |
|
NLogLoggingTemplateNotConstant |
.NET 9.0 |
178.789 us |
9.2528 us |
479200 B |
|
NLogLoggingTemplateConstant |
.NET 9.0 |
6.009 us |
1.3303 us |
- |
|
NLogLoggingTemplateNotConstant |
.NET Framework 4.8.1 |
1,086.260 us |
44.1670 us |
1115276 B |
|
NLogLoggingTemplateConstant |
.NET Framework 4.8.1 |
25.132 us |
0.5666 us |
- |
|
SerilogLoggingTemplateNotConstant |
.NET 9.0 |
234.460 us |
7.4831 us |
479200 B |
|
SerilogLoggingTemplateConstant |
.NET 9.0 |
49.854 us |
1.8232 us |
- |
|
SerilogLoggingTemplateNotConstant |
.NET Framework 4.8.1 |
1,103.939 us |
47.0203 us |
1115276 B |
|
SerilogLoggingTemplateConstant |
.NET Framework 4.8.1 |
35.752 us |
0.6022 us |
- |
|
Log4NetLoggingTemplateNotConstant |
.NET 9.0 |
255.754 us |
5.6046 us |
479200 B |
|
Log4NetLoggingTemplateConstant |
.NET 9.0 |
46.425 us |
1.7087 us |
240000 B |
|
Log4NetLoggingTemplateNotConstant |
.NET Framework 4.8.1 |
1,109.874 us |
23.4388 us |
1115276 B |
|
Log4NetLoggingTemplateConstant |
.NET Framework 4.8.1 |
92.305 us |
2.4161 us |
240707 B |
The results were generated by running the following snippet with BenchmarkDotNet:
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
[Params(10_000)]
public int Iterations;
private ILogger ms_logger;
private Castle.Core.Logging.ILogger cc_logger;
private log4net.ILog l4n_logger;
private Serilog.ILogger se_logger;
private NLog.ILogger nl_logger;
[GlobalSetup]
public void GlobalSetup()
{
ms_logger = new LoggerFactory().CreateLogger<LoggingTemplates>();
cc_logger = new Castle.Core.Logging.NullLogFactory().Create("Castle.Core.Logging");
l4n_logger = log4net.LogManager.GetLogger(typeof(LoggingTemplates));
se_logger = Serilog.Log.Logger;
nl_logger = NLog.LogManager.GetLogger("NLog");
}
[BenchmarkCategory("Microsoft.Extensions.Logging")]
[Benchmark]
public void MSLoggingTemplateNotConstant()
{
for (int i = 0; i < Iterations; i++)
{
ms_logger.LogInformation($"Param: {i}");
}
}
[BenchmarkCategory("Microsoft.Extensions.Logging")]
[Benchmark]
public void MSLoggingTemplateConstant()
{
for (int i = 0; i < Iterations; i++)
{
ms_logger.LogInformation("Param: {Parameter}", i);
}
}
[BenchmarkCategory("Castle.Core.Logging")]
[Benchmark]
public void CastleCoreLoggingTemplateNotConstant()
{
for (int i = 0; i < Iterations; i++)
{
cc_logger.Info($"Param: {i}");
}
}
[BenchmarkCategory("Castle.Core.Logging")]
[Benchmark]
public void CastleCoreLoggingTemplateConstant()
{
for (int i = 0; i < Iterations; i++)
{
cc_logger.InfoFormat("Param: {Parameter}", i);
}
}
[BenchmarkCategory("log4net")]
[Benchmark]
public void Log4NetLoggingTemplateNotConstant()
{
for (int i = 0; i < Iterations; i++)
{
l4n_logger.Info($"Param: {i}");
}
}
[BenchmarkCategory("log4net")]
[Benchmark]
public void Log4NetLoggingTemplateConstant()
{
for (int i = 0; i < Iterations; i++)
{
l4n_logger.InfoFormat("Param: {Parameter}", i);
}
}
[BenchmarkCategory("Serilog")]
[Benchmark]
public void SerilogLoggingTemplateNotConstant()
{
for (int i = 0; i < Iterations; i++)
{
se_logger.Information($"Param: {i}");
}
}
[BenchmarkCategory("Serilog")]
[Benchmark]
public void SerilogLoggingTemplateConstant()
{
for (int i = 0; i < Iterations; i++)
{
se_logger.Information("Param: {Parameter}", i);
}
}
[BenchmarkCategory("NLog")]
[Benchmark]
public void NLogLoggingTemplateNotConstant()
{
for (int i = 0; i < Iterations; i++)
{
nl_logger.Info($"Param: {i}");
}
}
[BenchmarkCategory("NLog")]
[Benchmark]
public void NLogLoggingTemplateConstant()
{
for (int i = 0; i < Iterations; i++)
{
nl_logger.Info("Param: {Parameter}", i);
}
}
Hardware Configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S2629.json ================================================ { "title": "Logging templates should be constant", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance", "logging" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-2629", "sqKey": "S2629", "scope": "Main", "quickfix": "infeasible" } ================================================ FILE: analyzers/rspec/cs/S2674.html ================================================
Invoking a stream reading method without verifying the number of bytes read can lead to erroneous assumptions. A Stream can represent any I/O
operation, such as reading a file, network communication, or inter-process communication. As such, it is not guaranteed that the byte[]
passed into the method will be filled with the requested number of bytes. Therefore, inspecting the value returned by the reading method is important
to ensure the number of bytes read.
Neglecting the returned length read can result in a bug that is difficult to reproduce.
This rule raises an issue when the returned value is ignored for the following methods:
Check the return value of stream reading methods to verify the actual number of bytes read, and use this value when processing the data to avoid potential bugs.
public byte[] ReadFile(string fileName)
{
using var stream = File.Open(fileName, FileMode.Open);
var result = new byte[stream.Length];
stream.Read(result, 0, (int)stream.Length); // Noncompliant
return result;
}
public byte[] ReadFile(string fileName)
{
using var stream = File.Open(fileName, FileMode.Open);
using var ms = new MemoryStream();
var buffer = new byte[1024];
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
Having inconsistent indentation and omitting curly braces from a control structure, such as an if statement or for loop,
is misleading and can induce bugs.
This rule raises an issue when the indentation of the lines after a control structure indicates an intent to include those lines in the block, but the omission of curly braces means the lines will be unconditionally executed once.
The following patterns are recognized:
if (condition) FirstActionInBlock(); SecondAction(); // Noncompliant: SecondAction is executed unconditionally ThirdAction();
if(condition) FirstActionInBlock(); SecondAction(); // Noncompliant: SecondAction is executed unconditionally
if (condition) FirstActionInBlock(); SecondAction(); // Noncompliant: SecondAction is executed unconditionally
string str = null; for (int i = 0; i < array.Length; i++) str = array[i]; DoTheThing(str); // Noncompliant: executed only on the last element
Note that this rule considers tab characters to be equivalent to 1 space. When mixing spaces and tabs, a code may look fine in one editor but be confusing in another configured differently.
double.NaN and float.NaN are not equal to anything, not even themselves.
When anything is compared with NaN using one of the comparison operators >,
>=, <, ⇐ or the equality operator
==, the result will always be false. In contrast, when anything is compared with NaN using the inequality operator
!=, the result will always be true.
Instead, the best way to see whether a variable is equal to NaN is to use the float.IsNaN and double.IsNaN methods, which work as expected.
var a = double.NaN;
if (a == double.NaN) // Noncompliant: always false
{
Console.WriteLine("a is not a number");
}
if (a != double.NaN) // Noncompliant: always true
{
Console.WriteLine("a is not NaN");
}
var a = double.NaN;
if (double.IsNaN(a))
{
Console.WriteLine("a is not a number");
}
if (!double.IsNaN(a))
{
Console.WriteLine("a is not NaN");
}
================================================
FILE: analyzers/rspec/cs/S2688.json
================================================
{
"title": "\"NaN\" should not be used in comparisons",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2688",
"sqKey": "S2688",
"scope": "All",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/cs/S2692.html
================================================
Most checks against an IndexOf value compare it with -1 because 0 is a valid index.
strings.IndexOf(someString) == -1 // Test for "index not found" strings.IndexOf(someString) < 0 // Test for "index not found" strings.IndexOf(someString) >= 0 // Test for "index found"
Any checks which look for values > 0 ignore the first element, which is likely a bug. If the intent is merely to check the
inclusion of a value in a string, List, or array, consider using the Contains method instead.
strings.Contains(someString) // bool result
This rule raises an issue when the output value of any of the following methods is tested against > 0:
string, list or
arraystringstring, list or
arraystringsomeArray.IndexOf(someItem) > 0 // Noncompliant: index 0 missing someString.IndexOfAny(charsArray) > 0 // Noncompliant: index 0 missing someList.LastIndexOf(someItem) > 0 // Noncompliant: index 0 missing someString.LastIndexOf(charsArray) > 0 // Noncompliant: index 0 missing
string color = "blue";
string name = "ishmael";
List<string> strings = new List<string>();
strings.Add(color);
strings.Add(name);
string[] stringArray = strings.ToArray();
if (strings.IndexOf(color) > 0) // Noncompliant
{
// ...
}
if (name.IndexOf("ish") > 0) // Noncompliant
{
// ...
}
if (name.IndexOf("ae") > 0) // Noncompliant
{
// ...
}
if (Array.IndexOf(stringArray, color) > 0) // Noncompliant
{
// ...
}
string color = "blue";
string name = "ishmael";
List<string> strings = new List<string>();
strings.Add(color);
strings.Add(name);
string[] stringArray = strings.ToArray();
if (strings.IndexOf(color) > -1)
{
// ...
}
if (name.IndexOf("ish") >= 0)
{
// ...
}
if (name.Contains("ae"))
{
// ...
}
if (Array.IndexOf(stringArray, color) >= 0)
{
// ...
}
This rule raises an issue each time a static field is updated from a non-static method or property.
Updating a static field from a non-static method introduces significant challenges and potential bugs. Multiple class
instances and threads can access and modify the static field concurrently, leading to unintended consequences for other instances or
threads (unexpected behavior, race conditions and
synchronization problems).
class MyClass
{
private static int count = 0;
public void DoSomething()
{
//...
count++; // Noncompliant: make the enclosing instance property 'static' or remove this set on the 'static' field.
}
}
interface MyInterface
{
private static int count = 0;
public void DoSomething()
{
//...
count++; // Noncompliant: remove this set, which updates a 'static' field from an instance method.
}
}
The rule targets test methods that lack an assertion and consist solely of an action and, optionally, a setup.
[TestMethod]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
}
Such tests only verify that the system under test does not throw any exceptions without providing any guarantees regarding the code’s behavior under test. Those tests increase the coverage without enforcing anything on the covered code, resulting in a false sense of security.
The rule identifies a potential issue when no assertions are present in tests utilizing the following frameworks:
MSTestNUnitxUnitFluentAssertions (4.x and 5.x)NFluentNSubstituteMoqShoudlyBy enforcing the presence of assertions, this rule aims to enhance the reliability and comprehensiveness of tests by ensuring that they provide meaningful validation of the expected behavior.
Test methods that include a call to a custom assertion method will not raise any issues.
To address this issue, you should include assertions to validate the expected behavior. Choose an appropriate assertion method provided by your testing framework (such as MSTest, NUnit, xUnit) or select a suitable assertion library like FluentAssertions, NFluent, NSubstitute, Moq, or Shouldly.
In addition to using built-in assertion methods, you also have the option to create custom assertion methods. To do this, declare an attribute
named [AssertionMethodAttribute] and apply it to the respective method. This allows you to encapsulate specific validation logic within
your custom assertion methods without raising the issue. Here’s an example:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CustomTestExample
{
[TestMethod]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Validator.AssertCustomEquality(0, actual); // Compliant
}
}
public static class Validator
{
[AssertionMethod]
public static void AssertCustomEquality(int expected, int actual)
{
// ...
}
}
public class AssertionMethodAttribute : Attribute { }
[TestMethod]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
}
[TestMethod]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Assert.AreEqual(0, actual);
}
[Test]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
}
[Test]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Assert.That(0, Is.EqualTo(actual));
}
[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
}
[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
var stringCalculator = new StringCalculator();
var actual = stringCalculator.Add("0");
Assert.Equal(0, actual);
}
Using literal boolean values in assertions can lead to less readable and less informative unit tests. When a test fails, it’s important to have a clear understanding of what the test was checking and why it failed. Most of the testing frameworks provide more explicit assertion methods that will provide a more helpful error message if the test fails.
In the context of xUnit, Assert.True and Assert.False are not flagged by this rule. This is because
Assert.Fail was only introduced in 2020 with version 2.4.2. Prior to this, developers used Assert.True(false,
message) and Assert.False(true, message) as workarounds to simulate the functionality of Assert.Fail().
bool someResult; Assert.AreEqual(false, someResult); // Noncompliant: use Assert.IsFalse Assert.AreEqual(true, someResult); // Noncompliant: use Assert.IsTrue Assert.AreNotEqual(false, someResult); // Noncompliant: use Assert.IsTrue Assert.AreNotEqual(true, someResult); // Noncompliant: use Assert.IsFalse Assert.IsFalse(true, "Should not reach this line!"); // Noncompliant: use Assert.Fail Assert.IsTrue(false, "Should not reach this line!"); // Noncompliant: use Assert.Fail Assert.IsFalse(false); // Noncompliant: remove it
bool someResult;
Assert.IsFalse(someResult);
Assert.IsTrue(someResult);
Assert.IsTrue(someResult);
Assert.IsFalse(someResult);
Assert.Fail("Should not reach this line!");
Assert.Fail("Should not reach this line!");
// Removed
bool someResult; Assert.AreEqual(false, someResult); // Noncompliant: use Assert.False Assert.AreEqual(true, someResult); // Noncompliant: use Assert.True Assert.AreNotEqual(false, someResult); // Noncompliant: use Assert.True Assert.AreNotEqual(true, someResult); // Noncompliant: use Assert.False Assert.False(true, "Should not reach this line!"); // Noncompliant: use Assert.Fail Assert.True(false, "Should not reach this line!"); // Noncompliant: use Assert.Fail Assert.False(false); // Noncompliant: remove it
bool someResult;
Assert.False(someResult);
Assert.True(someResult);
Assert.True(someResult);
Assert.False(someResult);
Assert.Fail("Should not reach this line!");
Assert.Fail("Should not reach this line!");
// Removed
bool someResult; Assert.Equal(false, someResult); // Noncompliant: use Assert.False Assert.Equal(true, someResult); // Noncompliant: use Assert.True Assert.NotEqual(false, someResult); // Noncompliant: use Assert.True Assert.NotEqual(true, someResult); // Noncompliant: use Assert.False
bool someResult; Assert.False(someResult); Assert.True(someResult); Assert.True(someResult); Assert.False(someResult);
A catch clause that only rethrows the caught exception has the same effect as omitting the catch altogether and letting
it bubble up automatically.
string s = "";
try
{
s = File.ReadAllText(fileName);
}
catch (Exception e) // Noncompliant
{
throw;
}
Such clauses should either be removed or populated with the appropriate logic.
string s = File.ReadAllText(fileName);
or
string s = "";
try
{
s = File.ReadAllText(fileName);
}
catch (Exception e)
{
logger.LogError(e);
throw;
}
This rule will not generate issues for catch blocks if they are followed by a catch block for a more general exception
type that does more than just rethrowing the exception.
var s = ""
try
{
s = File.ReadAllText(fileName);
}
catch (IOException) // Compliant by exception: removing it would change the logic
{
throw;
}
catch (Exception) // Compliant: does more than just rethrow
{
logger.LogError(e);
throw;
}
================================================
FILE: analyzers/rspec/cs/S2737.json
================================================
{
"title": "\"catch\" clauses should do more than rethrow",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"error-handling",
"unused",
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2737",
"sqKey": "S2737",
"scope": "Main",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S2743.html
================================================
A static field in a generic type is not shared among instances of different closed constructed types, thus
LengthLimitedSingletonCollection<int>.instances and LengthLimitedSingletonCollection<string>.instances will
point to different objects, even though instances is seemingly shared among all LengthLimitedSingletonCollection<>
generic classes.
If you need to have a static field shared among instances with different generic arguments, define a non-generic base class to store your static members, then set your generic type to inherit from the base class.
public class LengthLimitedSingletonCollection<T> where T : new()
{
protected const int MaxAllowedLength = 5;
protected static Dictionary<Type, object> instances = new Dictionary<Type, object>(); // Noncompliant
public static T GetInstance()
{
object instance;
if (!instances.TryGetValue(typeof(T), out instance))
{
if (instances.Count >= MaxAllowedLength)
{
throw new Exception();
}
instance = new T();
instances.Add(typeof(T), instance);
}
return (T)instance;
}
}
public class SingletonCollectionBase
{
protected static Dictionary<Type, object> instances = new Dictionary<Type, object>();
}
public class LengthLimitedSingletonCollection<T> : SingletonCollectionBase where T : new()
{
protected const int MaxAllowedLength = 5;
public static T GetInstance()
{
object instance;
if (!instances.TryGetValue(typeof(T), out instance))
{
if (instances.Count >= MaxAllowedLength)
{
throw new Exception();
}
instance = new T();
instances.Add(typeof(T), instance);
}
return (T)instance;
}
}
If the static field or property uses a type parameter, then the developer is assumed to understand that the static member is not shared among the closed constructed types.
public class Cache<T>
{
private static Dictionary<string, T> CacheDictionary { get; set; } // Compliant
}
================================================
FILE: analyzers/rspec/cs/S2743.json
================================================
{
"title": "Static fields should not be used in generic types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2743",
"sqKey": "S2743",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2755.html
================================================
This vulnerability allows the usage of external entities in XML.
External Entity Processing allows for XML parsing with the involvement of external entities. However, when this functionality is enabled without proper precautions, it can lead to a vulnerability known as XML External Entity (XXE) attack.
One significant danger of XXE vulnerabilities is the potential for sensitive data exposure. By crafting malicious XML payloads, attackers can reference external entities that contain sensitive information, such as system files, database credentials, or configuration files. When these entities are processed during XML parsing, the attacker can extract the contents and gain unauthorized access to sensitive data. This poses a severe threat to the confidentiality of critical information.
Another consequence of XXE vulnerabilities is the potential for denial-of-service attacks. By exploiting the ability to include external entities, attackers can construct XML payloads that cause resource exhaustion. This can overwhelm the system’s memory, CPU, or other critical resources, leading to system unresponsiveness or crashes. A successful DoS attack can disrupt the availability of services and negatively impact the user experience.
XXE vulnerabilities can also enable Server-Side Request Forgery (SSRF) attacks. By leveraging the ability to include external entities, an attacker can make the vulnerable application send arbitrary requests to other internal or external systems. This can result in unintended actions, such as retrieving data from internal resources, scanning internal networks, or attacking other systems. SSRF attacks can lead to severe consequences, including unauthorized data access, system compromise, or even further exploitation within the network infrastructure.
The following code contains examples of XML parsers that have external entity processing enabled. As a result, the parsers are vulnerable to XXE attacks if an attacker can control the XML file that is processed.
using System.Xml;
public static void decode()
{
XmlDocument parser = new XmlDocument();
parser.XmlResolver = new XmlUrlResolver(); // Noncompliant
parser.LoadXml("xxe.xml");
}
XmlDocument is safe by default since .NET Framework 4.5.2. For older versions, set XmlResolver explicitly to
null.
using System.Xml;
public static void decode()
{
XmlDocument parser = new XmlDocument();
parser.XmlResolver = null;
parser.LoadXml("xxe.xml");
}
The most effective approach to prevent XXE vulnerabilities is to disable external entity processing entirely, unless it is explicitly required for specific use cases. By default, XML parsers should be configured to reject the processing of external entities. This can be achieved by setting the appropriate properties or options in your XML parser library or framework.
If external entity processing is necessary for certain scenarios, adopt a whitelisting approach to restrict the entities that can be resolved
during XML parsing. Create a list of trusted external entities and disallow all others. This approach ensures that only known and safe entities are
processed.
You should rely on features provided by your XML parser to restrict the external entities.
Using operator pairs (=+, =-, or =!) that look like reversed single operators (+=,
-= or !=) is confusing. They compile and run but do not produce the same result as their mirrored counterpart.
int target = -5; int num = 3; target =- num; // Noncompliant: target = -3. Is that the intended behavior? target =+ num; // Noncompliant: target = 3
This rule raises an issue when =+, =-, or =! are used without any space between the operators and when there
is at least one whitespace after.
Replace the operators with a single one if that is the intention
int target = -5; int num = 3; target -= num; // target = -8
Or fix the spacing to avoid confusion
int target = -5; int num = 3; target = -num; // target = -3================================================ FILE: analyzers/rspec/cs/S2757.json ================================================ { "title": "Non-existent operators like \"\u003d+\" should not be used", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-2757", "sqKey": "S2757", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S2760.html ================================================
When the same condition is checked twice in a row, it is either confusing - why have separate checks? - or an error - some other condition should have been checked in the second test.
if (a == b)
{
doTheThing(b);
}
if (a == b) // Noncompliant; is this really what was intended?
{
doTheThing(c);
}
if (a == b)
{
doTheThing(b);
doTheThing(c);
}
or
if (a == b)
{
doTheThing(b);
}
if (b == c)
{
doTheThing(c);
}
Since it is a common pattern to test a variable, reassign it if it fails the test, then re-test it, that pattern is ignored.
================================================ FILE: analyzers/rspec/cs/S2760.json ================================================ { "title": "Sequential tests should not check the same condition", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "suspicious", "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-2760", "sqKey": "S2760", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S2761.html ================================================The repetition of a prefix operator (!, or ~) is usually a typo. The second operator invalidates the first one.
int v1 = 0; bool v2 = false; var v3 = !!v1; // Noncompliant: equivalent to "v1" var v4 = ~~v2; // Noncompliant: equivalent to "v2"================================================ FILE: analyzers/rspec/cs/S2761.json ================================================ { "title": "Doubled prefix operators \"!!\" and \"~~\" should not be used", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-2761", "sqKey": "S2761", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S2857.html ================================================
When concatenating strings, it is very easy to forget a whitespace.
In some scenarios this might cause runtime errors, one of which is while creating an SQL query via concatenation:
string select = "SELECT p.FirstName, p.LastName, p.PhoneNumber" +
"FROM Person as p" + // Noncompliant: concatenation results in "p.PhoneNumberFROM"
"WHERE p.Id = @Id"; // Noncompliant: concatenation results in "pWHERE"
This rule raises an issue when the spacing around SQL keywords appears to be missing, making the concatenated string invalid SQL syntax. It would require the user to add the appropriate whitespaces:
string select = "SELECT p.FirstName, p.LastName, p.PhoneNumber" +
" FROM Person as p" +
" WHERE p.Id = @Id";
================================================
FILE: analyzers/rspec/cs/S2857.json
================================================
{
"title": "SQL keywords should be delimited by whitespace",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"sql"
],
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-2857",
"sqKey": "S2857",
"scope": "Main",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/cs/S2925.html
================================================
Using Thread.Sleep in a test might introduce unpredictable and inconsistent results depending on the environment. Furthermore, it will
block the thread, which means the system resources are not being fully used.
[TestMethod]
public void SomeTest()
{
Thread.Sleep(500); // Noncompliant
// assertions...
}
An alternative is a task-based asynchronous approach, using async and await.
More specifically the Task.Delay method should be used, because of the following advantages:
Thread.Sleep
[TestMethod]
public async Task SomeTest()
{
await Task.Delay(500);
// assertions...
}
Another scenario is when some data might need to be mocked using Moq, and a delay needs to be introduced:
[TestMethod]
public void UserService_Test()
{
var userService = new Mock<UserService>();
var expected = new User();
userService
.Setup(m => m.GetUserById(42))
.Returns(() =>
{
Thread.Sleep(500); // Noncompliant
return Task.FromResult(expected);
});
// assertions...
}
An alternative to Thread.Sleep while mocking with Moq is to use ReturnsAsync and pass the amount of time to
delay there:
[TestMethod]
public void UserService_Test()
{
var userService = new Mock<UserService>();
var expected = new User();
userService
.Setup(m => m.GetUserById(42))
.ReturnsAsync(expected, TimeSpan.FromMilliseconds(500));
// assertions...
}
When writing managed code, there is no need to worry about memory
allocation or deallocation as it is taken care of by the garbage
collector. However, certain objects, such as Bitmap, utilize unmanaged memory for specific purposes like pointer arithmetic. These objects may have substantial
unmanaged memory footprints while having minimal managed footprints. Unfortunately, the garbage collector only recognizes the small managed footprint
and does not promptly reclaim the corresponding unmanaged memory (by invoking the finalizer method of Bitmap) for efficiency reasons.
In addition, it’s essential to manage other system resources besides memory. The operating system has limits on the number of file descriptors (e.g., FileStream) or sockets (e.g., WebClient) that can remain open simultaneously. Therefore, it’s
crucial to Dispose of these resources promptly when they are no longer required, instead of relying on the garbage collector to invoke
the finalizers of these objects at an unpredictable time in the future.
This rule keeps track of private fields and local variables of specific types that implement IDisposable or
IAsyncDisposable. It identifies instances of these types that are not properly disposed, closed, aliased, returned, or passed to other
methods. This applies to instances that are either directly created using the new operator or instantiated through a predefined list of
factory methods.
Here is the list of the types tracked by this rule:
FluentAssertions.Execution.AssertionScopeSystem.Drawing.BitmapSystem.Drawing.ImageSystem.IO.FileStreamSystem.IO.StreamReaderSystem.IO.StreamWriterSystem.Net.Sockets.TcpClientSystem.Net.Sockets.UdpClientSystem.Net.WebClientHere is the list of predefined factory methods tracked by this rule:
System.Drawing.Image.FromFile()System.Drawing.Image.FromStream()System.IO.File.Create()System.IO.File.Open()IDisposable / IAsyncDisposable variables returned from a method or passed to other methods are ignored, as are local
IDisposable / IAsyncDisposable objects that are initialized with other IDisposable /
IAsyncDisposable objects.
public Stream WriteToFile(string path, string text)
{
var fs = new FileStream(path, FileMode.Open); // Compliant: it is returned
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
return fs;
}
public void ReadFromStream(Stream s)
{
var sr = new StreamReader(s); // Compliant: it would close the underlying stream.
// ...
}
It is essential to identify what kind of disposable resource variable is used to know how to fix this issue.
In the case of a disposable resource store as a member (either as field or property), it should be disposed at the same time as the class. The best way to achieve this is to follow the dispose pattern.
When creating the disposable resource for a one-time use (cases not covered by the exceptions), it should be disposed at the end of its creation scope. The easiest to ensure your resource is disposed when reaching the end of a scope is to either use the using statement or the using declaration
public class ResourceHolder
{
private FileStream fs; // Noncompliant: dispose or close are never called
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open);
}
public void WriteToFile(string path, string text)
{
var fs = new FileStream(path, FileMode.Open); // Noncompliant: not disposed, returned or initialized with another disposable object
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
}
}
public class ResourceHolder : IDisposable, IAsyncDisposable
{
private FileStream fs; // Compliant: disposed in Dispose/DisposeAsync methods
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open);
}
public void Dispose()
{
this.fs.Dispose();
}
public async ValueTask DisposeAsync()
{
await fs.DisposeAsync().ConfigureAwait(false);
}
public void WriteToFile(string path, string text)
{
using (var fs = new FileStream(path, FileMode.Open)) // Compliant: disposed at the end of the using block
{
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
}
}
}
An IDisposable object should be disposed (there are some rare exceptions where not disposing is fine, most notably Task).
If a class has an IDisposable field, there can be two situations:
Dispose on it.In the second case, the safest way for the class to ensure Dispose is called is to call it in its own Dispose function,
and therefore to be itself IDisposable. A class is considered to own an IDisposable field resource if it created the object
referenced by the field.
public class ResourceHolder // Noncompliant; doesn't implement IDisposable
{
private FileStream fs; // This member is never Disposed
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open); // I create the FileStream, I'm owning it
}
public void CloseResource()
{
this.fs.Close();
}
}
public class ResourceHolder : IDisposable
{
private FileStream fs;
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open); // I create the FileStream, I'm owning it
}
public void CloseResource()
{
this.fs.Close();
}
public void Dispose()
{
this.fs.Dispose();
}
}
readonly fields can only be assigned in a class constructor. If a class has a field that’s not marked readonly but is
only set in the constructor, it could cause confusion about the field’s intended use. To avoid confusion, such fields should be marked
readonly to make their intended use explicit, and to prevent future maintainers from inadvertently changing their use.
Serializable attribute.partial classes.struct that are not primitive or pointer types are also ignored because of possible unwanted behavior.Mark the given field with the readonly modifier.
public class Person
{
private int _birthYear; // Noncompliant
Person(int birthYear)
{
_birthYear = birthYear;
}
}
public class Person
{
private readonly int _birthYear;
Person(int birthYear)
{
_birthYear = birthYear;
}
}
While the properties of a readonly
reference type field can still be changed
after initialization, those of a readonly value type field, such as a struct, cannot.
If the member could be either a class or a struct then assignment to its properties could be unreliable, working
sometimes but not others.
There are two ways to fix this issue:
class
interface IPoint
{
int X { get; set; }
int Y { get; set; }
}
class PointManager<T1, T2>
where T1 : IPoint
where T2 : IPoint
{
readonly T1 point1; // this could be a struct
readonly T2 point2; // this could be a struct
public PointManager(T1 point1, T2 point2)
{
this.point1 = point1;
this.point2 = point2;
}
public void MovePoints(int newX)
{
point1.X = newX; //Noncompliant: if point is a struct, then nothing happened
point2.X = newX; //Noncompliant: if point is a struct, then nothing happened
}
}
interface IPoint
{
int X { get; set; }
int Y { get; set; }
}
class PointManager<T1, T2>
where T1 : IPoint
where T2 : class, IPoint
{
readonly T1 point1; // this could be a struct
readonly T2 point2; // this is a class
public PointManager(T1 point1, T2 point2)
{
this.point1 = point1;
this.point2 = point2;
}
public void MovePoints(int newX) // assignment to point1 has been removed
{
point2.X = newX; // Compliant: point2 is a class
}
}
It is possible in an IDisposable to call Dispose on class members from any method, but the contract of
Dispose is that it will clean up all unmanaged resources. Move disposing of members to some other method, and you risk resource
leaks.
This rule also applies for disposable ref structs.
public class ResourceHolder : IDisposable
{
private FileStream fs;
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open);
}
public void CloseResource()
{
this.fs.Close();
}
public void CleanUp()
{
this.fs.Dispose(); // Noncompliant; Dispose not called in class' Dispose method
}
public void Dispose()
{
// method added to satisfy demands of interface
}
}
public class ResourceHolder : IDisposable
{
private FileStream fs;
public void OpenResource(string path)
{
this.fs = new FileStream(path, FileMode.Open);
}
public void CloseResource()
{
this.fs.Close();
}
public void Dispose()
{
this.fs.Dispose();
}
}
IDisposable is an interface implemented by all types which need to provide a mechanism for releasing unmanaged resources.
Unlike managed memory, which is taken care of by the garbage collection,
The interface declares a Dispose method, which the implementer has to define.
The method name Dispose should be used exclusively to implement IDisposable.Dispose to prevent any confusion.
It may be tempting to create a Dispose method for other purposes, but doing so will result in confusion and likely lead to problems in
production.
Methods named Dispose and invoked from the IDisposable.Dispose implementation are not reported.
public class GarbageDisposal : IDisposable
{
protected virtual void Dispose(bool disposing)
{
//...
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
First, it is important to determine whether instances of the type defining the Dispose method should support the IDisposable interface or not.
The decision would be based on whether the instance can have unmanaged resources which have to be dealt with, upon destruction or earlier in the lifetime of the object.
The Dispose pattern can help to take the decision.
If the type should not support the pattern, the Dispose method should be renamed to something which is different than
Dispose, but still relevant and possibly more specific to the context.
public class GarbageDisposal
{
private int Dispose() // Noncompliant
{
// ...
}
}
public class GarbageDisposal : IDisposable
{
public void Dispose()
{
// ...
}
}
or
public class GarbageDisposal
{
private int Grind()
{
// ...
}
}
In C#, without constraints on a generic type parameter, both reference and value types can be passed. However, comparing
this type parameter to null can be misleading as value types, like struct, can never be null.
To avoid unexpected comparisons:
bool IsDefault<T>(T value)
{
if (value == null) // Noncompliant
{
// ...
}
}
bool IsDefault<T>(T value)
{
if (EqualityComparer<T>.Default.Equals(value, default(T)))
{
// ...
}
}
or
bool IsDefault<T>(T value) where T : class
{
if (value == null)
{
// ...
}
}
default operatorThis rule addresses the issue of incomplete assertions that can occur when using certain test frameworks. Incomplete assertions can lead to tests that do not effectively verify anything. The rule enforces the use of complete assertions in specific cases, namely:
string actual = "Using Fluent Assertions"; actual.Should(); // Noncompliant
string actual = "Using NFluent"; Check.That(actual); // Noncompliant
command.Received(); // Noncompliant
In such cases, what is intended to be a test doesn’t actually verify anything.
Fluent Assertions provides an interface for writing assertions, and it is important to ensure that Should() is properly
used in conjunction with an assertion method.
string actual = "Hello World!"; actual.Should(); // Noncompliant
string actual = "Hello World!";
actual.Should().Contain("Hello");
NFluent offers a syntax for assertions, and it’s important to follow Check.That() with an assertion method to complete
the assertion.
string actual = "Hello World!"; Check.That(actual); // Noncompliant
string actual = "Hello World!";
Check.That(actual).Contains("Hello");
NSubstitute is a mocking framework, and Received() is used to verify that a specific method has been called. However,
invoking a method on the mock after calling Received() is necessary to ensure the complete assertion.
command.Received(); // Noncompliant
command.Received().Execute();
In the interests of readability, code that can be simplified should be simplified. To that end, there are several ways IEnumerable language integrated queries (LINQ) can be simplified. This not only improves readabilty but can also lead to improved performance.
Simplify the LINQ expressions:
OfType instead of using Where and the
is operator, followed
by a cast in a SelectAny instead of Where(element ⇒ [expression]).Any().Using Entity Framework may require enforcing client evaluations. Such queries should use AsEnumerable() instead of ToArray() or
ToList() in the middle of a query chain.
public void Foo(IEnumerable<Vehicle> seq, List<int> list)
{
var result1 = seq.Select(x => x as Car).Any(x => x != null); // Noncompliant; use OfType
var result2 = seq.Select(x => x as Car).Any(x => x != null && x.HasOwner); // Noncompliant; use OfType before calling Any
var result3 = seq.Where(x => x is Car).Select(x => x as Car); // Noncompliant; use OfType
var result4 = seq.Where(x => x is Car).Select(x => (Car)x); // Noncompliant; use OfType
var result5 = seq.Where(x => x.HasOwner).Any(); // Noncompliant; use Any([predicate])
var num = list.Count(); // Noncompliant; use the Count property
var arr = seq.ToList().ToArray(); // Noncompliant; ToList is not needed
var count = seq.ToList().Count(x => x.HasOwner); // Noncompliant; ToList is not needed
}
public void Foo(IEnumerable<Vehicle> seq, List<int> list)
{
var result1 = seq.OfType<Car>().Any();
var result2 = seq.OfType<Car>().Any(x => x.HasOwner);
var result3 = seq.OfType<Car>();
var result4 = seq.OfType<Car>();
var result5 = seq.Any(x => x.HasOwner);
var num = list.Count;
var arr = seq.ToArray();
var count = seq.Count(x => x.HasOwner);
}
In C#, the Object.ReferenceEquals method is
used to compare two reference type
variables. If you use this method to compare two value types, such as int,
float, or bool you will not get the expected results because value type variables contain an instance of the type and not a
reference to it.
Due to value type variables containing directly an instance of the type, they can’t have the same reference, and using
Object.ReferenceEquals to compare them will always return false even if the compared variables have the same value.
When comparing value types, prefer using the Object.Equals.
Note that in the case of structure types, it is recommended to implement value equality. If not, {rule:csharpsquid:S3898} might raise.
using System;
struct MyStruct
{
int valueA;
int valueB;
}
static class MyClass
{
public static void Method(MyStruct struct1, MyStruct struct2)
{
if (Object.ReferenceEquals(struct1, struct2)) // Noncompliant: this will be always false
{
// ...
}
}
}
using System;
struct MyStruct : IEquatable<MyStruct>
{
int valueA;
int valueB;
public bool Equals(MyStruct other) => valueA == other.valueA && valueB == other.valueB;
public override bool Equals(object obj) => obj is MyStruct other && Equals(other);
public override int GetHashCode() => HashCode.Combine(valueA, valueB);
public static bool operator ==(MyStruct lhs, MyStruct rhs) => lhs.Equals(rhs);
public static bool operator !=(MyStruct lhs, MyStruct rhs) => !(lhs == rhs);
}
static class MyClass
{
public static void Method(MyStruct struct1, MyStruct struct2)
{
if (struct1.Equals(struct2)) // Compliant: value are compared
{
// ...
}
}
}
Object.ReferenceEquals(Object,
Object) MethodObject.Equals MethodWhen an object has a field annotated with ThreadStatic, that field is shared within a given thread, but unique across threads. Since a
class' static initializer is only invoked for the first thread created, it also means that only the first thread will have the expected initial
values.
Instead, allow such fields to be initialized to their default values or make the initialization lazy.
public class Foo
{
[ThreadStatic]
public static object PerThreadObject = new object(); // Noncompliant. Will be null in all the threads except the first one.
}
public class Foo
{
[ThreadStatic]
public static object _perThreadObject;
public static object PerThreadObject
{
get
{
if (_perThreadObject == null)
{
_perThreadObject = new object();
}
return _perThreadObject;
}
}
}
================================================
FILE: analyzers/rspec/cs/S2996.json
================================================
{
"title": "\"ThreadStatic\" fields should not be initialized",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"multi-threading"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2996",
"sqKey": "S2996",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S2997.html
================================================
When you use a using statement, the
goal is to ensure the correct disposal of an IDisposable
instance when the control leaves the using statement block.
If you return that IDisposable instance inside the block, using will dispose it before the caller can use it, likely
causing exceptions at runtime. You should either remove using statement or avoid returning the IDisposable in the
using statement block.
public FileStream WriteToFile(string path, string text)
{
using (var fs = File.Create(path)) // Noncompliant: 'fs' is disposed at the end of the using scope
{
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
return fs;
}
}
public FileStream WriteToFile(string path, string text)
{
var fs = File.Create(path);
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
return fs; // Compliant: 'fs' is not disposed once the end of the scope is reached and the caller can use it
}
When you annotate a field with the ThreadStatic
attribute, it is an indication that the value of this field is unique for each thread. But if you don’t mark the field as static,
then the ThreadStatic attribute is ignored.
The ThreadStatic attribute should either be removed or replaced with the use of ThreadLocal<T> class, which gives a similar
behavior for non-static fields.
public class MyClass
{
[ThreadStatic] // Noncompliant
private int count = 0;
// ...
}
public class MyClass
{
private int count = 0;
// ...
}
or
public class MyClass
{
private readonly ThreadLocal<int> count = new ThreadLocal<int>();
public int Count
{
get { return count.Value; }
set { count.Value = value; }
}
// ...
}
Assigning a value to a static field in a constructor could cause unreliable behavior at runtime since it will change the value for all
instances of the class.
Instead remove the field’s static modifier, or initialize it statically.
public class Person
{
private static DateTime dateOfBirth;
private static int expectedFingers;
public Person(DateTime birthday)
{
dateOfBirth = birthday; // Noncompliant; now everyone has this birthday
expectedFingers = 10; // Noncompliant
}
}
public class Person
{
private DateTime dateOfBirth;
private static int expectedFingers = 10;
public Person(DateTime birthday)
{
this.dateOfBirth = birthday;
}
}
================================================
FILE: analyzers/rspec/cs/S3010.json
================================================
{
"title": "Static fields should not be updated in constructors",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3010",
"sqKey": "S3010",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3011.html
================================================
Altering or bypassing the accessibility of classes, methods, or fields through reflection violates the encapsulation principle. This can break the internal contracts of the accessed target and lead to maintainability issues and runtime errors.
This rule raises an issue when reflection is used to change the visibility of a class, method or field, and when it is used to directly update a field value.
using System.Reflection;
Type dynClass = Type.GetType("MyInternalClass");
// Noncompliant. Using BindingFlags.NonPublic will return non-public members
BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Static;
MethodInfo dynMethod = dynClass.GetMethod("mymethod", bindingAttr);
object result = dynMethod.Invoke(dynClass, null);
The compiler automatically initializes class fields, auto-properties and events to their default values before setting them with any initialization values, so there is no need to explicitly set a member to its default value. Further, under the logic that cleaner code is better code, it’s considered poor style to do so.
class X
{
public int field = 0; // Noncompliant
public object o = null; // Noncompliant
public object MyProperty { get; set; } = null; // Noncompliant
public event EventHandler MyEvent = null; // Noncompliant
}
class X
{
public int field;
public object o;
public object MyProperty { get; set; }
public event EventHandler MyEvent;
}
const fields are ignored.
There’s no point in having a public member in a non-public type because objects that can’t access the type will never
have the chance to access the member.
This rule raises an issue when a type has methods, fields, or inner types with higher visibility than the type itself has.
internal class MyClass
{
public static decimal PI = 3.14m; // Noncompliant
public int GetOne() // Noncompliant
{
return 1;
}
protected record NestedType // Noncompliant: outer class is internal
{
public bool FlipCoin() // Noncompliant: outer class is internal
{
return false;
}
// ...
}
}
public class MyClass // Class visibility upgrade makes members compliant
{
public static decimal PI = 3.14m;
public int GetOne()
{
return 1;
}
protected record NestedType
{
public bool FlipCoin() // Outer type is public
{
return false;
}
// ...
}
}
User defined operators need to be public:
public static implicit operator byte(MyClass a) => 1; // Compliant public static explicit operator MyClass(byte a) => new MyClass(a); // Compliant
Nested types, even if private, can be used and inherited in the parent type. In this case, the visibility of the outer type is considered.
internal class MyClass
{
private class NestedClass
{
public int PublicProperty { get; } // Noncompliant: should be internal
protected internal int ProtectedInternalProperty { get; } // Compliant: can be used in `InternalsVisibleTo` assemblies
internal int InternalProperty { get; } // Compliant: can be used in `InternalsVisibleTo` assemblies
protected int ProtectedProperty { get; } // Compliant: can be used in derived type
private protected int PrivateProtectedProperty { get; } // Compliant: can be used in derived type
private int PrivateProperty { get; }
}
}
================================================
FILE: analyzers/rspec/cs/S3059.json
================================================
{
"title": "Types should not have members with visibility set higher than the type\u0027s visibility",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3059",
"sqKey": "S3059",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3060.html
================================================
One of the possible ways of performing type-testing is via the is operator: food is Pizza.
The is operator is often used before a direct cast to the target type,
as a more flexible and powerful alternative to the as operator, especially when
used to perform pattern
matching.
if (food is Pizza pizza)
There’s no valid reason to test this with is. The only plausible explanation for such a test is that you’re executing
code in a parent class conditionally based on the kind of child class this is.
public class Food
{
public void DoSomething()
{
if (this is Pizza) // Noncompliant
{
// Code specific to Pizza...
}
}
}
However, code that’s specific to a child class should be in that child class, not in the parent.
One way is to take advantage of the object-orientation of C# and use polymorphism.
For example, when simple method polymorphism is not enough because it is necessary to reuse multiple sections of the parent method, the Template method pattern might help.
public class Food
{
public void DoSomething()
{
// Code shared by all Food...
if (this is Pizza) // Noncompliant
{
// Code specific to Pizza...
}
}
}
public class Food
{
public virtual void DoSomething()
{
// Code shared by all Food...
}
}
public class Pizza : Food
{
public override void DoSomething()
{
base.DoSomething();
// Code specific to Pizza...
}
}
StringBuilder instances that never build a string clutter the code and worse are a drag on performance. Either they
should be removed, or the missing ToString() call should be added.
public void DoSomething(List<string> strings) {
var sb = new StringBuilder(); // Noncompliant
sb.Append("Got: ");
foreach(var str in strings) {
sb.Append(str).Append(", ");
// ...
}
}
public void DoSomething(List<string> strings) {
foreach(var str in strings) {
// ...
}
}
or
public void DoSomething(List<string> strings) {
var sb = new StringBuilder();
sb.Append("Got: ");
foreach(var str in strings) {
sb.Append(str).Append(", ");
// ...
}
logger.LogInformation(sb.ToString());
}
No issue is reported when StringBuilder is:
sb.CopyTo(), sb.GetChunks(), sb.Length, or sb[index].ToString() invocation there.var sb = GetStringBuilder();).An async method with a
void return type does not follow the task asynchronous programming
(TAP) model since the return type should be Task or Task<TResult>
Doing so prevents control over the asynchronous execution, such as:
EventHandler delegate signature
Using void for EventHandler is compliant with the TAP model.
public async void button1_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
}
On[A-Z]\w* pattern Some frameworks may not use the same EventHandler method signature.
public async void OnClick(EventContext data)
{
await DoSomethingAsync();
}
Update the return type of the method from void to Task.
private async void ThrowExceptionAsync() // Noncompliant: async method return type is 'void'
{
throw new InvalidOperationException();
}
public void Method()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here
throw;
}
}
private async Task ThrowExceptionAsync() // Compliant: async method return type is 'Task'
{
throw new InvalidOperationException();
}
public async Task Method()
{
try
{
await ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is caught here
throw;
}
}
async (C#
Reference)Task ClassTask<TResult>
ClassEventHandler DelegateThere’s no point in chaining multiple OrderBy calls in a LINQ; only the last one will be reflected in the result because each
subsequent call completely reorders the list. Thus, calling OrderBy multiple times is a performance issue as well, because all of the
sorting will be executed, but only the result of the last sort will be kept.
Instead, use ThenBy for each call after the first.
var x = personList .OrderBy(person => person.Age) .OrderBy(person => person.Name) // Noncompliant .ToList(); // x is sorted by Name, not sub-sorted
var x = personList .OrderBy(person => person.Age) .ThenBy(person => person.Name) .ToList();
| Method | Runtime | Mean | StdDev | Allocated |
|---|---|---|---|---|
|
OrderByAge |
.NET 9.0 |
12.84 ms |
0.804 ms |
1.53 MB |
|
OrderByAgeOrderBySize |
.NET 9.0 |
24.08 ms |
0.267 ms |
3.05 MB |
|
OrderByAgeThenBySize |
.NET 9.0 |
18.58 ms |
0.747 ms |
1.91 MB |
|
OrderByAge |
.NET Framework 4.8.1 |
22.99 ms |
0.228 ms |
1.53 MB |
|
OrderByAgeOrderBySize |
.NET Framework 4.8.1 |
44.90 ms |
0.581 ms |
4.3 MB |
|
OrderByAgeThenBySize |
.NET Framework 4.8.1 |
31.72 ms |
0.402 ms |
1.91 MB |
The results were generated by running the following snippet with BenchmarkDotNet:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Size { get; set; }
}
private Random random = new Random(1);
private Consumer consumer = new Consumer();
private Person[] array;
[Params(100_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
array = Enumerable.Range(0, N).Select(x => new Person
{
Name = Path.GetRandomFileName(),
Age = random.Next(0, 100),
Size = random.Next(0, 200)
}).ToArray();
}
[Benchmark(Baseline = true)]
public void OrderByAge() =>
array.OrderBy(x => x.Age).Consume(consumer);
[Benchmark]
public void OrderByAgeOrderBySize() =>
array.OrderBy(x => x.Age).OrderBy(x => x.Size).Consume(consumer);
[Benchmark]
public void OrderByAgeThenBySize() =>
array.OrderBy(x => x.Age).ThenBy(x => x.Size).Consume(consumer);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) Intel Core Ultra 7 165H, 1 CPU, 22 logical and 16 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S3169.json ================================================ { "title": "Multiple \"OrderBy\" calls should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3169", "sqKey": "S3169", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S3172.html ================================================
In C#, delegates can be added together to chain their execution, and subtracted to remove their execution from the chain.
Subtracting a chain of delegates from another one might yield unexpected results as shown hereunder - and is likely to be a bug.
MyDelegate first, second, third, fourth;
first = () => Console.Write("1");
second = () => Console.Write("2");
third = () => Console.Write("3");
fourth = () => Console.Write("4");
MyDelegate chain1234 = first + second + third + fourth; // Compliant - chain sequence = "1234"
MyDelegate chain12 = chain1234 - third - fourth; // Compliant - chain sequence = "12"
MyDelegate chain14 = first + fourth; // creates a new MyDelegate instance which is a list under the covers
MyDelegate chain23 = chain1234 - chain14; // Noncompliant; (first + fourth) doesn't exist in chain1234
// The chain sequence of "chain23" will be "1234" instead of "23"!
// Indeed, the sequence "1234" does not contain the subsequence "14", so nothing is subtracted
// (but note that "1234" contains both the "1" and "4" subsequences)
chain23 = chain1234 - (first + fourth); // Noncompliant
chain23(); // will print "1234"!
MyDelegate chain23 = chain1234 - first - fourth; // Compliant - "1" is first removed, followed by "4" chain23(); // will print "23"================================================ FILE: analyzers/rspec/cs/S3172.json ================================================ { "title": "Delegates should not be subtracted", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "30min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3172", "sqKey": "S3172", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3215.html ================================================
Needing to cast from an interface to a concrete type indicates that something is wrong with the abstractions in use, likely that
something is missing from the interface. Instead of casting to a discrete type, the missing functionality should be added to the
interface. Otherwise there is a risk of runtime exceptions.
public interface IMyInterface
{
void DoStuff();
}
public class MyClass1 : IMyInterface
{
public int Data { get { return new Random().Next(); } }
public void DoStuff()
{
// TODO...
}
}
public static class DowncastExampleProgram
{
static void EntryPoint(IMyInterface interfaceRef)
{
MyClass1 class1 = (MyClass1)interfaceRef; // Noncompliant
int privateData = class1.Data;
class1 = interfaceRef as MyClass1; // Noncompliant
if (class1 != null)
{
// ...
}
}
}
Casting to object doesn’t raise an issue, because it can never fail.
static void EntryPoint(IMyInterface interfaceRef)
{
var o = (object)interfaceRef;
...
}
================================================
FILE: analyzers/rspec/cs/S3215.json
================================================
{
"title": "\"interface\" instances should not be cast to concrete types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1h"
},
"tags": [
"design"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-3215",
"sqKey": "S3215",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3216.html
================================================
After an awaited Task has executed, you can continue execution in the original, calling thread or any arbitrary thread.
Unless the rest of the code needs the context from which the Task was spawned, Task.ConfigureAwait(false) should be used to
keep execution in the Task thread to avoid the need for context switching and the possibility of deadlocks.
This rule raises an issue when code in a class library targeting .Net Framework awaits a Task and continues execution in
the original calling thread.
The rule does not raise for .Net Core libraries as there is no SynchronizationContext in .Net Core.
var response = await httpClient.GetAsync(url); // Noncompliant
var response = await httpClient.GetAsync(url).ConfigureAwait(false);================================================ FILE: analyzers/rspec/cs/S3216.json ================================================ { "title": "\"ConfigureAwait(false)\" should be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "COMPLETE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "15min" }, "tags": [ "multi-threading", "async-await", "suspicious", "performance" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-3216", "sqKey": "S3216", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3217.html ================================================
The foreach
statement was introduced in the C# language prior to generics to make it easier to work with the non-generic collections available at that time such
as ArrayList. The foreach statements allow you to
downcast elements of a collection of Objects to any other type.
The problem is that to achieve the cast, the foreach statements silently perform explicit type
conversion, which at runtime can result in an InvalidCastException.
C# code iterating on generic collections or arrays should not rely on foreach statement’s silent explicit
conversions.
public class Fruit { }
public class Orange : Fruit { }
public class Apple : Fruit { }
class MyTest
{
public void Test()
{
var fruitBasket = new List<Fruit>();
fruitBasket.Add(new Orange());
fruitBasket.Add(new Orange());
fruitBasket.Add(new Apple());
foreach (Orange orange in fruitBasket) // Noncompliant
{
//...
}
}
}
public class Fruit { }
public class Orange : Fruit { }
public class Apple : Fruit { }
class MyTest
{
public void Test()
{
var fruitBasket = new List<Fruit>();
fruitBasket.Add(new Orange());
fruitBasket.Add(new Orange());
fruitBasket.Add(new Apple());
foreach (Orange orange in fruitBasket.OfType<Orange>())
{
//...
}
}
}
The rule ignores iterations on collections of objects. This includes legacy code that uses ArrayList. Furthermore, the
rule does not report on cases when user-defined conversions are being called.
Naming the members of an inner class the same as the static members of its enclosing class is possible but generally considered a bad practice. That’s because maintainers may be confused about which members are being used in a given context. Instead the inner class member should be given distinct and descriptive name, and all references to it should be updated accordingly.
class Outer
{
public static int A;
public class Inner
{
public int A; // Noncompliant
public int MyProp
{
get => A; // Returns inner A. Was that intended?
}
}
}
Here’s an example of compliant code after renaming the inner class member, this way the property will return the Outer A:
class Outer
{
public static int A;
public class Inner
{
public int B; // Compliant
public int MyProp
{
get => A; // Returns outer A
}
}
}
Or if you want to reference the Inner A field:
class Outer
{
public static int B;
public class Inner
{
public int A; // Compliant
public int MyProp
{
get => A; // Returns inner A
}
}
}
The rules for method resolution are complex and perhaps not properly understood by all coders. The params keyword can make method
declarations overlap in non-obvious ways, so that slight changes in the argument types of an invocation can resolve to different methods.
This rule raises an issue when an invocation resolves to a method declaration with params, but could also resolve to another
non-params method too.
public class MyClass
{
private void Format(string a, params object[] b) { }
private void Format(object a, object b, object c) { }
}
// ...
MyClass myClass = new MyClass();
myClass.Format("", null, null); // Noncompliant, resolves to the first Format with params, but was that intended?
================================================
FILE: analyzers/rspec/cs/S3220.json
================================================
{
"title": "Method calls should not resolve ambiguously to overloads with \"params\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3220",
"sqKey": "S3220",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3234.html
================================================
GC.SuppressFinalize asks the Common Language Runtime not to call the finalizer of an object. This is useful when implementing the
dispose pattern where object finalization is already handled in IDisposable.Dispose. However, it has no effect if there is no finalizer
defined in the object’s type, so using it in such cases is just confusing.
This rule raises an issue when GC.SuppressFinalize is called for objects of sealed types without a finalizer.
Note: {rule:csharpsquid:S3971} is a stricter version of this rule. Typically it makes sense to activate only one of these 2 rules.
sealed class MyClass
{
public void Method()
{
...
GC.SuppressFinalize(this); //Noncompliant
}
}
sealed class MyClass
{
public void Method()
{
...
}
}
================================================
FILE: analyzers/rspec/cs/S3234.json
================================================
{
"title": "\"GC.SuppressFinalize\" should not be invoked for types without destructors",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused",
"confusing"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3234",
"sqKey": "S3234",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3235.html
================================================
Redundant parentheses are simply wasted keystrokes, and should be removed.
[MyAttribute()] //Noncompliant
class MyClass
{
public int MyProperty { get; set; }
public static MyClass CreateNew(int propertyValue)
{
return new MyClass() //Noncompliant
{
MyProperty = propertyValue
};
}
}
[MyAttribute]
class MyClass
{
public int MyProperty { get; set; }
public static MyClass CreateNew(int propertyValue)
{
return new MyClass
{
MyProperty = propertyValue
};
}
}
================================================
FILE: analyzers/rspec/cs/S3235.json
================================================
{
"title": "Redundant parentheses should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused",
"finding"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3235",
"sqKey": "S3235",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3236.html
================================================
Caller information attributes: CallerFilePathAttribute, CallerLineNumberAttribute, and
CallerArgumentExpressionAttribute provide a way to get information about the caller of a method through optional parameters. But the
arguments for these optional parameters are only generated if they are not explicitly defined in the call. Thus, specifying the argument values
defeats the purpose of the attributes.
void TraceMessage(string message,
[CallerFilePath] string filePath = null,
[CallerLineNumber] int lineNumber = 0)
{
/* ... */
}
void MyMethod()
{
TraceMessage("my message", "A.B.C.Foo.cs", 42); // Noncompliant
}
void TraceMessage(string message,
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
/* ... */
}
void MyMethod()
{
TraceMessage("my message");
}
CallerMemberName is not checked to avoid False-Positives with WPF/UWP applications.System.Diagnostics.Debug.Assert is excluded as a custom message from the developer is sometimes preferred as an explanation of the
failed assertion.When you need to get external input for set and init methods defined for properties and indexers or for
remove and add methods for events, you should always get this input throught the value contextual keyword.
The contextual keyword value is similar to an input parameter of a method; it references the value that the client code is attempting
to assign to the property, indexer or event.
The keyword value holds the value the accessor was called with. Not using it means that the accessor ignores the caller’s intent which
could cause unexpected results at runtime.
private int count;
public int Count
{
get { return count; }
set { count = 42; } // Noncompliant
}
private int count;
public int Count
{
get { return count; }
set { count = value; }
}
This rule doesn’t raise an issue when the setter is empty and part of the implementation of an interface. The assumption is that this
part of the interface is not meaningful to that particular implementation. A good example of that would be a "sink" logger that discards any logs.
In the interests of keeping code clean, the simplest possible conditional syntax should be used. That means
??= operator for a self-assign-if-not-null operation,?? operator for an assign-if-not-null operation, and?: for assignment to a single variable.
object a = null, b = null, x;
if (a != null) // Noncompliant; needlessly verbose
{
x = a;
}
else
{
x = b;
}
x = a != null ? a : b; // Noncompliant; better but could still be simplified
x = (a == null) ? new object() : a; // Noncompliant
if (condition) // Noncompliant
{
x = a;
}
else
{
x = b;
}
if (a == null) // Noncompliant
a = new object();
var y = null ?? new object(); // Noncompliant
a = a ?? new object(); // Noncompliant for C# 8
object x; x = a ?? b; x = a ?? b; x = a ?? new object(); x = condition ? a : b; a ??= new object(); var y = new object(); a ??= new object();================================================ FILE: analyzers/rspec/cs/S3240.json ================================================ { "title": "The simplest possible condition syntax should be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3240", "sqKey": "S3240", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S3241.html ================================================
Private methods are intended for use only within their scope. If these methods return values that are not utilized by any calling functions, it indicates that the return operation is unnecessary. Removing such returns can enhance both efficiency and code clarity.
class SomeClass
{
private int PrivateMethod() => 42;
public void PublicMethod()
{
PrivateMethod(); // Noncompliant: the result of PrivateMethod is not used
}
}
================================================
FILE: analyzers/rspec/cs/S3241.json
================================================
{
"title": "Methods should not return values that are never used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"design",
"unused"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3241",
"sqKey": "S3241",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3242.html
================================================
When a derived type is used as a parameter instead of the base type, it limits the uses of the method. If the additional functionality that is provided in the derived type is not required then that limitation isn’t required, and should be removed.
This rule raises an issue when a method declaration includes a parameter that is a derived type and accesses only members of the base type.
using System;
using System.IO;
namespace MyLibrary
{
public class Foo
{
public void ReadStream(FileStream stream) // Noncompliant: Uses only System.IO.Stream methods
{
int a;
while ((a = stream.ReadByte()) != -1)
{
// Do something.
}
}
}
}
using System;
using System.IO;
namespace MyLibrary
{
public class Foo
{
public void ReadStream(Stream stream)
{
int a;
while ((a = stream.ReadByte()) != -1)
{
// Do something.
}
}
}
}
================================================
FILE: analyzers/rspec/cs/S3242.json
================================================
{
"title": "Method parameters should be declared with base types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3242",
"sqKey": "S3242",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3244.html
================================================
When working with anonymous functions, it is important to keep in mind that each time you create one, it is a completely new instance.
In this example, even though the same lambda expression is used, the expressions are stored separately in the memory and are therefore not equal or the same.
Func<int, int> lambda1 = x => x + 1; Func<int, int> lambda2 = x => x + 1; var result = lambda1 == lambda2; // result is false here
This is even more true when working with events since they are multicast delegates that offer ways of subscribing and unsubscribing to them. If an anonymous function is used to subscribe to an event, it is impossible to unsubscribe from it. This happens because to remove the entry from the subscription list, a reference to the original method is needed, but if the anonymous function has not been stored before subscribing, there is no way to find a reference to it.
Instead, store the callback to a variable or a named method and use the variable or method to subscribe and unsubscribe.
Store the callback to a variable or a named method and use the variable or method to subscribe and unsubscribe.
event EventHandler myEvent;
void DoWork()
{
myEvent += (s, e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!");
// ...
myEvent -= (s, e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!"); // Noncompliant: this callback was never subscribed
}
event EventHandler myEvent;
void LogEvent(object s, EventArgs e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!");
void DoWork()
{
myEvent += LogEvent;
// ...
myEvent -= LogEvent; // Compliant: LogEvent points to the same callback used for subscribing
}
In the interests of making code as usable as possible, interfaces and delegates with generic parameters should use the out and
in modifiers when possible to make the interfaces and delegates covariant and contravariant, respectively.
The out keyword can be used when the type parameter is used only as a return type in the interface or delegate. Doing so makes the
parameter covariant, and allows interface and delegate instances created with a sub-type to be used as instances created with a base type. The most
notable example of this is IEnumerable<out T>, which allows the assignment of an IEnumerable<string> instance to
an IEnumerable<object> variable, for instance.
The in keyword can be used when the type parameter is used only as a method parameter in the interface or a parameter in the delegate.
Doing so makes the parameter contravariant, and allows interface and delegate instances created with a base type to be used as instances created with
a sub-type. I.e. this is the inversion of covariance. The most notable example of this is the Action<in T> delegate, which allows
the assignment of an Action<object> instance to a Action<string> variable, for instance.
interface IConsumer<T> // Noncompliant
{
bool Eat(T fruit);
}
interface IConsumer<in T>
{
bool Eat(T fruit);
}
================================================
FILE: analyzers/rspec/cs/S3246.json
================================================
{
"title": "Generic type parameters should be co\/contravariant when possible",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "MODULAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3246",
"sqKey": "S3246",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3247.html
================================================
In C#, the is
type testing operator can be used to check if the run-time type of an object is compatible with a given type. If the object is not null, then the
is operator performs a cast, and so performing another cast following the check result is redundant.
This can impact:
Use pattern macthing to perform the check and retrieve the cast result.
if (x is Fruit) // Noncompliant
{
var f = (Fruit)x; // or x as Fruit
// ...
}
if (x is Fruit fruit)
{
// ...
}
is, as, typeof and casts| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
IsPattern_Class |
.NET 9.0 |
176.48 ns |
0.765 ns |
|
IsWithCast_Class |
.NET 9.0 |
246.12 ns |
22.391 ns |
|
IsPattern_Class |
.NET Framework 4.8.1 |
325.11 ns |
14.435 ns |
|
IsWithCast_Class |
.NET Framework 4.8.1 |
311.22 ns |
11.145 ns |
|
IsPattern_Interface |
.NET 9.0 |
26.77 ns |
1.123 ns |
|
IsWithCast_Interface |
.NET 9.0 |
26.45 ns |
2.115 ns |
|
IsPattern_Interface |
.NET Framework 4.8.1 |
119.80 ns |
5.411 ns |
|
IsWithCast_Interface |
.NET Framework 4.8.1 |
119.33 ns |
4.380 ns |
|
IsPattern_ValueType |
.NET 9.0 |
22.58 ns |
1.161 ns |
|
IsWithCast_ValueType |
.NET 9.0 |
19.41 ns |
2.675 ns |
|
IsPattern_ValueType |
.NET Framework 4.8.1 |
39.66 ns |
0.645 ns |
|
IsWithCast_ValueType |
.NET Framework 4.8.1 |
41.34 ns |
0.462 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private Random random = new Random(1);
private object ReturnSometimes<T>() where T : new() =>
random.Next(2) switch
{
0 => new T(),
1 => new object(),
};
[BenchmarkCategory("ValueType"), Benchmark(Baseline = true)]
public int IsPattern_ValueType()
{
var i = ReturnSometimes<int>();
return i is int d
? d
: default;
}
[BenchmarkCategory("ValueType"), Benchmark]
public int IsWithCast_ValueType()
{
var i = ReturnSometimes<int>();
return i is int
? (int)i
: default;
}
[BenchmarkCategory("Class"), Benchmark(Baseline = true)]
public DuplicateCasts IsPattern_Class()
{
var i = ReturnSometimes<DuplicateCasts>();
return i is DuplicateCasts d
? d
: default;
}
[BenchmarkCategory("Class"), Benchmark]
public DuplicateCasts IsWithCast_Class()
{
var i = ReturnSometimes<DuplicateCasts>();
return i is DuplicateCasts
? (DuplicateCasts)i
: default;
}
[BenchmarkCategory("Interface"), Benchmark(Baseline = true)]
public IReadOnlyList<int> IsPattern_Interface()
{
var i = ReturnSometimes<List<int>>();
return i is IReadOnlyList<int> d
? d
: default;
}
[BenchmarkCategory("Interface"), Benchmark]
public IReadOnlyList<int> IsWithCast_Interface()
{
var i = ReturnSometimes<List<int>>();
return i is IReadOnlyList<int>
? (IReadOnlyList<int>)i
: default;
}
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) Intel Core Ultra 7 165H, 1 CPU, 22 logical and 16 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S3247.json ================================================ { "title": "Duplicate casts should not be made", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3247", "sqKey": "S3247", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3249.html ================================================
Making a base call when overriding a method is generally a good idea, but not in the case of GetHashCode and Equals for classes that directly extend Object.
These methods are based on the object’s reference, meaning that no two objects that use those base methods can be equal or have the same
hash.
This rule doesn’t report on guard conditions checking for reference equality. For example:
public override bool Equals(object obj)
{
if (base.Equals(obj)) // Compliant, it's a guard condition.
{
return true;
}
...
}
var m1 = new MyClass(2);
var m2 = new MyClass(2);
m1.Equals(m2) // False
m1.GetHashCode(); // 43942919
m2.GetHashCode(); // 59941935
class MyClass
{
private readonly int x;
public MyClass(int x) =>
this.x = x;
public override bool Equals(Object obj) =>
base.Equals();
public override int GetHashCode() =>
x.GetHashCode() ^ base.GetHashCode(); // Noncompliant, base.GetHashCode returns a code based on the objects reference
}
var m1 = new MyClass(2);
var m2 = new MyClass(2);
m1.Equals(m2) // True
m1.GetHashCode(); // 2
m2.GetHashCode(); // 2
class MyClass
{
private readonly int x;
public MyClass(int x) =>
this.x = x;
public override bool Equals(Object obj) =>
this.x == ((MyClass)obj).x;
public override int GetHashCode() =>
x.GetHashCode()
}
partial methods allow an increased degree of flexibility in programming a system. Hooks can be added to generated code by invoking
methods that define their signature, but might not have an implementation yet. But if the implementation is still missing when the code makes it to
production, the compiler silently removes the call. In the best case scenario, such calls simply represent cruft, but in they worst case they are
critical, missing functionality, the loss of which will lead to unexpected results at runtime.
This rule raises an issue for partial methods for which no implementation can be found in the assembly.
partial class C
{
partial void M(); //Noncompliant
void OtherM()
{
M(); //Noncompliant. Will be removed.
}
}
================================================
FILE: analyzers/rspec/cs/S3251.json
================================================
{
"title": "Implementations should be provided for \"partial\" methods",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"suspicious"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3251",
"sqKey": "S3251",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3253.html
================================================
Since the compiler will automatically invoke the base type’s no-argument constructor, there’s no need to specify its invocation explicitly. Also,
when only a single public parameterless constructor is defined in a class, then that constructor can be removed because the compiler
would generate it automatically. Similarly, empty static constructors and empty destructors are also wasted keystrokes.
class X
{
public X() { } // Noncompliant
static X() { } // Noncompliant
~X() { } // Noncompliant
...
}
class Y : X
{
public Y(int parameter) : base() // Noncompliant
{
/* does something with the parameter */
}
}
class X
{
...
}
class Y : X
{
public Y(int parameter)
{
/* does something with the parameter */
}
}
================================================
FILE: analyzers/rspec/cs/S3253.json
================================================
{
"title": "Constructor and destructor declarations should not be redundant",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3253",
"sqKey": "S3253",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3254.html
================================================
Specifying the default parameter values in a method call is redundant. Such values should be omitted in the interests of readability.
public void M(int x, int y=5, int z = 7) { /* ... */ }
// ...
M(1, 5); // Noncompliant, y has the default value
M(1, z: 7); // Noncompliant, z has the default value
public void M(int x, int y=5, int z = 7) { /* ... */ }
// ...
M(1);
M(1);
================================================
FILE: analyzers/rspec/cs/S3254.json
================================================
{
"title": "Default parameter values should not be passed as arguments",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3254",
"sqKey": "S3254",
"scope": "Main",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3256.html
================================================
Using string.Equals to determine if a string is empty is significantly slower than using string.IsNullOrEmpty() or
checking for string.Length == 0. string.IsNullOrEmpty() is both clear and concise, and therefore preferred to laborious,
error-prone, manual null- and emptiness-checking.
"".Equals(name); // Noncompliant
!name.Equals(""); // Noncompliant
name.Equals(string.Empty); // Noncompliant
name != null && name.Length > 0 // Compliant but more error prone !string.IsNullOrEmpty(name) string.IsNullOrEmpty(name)================================================ FILE: analyzers/rspec/cs/S3256.json ================================================ { "title": "\"string.IsNullOrEmpty\" should be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3256", "sqKey": "S3256", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3257.html ================================================
In C#, the type of a variable can often be inferred by the compiler. The use of the [var keyword](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/implicitly-typed-local-variables) allows you to avoid repeating the type name in a variable declaration and object instantiation because the declared type can often be inferred by the compiler.
Additionally, initializations providing the default value can also be omitted, helping to make the code more concise and readable.
Unnecessarily verbose declarations and initializations should be simplified. Specifically, the following should be omitted when they can be inferred:
new DelegateTypenew Nullable<Type>Remove any unneeded code. C# provides many features designed to help you write more concise code.
var l = new List<int>() {}; // Noncompliant, {} can be removed
var o = new object() {}; // Noncompliant, {} can be removed
var ints = new int[] {1, 2, 3}; // Noncompliant, int can be omitted
ints = new int[3] {1, 2, 3}; // Noncompliant, the size specification can be removed
int? i = new int?(5); // Noncompliant new int? could be omitted, it can be inferred from the declaration, and there's implicit conversion from T to T?
var j = new int?(5);
Func<int, int> f1 = (int i) => 1; //Noncompliant, can be simplified
class Class
{
private event EventHandler MyEvent;
public Class()
{
MyEvent += new EventHandler((a,b)=>{ }); // Noncompliant, needlessly verbose
}
}
var l = new List<int>();
var o = new object();
var ints = new [] {1, 2, 3};
ints = new [] {1, 2, 3};
int? i = 5;
var j = new int?(5);
Func<int, int> f1 = (i) => 1;
class Class
{
private event EventHandler MyEvent;
public Class()
{
MyEvent += (a,b)=>{ };
}
}
Classes and records with either private or file access modifiers aren’t visible outside of their assemblies or files, so
if they’re not extended inside their scope, they should be made explicitly non-extensible with the addition of the sealed keyword.
We measured at least 4x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
The code can be improved by adding the sealed keyword in front of the class or record types that have no
inheritors.
private class MyClass // Noncompliant
{
// ...
}
private record MyRecord // Noncompliant
{
// ...
}
file class MyClass // Noncompliant
{
// ...
}
file record MyRecord // Noncompliant
{
// ...
}
private sealed class MyClass
{
// ...
}
private sealed record MyRecord
{
// ...
}
file sealed class MyClass
{
// ...
}
file sealed record MyRecord
{
// ...
}
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
UnsealedType |
.NET 5.0 |
918.7 us |
10.72 us |
|
SealedType |
.NET 5.0 |
231.2 us |
3.20 us |
|
UnsealedType |
.NET 6.0 |
867.9 us |
5.65 us |
|
SealedType |
.NET 6.0 |
218.4 us |
0.59 us |
|
UnsealedType |
.NET 7.0 |
1,074.5 us |
3.15 us |
|
SealedType |
.NET 7.0 |
216.1 us |
1.19 us |
The results were generated by running the following snippet with BenchmarkDotNet:
[Params(1_000_000)]
public int Iterations { get; set; }
private readonly UnsealedClass unsealedType = new UnsealedClass();
private readonly SealedClass sealedType = new SealedClass();
[Benchmark(Baseline = true)]
public void UnsealedType()
{
for (int i = 0; i < Iterations; i++)
{
unsealedType.DoNothing();
}
}
[Benchmark]
public void SealedType()
{
for (int i = 0; i < Iterations; i++)
{
sealedType.DoNothing();
}
}
private class BaseType
{
public virtual void DoNothing() { }
}
private class UnsealedClass : BaseType
{
public override void DoNothing() { }
}
private sealed class SealedClass : BaseType
{
public override void DoNothing() { }
}
Hardware Configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2 .NET 6.0 : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S3260.json ================================================ { "title": "Non-derived \"private\" classes and records should be \"sealed\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3260", "sqKey": "S3260", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3261.html ================================================
Namespaces with no lines of code clutter a project and should be removed.
namespace MyEmptyNamespace // Noncompliant
{
}
================================================
FILE: analyzers/rspec/cs/S3261.json
================================================
{
"title": "Namespaces should not be empty",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3261",
"sqKey": "S3261",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3262.html
================================================
Overriding methods automatically inherit the params behavior. To ease readability, this modifier should be explicitly used in the
overriding method as well.
class Base
{
public virtual void Method(params int[] numbers)
{
...
}
}
class Derived : Base
{
public override void Method(int[] numbers) // Noncompliant, the params is missing.
{
...
}
}
class Base
{
public virtual void Method(params int[] numbers)
{
...
}
}
class Derived : Base
{
public override void Method(params int[] numbers)
{
...
}
}
================================================
FILE: analyzers/rspec/cs/S3262.json
================================================
{
"title": "\"params\" should be used on overrides",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3262",
"sqKey": "S3262",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3263.html
================================================
Static field initializers are executed in the order in which they appear in the class from top to bottom. Thus, placing a static field in a class above the field or fields required for its initialization will yield unexpected results.
class MyClass
{
public static int X = Y; // Noncompliant; Y at this time is still assigned default(int), i.e. 0
public static int Y = 42;
}
class MyClass
{
public static int Y = 42;
public static int X = Y;
}
or
class MyClass
{
public static int X;
public static int Y = 42;
static MyClass()
{
X = Y;
}
}
================================================
FILE: analyzers/rspec/cs/S3263.json
================================================
{
"title": "Static fields should appear in the order they must be initialized ",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3263",
"sqKey": "S3263",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3264.html
================================================
Events that are not invoked anywhere are dead code, and there’s no good reason to keep them in the source.
class UninvokedEventSample
{
private event Action<object, EventArgs> Happened; // Noncompliant
public void RegisterEventHandler(Action<object, EventArgs> handler)
{
Happened += handler; // we register some event handlers
}
public void RaiseEvent()
{
if (Happened != null)
{
// Happened(this, null); // the event is never triggered, because this line is commented out.
}
}
}
================================================
FILE: analyzers/rspec/cs/S3264.json
================================================
{
"title": "Events should be invoked",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"unused"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3264",
"sqKey": "S3264",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3265.html
================================================
Enumerations are commonly used to identify distinct elements from a set of values.
However, they can also serve as bit flags, enabling bitwise operations to combine multiple elements within a single value.
// Saturday = 0b00100000, Sunday = 0b01000000, weekend = 0b01100000 var weekend = Days.Saturday | Days.Sunday; // Combining elements
When enumerations are used as bit flags, it is considered good practice to annotate the enum type with the FlagsAttribute:
enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}
// ...
var x = Permissions.Read | Permissions.Write; // Noncompliant: enum is not annotated with [Flags]
The FlagsAttribute explicitly marks an enumeration as bit flags, making it clear that it uses bit fields and is intended to be used as
flags.
[Flags]
enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}
// ...
var x = Permissions.Read | Permissions.Write; // Compliant: enum is annotated with [Flags]
Additionally, adding the FlagsAttribute to the enumeration enable a better string representation when using the Enum.ToString method.
This rule will not raise for MethodImplAttribute because,
despite not having the FlagsAttribute, it is often used in a similar fashion.
Using explicit loops for filtering, selecting, or aggregating elements can make code more verbose and harder to read. LINQ expressions provide a more concise and expressive way to perform these operations, improving code clarity and maintainability.
If the affected code is part of a performance-critical hot path and that the fix would negatively impact performance, you can self-declare the
PerformanceSensitiveAttribute in your codebase, or use the one provided by Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers:
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
public sealed class PerformanceSensitiveAttribute() : Attribute;
[PerformanceSensitiveAttribute]
List<string> Method(IEnumerable<string> collection, Predicate<string> condition)
{
var result = new List<string>();
foreach (var element in collection) // Without the attribute, this would raise an issue
{
if (condition(element))
{
result.Add(element);
}
}
return result;
}
The rule will respect the AllowGenericEnumeration
property:
[PerformanceSensitive("Enumeration", AllowGenericEnumeration = true)]
List<string> Method(IEnumerable<string> collection, Predicate<string> condition) { }
In this case, the rule will not be disabled even if the method is marked with the PerformanceSensitiveAttribute attribute.
Replace explicit loops and conditional blocks with equivalent LINQ expressions.
Use the System.Linq.Async package to enable LINQ operations on IAsyncEnumerable prior to .NET 10.
List<string> Method(IEnumerable<string> collection, Predicate<string> condition)
{
var result = new List<string>();
foreach (var element in collection) // Noncompliant
{
if (condition(element))
{
result.Add(element);
}
}
return result;
}
List<string> Method(IEnumerable<MyDto> collection)
{
var result = new List<string>();
foreach (var element in collection) // Noncompliant
{
var someValue = element.Property;
if (someValue != null)
{
result.Add(someValue);
}
}
return result;
}
public void Method(List<string> list)
{
foreach (var element in list)
{
var someValue = element.Length;
}
}
async void Method(IAsyncEnumerable<int> collection)
{
await foreach (var element in collection)
{
if (element is 42)
{
Console.WriteLine("The meaning of Life.");
}
}
}
List<string> Method(IEnumerable<string> collection, Predicate<string> condition) => collection.Where(x => condition(x)).ToList();
List<string> Method(IEnumerable<MyDto> collection) => collection.Select(x => x.Property).Where(y => y != null).ToList();
void Method(List<int> list)
{
foreach (var length in list.Select(x => x.Length))
{
var someValue = length;
}
}
async void Method(IAsyncEnumerable<int> collection)
{
await foreach (var element in collection.Where(x => x is 42)))
{
Console.WriteLine("The meaning of Life.");
}
}
This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
In the mode Cipher Block Chaining (CBC), each block is used as cryptographic input for the next block. For this reason, the first block requires an initialization vector (IV), also called a "starting variable" (SV).
If the same IV is used for multiple encryption sessions or messages, each new encryption of the same plaintext input would always produce the same ciphertext output. This may allow an attacker to detect patterns in the ciphertext.
After retrieving encrypted data and performing cryptographic attacks on it on a given timeframe, attackers can recover the plaintext that encryption was supposed to protect.
Depending on the recovered data, the impact may vary.
Below are some real-world scenarios that illustrate the potential impact of an attacker exploiting the vulnerability.
By modifying the plaintext of the encrypted message, an attacker may be able to trigger additional vulnerabilities in the code. An attacker can
further exploit a system to obtain more information.
Encrypted values are often considered trustworthy because it would not be possible for a third party to modify them under normal circumstances.
When encrypted data contains personal or sensitive information, its retrieval by an attacker can lead to privacy violations, identity theft, financial loss, reputational damage, or unauthorized access to confidential systems.
In this scenario, a company, its employees, users, and partners could be seriously affected.
The impact is twofold, as data breaches and exposure of encrypted data can undermine trust in the organization, as customers, clients and stakeholders may lose confidence in the organization’s ability to protect their sensitive data.
In many industries and locations, there are legal and compliance requirements to protect sensitive data. If encrypted data is compromised and the plaintext can be recovered, companies face legal consequences, penalties, or violations of privacy laws.
using System.IO;
using System.Security.Cryptography;
public void Encrypt(byte[] key, byte[] dataToEncrypt, MemoryStream target)
{
var aes = new AesCryptoServiceProvider();
byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
var encryptor = aes.CreateEncryptor(key, iv); // Noncompliant
var cryptoStream = new CryptoStream(target, encryptor, CryptoStreamMode.Write);
var swEncrypt = new StreamWriter(cryptoStream);
swEncrypt.Write(dataToEncrypt);
}
In this example, the code implicitly uses a number generator that is considered strong, thanks to aes.IV.
using System.IO;
using System.Security.Cryptography;
public void Encrypt(byte[] key, byte[] dataToEncrypt, MemoryStream target)
{
var aes = new AesCryptoServiceProvider();
var encryptor = aes.CreateEncryptor(key, aes.IV);
var cryptoStream = new CryptoStream(target, encryptor, CryptoStreamMode.Write);
var swEncrypt = new StreamWriter(cryptoStream);
swEncrypt.Write(dataToEncrypt);
}
To ensure high security, initialization vectors must meet two important criteria:
The IV does not need be secret, so the IV or information sufficient to determine the IV may be transmitted along with the ciphertext.
In the previous non-compliant example, the problem is not that the IV is hard-coded.
It is that the same IV is used for multiple encryption attempts.
When a cookie is configured with the HttpOnly attribute set to true, the browser guaranties that no client-side script will
be able to read it. In most cases, when a cookie is created, the default value of HttpOnly is false and it’s up to the developer
to decide whether or not the content of the cookie can be read by the client-side script. As a majority of Cross-Site Scripting (XSS) attacks target
the theft of session-cookies, the HttpOnly attribute can help to reduce their impact as it won’t be possible to exploit the XSS
vulnerability to steal session-cookies.
HttpOnly attribute offer an additional protection (not the case for an XSRF-TOKEN cookie / CSRF token for
example)There is a risk if you answered yes to any of those questions.
HttpOnly flag should be set to true for most of the cookies and it’s mandatory for session /
sensitive-security cookies.When the HttpCookie.HttpOnly property is set to false then the cookie can be accessed by client side code:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
myCookie.HttpOnly = false; // Sensitive: this cookie is created with the httponly flag set to false and so it can be stolen easily in case of XSS vulnerability
The default value of
HttpOnly flag is false, unless overwritten by an application’s configuration file:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
// Sensitive: this cookie is created without the httponly flag (by default set to false) and so it can be stolen easily in case of XSS vulnerability
Set the HttpCookie.HttpOnly property to true:
HttpCookie myCookie = new HttpCookie("Sensitive cookie");
myCookie.HttpOnly = true; // Compliant: the sensitive cookie is protected against theft thanks to the HttpOnly property set to true (HttpOnly = true)
Or change the default flag values for the whole application by editing the Web.config configuration file:
<httpCookies httpOnlyCookies="true" requireSSL="true" />
requireSSL attribute corresponds programmatically to the Secure field.httpOnlyCookies attribute corresponds programmatically to the httpOnly field.Caller information attributes provide a way to get information about the caller of a method through optional parameters. But they only work right if their values aren’t provided explicitly. So if you define a method with caller info attributes in the middle of the parameter list, the caller is forced to use named arguments if they want to use the method properly.
This rule raises an issue when the following attributes are used on parameters before the end of the parameter list:
Move the decorated parameters to the end of the parameter list.
void TraceMessage([CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0,
string message = null) // Noncompliant: decorated parameters appear before "message" parameter
{
/* ... */
}
void TraceMessage(string message = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
/* ... */
}
An assertion is a piece of code that’s used during development when the compilation debug mode is activated. It
allows a program to check itself as it runs. When an assertion is true, that means everything is operating as expected.
In non-debug mode, all Debug.Assert calls
are automatically left out (via the Conditional("DEBUG") mechanism). So, by
contract, the boolean expressions that are evaluated by those assertions must not contain any side effects. Otherwise, when leaving the debug mode, the functional behavior
of the application is not the same anymore.
The rule will raise if the method name starts with any of the following remove, delete, add,
pop, update, retain, insert, push, append, clear,
dequeue, enqueue, dispose, put, or set, although SetEquals will be
ignored.
In the following example, the assertion checks the return value of the remove method in the argument. Because the whole line is skipped in
non-debug builds, the call to Remove never happens in such builds.
Debug.Assert(list.Remove("dog"));
The Remove call must be extracted and the return value needs to be asserted instead.
bool result = list.Remove("dog");
Debug.Assert(result);
Debug.Assert
MethodConditional attributeIf a variable that is not supposed to change is not marked as const, it could be accidentally reassigned elsewhere in the code,
leading to unexpected behavior and bugs that can be hard to track down.
By declaring a variable as const, you ensure that its value remains constant throughout the code. It also signals to other developers
that this value is intended to remain constant. This can make the code easier to understand and maintain.
In some cases, using const can lead to performance improvements. The compiler might be able to make optimizations knowing that the
value of a const variable will not change.
Mark the given variable with the const modifier.
public bool Seek(int[] input)
{
var target = 32; // Noncompliant
foreach (int i in input)
{
if (i == target)
{
return true;
}
}
return false;
}
public bool Seek(int[] input)
{
const int target = 32;
foreach (int i in input)
{
if (i == target)
{
return true;
}
}
return false;
}
public class Sample
{
public void Method()
{
var context = $"{nameof(Sample)}.{nameof(Method)}"; // Noncompliant (C# 10 and above only)
}
}
public class Sample
{
public void Method()
{
const string context = $"{nameof(Sample)}.{nameof(Method)}";
}
}
Nested ternaries are hard to read and can make the order of operations complex to understand.
public string GetReadableStatus(Job j)
{
return j.IsRunning ? "Running" : j.HasErrors ? "Failed" : "Succeeded"; // Noncompliant
}
Instead, use another line to express the nested operation in a separate statement.
public string GetReadableStatus(Job j)
{
if (j.IsRunning)
{
return "Running";
}
return j.HasErrors ? "Failed" : "Succeeded";
}
================================================
FILE: analyzers/rspec/cs/S3358.json
================================================
{
"title": "Ternary operators should not be nested",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3358",
"sqKey": "S3358",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3363.html
================================================
You should only set a property of a temporal type (like DateTime or DateTimeOffset) as the primary key of a table if the
values are guaranteed to be unique.
Using temporal types as the primary key of a table is risky. When these types are used as primary keys, it usually means that each new key is
created with the use of .Now or .UtcNow properties from DateTime and DateTimeOffset classes. In
those cases, duplicate keys exceptions may occur in many ways:
DateTime type);The rule raises an issue if:
Id, <type name>Id or decorated by the [Key] or
[PrimaryKey] attribute.Either use a GUID or the auto generated ID as a primary key.
internal class Account
{
public DateTime Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
internal class Account
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
or
internal class Person
{
[Key]
public DateTime PersonIdentifier { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
internal class Person
{
[Key]
public Guid PersonIdentifier { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
In single-threaded environments, the use of this in constructors is normal, and expected. But in multi-threaded environments, it could
expose partially-constructed objects to other threads, and should be used with caution.
The classic example is a class with a static list of its instances. If the constructor stores this in the list, another
thread could access the object before it’s fully-formed. Even when the storage of this is the last instruction in the constructor,
there’s still a danger if the class is not final. In that case, the initialization of subclasses won’t be complete before
this is exposed.
This rule raises an issue when this is assigned to any globally-visible object in a constructor, and when it is passed to the method
of another object in a constructor
public class Monument
{
public static readonly List<Monument> ALL_MONUMENTS = new List<Monument>();
// ...
public Monument(string location, ...)
{
ALL_MONUMENTS.Add(this); // Noncompliant; passed to a method of another object
this.location = location;
// ...
}
}
This rule ignores instances of assigning this directly to a static field of the same class because that case is covered
by {rule:csharpsquid:S3010} .
Adherence to the standard naming conventions makes your code not only more readable, but more usable. For instance, class FirstAttribute :
Attribute can be used simply with First, but you must use the full name for class AttributeOne : Attribute.
This rule raises an issue when classes extending Attribute, EventArgs, or Exception, do not end with their
parent class names.
class AttributeOne : Attribute // Noncompliant
{
}
class FirstAttribute : Attribute
{
}
If a class' direct base class doesn’t follow the convention, then no issue is reported on the class itself, regardless of whether or not it conforms to the convention.
class Timeout : Exception // Noncompliant
{
}
class ExtendedTimeout : Timeout // Ignored; doesn't conform to convention, but the direct base doesn't conform either
{
}
================================================
FILE: analyzers/rspec/cs/S3376.json
================================================
{
"title": "Attribute, EventArgs, and Exception type names should end with the type being extended",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3376",
"sqKey": "S3376",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3397.html
================================================
object.Equals() overrides can be optimized by checking first for reference equality between this and the parameter. This
check can be implemented by calling object.ReferenceEquals() or base.Equals(), where base is
object. However, using base.Equals() is a maintenance hazard because while it works if you extend Object
directly, if you introduce a new base class that overrides Equals, it suddenly stops working.
This rule raises an issue if base.Equals() is used but base is not object.
class Base
{
private int baseField;
public override bool Equals(object other)
{
if (base.Equals(other)) // Okay; base is object
{
return true;
}
return this.baseField == ((Base)other).baseField;
}
}
class Derived : Base
{
private int derivedField;
public override bool Equals(object other)
{
if (base.Equals(other)) // Noncompliant
{
return true;
}
return this.derivedField == ((Derived)other).derivedField;
}
}
class Base
{
private int baseField;
public override bool Equals(object other)
{
if (object.ReferenceEquals(this, other)) // base.Equals is okay here, but object.ReferenceEquals is better
{
return true;
}
return this.baseField == ((Base)other).baseField;
}
}
class Derived : Base
{
private int derivedField;
public override bool Equals(object other)
{
if (object.ReferenceEquals(this, other))
{
return true;
}
return base.Equals(other) && this.derivedField == ((Derived)other).derivedField;
}
}
================================================
FILE: analyzers/rspec/cs/S3397.json
================================================
{
"title": "\"base.Equals\" should not be used to check for reference equality in \"Equals\" if \"base\" is not \"object\"",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3397",
"sqKey": "S3397",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3398.html
================================================
When a private static method is only invoked by a nested class, there’s no reason not to move it into that class. It will still have
the same access to the outer class' static members, but the outer class will be clearer and less cluttered.
public class Outer
{
private const int base = 42;
private static void Print(int num) // Noncompliant - static method is only used by the nested class, should be moved there
{
Console.WriteLine(num + base);
}
public class Nested
{
public void SomeMethod()
{
Outer.Print(1);
}
}
}
public class Outer
{
private const int base = 42;
public class Nested
{
public void SomeMethod()
{
Print(1);
}
private static void Print(int num)
{
Console.WriteLine(num + base);
}
}
}
================================================
FILE: analyzers/rspec/cs/S3398.json
================================================
{
"title": "\"private\" methods called only by inner classes should be moved to those classes",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3398",
"sqKey": "S3398",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3400.html
================================================
There’s no point in forcing the overhead of a method call for a method that always returns the same constant value. Even worse, the fact that a method call must be made will likely mislead developers who call the method thinking that something more is done. Declare a constant instead.
This rule raises an issue if on methods that contain only one statement: the return of a constant value.
int GetBestNumber()
{
return 12; // Noncompliant
}
const int BestNumber = 12;
or
static readonly int BestNumber = 12;================================================ FILE: analyzers/rspec/cs/S3400.json ================================================ { "title": "Methods should not return constants", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "confusing" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3400", "sqKey": "S3400", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3415.html ================================================
The standard assertions library methods such as AreEqual and AreSame in MSTest and
NUnit, or Equal and Same in XUnit, expect the first argument to be the expected value and
the second argument to be the actual value.
Having the expected value and the actual value in the wrong order will not alter the outcome of tests, (succeed/fail when it should) but the error messages will contain misleading information.
This rule raises an issue when the actual argument to an assertions library method is a hard-coded value and the expected argument is not.
You should provide the assertion methods with a hard-coded value as the expected value, while the actual value of the assertion should derive from the portion of code that you want to test.
Assert.AreEqual(runner.ExitCode, 0, "Unexpected exit code"); // Noncompliant; Yields error message like: Expected:<-1>. Actual:<0>.
Assert.AreEqual(0, runner.ExitCode, "Unexpected exit code");================================================ FILE: analyzers/rspec/cs/S3415.json ================================================ { "title": "Assertion arguments should be passed in the correct order", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "tests", "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3415", "sqKey": "S3415", "scope": "Tests", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3416.html ================================================
It is a well-established convention to name each logger after its enclosing type. This rule raises an issue when the convention is not respected.
class EnclosingType
{
private readonly ILogger logger;
public EnclosingType(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<AnotherType>(); // Noncompliant
logger = loggerFactory.CreateLogger<EnclosingType>(); // Compliant
}
}
Not following such a convention can result in confusion and logging misconfiguration.
For example, the person configuring the log may attempt to change the logging behavior for the MyNamespace.EnclosingType type, by
overriding defaults for the logger named after that type.
{
"Logging": {
"LogLevel": {
"Default": "Error",
"MyNamespace.EnclosingType": "Debug"
}
}
}
However, if the convention is not in place, the override would not affect logs from MyNamespace.EnclosingType, since they are made via
a logger with a different name.
Moreover, using the same logger name for multiple types prevents the granular configuration of each type’s logger, since there is no way to distinguish them in configuration.
The rule targets the following logging frameworks: * Microsoft Extensions Logging * Apache log4net * NLog
The rule doesn’t raise issues when custom handling of logging names is in place, and the logger name is not derived from a Type.
class EnclosingType
{
private readonly ILogger logger;
EnclosingType(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger("My cross-type logging category"); // Compliant
logger = loggerFactory.CreateLogger(AComplexLogicToFindTheRightType()); // Compliant
}
}
When the logger name is defined by a generic type parameter:
class EnclosingType
{
private readonly ILogger logger;
public EnclosingType(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<AnotherType>(); // Noncompliant
logger = loggerFactory.CreateLogger<EnclosingType>(); // Compliant
}
}
When the logger name is defined by an input parameter of type Type:
class EnclosingType
{
private readonly ILogger logger;
public EnclosingType(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger(typeof(AnotherType)); // Noncompliant
logger = loggerFactory.CreateLogger(typeof(EnclosingType)); // Compliant
logger = loggerFactory.CreateLogger(GetType()); // Compliant
}
}
When the logger name is a string, derived from a Type:
class EnclosingType
{
private readonly ILogger logger;
public EnclosingType(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger(typeof(AnotherType).Name); // Noncompliant
logger = loggerFactory.CreateLogger(typeof(AnotherType).FullName); // Noncompliant
logger = loggerFactory.CreateLogger(nameof(AnotherType)); // Noncompliant
// Fix by referring to the right type
logger = loggerFactory.CreateLogger(typeof(EnclosingType).Name); // Compliant
logger = loggerFactory.CreateLogger(typeof(EnclosingType).FullName); // Compliant
logger = loggerFactory.CreateLogger(nameof(EnclosingType)); // Compliant
// or by retrieving the right type dynamically
logger = loggerFactory.CreateLogger(GetType().FullName); // Compliant
}
}
The rules for method resolution can be complex and may not be fully understood by all developers. The situation becomes even more challenging when dealing with method overloads that have optional parameter values.
This rule raises an issue when an overload with default parameter values is hidden by another overload that does not have the optional parameters.
See the following example:
MyClass.Print(1); // which overload of Print will be called?
public static class MyClass
{
public static void Print(int number) { }
public static void Print(int number, string delimiter = "\n") { } // Noncompliant, default parameter value is hidden by overload
}
In this code snippet, the Print method is overloaded with two versions, where the first one hides the second one. This can lead to
confusion and uncertainty about which overload of the method will be invoked when calling it.
To address the problem you have a couple of options:
MyClass.Print(1); // which overload of Print will be called?
public static class MyClass
{
public static void Print(int number) { }
public static void Print(int number, string delimiter = "\n") { } // Noncompliant: default parameter value is hidden by overload
}
MyClass.PrintWithDelimiter(1);
public static class MyClass
{
public static void Print(int number) { }
public static void PrintWithDelimiter(int number, string delimiter = "\n") { } // Compliant
}
It should be clear to a casual reader what code a test is testing and what results are expected. Unfortunately, that’s not usually the case with
the ExpectedException attribute since an exception could be thrown from almost any line in the method.
This rule detects MSTest and NUnit ExpectedException attribute.
This rule ignores:
catch or finally clause
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void UsingTest()
{
Console.ForegroundColor = ConsoleColor.Black;
try
{
using var _ = new ConsoleAlert();
Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor);
throw new InvalidOperationException();
}
finally
{
Assert.AreEqual(ConsoleColor.Black, Console.ForegroundColor); // The exception itself is not relevant for the test.
}
}
public sealed class ConsoleAlert : IDisposable
{
private readonly ConsoleColor previous;
public ConsoleAlert()
{
previous = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
}
public void Dispose() =>
Console.ForegroundColor = previous;
}
Remove the ExpectedException attribute in favor of using the Assert.ThrowsException
assertion.
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))] // Noncompliant
public void Method_NullParam()
{
var sut = new MyService();
sut.Method(null);
}
[TestMethod]
public void Method_NullParam()
{
var sut = new MyService();
Assert.ThrowsException<ArgumentNullException>(() => sut.Method(null));
}
Remove the ExpectedException attribute in favor of using the Assert.Throws assertion.
[Test]
[ExpectedException(typeof(ArgumentNullException))] // Noncompliant
public void Method_NullParam()
{
var sut = new MyService();
sut.Method(null);
}
[Test]
public void Method_NullParam()
{
var sut = new MyService();
Assert.Throws<ArgumentNullException>(() => sut.Method(null));
}
A method is identified as a test method if it is marked with one of the following attributes:
[TestMethod] or [DataTestMethod] (for MSTest).[Fact] or [Theory] (for xUnit).[Test], [TestCase], [TestCaseSource], or [Theory] (for NUnit).However, non-public methods are not considered test methods and will not be executed, regardless of whether they have a test
attribute. Additionally, methods with the async void modifier or methods that contain generics <T> anywhere in their
signatures are also excluded from being recognized as tests and will not be executed.
[TestMethod]
void TestNullArg() // Noncompliant, method is not public
{ /* ... */ }
[TestMethod]
public async void MyIgnoredTestMethod() // Noncompliant, this is an 'async void' method
{ /* ... */ }
[TestMethod]
public void MyIgnoredGenericTestMethod<T>(T foo) // Noncompliant, method has generics in its signature
{ /* ... */ }
[TestMethod]
public void TestNullArg()
{ /* ... */ }
[TestMethod]
public async Task MyIgnoredTestMethod()
{ /* ... */ }
[TestMethod]
public void MyIgnoredGenericTestMethod(int foo)
{ /* ... */ }
For xUnit, accessibility is disregarded when it comes to [Fact] test methods, as they do not necessarily need to be
declared as public.
In xUnit, [Theory] test methods, as well as [TestCase] and [TestCaseSource] test methods in
NUnit, have the flexibility to be generic, allowing for a wider range of test scenarios.
There’s no point in checking a variable against the value you’re about to assign it. Save the cycles and lines of code, and simply perform the assignment.
if (x != a) // Noncompliant; why bother?
{
x = a;
}
x = a;
Properties and checks inside setters are excluded from this rule because they could have side effects and removing the check could lead to undesired side effects.
if (MyProperty != a)
{
MyProperty = a; // Compliant because the setter could be expensive call
}
private int myField;
public int SomeProperty
{
get
{
return myField;
}
set
{
if (myField != value)
{
myField = value;
}
}
}
================================================
FILE: analyzers/rspec/cs/S3440.json
================================================
{
"title": "Variables should not be checked against the values they\u0027re about to be assigned",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3440",
"sqKey": "S3440",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3441.html
================================================
When an anonymous type’s properties are copied from properties or variables with the same names, it yields cleaner code to omit the new type’s property name and the assignment operator.
var X = 5;
var anon = new
{
X = X, //Noncompliant, the new object would have the same property without the "X =" part.
Y = "my string"
};
var X = 5;
var anon = new
{
X,
Y = "my string"
};
================================================
FILE: analyzers/rspec/cs/S3441.json
================================================
{
"title": "Redundant property names should be omitted in anonymous classes",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3441",
"sqKey": "S3441",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3442.html
================================================
The abstract modifier in a class declaration is used to indicate that a class is intended only to be a base class of other classes, not instantiated on its own.
Since abstract classes cannot be instantiated, there is no need for public or internal constructors. If
there is basic initialization logic that should run when an extending class instance is created, you can add it in a private,
private protected or protected constructor.
Restrict the constructor visibility to the minimum: private, private protected or protected, depending on
the usage.
abstract class Base
{
public Base() // Noncompliant: should be private, private protected or protected.
{
//...
}
}
abstract class Base
{
protected Base()
{
//...
}
}
Calling GetType on a Type variable will always return the System.Type representation, which is equivalent to
typeof(System.Type). This also applies to passing a Type argument to IsInstanceOfType which always returns
false.
In both cases, the results are entirely predictable and should be avoided.
Calling GetType on System.Type is considered compliant to get an instance of System.RuntimeType, as
demonstrated in the following example:
typeof(Type).GetType(); // Can be used by convention to get an instance of 'System.RuntimeType'
Make sure the usage of GetType or IsInstanceOfType is invoked with the correct variable or argument type.
void ExamineSystemType(string str)
{
Type stringType = str.GetType();
Type runtimeType = stringType.GetType(); // Noncompliant
if (stringType.IsInstanceOfType(typeof(string))) // Noncompliant; will always return false
{ /* ... */ }
}
void ExamineSystemType(string str)
{
Type stringType = str.GetType();
if (stringType.IsInstanceOfType(str)) // Compliant
{ /* ... */ }
}
When an interface inherits from two interfaces that both define a member with the same name, trying to access that member through the derived
interface will result in the compiler error CS0229 Ambiguity between 'IBase1.SomeProperty' and 'IBase2.SomeProperty'.
So instead, every caller will be forced to cast instances of the derived interface to one or the other of its base interfaces to resolve the ambiguity and be able to access the member. Instead, it is better to resolve the ambiguity in the definition of the derived interface either by:
public interface IBase1
{
string SomeProperty { get; set; }
}
public interface IBase2
{
string SomeProperty { get; set; }
}
public interface IDerived : IBase1, IBase2 // Noncompliant, accessing IDerived.SomeProperty is ambiguous
{
}
public class MyClass : IDerived
{
// Implements both IBase1.SomeProperty and IBase2.SomeProperty
public string SomeProperty { get; set; } = "Hello";
public static void Main()
{
MyClass myClass = new MyClass();
Console.WriteLine(myClass.SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IBase1)myClass).SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IBase2)myClass).SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IDerived)myClass).SomeProperty); // Error CS0229 Ambiguity between 'IBase1.SomeProperty' and 'IBase2.SomeProperty'
}
}
public interface IDerived : IBase1, IBase2
{
new string SomeProperty { get; set; }
}
public class MyClass : IDerived
{
// Implements IBase1.SomeProperty, IBase2.SomeProperty and IDerived.SomeProperty
public string SomeProperty { get; set; } = "Hello";
public static void Main()
{
MyClass myClass = new MyClass();
Console.WriteLine(myClass.SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IBase1)myClass).SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IBase2)myClass).SomeProperty); // Writes "Hello" as expected
Console.WriteLine(((IDerived)myClass).SomeProperty); // Writes "Hello" as expected
}
}
or
public interface IBase1
{
string SomePropertyOne { get; set; }
}
public interface IBase2
{
string SomePropertyTwo { get; set; }
}
public interface IDerived : IBase1, IBase2
{
}
================================================
FILE: analyzers/rspec/cs/S3444.json
================================================
{
"title": "Interfaces should not simply inherit from base interfaces with colliding members",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3444",
"sqKey": "S3444",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3445.html
================================================
In C#, the throw statement can be used in two different ways:
In the software development context, an expression is a value or anything that executes and ends up being a value. The expression shall be
implicitly convertible to System.Exception, and the result of evaluating the expression is converted to System.Exception
before being thrown.
try
{
}
catch(Exception exception)
{
// code that uses the exception
throw exception; // The exception stack trace is cleared up to this point.
}
In this case, the stack trace, will be cleared, losing the list of method calls between the original method that threw the exception and the current method.
This syntax is supported only in a catch block, in which case, that statement re-throws the exception currently being handled by that
catch block, preserving the stack trace.
try
{
}
catch(Exception exception)
{
// code that uses the exception
throw; // The stack trace of the initial exception is preserved.
}
It is allowed using the thrown exception as an argument and wrapping it in another exception.
try
{
}
catch(Exception exception)
{
throw new Exception("Additional information", exception);
}
The recommended way to re-throw an exception is to use the throw statement without including an expression. This ensures that all call stack information is preserved when the exception is propagated to the caller, making debugging easier.
try
{
}
catch(Exception exception)
{
throw exception;
}
try
{
}
catch(Exception)
{
throw;
}
The use of ref or out in combination with Optional attribute is both confusing and
contradictory. [Optional] indicates that the parameter doesn’t have to be provided, while out and ref mean that
the parameter will be used to return data to the caller (ref additionally indicates that the parameter may also be used to pass data into
the method).
Thus, making it [Optional] to provide the parameter in which you will be passing back the method results doesn’t make sense. In fact,
the compiler will raise an error on such code. Unfortunately, it raises the error on method calls where the [Optional] parameter has been
omitted, not the source of the problem, the method declaration.
class MyClass
{
public void DoStuff([Optional] ref int i) // Noncompliant
{
Console.WriteLine(i);
}
public static void Main()
{
new MyClass().DoStuff(); // Compilation Error [CS7036]
}
}
class MyClass
{
public void DoStuff(ref int i)
{
Console.WriteLine(i);
}
public static void Main()
{
var i = 42;
new MyClass().DoStuff(ref i);
}
}
Numbers can be shifted with the << and >> operators,
but the right operand of the operation needs to be an int or a type that has an implicit
conversion to int. However, when the left operand is dynamic, the compiler’s type checking is turned
off, so you can pass anything to the right of a shift operator and have it compile. And if the argument can’t be implicitly converted to
int at runtime, then a RuntimeBinderException will be
raised.
dynamic d = 5; var x = d >> 5.4; // Noncompliant x = d << null; // Noncompliant x <<= new object(); // Noncompliant
There is no point in providing a default value for a parameter if callers are required to provide a value for it anyway. Thus,
[DefaultParameterValue] should always be used in conjunction with [Optional].
public void MyMethod([DefaultParameterValue(5)] int j) //Noncompliant, useless
{
Console.WriteLine(j);
}
public void MyMethod(int j = 5)
{
Console.WriteLine(j);
}
or
public void MyMethod([DefaultParameterValue(5)][Optional] int j)
{
Console.WriteLine(j);
}
================================================
FILE: analyzers/rspec/cs/S3450.json
================================================
{
"title": "Parameters with \"[DefaultParameterValue]\" attributes should also be marked \"[Optional]\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3450",
"sqKey": "S3450",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3451.html
================================================
DefaultValue does not make the compiler set the default value, as its name may suggest. What you probably wanted to use is DefaultParameterValue.
The DefaultValue attribute from the
System.ComponentModel namespace, is sometimes used to declare a member’s default value. This can be used, for instance, by the reset
feature of a visual designer or by a code generator.
public void DoStuff([DefaultValue(4)] int i)
{
// i is not automatically assigned 4
}
The Optional attribute from the
System.Runtime.InteropServices namespace is sometimes used to indicate that a parameter is optional, as an alternative to the
language-specific construct.
public void DoStuff([Optional] int i)
{
// i would be assigned default(int) = 0
}
The use of [DefaultValue] with [Optional] has no more effect than [Optional] alone. That’s because
[DefaultValue] doesn’t actually do anything; it merely indicates the intent for the value.
class MyClass
{
public void DoStuff([Optional][DefaultValue(4)] int i, int j = 5) // Noncompliant
{
Console.WriteLine(i);
}
public static void Main()
{
new MyClass().DoStuff(); // prints 0, since [DefaultValue] doesn't actually set the default, and default(int) is used instead
}
}
More than likely, [DefaultValue] was used in confusion instead of [DefaultParameterValue], the language-agnostic version
of the default parameter initialization mechanism provided by C#.
class MyClass
{
public void DoStuff([Optional][DefaultParameterValue(4)] int i, int j = 5)
{
Console.WriteLine(i);
}
public static void Main()
{
new MyClass().DoStuff(); // prints 4
}
}
Notice that you can’t use both [DefaultParameterValue] and default parameter initialization on the same parameter.
void DoStuff([Optional][DefaultParameterValue(4)] int i = 5) // Error CS1745 Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute
When a class has only a private constructor, it can’t be instantiated except within the class itself. Such classes can be considered
dead code and should be fixed
static members are also ignored because they are covered by Rule {rule:csharpsquid:S1118}.
public class MyClass // Noncompliant: the class contains only private constructors
{
private MyClass() { ... }
}
public class MyClass // Compliant: the class contains at least one non-private constructor
{
public MyClass() { ... }
}
The string type offers an indexer property that allows you to treat it as a char array. Therefore, if you just need to
access a specific character or iterate over all of them, the ToCharArray call should be omitted. For these cases, not omitting makes the
code harder to read and less efficient as ToCharArray copies the characters from the string object into a new Unicode
character array.
The same principle applies to utf-8 literals
types (ReadOnlySpan<byte>, Span<byte>) and the ToArray method.
string str = "some string";
foreach (var c in str.ToCharArray()) // Noncompliant
{
// ...
}
ReadOnlySpan<byte> span = "some UTF-8 string literal"u8;
foreach (var c in span.ToArray()) // Noncompliant
{
// ...
}
string str = "some string";
foreach (var c in str)
{
// ...
}
ReadOnlySpan<byte> span = "some UTF-8 string literal"u8;
foreach (var b in span) // Compliant
{
// ...
}
A composite format string is a string that contains placeholders, represented by indices inside curly braces "{0}", "{1}", etc. These placeholders are replaced by values when the string is printed or logged.
Because composite format strings are interpreted at runtime, rather than validated by the compiler, they can contain errors that lead to unexpected behaviors or runtime errors.
This rule validates the correspondence between arguments and composite formats when calling the following methods:
String.Format StringBuilder.AppendFormat Console.Write Console.WriteLine TextWriter.Write TextWriter.WriteLine Debug.WriteLine(String, Object[])
Trace.TraceError(String, Object[])
Trace.TraceInformation(String, Object[]) Trace.TraceWarning(String, Object[])
TraceSource.TraceInformation(String, Object[])
var pattern = "{0} {1} {2}";
var res = string.Format(pattern, 1, 2); // Incorrect, but the analyzer doesn't raise any warnings here
var array = new int[] {};
var res = string.Format("{0} {1}", array); // Compliant; we don't know the size of the array
:) is actually valid. A composite format string contains placeholders, replaced by values when the string is printed or logged. Mismatch in the format specifiers and the arguments provided can lead to incorrect strings being created.
To avoid issues, a developer should ensure that the provided arguments match format specifiers.
Moreover, use string interpolation when possible.
Instead of
string str = string.Format("Hello {0} {1}!", firstName, lastName);
use
string str = $"Hello {firstName} {lastName}!";
With string interpolation:
s = string.Format("{0}", arg0, arg1); // Noncompliant, arg1 is declared but not used.
s = string.Format("{0} {2}", arg0, arg1, arg2); // Noncompliant, the format item with index 1 is missing, so arg1 will not be used.
s = string.Format("foo"); // Noncompliant; there is no need to use "string.Format" here.
s = string.Format("{0}", arg0);
s = string.Format("{0} {1}", arg0, arg2);
s = "foo";
================================================
FILE: analyzers/rspec/cs/S3457.json
================================================
{
"title": "Composite format strings should be used correctly",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3457",
"sqKey": "S3457",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3458.html
================================================
Empty case clauses that fall through to the default are useless. Whether or not such a case is present, the
default clause will be invoked. Such cases simply clutter the code, and should be removed.
switch(ch)
{
case 'a' :
HandleA();
break;
case 'b' :
HandleB();
break;
case 'c' : // Noncompliant
default:
HandleTheRest();
break;
}
switch(ch)
{
case 'a' :
HandleA();
break;
case 'b' :
HandleB();
break;
default:
HandleTheRest();
break;
}
================================================
FILE: analyzers/rspec/cs/S3458.json
================================================
{
"title": "Empty \"case\" clauses that fall through to the \"default\" should be omitted",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3458",
"sqKey": "S3458",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S3459.html
================================================
Fields and auto-properties that are never assigned to hold the default values for their types. They are either pointless code or, more likely, mistakes.
class MyClass
{
private int field; // Noncompliant, shouldn't it be initialized? This way the value is always default(int), 0.
private int Property { get; set; } // Noncompliant
public void Print()
{
Console.WriteLine(field); //Will always print 0
Console.WriteLine(Property); //Will always print 0
}
}
class MyClass
{
private int field = 1;
private int Property { get; set; } = 42;
public void Print()
{
field++;
Console.WriteLine(field);
Console.WriteLine(Property);
}
}
System.SerializableAttribute attribute.Recursion is a technique used to define a problem in terms of the problem itself, usually in terms of a simpler version of the problem itself.
For example, the implementation of the generator for the n-th value of the Fibonacci sequence comes naturally from its mathematical definition, when recursion is used:
int NthFibonacciNumber(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return NthFibonacciNumber(n - 1) + NthFibonacciNumber(n - 2);
}
}
As opposed to:
int NthFibonacciNumber(int n)
{
int previous = 0;
int last = 1;
for (var i = 0; i < n; i++)
{
(previous, last) = (last, last + previous);
}
return last;
}
The use of recursion is acceptable in methods, like the one above, where you can break out of it.
int NthFibonacciNumber(int n)
{
if (n <= 1)
{
return 1; // Base case: stop the recursion
}
// ...
}
It is also acceptable and makes sense in some type definitions:
class Box : IComparable<Box>
{
public int CompareTo(Box? other)
{
// Compare the two Box instances...
}
}
With types, some invalid recursive definitions are caught by the compiler:
class C2<T> : C2<T> // Error CS0146: Circular base type dependency
{
}
class C2<T> : C2<C2<T>> // Error CS0146: Circular base type dependency
{
}
In more complex scenarios, however, the code will compile but execution will result in a TypeLoadException if you try to instantiate the class.
class C1<T>
{
}
class C2<T> : C1<C2<C2<T>>> // Noncompliant
{
}
var c2 = new C2<int>(); // This would result into a TypeLoadException
When optional parameter values are not passed to base method calls, the value passed in by the caller is ignored. This can cause the function to behave differently than expected, leading to errors and making the code difficult to debug.
public class BaseClass
{
public virtual void MyMethod(int i = 1)
{
Console.WriteLine(i);
}
}
public class DerivedClass : BaseClass
{
public override void MyMethod(int i = 1)
{
// ...
base.MyMethod(); // Noncompliant: caller's value is ignored
}
static int Main(string[] args)
{
DerivedClass dc = new DerivedClass();
dc.MyMethod(12); // prints 1
}
}
public class BaseClass
{
public virtual void MyMethod(int i = 1)
{
Console.WriteLine(i);
}
}
public class DerivedClass : BaseClass
{
public override void MyMethod(int i = 1)
{
// ...
base.MyMethod(i);
}
static int Main(string[] args)
{
DerivedClass dc = new DerivedClass();
dc.MyMethod(12); // prints 12
}
}
Microsoft Learn - Optional Arguments
================================================ FILE: analyzers/rspec/cs/S3466.json ================================================ { "title": "Optional parameters should be passed to \"base\" calls", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3466", "sqKey": "S3466", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S3532.html ================================================The default clause should take appropriate action. Having an empty default is a waste of keystrokes.
enum Fruit
{
Apple,
Orange,
Banana
}
void PrintName(Fruit fruit)
{
switch(fruit)
{
case Fruit.Apple:
Console.WriteLine("apple");
break;
default: //Noncompliant
break;
}
}
enum Fruit
{
Apple,
Orange,
Banana
}
void PrintName(Fruit fruit)
{
switch(fruit)
{
case Fruit.Apple:
Console.WriteLine("apple");
break;
default:
throw new NotSupportedException();
}
}
or
void PrintName(Fruit fruit)
{
switch(fruit)
{
case Fruit.Apple:
Console.WriteLine("apple");
break;
}
}
default clauses containing only a comment are ignored with the assumption that they are empty on purpose and the comment documents
why.
The ServiceContract attribute specifies that a class or interface defines the communication contract of a Windows Communication
Foundation (WCF) service. The service operations of this class or interface are defined by OperationContract attributes added to methods.
It doesn’t make sense to define a contract without any service operations; thus, in a ServiceContract class or interface at least one
method should be annotated with OperationContract. Similarly, WCF only serves OperationContract methods that are defined
inside ServiceContract classes or interfaces; thus, this rule also checks that ServiceContract is added to the containing
type of OperationContract methods.
[ServiceContract]
interface IMyService // Noncompliant
{
int MyServiceMethod();
}
[ServiceContract]
interface IMyService
{
[OperationContract]
int MyServiceMethod();
}
================================================
FILE: analyzers/rspec/cs/S3597.json
================================================
{
"title": "\"ServiceContract\" and \"OperationContract\" attributes should be used together",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3597",
"sqKey": "S3597",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3598.html
================================================
When declaring a Windows Communication Foundation (WCF) OperationContract
method as one-way,
that service method won’t return any result, not even an underlying empty confirmation message. These are fire-and-forget methods that are useful in
event-like communication. Therefore, specifying a return type has no effect and can confuse readers.
The rule doesn’t report if OperationContractAttribute.AsyncPattern
is set to true.
[ServiceContract]
interface IMyService
{
[OperationContract(IsOneWay = true)]
int SomethingHappened(int parameter); // Noncompliant
}
[ServiceContract]
interface IMyService
{
[OperationContract(IsOneWay = true)]
void SomethingHappened(int parameter);
}
Microsoft Learn - OperationContractAttribute
================================================ FILE: analyzers/rspec/cs/S3598.json ================================================ { "title": "One-way \"OperationContract\" methods should have \"void\" return type", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "15min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3598", "sqKey": "S3598", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S3600.html ================================================Adding params to a method override has no effect. The compiler accepts it, but the callers won’t be able to benefit from the added modifier.
class Base
{
public virtual void Method(int[] numbers)
{
...
}
}
class Derived : Base
{
public override void Method(params int[] numbers) // Noncompliant, method can't be called with params syntax.
{
...
}
}
class Base
{
public virtual void Method(int[] numbers)
{
...
}
}
class Derived : Base
{
public override void Method(int[] numbers)
{
...
}
}
Marking a method with the Pure
attribute indicates that the method doesn’t make any visible state changes. Therefore, a Pure method should return a result. Otherwise,
it indicates a no-operation call.
Using Pure on a void method is either by mistake or the method is not doing a meaningful task.
class Person
{
private int age;
[Pure] // Noncompliant: The method makes a state change
void ConfigureAge(int age) =>
this.age = age;
}
class Person
{
private int age;
void ConfigureAge(int age) =>
this.age = age;
}
Fields, properties and events can be initialized either inline or in the constructor. Initializing them inline and in the constructor at the same time is redundant; the inline initialization will be overridden.
class Person
{
int age = 42; // Noncompliant
public Person(int age)
{
this.age = age;
}
}
class Person
{
int age;
public Person(int age)
{
this.age = age;
}
}
This rule doesn’t report an issue if not all constructors initialize the field. If the field is initialized inline to its default value, then {rule:csharpsquid:S3052} already reports an issue on the initialization.
================================================ FILE: analyzers/rspec/cs/S3604.json ================================================ { "title": "Member initializer values should not be redundant", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3604", "sqKey": "S3604", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3610.html ================================================Calling GetType() on a nullable value type object returns
the underlying value type. Therefore, comparing the returned Type
object to typeof(Nullable<SomeType>) will either throw an NullReferenceException or the result will always be
true or false and can be known at compile time.
void DoChecks<T>(Nullable<T> value) where T : struct
{
bool areEqual = value.GetType() == typeof(Nullable<int>); // Noncompliant: always false
bool areNotEqual = value.GetType() != typeof(Nullable<int>); // Noncompliant: always true
Nullable<int> nullable = null;
bool nullComparison = nullable.GetType() != typeof(Nullable<int>); // Noncompliant: throws NullReferenceException
}
void DoChecks<T>(Nullable<T> value) where T : struct
{
bool areEqual = value.GetType() == typeof(int); // Compliant: can be true or false
bool areNotEqual = value.GetType() != typeof(int); // Compliant: can be true or false
Nullable<int> nullable = null;
bool nullComparison = nullable is not null && nullable.GetType() == typeof(int); // Compliant: does not throw NullReferenceException
}
Jump statements, such as return, yield break, goto, and continue are used to change the normal
flow of execution in a program. However, redundant jump statements can make code difficult to read and maintain.
void Foo()
{
goto A; // Noncompliant
A:
while (condition1)
{
if (condition2)
{
continue; // Noncompliant
}
else
{
DoTheThing();
}
}
return; // Noncompliant; this is a void method
}
void Foo()
{
while (condition1)
{
if (!condition2)
{
DoTheThing();
}
}
}
return statements followed by a local function declaration are not considered redundant, as they help with readability.Nullable value types can hold either a value or null.
The value held in the nullable type can be accessed with the Value property or by casting it to the underlying type. Still, both
operations throw an InvalidOperationException when the value is null. A nullable type should always be tested before
accessing the value to avoid raising exceptions.
void Sample(bool condition)
{
int? nullableValue = condition ? 42 : null;
Console.WriteLine(nullableValue.Value); // Noncompliant: InvalidOperationException is raised
int? nullableCast = condition ? 42 : null;
Console.WriteLine((int)nullableCast); // Noncompliant: InvalidOperationException is raised
}
void Sample(bool condition)
{
int? nullableValue = condition ? 42 : null;
if (nullableValue.HasValue)
{
Console.WriteLine(nullableValue.Value);
}
int? nullableCast = condition ? 42 : null;
if (nullableCast is not null)
{
Console.WriteLine((int)nullableCast);
}
}
NotImplementedException is often used to mark methods which must be implemented for the overall functionality to be complete, but
which the developer wants to implement later. That’s as opposed to the NotSupportedException which is thrown by methods which are
required by base classes or interfaces, but which are not appropriate to the current class.
This rule raises an exception when NotImplementedException is thrown.
void doTheThing()
{
throw new NotImplementedException();
}
Exceptions derived from NotImplementedException are ignored.
This rule raises an issue when the code cognitive complexity of a function is above a certain threshold.
Cognitive Complexity is a measure of how hard it is to understand the control flow of a unit of code. Code with high cognitive complexity is hard to read, understand, test, and modify.
As a rule of thumb, high cognitive complexity is a sign that the code should be refactored into smaller, easier-to-manage pieces.
Here are the core concepts:
The method of computation is fully detailed in the pdf linked in the resources.
Developers spend more time reading and understanding code than writing it. High cognitive complexity slows down changes and increases the cost of maintenance.
Reducing cognitive complexity can be challenging.
Here are a few suggestions:
.? or ?? operator replaces multiple tests and simplifies the flow.Extraction of a complex condition in a new function.
The code is using a complex condition and has a cognitive cost of 3.
decimal CalculateFinalPrice(User user, Cart cart)
{
decimal total = CalculateTotal(cart);
if (user.HasMembership() // +1 (if)
&& user.OrdersCount > 10 // +1 (more than one condition)
&& user.AccountActive
&& !user.HasDiscount
|| user.OrdersCount == 1) // +1 (change of operator in condition)
{
total = ApplyDiscount(user, total);
}
return total;
}
Even if the cognitive complexity of the whole program did not change, it is easier for a reader to understand the code of the
calculateFinalPrice function, which now only has a cognitive cost of 1.
decimal CalculateFinalPrice(User user, Cart cart)
{
decimal total = CalculateTotal(cart);
if (IsEligibleForDiscount(user)) // +1 (if)
{
total = applyDiscount(user, total);
}
return total;
}
bool IsEligibleForDiscount(User user)
{
return user.HasMembership()
&& user.OrdersCount > 10 // +1 (more than one condition)
&& user.AccountActive
&& !user.HasDiscount
|| user.OrdersCount == 1; // +1 (change of operator in condition)
}
Break down large functions.
For example, consider a function that calculates the total price of a shopping cart, including sales tax and shipping.
Note: The code is simplified here, to illustrate the purpose. Please imagine there is more happening in the foreach loops.
decimal CalculateTotal(Cart cart)
{
decimal total = 0;
foreach (Item item in cart.Items) // +1 (foreach)
{
total += item.Price;
}
// calculateSalesTax
foreach (Item item in cart.Items) // +1 (foreach)
{
total += 0.2m * item.Price;
}
//calculateShipping
total += 5m * cart.Items.Count;
return total;
}
This function could be refactored into smaller functions: The complexity is spread over multiple functions and the complex
CalculateTotal has now a complexity score of zero.
decimal CalculateTotal(Cart cart)
{
decimal total = 0;
total = CalculateSubtotal(cart, total);
total += CalculateSalesTax(cart, total);
total += CalculateShipping(cart, total);
return total;
}
decimal CalculateSubtotal(Cart cart, decimal total)
{
foreach (Item item in cart.Items) // +1 (foreach)
{
total += item.Price;
}
return total;
}
decimal CalculateSalesTax(Cart cart, decimal total)
{
foreach (Item item in cart.Items) // +1 (foreach)
{
total += 0.2m * item.Price;
}
return total;
}
decimal CalculateShipping(Cart cart, decimal total)
{
total += 5m * cart.Items.Count;
return total;
}
Avoid deep nesting by returning early.
The below code has a cognitive complexity of 6.
decimal CalculateDiscount(decimal price, User user)
{
if (IsEligibleForDiscount(user)) // +1 ( if )
{
if (user.HasMembership()) // +2 ( nested if )
{
return price * 0.9m;
}
else if (user.OrdersCount == 1) // +1 ( else )
{
return price * 0.95m;
}
else // +1 ( else )
{
return price;
}
}
else // +1 ( else )
{
return price;
}
}
Checking for the edge case first flattens the if statements and reduces the cognitive complexity to 3.
decimal CalculateDiscount(decimal price, User user)
{
if (!IsEligibleForDiscount(user)) // +1 ( if )
{
return price;
}
if (user.HasMembership()) // +1 ( if )
{
return price * 0.9m;
}
if (user.OrdersCount == 1) // +1 ( else )
{
return price * 0.95m;
}
return price;
}
Use the null-conditional operator to access data.
In the below code, the cognitive complexity is increased due to the multiple checks required to access the manufacturer’s name. This can be simplified using the optional chaining operator.
string GetManufacturerName(Product product)
{
string manufacturerName = null;
if (product != null && product.Details != null &&
product.Details.Manufacturer != null) // +1 (if) +1 (multiple condition)
{
manufacturerName = product.Details.Manufacturer.Name;
}
if (manufacturerName != null) // +1 (if)
{
return manufacturerName;
}
return "Unknown";
}
The optional chaining operator will return null if any reference in the chain is null, avoiding multiple checks. The
?? operator allows to provide the default value to use.
string GetManufacturerName(Product product)
{
return product?.Details?.Manufacturer?.Name ?? "Unknown";
}
As this code is complex, ensure that you have unit tests that cover the code before refactoring.
The SafeHandle.DangerousGetHandle method poses significant risks and should be used carefully. This method carries the inherent danger
of potentially returning an invalid handle, which can result in resource leaks and security vulnerabilities. Although it is technically possible to
utilize this method without encountering issues, doing so correctly requires a high level of expertise. Therefore, it is recommended to avoid using
this method altogether.
The SafeHandle.DangerousGetHandle method is potentially prone to leaks and vulnerabilities due to its nature and usage. Here are a few
reasons why:
SafeHandle class, there is an increased risk of failing to dispose system resources correctly.SafeHandle.DangerousGetHandle without proper validation can lead to security vulnerabilities that can be exploited by an attacker.
static void Main(string[] args)
{
System.Reflection.FieldInfo fieldInfo = ...;
SafeHandle handle = (SafeHandle)fieldInfo.GetValue(rKey);
IntPtr dangerousHandle = handle.DangerousGetHandle(); // Noncompliant
}
The point of having custom exception types is to convey more information than is available in standard types. But custom exception types must be
public for that to work.
If a method throws a non-public exception, the best you can do on the caller’s side is to catch the closest public base
of the class. However, you lose all the information that the new exception type carries.
This rule will raise an issue if you directly inherit one of the following exception types in a non-public class:
internal class MyException : Exception // Noncompliant
{
// ...
}
public class MyException : Exception
{
// ...
}
The name of a method should communicate what it does, and the names of its parameters should indicate how they’re used. If a method and its parameter have the same name it is an indication that one of these rules of thumb has been broken, if not both. Even if by some trick of language that’s not the case, it is still likely to confuse callers and maintainers.
public void Login(string login) // Noncompliant
{
//...
}
public void Login(string userName)
{
//...
}
================================================
FILE: analyzers/rspec/cs/S3872.json
================================================
{
"title": "Parameter names should not duplicate the names of their methods",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention",
"confusing"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3872",
"sqKey": "S3872",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3874.html
================================================
Passing a parameter by reference, which is what happens when you use the out or ref parameter modifiers, means that the
method will receive a pointer to the argument, rather than the argument itself. If the argument was a value type, the method will be able to change
the argument’s values. If it was a reference type, then the method receives a pointer to a pointer, which is usually not what was intended. Even when
it is what was intended, this is the sort of thing that’s difficult to get right, and should be used with caution.
This rule raises an issue when out or ref is used on a non-Optional parameter in a public method.
Optional parameters are covered by {rule:csharpsquid:S3447}.
public void GetReply(
ref MyClass input, // Noncompliant
out string reply) // Noncompliant
{ ... }
public string GetReply(MyClass input)
{ ... }
public bool TryGetReply(MyClass input, out string reply)
{ ... }
public ReplyData GetReply(MyClass input)
{ ... }
internal void GetReply(ref MyClass input, out string reply)
{ ... }
This rule will not raise issues for:
The use of == to compare two objects is expected to do a reference comparison. That is, it is expected to return true if
and only if they are the same object instance. Overloading the operator to do anything else will inevitably lead to the introduction of bugs by
callers.
public static bool operator ==(MyType x, MyType y) // Noncompliant: confusing for the caller
{
// custom implementation
}
On the other hand, overloading it to do exactly that is pointless; that’s what == does by default.
public static bool operator ==(MyType x, MyType y) // Noncompliant: redundant
{
if (x == null)
{
return y == null;
}
return object.ReferenceEquals(x,y);
}
operator + or operator - are ignored.IComparable<T> or IEquatable<T> most probably behave as value-type objects and are
ignored.Strings and integral types are typically used as indexers. When some other type is required, it typically indicates design problems, and potentially a situation where a method should be used instead.
public int this[MyCustomClass index] // Noncompliant
{
// get and set accessors
}
================================================
FILE: analyzers/rspec/cs/S3876.json
================================================
{
"title": "Strings or integral types should be used for indexers",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3876",
"sqKey": "S3876",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3877.html
================================================
The rule is reporting when an exception is thrown from certain methods and constructors. These methods are expected to behave in a specific way and throwing an exception from them can lead to unexpected behavior and break the calling code.
public override string ToString()
{
if (string.IsNullOrEmpty(Name))
{
throw new ArgumentException(nameof(Name)); // Noncompliant
}
//...
}
An issue is raised when an exception is thrown from any of the following:
Certain exceptions will be ignored in specific contexts, thus not raising the issue:
System.NotImplementedException and its derivatives are ignored for all the aforementioned.System.InvalidOperationException, System.NotSupportedException, and System.ArgumentException and their
derivatives are ignored in event accessors.Creating an array or using a collection expression solely for the purpose of passing it to a params parameter is unnecessary. Simply
pass the elements directly, and they will be automatically consolidated into the appropriate collection type.
public void Base()
{
Method(new string[] { "s1", "s2" }); // Noncompliant: resolves to string[] overload
Method(new string[] { }); // Noncompliant: resolves to string[] overload
Method(["s3", "s4"]); // Noncompliant: resolves to ReadOnlySpan overload
Method(new string[12]); // Compliant: resolves to string[] overload
}
public void Method(params string[] args)
{
// ...
}
public void Method(params ReadOnlySpan<string> args) // C# 13 params collections
{
// C# 13 params collection
}
public void Base()
{
Method("s1", "s2"); // resolves to ReadOnlySpan overload
Method(); // resolves to ReadOnlySpan overload
Method("s3", "s4"); // resolves to ReadOnlySpan overload
Method(new string[12]); // resolves to string[] overload
}
public void Method(params string[] args)
{
// ..
}
public void Method(params ReadOnlySpan<string> args) // C# 13 params collections
{
// ..
}
params
modifierparams
collectionsFinalizers come with a performance cost due to the overhead of tracking the life cycle of objects. An empty one is consequently costly with no benefit or justification.
public class Foo
{
~Foo() // Noncompliant
{
}
}
================================================
FILE: analyzers/rspec/cs/S3880.json
================================================
{
"title": "Finalizers should not be empty",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3880",
"sqKey": "S3880",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3881.html
================================================
The IDisposable interface is a mechanism to release unmanaged resources, if not implemented correctly this could result in resource
leaks or more severe bugs.
This rule raises an issue when the recommended dispose pattern, as defined by Microsoft, is not adhered to. See the Compliant Solution section for examples.
Satisfying the rule’s conditions will enable potential derived classes to correctly dispose the members of your class:
sealed classes are not checked.IDisposable your class should not have IDisposable in the list of its interfaces. In such
cases it is recommended to override the base class’s protected virtual void Dispose(bool) method or its equivalent.IDisposable explicitly, e.g. the Dispose() method should be public.protected virtual void Dispose(bool) method. This method allows the derived classes to correctly dispose
the resources of this class.Dispose() method should be invocation of Dispose(true) followed by
GC.SuppressFinalize(this)Dispose(false).IDisposable it must call the Dispose, or Dispose(bool)
method of the base class from within its own implementation of Dispose or Dispose(bool), respectively. This ensures that
all resources from the base class are properly released.
public class Foo1 : IDisposable // Noncompliant - provide protected overridable implementation of Dispose(bool) on Foo or mark the type as sealed.
{
public void Dispose() // Noncompliant - should contain only a call to Dispose(true) and then GC.SuppressFinalize(this)
{
// Cleanup
}
}
public class Foo2 : IDisposable
{
void IDisposable.Dispose() // Noncompliant - Dispose() should be public
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose() // Noncompliant - Dispose() should be sealed
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
public class Foo3 : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Cleanup
}
~Foo3() // Noncompliant - Modify Foo.~Foo() so that it calls Dispose(false) and then returns.
{
// Cleanup
}
}
// Sealed class
public sealed class Foo1 : IDisposable
{
public void Dispose()
{
// Cleanup
}
}
// Simple implementation
public class Foo2 : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Cleanup
}
}
// Implementation with a finalizer
public class Foo3 : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Cleanup
}
~Foo3()
{
Dispose(false);
}
}
// Base disposable class
public class Foo4 : DisposableBase
{
protected override void Dispose(bool disposing)
{
// Cleanup
// Do not forget to call base
base.Dispose(disposing);
}
}
This rule is deprecated, and will eventually be removed.
CoSetProxyBlanket and CoInitializeSecurity both work to set the permissions context in which the process invoked
immediately after is executed. Calling them from within that process is useless because it’s too late at that point; the permissions context has
already been set.
Specifically, these methods are meant to be called from non-managed code such as a C++ wrapper that then invokes the managed, i.e. C# or VB.NET, code.
[DllImport("ole32.dll")]
static extern int CoSetProxyBlanket([MarshalAs(UnmanagedType.IUnknown)]object pProxy, uint dwAuthnSvc, uint dwAuthzSvc,
[MarshalAs(UnmanagedType.LPWStr)] string pServerPrincName, uint dwAuthnLevel, uint dwImpLevel, IntPtr pAuthInfo,
uint dwCapabilities);
public enum RpcAuthnLevel
{
Default = 0,
None = 1,
Connect = 2,
Call = 3,
Pkt = 4,
PktIntegrity = 5,
PktPrivacy = 6
}
public enum RpcImpLevel
{
Default = 0,
Anonymous = 1,
Identify = 2,
Impersonate = 3,
Delegate = 4
}
public enum EoAuthnCap
{
None = 0x00,
MutualAuth = 0x01,
StaticCloaking = 0x20,
DynamicCloaking = 0x40,
AnyAuthority = 0x80,
MakeFullSIC = 0x100,
Default = 0x800,
SecureRefs = 0x02,
AccessControl = 0x04,
AppID = 0x08,
Dynamic = 0x10,
RequireFullSIC = 0x200,
AutoImpersonate = 0x400,
NoCustomMarshal = 0x2000,
DisableAAA = 0x1000
}
[DllImport("ole32.dll")]
public static extern int CoInitializeSecurity(IntPtr pVoid, int cAuthSvc, IntPtr asAuthSvc, IntPtr pReserved1,
RpcAuthnLevel level, RpcImpLevel impers, IntPtr pAuthList, EoAuthnCap dwCapabilities, IntPtr pReserved3);
static void Main(string[] args)
{
var hres1 = CoSetProxyBlanket(null, 0, 0, null, 0, 0, IntPtr.Zero, 0); // Noncompliant
var hres2 = CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, RpcAuthnLevel.None,
RpcImpLevel.Impersonate, IntPtr.Zero, EoAuthnCap.None, IntPtr.Zero); // Noncompliant
}
The parameter to Assembly.Load includes the full specification of the dll to be loaded. Use another method, and you might end up with
a dll other than the one you expected.
This rule raises an issue when Assembly.LoadFrom, Assembly.LoadFile, or Assembly.LoadWithPartialName is
called.
The rule does not raise an issue when the methods are used within an AssemblyResolve event handler. In this context,
using Assembly.Load can cause a StackOverflowException due to recursive event firing, making Assembly.LoadFrom
or Assembly.LoadFile the appropriate choices.
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
return Assembly.LoadFrom("MyAssembly.dll"); // Compliant: within AssemblyResolve handler
}
static void Main(string[] args)
{
Assembly.LoadFrom("MyAssembly.dll"); // Noncompliant
}
static void Main(string[] args)
{
Assembly.LoadFile(@"C:\MyPath\MyAssembly.dll"); // Noncompliant
}
static void Main(string[] args)
{
Assembly.LoadWithPartialName("MyAssembly"); // Noncompliant
}
static void Main(string[] args)
{
Assembly.Load("MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); // Compliant
}
static void Main(string[] args)
{
Assembly.Load("MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); // Compliant
}
static void Main(string[] args)
{
Assembly.Load("MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); // Compliant
}
Using the readonly keyword on a
field means it can’t be changed after initialization. However, that’s only partly true when applied to collections or arrays. The
readonly keyword enforces that another instance can’t be assigned to the field, but it cannot keep the contents from being updated. In
practice, the field value can be changed, and the use of readonly on such a field is misleading, and you’re likely not getting the
behavior you expect.
This rule raises an issue when a non-private, readonly field is an array or collection.
To fix this, you should either use an immutable or frozen collection or remove the readonly modifier to
clarify the behavior.
public class MyClass
{
public readonly string[] strings1; // Noncompliant
public readonly string[] strings2; // Noncompliant
public readonly string[] strings3; // Noncompliant
// ...
}
public class MyClass
{
public string[] strings1; // Compliant: remove readonly modifier
public readonly ImmutableArray<string> strings; // Compliant: use an Immutable collection
private readonly string[] strings; // Compliant: reduced accessibility to private
// ...
}
Thread.Suspend and Thread.Resume can give unpredictable results, and both methods have been deprecated. Indeed, if
Thread.Suspend is not used very carefully, a thread can be suspended while
holding a lock, thus leading to a deadlock.
There are other synchronization mechanisms that are safer and should be used instead, such as:
Monitor provides a mechanism that synchronizes access to objects.Mutex provides a mechanism that synchronizes interprocess access to a protected resource.Semaphore provides a mechanism that allows limiting the number of threads that have access to a protected resources
concurrently.Events enable a class to notify others when something of interest occurs.The IEquatable<T> interface has only one method in it: Equals(<T>). If you’ve already written
Equals(T), there’s no reason not to explicitly implement IEquatable<T>. Doing so expands the utility of your class by
allowing it to be used where an IEquatable is called for.
Note: Classes that implement IEquatable<T> should also be sealed.
class MyClass // Noncompliant
{
public bool Equals(MyClass other)
{
//...
}
}
sealed class MyClass : IEquatable<MyClass>
{
public override bool Equals(object other)
{
return Equals(other as MyClass);
}
public bool Equals(MyClass other)
{
//...
}
}
================================================
FILE: analyzers/rspec/cs/S3897.json
================================================
{
"title": "Classes that provide \"Equals(\u003cT\u003e)\" should implement \"IEquatable\u003cT\u003e\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3897",
"sqKey": "S3897",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3898.html
================================================
If you’re using a struct, it is likely because you’re interested in performance. But by failing to implement
IEquatable<T> you’re loosing performance when comparisons are made because without IEquatable<T>, boxing and
reflection are used to make comparisons.
struct MyStruct // Noncompliant
{
public int Value { get; set; }
}
struct MyStruct : IEquatable<MyStruct>
{
public int Value { get; set; }
public bool Equals(MyStruct other)
{
// ...
}
}
Methods declared as public, protected, or protected internal can be accessed from other assemblies, which
means you should validate parameters to be within the expected constraints. In general, checking against null is recommended in defensive
programming.
This rule raises an issue when a parameter of a publicly accessible method is not validated against null before being
dereferenced.
public class MyClass
{
private MyOtherClass other;
public void Foo(MyOtherClass other)
{
this.other = other.Clone(); // Noncompliant
}
protected void Bar(MyOtherClass other)
{
this.other = other.Clone(); // Noncompliant
}
}
public class MyClass
{
private MyOtherClass other;
public void Foo(MyOtherClass other)
{
if (other != null)
{
this.other = other.Clone();
}
}
protected void Bar(MyOtherClass other)
{
if (other != null)
{
this.other = other.Clone();
}
}
public void Baz(MyOtherClass other)
{
ArgumentNullException.ThrowIfNull(other);
this.other = other.Clone();
}
public void Qux(MyOtherClass other)
{
this.other = other; // Compliant: "other" is not being dereferenced
}
private void Xyzzy(MyOtherClass other)
{
this.other = other.Clone(); // Compliant: method is not publicly accessible
}
}
null via helper methods should be annotated with the [NotNull] attribute.[NotNull] Resharper code annotation
attribute are supported as well.ValidatedNotNullAttribute and mark the parameter that is
validated for null in your method declaration with it:
using System;
[AttributeUsage(AttributeTargets.Parameter, Inherited=false)]
public sealed class ValidatedNotNullAttribute : Attribute { }
public static class Guard
{
public static void NotNull<T>([ValidatedNotNullAttribute] T value, [CallerArgumentExpression("value")] string name = "") where T : class
{
if (value == null)
throw new ArgumentNullException(name);
}
}
public static class Utils
{
public static string ToUpper(string value)
{
Guard.NotNull(value);
return value.ToUpper(); // Compliant
}
}
================================================
FILE: analyzers/rspec/cs/S3900.json
================================================
{
"title": "Arguments of public methods should be validated against null",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention",
"symbolic-execution"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3900",
"sqKey": "S3900",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3902.html
================================================
Using Type.Assembly to get the current assembly is nearly free in terms of performance; it’s a simple property access. On the other
hand, Assembly.GetExecutingAssembly() can take up to 30 times as long because it walks up the call stack to find the assembly.
Note that Assembly.GetExecutingAssembly() is different than Type.Assembly because it dynamically returns the assembly
that contains the startup object of the currently executed application. For example, if executed from an application it will return the application
assembly, but if executed from a unit test project it could return the unit test assembly. Type.Assembly always returns the assembly that
contains the specified type.
public class Example
{
public static void Main()
{
Assembly assem = Assembly.GetExecutingAssembly(); // Noncompliant
Console.WriteLine("Assembly name: {0}", assem.FullName);
}
}
public class Example
{
public static void Main()
{
Assembly assem = typeof(Example).Assembly; // Here we use the type of the current class
Console.WriteLine("Assembly name: {0}", assem.FullName);
}
}
================================================
FILE: analyzers/rspec/cs/S3902.json
================================================
{
"title": "\"Assembly.GetExecutingAssembly\" should not be called",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"performance"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3902",
"sqKey": "S3902",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3903.html
================================================
Types are declared in namespaces in order to prevent name collisions and as a way to organize them into the object hierarchy. Types that are defined outside any named namespace are in a global namespace that cannot be referenced in code.
public class Foo // Noncompliant
{
}
public struct Bar // Noncompliant
{
}
namespace SomeSpace
{
public class Foo
{
}
public struct Bar
{
}
}
or alternatively in C#10 and above
namespace SomeSpace;
public class Foo
{
}
public struct Bar
{
}
================================================
FILE: analyzers/rspec/cs/S3903.json
================================================
{
"title": "Types should be defined in named namespaces",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "MODULAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3903",
"sqKey": "S3903",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3904.html
================================================
The AssemblyVersion attribute is used to specify the version number of an assembly. An assembly is a compiled unit of code, which can be marked with a version number by applying the attribute to an assembly’s source code file.
The AssemblyVersion attribute is useful for many reasons:
AssemblyVersion attribute, you can ensure that the correct version of the referenced assembly is used. This
helps avoid compatibility issues and ensures that the expected behavior and functionality are maintained.If no AssemblyVersion is provided, the same default version will be used for every build. Since the version number is used by .NET
Framework to uniquely identify an assembly, this can lead to broken dependencies.
using System.Reflection;
[assembly: AssemblyTitle("MyAssembly")] // Noncompliant
namespace MyLibrary
{
// ...
}
using System.Reflection;
[assembly: AssemblyTitle("MyAssembly")]
[assembly: AssemblyVersion("42.1.125.0")]
namespace MyLibrary
{
// ...
}
Delegate event handlers (i.e. delegates used as type of an event) should have a very specific signature:
void.System.Object and named 'sender'.System.EventArgs (or any derived type) and is named 'e'.This rule raises an issue whenever a delegate declaration doesn’t match that signature.
public delegate void AlarmEventHandler(object s);
public class Foo
{
public event AlarmEventHandler AlarmEvent; // Noncompliant
}
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
public class Foo
{
public event AlarmEventHandler AlarmEvent; // Compliant
}
Since .Net Framework version 2.0 it is not necessary to declare a delegate that specifies a class derived from System.EventArgs. The
System.EventHandler<TEventArgs> delegate mechanism should be used instead as it allows any class derived from
EventArgs to be used with that handler.
This rule raises an issue when an old style delegate is used as an event handler.
public class MyEventArgs : EventArgs
{
}
public delegate void MyEventHandler(object sender, MyEventArgs e); // Noncompliant
public class EventProducer
{
public event MyEventHandler MyEvent;
protected virtual void OnMyEvent(MyEventArgs e)
{
if (MyEvent != null)
{
MyEvent(e);
}
}
}
public class EventConsumer
{
public EventConsumer(EventProducer producer)
{
producer.MyEvent += HandleEvent;
}
private void HandleEvent(object sender, MyEventArgs e)
{
// Do something...
}
}
public class MyEventArgs : EventArgs
{
}
public class EventProducer
{
public event EventHandler<MyEventArgs> MyEvent;
protected virtual void OnMyEvent(MyEventArgs e)
{
if (MyEvent != null)
{
MyEvent(e);
}
}
}
public class EventConsumer
{
public EventConsumer(EventProducer producer)
{
producer.MyEvent += HandleEvent;
}
private void HandleEvent(object sender, MyEventArgs e)
{
// Do something...
}
}
================================================
FILE: analyzers/rspec/cs/S3908.json
================================================
{
"title": "Generic event handlers should be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3908",
"sqKey": "S3908",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3909.html
================================================
The NET Framework 2.0 introduced the generic interface System.Collections.Generic.IEnumerable<T> and it should be preferred over
the older, non generic, interfaces.
This rule raises an issue when a public type implements System.Collections.IEnumerable.
using System;
using System.Collections;
public class MyData
{
public MyData()
{
}
}
public class MyList : CollectionBase // Noncompliant
{
public void Add(MyData data)
{
InnerList.Add(data);
}
// ...
}
using System;
using System.Collections.ObjectModel;
public class MyData
{
public MyData()
{
}
}
public class MyList : Collection<MyData>
{
// Implementation...
}
================================================
FILE: analyzers/rspec/cs/S3909.json
================================================
{
"title": "Collections should implement the generic interface",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3909",
"sqKey": "S3909",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3923.html
================================================
Having all branches of a switch or if chain with the same implementation indicates a problem.
In the following code:
if (b == 0) // Noncompliant
{
DoTheThing();
}
else
{
DoTheThing();
}
int b = a > 12 ? 4 : 4; // Noncompliant
switch (i) // Noncompliant
{
case 1:
DoSomething();
break;
case 2:
DoSomething();
break;
case 3:
DoSomething();
break;
default:
DoSomething();
}
Either there is a copy-paste error that needs fixing or an unnecessary switch or if chain that should be removed.
This rule does not apply to if chains without else, nor to switch without a default clause.
if (b == 0) //no issue, this could have been done on purpose to make the code more readable
{
DoSomething();
}
else if (b == 1)
{
DoSomething();
}
================================================
FILE: analyzers/rspec/cs/S3923.json
================================================
{
"title": "All branches in a conditional structure should not have exactly the same implementation",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3923",
"sqKey": "S3923",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3925.html
================================================
The ISerializable interface is
the mechanism to control the type serialization process. If not implemented correctly this could result in an invalid serialization and hard-to-detect
bugs.
This rule raises an issue on types that implement ISerializable without following the serialization pattern recommended by Microsoft.
Specifically, this rule checks for these problems:
SerializableAttribute attribute is
missing.NonSerializedAttribute attribute.protected.private.ISerializable.GetObjectData that is not both public and virtual.base constructor.ISerializable.GetObjectData method that does not call the base method.ISerializable.GetObjectData method is not overridden.Classes that inherit from Exception are implementing
ISerializable. Make sure the [Serializable] attribute is used and that ISerializable is correctly implemented.
Even if you don’t plan to explicitly serialize the object yourself, it might still require serialization, for instance when crossing the boundary of
an AppDomain.
This rule only raises an issue on classes that indicate that they are interested in serialization (see the Exceptions section). That is to
reduce noise because a lot of classes in the base class library are implementing ISerializable, including the following classes: Exception, Uri, Hashtable, Dictionary<TKey,TValue>, DataSet, HttpWebRequest, Regex TreeNode, and others. There is often no need to add
serialization support in classes derived from these types.
[Serializable] attributeISerializable in their base type list
[Serializable] // 1.
public class SerializationOptIn_Attribute
{
}
public class SerializationOptIn_Interface : ISerializable // 2.
{
}
public class SerializationOptIn_Constructor
{
protected SerializationOptIn_Constructor(SerializationInfo info, StreamingContext context) // 3.
{
}
}
Make sure to follow the recommended guidelines when
implementing ISerializable.
public class Bar
{
}
public class Foo : ISerializable // Noncompliant: serialization constructor is missing
// Noncompliant: the [Serializable] attribute is missing
{
private readonly Bar bar; // Noncompliant: the field is not marked with [NonSerialized]
}
public sealed class SealedFoo : Foo
{
private int val; // Noncompliant: 'val' is serializable and GetObjectData is not overridden
public SealedFoo()
{
// ...
}
public SealedFoo(SerializationInfo info, StreamingContext context) // Noncompliant: serialization constructor is not `private`
// Noncompliant: serialization constructor does not call base constructor
{
// ...
}
}
public class UnsealedFoo : Foo
{
public UnsealedFoo()
{
// ...
}
public UnsealedFoo(SerializationInfo info, StreamingContext context) // Noncompliant: serialization constructor is not `protected`
: base(info, context)
{
// ...
}
protected void GetObjectData(SerializationInfo info, StreamingContext context) // Noncompliant: GetObjectData is not public virtual
{
// Noncompliant: does not call base.GetObjectData(info, context)
}
}
public class Bar
{
}
[Serializable]
public class Foo : ISerializable // Compliant: the class is marked with [Serializable]
{
[NonSerialized]
private readonly Bar bar; // Compliant: the field is marked with [NonSerialized]
public Foo()
{
// ...
}
protected Foo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is present
{
// ...
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
// ...
}
}
[Serializable]
public sealed class SealedFoo : Foo
{
private int val; // Compliant: 'val' is serializable and GetObjectData is overridden
public SealedFoo()
{
// ...
}
private SealedFoo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is `private`
: base(info, context) // Compliant: serialization constructor calls base constructor
{
// ...
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
// ...
}
}
[Serializable]
public class UnsealedFoo : Foo
{
public UnsealedFoo()
{
// ...
}
protected UnsealedFoo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is `protected`
: base(info, context)
{
// ...
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) // Compliant: GetObjectData is public virtual
{
base.GetObjectData(info, context); // Compliant: calls base.GetObjectData(info, context)
// ...
}
}
ISerializable
InterfaceSerializableAttribute
ClassNonSerializedAttribute
ClassISerializable.GetObjectData MethodException ClassFields marked with System.Runtime.Serialization.OptionalFieldAttribute are serialized just like any other field. But such fields are
ignored on deserialization, and retain the default values associated with their types. Therefore, deserialization event handlers should be declared to
set such fields during the deserialization process.
This rule raises when at least one field with the System.Runtime.Serialization.OptionalFieldAttribute attribute is declared but one
(or both) of the following event handlers System.Runtime.Serialization.OnDeserializingAttribute or
System.Runtime.Serialization.OnDeserializedAttribute are not present.
[Serializable]
public class Foo
{
[OptionalField(VersionAdded = 2)]
int optionalField = 5;
}
[Serializable]
public class Foo
{
[OptionalField(VersionAdded = 2)]
int optionalField = 5;
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
optionalField = 5;
}
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
// Set optionalField if dependent on other deserialized values.
}
}
================================================
FILE: analyzers/rspec/cs/S3926.json
================================================
{
"title": "Deserialization methods should be provided for \"OptionalField\" members",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"serialization"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3926",
"sqKey": "S3926",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3927.html
================================================
Serialization event handlers that don’t have the correct signature will not be called, bypassing augmentations to automated serialization and deserialization events.
A method is designated a serialization event handler by applying one of the following serialization event attributes:
Serialization event handlers take a single parameter of type StreamingContext, return
void, and have private visibility.
This rule raises an issue when any of these constraints are not respected.
[Serializable]
public class Foo
{
[OnSerializing]
public void OnSerializing(StreamingContext context) {} // Noncompliant: should be private
[OnSerialized]
int OnSerialized(StreamingContext context) {} // Noncompliant: should return void
[OnDeserializing]
void OnDeserializing() {} // Noncompliant: should have a single parameter of type StreamingContext
[OnSerializing]
public void OnSerializing2<T>(StreamingContext context) {} // Noncompliant: should have no type parameters
[OnDeserialized]
void OnDeserialized(StreamingContext context, string str) {} // Noncompliant: should have a single parameter of type StreamingContext
}
[Serializable]
public class Foo
{
[OnSerializing]
private void OnSerializing(StreamingContext context) {}
[OnSerialized]
private void OnSerialized(StreamingContext context) {}
[OnDeserializing]
private void OnDeserializing(StreamingContext context) {}
[OnDeserialized]
private void OnDeserialized(StreamingContext context) {}
}
Some constructors of the ArgumentException, ArgumentNullException, ArgumentOutOfRangeException and
DuplicateWaitObjectException classes must be fed with a valid parameter name. This rule raises an issue in two cases:
public void Foo(Bar a, int[] b)
{
throw new ArgumentException(); // Noncompliant
throw new ArgumentException("My error message", "c"); // Noncompliant
throw new ArgumentException("My error message", "c", innerException); // Noncompliant
throw new ArgumentNullException("c"); // Noncompliant
throw new ArgumentNullException(nameof(c)); // Noncompliant
throw new ArgumentNullException("My error message", "a"); // Noncompliant
throw new ArgumentOutOfRangeException("c"); // Noncompliant
throw new ArgumentOutOfRangeException("c", "My error message"); // Noncompliant
throw new ArgumentOutOfRangeException("c", b, "My error message"); // Noncompliant
throw new DuplicateWaitObjectException("c", "My error message"); // Noncompliant
}
public void Foo(Bar a, int[] b)
{
throw new ArgumentException("My error message", "a");
throw new ArgumentException("My error message", "b", innerException);
throw new ArgumentNullException("a");
throw new ArgumentNullException(nameof(a));
throw new ArgumentNullException("a", "My error message");
throw new ArgumentOutOfRangeException("b");
throw new ArgumentOutOfRangeException("b", "My error message");
throw new ArgumentOutOfRangeException("b", b, "My error message");
throw new DuplicateWaitObjectException("b", "My error message");
}
The rule won’t raise an issue if the parameter name is not a constant value.
================================================ FILE: analyzers/rspec/cs/S3928.json ================================================ { "title": "Parameter names used into ArgumentException constructors should match an existing one ", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3928", "sqKey": "S3928", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3937.html ================================================The use of punctuation characters to separate subgroups in a number can make the number more readable. For instance consider 1,000,000,000 versus 1000000000. But when the grouping is irregular, such as 1,000,00,000; it indicates an error.
This rule raises an issue when underscores (_) are used to break a number into irregular subgroups.
int thousand = 100_0; int tenThousand = 100_00; int million = 1_000_00_000;
int thousand = 1000; int tenThousand = 10_000; int tenThousandWithout = 10000; int duos = 1_00_00; int million = 100_000_000;================================================ FILE: analyzers/rspec/cs/S3937.json ================================================ { "title": "Number patterns should be regular", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "suspicious" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-3937", "sqKey": "S3937", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3949.html ================================================
Numbers are infinite, but the types that hold them are not. Each numeric type has hard upper and lower bounds. Try to calculate or assign numbers beyond those bounds, and the result will be a value that has silently wrapped around from the expected positive value to a negative one, or vice versa.
public int Transform(int value)
{
if (value <= 0)
{
return value;
}
int number = int.MaxValue;
return number + value; // Noncompliant
}
public long Transform(int value)
{
if (value <= 0)
{
return value;
}
long number = int.MaxValue;
return number + value;
}
System.Collections.Generic.List<T> is a generic collection that is designed for performance and not inheritance. For example, it
does not contain virtual members that make it easier to change the behavior of an inherited class. That means that future attempts to expand the
behavior will be spoiled because the extension points simply aren’t there. Instead, one of the following generic collections should be used:
System.Collections.Generic.IEnumerable<T>System.Collections.Generic.IReadOnlyCollection<T>System.Collections.Generic.ICollection<TKey>System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IList<TKey>System.Collections.ObjectModel.Collection<T>System.Collections.ObjectModel.ReadOnlyCollection<T>System.Collections.ObjectModel.KeyedCollection<TKey, Titem>This rule raises an issue every time a System.Collections.Generic.List<T> is exposed:
namespace Foo
{
public class Bar
{
public List<T> Method1(T arg) // Noncompliant
{
//...
}
}
}
namespace Foo
{
public class Bar
{
public Collection<T> Method1(T arg)
{
//...
}
}
}
================================================
FILE: analyzers/rspec/cs/S3956.json
================================================
{
"title": "\"Generic.List\" instances should not be part of public APIs",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3956",
"sqKey": "S3956",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3962.html
================================================
The value of a static readonly field is computed at runtime while the value of a const field is calculated at compile
time, which improves performance.
This rule raises an issue when a static readonly field is initialized with a value that is computable at compile time.
As specified by Microsoft, the list of types that can have a constant value are:
| C# type | .Net Fwk type |
|---|---|
|
bool |
System.Boolean |
|
byte |
System.Byte |
|
sbyte |
System.SByte |
|
char |
System.Char |
|
decimal |
System.Decimal |
|
double |
System.Double |
|
float |
System.Single |
|
int |
System.Int32 |
|
uint |
System.UInt32 |
|
long |
System.Int64 |
|
ulong |
System.UInt64 |
|
short |
System.Int16 |
|
ushort |
System.UInt16 |
|
string |
System.String |
namespace myLib
{
public class Foo
{
static readonly int x = 1; // Noncompliant
static readonly int y = x + 4; // Noncompliant
static readonly string s = "Bar"; // Noncompliant
}
}
namespace myLib
{
public class Foo
{
const int x = 1;
const int y = x + 4;
const string s = "Bar";
}
}
================================================
FILE: analyzers/rspec/cs/S3962.json
================================================
{
"title": "\"static readonly\" constants should be \"const\" instead",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3962",
"sqKey": "S3962",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3963.html
================================================
When a static constructor serves no other purpose that initializing static fields, it comes with an unnecessary
performance cost because the compiler generates a check before each static method or instance constructor invocation.
Instead, inline initialization is highly recommended.
namespace myLib
{
public class Foo
{
static int i;
static string s;
static Foo() // Noncompliant
{
i = 3;
ResourceManager sm = new ResourceManager("strings", Assembly.GetExecutingAssembly());
s = sm.GetString("mystring");
}
}
}
namespace myLib
{
public class Foo
{
static int i =3;
static string s = InitString();
static string InitString()
{
ResourceManager sm = new ResourceManager("strings", Assembly.GetExecutingAssembly());
return sm.GetString("mystring");
}
}
}
================================================
FILE: analyzers/rspec/cs/S3963.json
================================================
{
"title": "\"static\" fields should be initialized inline",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3963",
"sqKey": "S3963",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3966.html
================================================
Disposing an object twice in the same method, either with the using keyword or by calling Dispose
directly, is confusing and error-prone. For example, another developer might try to use an already-disposed object, or there can be runtime errors for
specific paths in the code.
In addition, even if the documentation explicitly states that
calling the Dispose method multiple times should not throw an exception, some implementations still do it. Thus it is safer to not
dispose of an object twice when possible.
var foo = new Disposable(); foo.Dispose(); foo.Dispose(); // Noncompliant
using (var bar = new Disposable()) // Noncompliant
{
bar.Dispose();
}
var foo = new Disposable(); foo.Dispose();
using (var bar = new Disposable()) // Compliant
{
}
A jagged array is an array whose elements are arrays. It is recommended over a multidimensional array because the arrays that make up the elements can be of different sizes, which avoids wasting memory space.
int [,] myArray = // Noncompliant
{
{1,2,3,4},
{5,6,7,0},
{8,0,0,0},
{9,0,0,0}
};
// ...
myArray[1,1] = 0;
int[][] myArray =
{
new int[] {1,2,3,4},
new int[] {5,6,7},
new int[] {8},
new int[] {9}
};
// ...
myArray[1][1] = 0;
================================================
FILE: analyzers/rspec/cs/S3967.json
================================================
{
"title": "Multidimensional arrays should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3967",
"sqKey": "S3967",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3971.html
================================================
GC.SuppressFinalize requests that the system not call the finalizer for the specified object. This should only be done when
implementing Dispose as part of the Dispose Pattern.
This rule raises an issue when GC.SuppressFinalize is called outside that pattern.
Placing an if statement on the same line as the closing } from a preceding if, else, or
else if block can lead to confusion and potential errors. It may indicate a missing else statement or create ambiguity for
maintainers who might fail to understand that the two statements are unconnected.
The following code snippet is confusing:
if (condition1) {
// ...
} if (condition2) { // Noncompliant
//...
}
Either the two conditions are unrelated and they should be visually separated:
if (condition1) {
// ...
}
if (condition2) {
//...
}
Or they were supposed to be exclusive and you should use else if instead:
if (condition1) {
// ...
} else if (condition2) {
//...
}
When the line immediately after conditional statements has neither curly braces nor indentation, the intent of the code is unclear and perhaps not executed as expected. Additionally, such code is confusing to maintainers.
The rule will check the line indentation after the following conditional statements:
if (condition) // Noncompliant DoTheThing(); DoTheOtherThing(); // Was the intent to call this function unconditionally?
It becomes even more confusing and bug-prone if lines get commented out.
if (condition) // Noncompliant // DoTheThing(); DoTheOtherThing(); // Was the intent to call this function conditionally?
Indentation alone or together with curly braces makes the intent clear.
if (condition)
DoTheThing();
DoTheOtherThing(); // Clear intent to call this function unconditionally
// or
if (condition)
{
DoTheThing();
}
DoTheOtherThing(); // Clear intent to call this function unconditionally
================================================
FILE: analyzers/rspec/cs/S3973.json
================================================
{
"title": "A conditionally executed single line should be denoted by indentation",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"confusing",
"suspicious"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-3973",
"sqKey": "S3973",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3981.html
================================================
The size of a collection and the length of an array are always greater than or equal to zero. Testing it doesn’t make sense, since the result is
always true.
if(collection.Count >= 0){...} // Noncompliant: always true
if(array.Length >= 0){...} // Noncompliant: always true
Similarly testing that it is less than zero will always return false.
if(enumerable.Count() < 0){...} // Noncompliant: always false
Fix the code to properly check for emptiness if it was the intent, or remove the redundant code to keep the current behavior.
================================================ FILE: analyzers/rspec/cs/S3981.json ================================================ { "title": "Collection sizes and array length comparisons should make sense", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "confusing" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3981", "sqKey": "S3981", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S3984.html ================================================Creating a new Exception without actually throwing does
not achieve the intended purpose.
if (x < 0)
{
new ArgumentException("x must be nonnegative");
}
Ensure to throw the Exception with a throw
statement.
if (x < 0)
{
throw new ArgumentException("x must be nonnegative");
}
Exception Classthrow, try-catch, try-finally, and try-catch-finallyAssemblies should conform with the Common Language Specification (CLS) in order to be usable across programming languages. To be compliant an
assembly has to indicate it with System.CLSCompliantAttribute.
using System;
[assembly:CLSCompliant(true)]
namespace MyLibrary
{
}
================================================
FILE: analyzers/rspec/cs/S3990.json
================================================
{
"title": "Assemblies should be marked as CLS compliant",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3990",
"sqKey": "S3990",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3992.html
================================================
Assemblies should explicitly indicate whether they are meant to be COM visible or not. If the ComVisibleAttribute is not present, the
default is to make the content of the assembly visible to COM clients.
Note that COM visibility can be overridden for individual types and members.
using System;
namespace MyLibrary // Noncompliant
{
}
using System;
[assembly: System.Runtime.InteropServices.ComVisible(false)]
namespace MyLibrary
{
}
================================================
FILE: analyzers/rspec/cs/S3992.json
================================================
{
"title": "Assemblies should explicitly specify COM visibility",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"api-design"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3992",
"sqKey": "S3992",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3993.html
================================================
When defining custom attributes, AttributeUsageAttribute must be used to indicate where the attribute can be applied. This will:
public sealed class MyAttribute : Attribute // Noncompliant - AttributeUsage is missing
{
private string text;
public MyAttribute(string text)
{
this.text = text;
}
public string Text => text;
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate)]
public sealed class MyAttribute : Attribute
{
private string text;
public MyAttribute(string text)
{
this.text = text;
}
public string Text => text;
}
String representations of URIs or URLs are prone to parsing and encoding errors which can lead to vulnerabilities. The System.Uri
class is a safe alternative and should be preferred. At minimum, an overload of the method taking a System.Uri as a parameter should be
provided in each class that contains a method with an apparent Uri passed as a string.
This rule raises issues when a method has a string parameter with a name containing "uri", "Uri", "urn", "Urn", "url" or "Url", and the type
doesn’t declare a corresponding overload taking an System.Uri parameter instead.
using System;
namespace MyLibrary
{
public class MyClass
{
public void FetchResource(string uriString) { } // Noncompliant
}
}
using System;
namespace MyLibrary
{
public class MyClass
{
public void FetchResource(string uriString)
{
FetchResource(new Uri(uriString));
}
public void FetchResource(Uri uri) { }
}
}
================================================
FILE: analyzers/rspec/cs/S3994.json
================================================
{
"title": "URI Parameters should not be strings",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3994",
"sqKey": "S3994",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3995.html
================================================
String representations of URIs or URLs are prone to parsing and encoding errors which can lead to vulnerabilities. The System.Uri
class is a safe alternative and should be preferred.
This rule raises an issue when a method has a string return type and its name contains "Uri", "Urn", or "Url" or begins with "uri",
"urn", or "url".
using System;
namespace MyLibrary
{
public class MyClass
{
public string GetParentUri() // Noncompliant
{
return "http://www.mysite.com";
}
}
}
using System;
namespace MyLibrary
{
public class MyClass
{
public Uri GetParentUri()
{
return new URI("http://www.mysite.com");
}
}
}
================================================
FILE: analyzers/rspec/cs/S3995.json
================================================
{
"title": "URI return values should not be strings",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3995",
"sqKey": "S3995",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3996.html
================================================
String representations of URIs or URLs are prone to parsing and encoding errors which can lead to vulnerabilities. The System.Uri
class is a safe alternative and should be preferred.
This rule raises an issue when a property is a string type and its name contains "uri", "Uri", "urn", "Urn", "url" or "Url".
using System;
namespace MyLibrary
{
public class MyClass
{
string myUri;
public string MyUri // Noncompliant
{
get { return myURI; }
set { myUri = value; }
}
}
}
using System;
namespace MyLibrary
{
public class MyClass
{
Uri myUri;
public Uri MyUri
{
get { return myURI; }
set { myUri = value; }
}
}
}
================================================
FILE: analyzers/rspec/cs/S3996.json
================================================
{
"title": "URI properties should not be strings",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3996",
"sqKey": "S3996",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3997.html
================================================
String representations of URIs or URLs are prone to parsing and encoding errors which can lead to vulnerabilities. The System.Uri
class is a safe alternative and should be preferred.
This rule raises an issue when two overloads differ only by the string / Uri parameter and the string overload doesn’t call the
Uri overload. It is assumed that the string parameter represents a URI because of the exact match besides that parameter type. It stands
to reason that the safer overload should be used.
using System;
namespace MyLibrary
{
public class MyClass
{
public void FetchResource(string uriString) // Noncompliant
{
// No calls to FetResource(Uri)
}
public void FetchResource(Uri uri) { }
}
}
using System;
namespace MyLibrary
{
public class MyClass
{
public void FetchResource(string uriString)
{
FetchResource(new Uri(uriString));
}
public void FetchResource(Uri uri) { }
}
}
================================================
FILE: analyzers/rspec/cs/S3997.json
================================================
{
"title": "String URI overloads should call \"System.Uri\" overloads",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3997",
"sqKey": "S3997",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S3998.html
================================================
Objects that can be accessed across application domain boundaries are said to have weak identity. This means that these objects can be considered shared resources outside of the domain, which can be lead to them being accessed or modified by multiple threads or concurrent parts of a program, outside of the domain.
A thread acquiring a lock on such an object runs the risk of being blocked by another thread in a different application domain, leading to poor performance and potentially thread starvation and deadlocks.
Types with weak identity are:
public class Sample
{
private readonly StackOverflowException myLock = new();
public void Go()
{
lock (myLock) // Noncompliant
{
// ...
}
}
}
public class Sample
{
private readonly object myLock = new();
public void Go()
{
lock (myLock)
{
// ...
}
}
}
Pointer and unmanaged function pointer types such as IntPtr, UIntPtr, int* etc. are used to access unmanaged
memory, usually in order to use C or C++ libraries. If such a pointer is not secured by making it private, internal or
readonly, it can lead to a vulnerability allowing access to arbitrary locations.
using System;
namespace MyLibrary
{
public class MyClass
{
public IntPtr myPointer; // Noncompliant
protected UIntPtr myOtherPointer; // Noncompliant
}
}
using System;
namespace MyLibrary
{
public class MyClass
{
private IntPtr myPointer;
protected readonly UIntPtr myOtherPointer;
}
}
================================================
FILE: analyzers/rspec/cs/S4000.json
================================================
{
"title": "Pointers to unmanaged memory should not be visible",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-4000",
"sqKey": "S4000",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4002.html
================================================
This rule raises an issue when a disposable type contains fields of the following types and does not implement a finalizer:
System.IntPtrSystem.UIntPtrSystem.Runtime.InteropService.HandleRef
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
public class Foo : IDisposable // Noncompliant: Doesn't have a finalizer
{
private IntPtr myResource;
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
// Dispose of resources held by this instance.
FreeResource(myResource);
disposed = true;
// Suppress finalization of this disposed instance.
if (disposing)
{
GC.SuppressFinalize(this);
}
}
}
public void Dispose() {
Dispose(true);
}
}
}
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
public class Foo : IDisposable
{
private IntPtr myResource;
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
// Dispose of resources held by this instance.
FreeResource(myResource);
disposed = true;
// Suppress finalization of this disposed instance.
if (disposing)
{
GC.SuppressFinalize(this);
}
}
}
~Foo()
{
Dispose(false);
}
}
}
A writable collection property can be replaced by a completely different collection. Making it readonly prevents that while still
allowing individual members to be set. If you want to allow the replacement of the whole collection the recommended pattern is to implement a method
to remove all the elements (e.g. System.Collections.List<T>.Clear) and a method to populate the collection (e.g.
System.Collections.List<T>.AddRange).
This rule raises an issue when an externally visible writable property is of a type that implements System.Collections.ICollection or
System.Collections.Generic.ICollection<T>.
using System;
using System.Collections;
namespace MyLibrary
{
public class Foo
{
List<string> strings;
public List<string> SomeStrings
{
get { return strings; }
set { strings = value; } // Noncompliant
}
}
}
using System;
using System.Collections;
namespace MyLibrary
{
public class Foo
{
List<string> strings;
public List<string> SomeStrings
{
get { return strings; }
}
}
}
This rule does not raise issues for
string, Array and PermissionSet,DataMemberAttributeSerializable================================================ FILE: analyzers/rspec/cs/S4004.json ================================================ { "title": "Collection properties should be readonly", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4004", "sqKey": "S4004", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4005.html ================================================
String representations of URIs or URLs are prone to parsing and encoding errors which can lead to vulnerabilities. The System.Uri
class is a safe alternative and should be preferred.
This rule raises an issue when a called method has a string parameter with a name containing "uri", "Uri", "urn", "Urn", "url" or "Url" and the
declaring type contains a corresponding overload that takes a System.Uri as a parameter.
When there is a choice between two overloads that differ only regarding the representation of a URI, the user should choose the overload that takes
a System.Uri argument.
using System;
namespace MyLibrary
{
public class Foo
{
public void FetchResource(string uriString) { }
public void FetchResource(Uri uri) { }
public string ReadResource(string uriString, string name, bool isLocal) { }
public string ReadResource(Uri uri, string name, bool isLocal) { }
public void Main() {
FetchResource("http://www.mysite.com"); // Noncompliant
ReadResource("http://www.mysite.com", "foo-resource", true); // Noncompliant
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
public void FetchResource(string uriString) { }
public void FetchResource(Uri uri) { }
public string ReadResource(string uriString, string name, bool isLocal) { }
public string ReadResource(Uri uri, string name, bool isLocal) { }
public void Main() {
FetchResource(new Uri("http://www.mysite.com"));
ReadResource(new Uri("http://www.mysite.com"), "foo-resource", true);
}
}
}
================================================
FILE: analyzers/rspec/cs/S4005.json
================================================
{
"title": "\"System.Uri\" arguments should be used instead of strings",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4005",
"sqKey": "S4005",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4015.html
================================================
Decreasing the accessibility level of an inherited method that is not overridable to private will shadow the name of the base method and can lead to confusion.
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeMethod(int count) { } // Noncompliant
}
class Program
{
public void DoWork()
{
var derived = new Derived();
derived.SomeMethod(42); // Base.SomeMethod is accessed here
}
}
Another potential problem is the case of a class deriving from Derived and accessing SomeMethod. In this scenario, the
method accessed will instead be the Base implementation, which might not be what was expected.
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeMethod(int count) { } // Noncompliant
}
public class SecondDerived : Derived
{
public void DoWork()
{
SomeMethod(42); // Base.SomeMethod is accessed here
}
}
One way to mitigate this, is by sealing the Derived class by using the sealed modifier, thus preventing inheritance from this
point on.
public class Base
{
public void SomeMethod(int count) { }
}
public sealed class Derived : Base
{
private void SomeMethod(int count) { } // Compliant: class is marked as sealed
}
Another way to mitigate this, is by having the Derived implementation match the accessibility modifier of the
Base implementation of SomeMethod. From a caller’s perspective, this is closer to the expected behavior.
using System;
namespace MyLibrary
{
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
public void SomeMethod(int count) { } // Compliant: same accessibility as Base.SomeMethod
}
public class Program
{
public void DoWork()
{
var derived = new Derived();
derived.SomeMethod(42); // Derived.SomeMethod is called
}
}
}
Last but not least, consider using a different name for the Derived method, thus completely eliminating any confusion caused by the
naming collision.
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeOtherMethod(int count) { } // Compliant
}
If an enum member’s name contains the word "reserved" it implies it is not currently used and will be change in the future. However
changing an enum member is a breaking change and can create significant problems. There is no need to reserve an enum member
since a new member can be added in the future, and such an addition will usually not be a breaking change.
This rule raises an issue when the name of an enumeration member contains "reserved".
using System;
namespace MyLibrary
{
public enum Color
{
None,
Red,
Orange,
Yellow,
ReservedColor // Noncompliant
}
}
================================================
FILE: analyzers/rspec/cs/S4016.json
================================================
{
"title": "Enumeration members should not be named \"Reserved\"",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4016",
"sqKey": "S4016",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4017.html
================================================
A nested type is a type argument that is also a generic type. Calling a method with such a nested type argument requires complicated and confusing code. It should be avoided as much as possible.
using System;
using System.Collections.Generic;
namespace MyLibrary
{
public class Foo
{
public void DoSomething(ICollection<ICollection<int>> outerCollect) // Noncompliant
{
}
}
}
================================================
FILE: analyzers/rspec/cs/S4017.json
================================================
{
"title": "Method signatures should not contain nested generic types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "30min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4017",
"sqKey": "S4017",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4018.html
================================================
Type inference enables the call of a generic method without explicitly specifying its type arguments. This is not possible when a parameter type is missing from the argument list.
using System;
namespace MyLibrary
{
public class Foo
{
public void MyMethod<T>() // Noncompliant
{
// this method can only be invoked by providing the type argument e.g. 'MyMethod<int>()'
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
public void MyMethod<T>(T param)
{
// type inference allows this to be invoked 'MyMethod(arg)'
}
}
}
================================================
FILE: analyzers/rspec/cs/S4018.json
================================================
{
"title": "All type parameters should be used in the parameter list to enable type inference",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4018",
"sqKey": "S4018",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4019.html
================================================
When a method in a derived class has:
string in the base class and object in the derived class)the result is that the base method becomes hidden.
As shown in the following code snippet, when an instance of the derived class is used, invoking the method with an argument that matches the less derived parameter type will invoke the derived class method instead of the base class method:
class BaseClass
{
internal void MyMethod(string str) => Console.WriteLine("BaseClass: Method(string)");
}
class DerivedClass : BaseClass
{
internal void MyMethod(object str) => Console.WriteLine("DerivedClass: Method(object)"); // Noncompliant
}
// ...
BaseClass baseObj = new BaseClass();
baseObj.MyMethod("Hello"); // Output: BaseClass: Method(string)
DerivedClass derivedObj = new DerivedClass();
derivedObj.MyMethod("Hello"); // Output: DerivedClass: Method(object) - DerivedClass method is hiding the BaseClass method
BaseClass derivedAsBase = new DerivedClass();
derivedAsBase.MyMethod("Hello"); // Output: BaseClass: Method(string)
class BaseClass
{
internal void MyMethod(string str) => Console.WriteLine("BaseClass: Method(string)");
}
class DerivedClass : BaseClass
{
internal void MyOtherMethod(object str) => Console.WriteLine("DerivedClass: Method(object)"); // Compliant
}
// ...
BaseClass baseObj = new BaseClass();
baseObj.MyMethod("Hello"); // Output: BaseClass: Method(string)
DerivedClass derivedObj = new DerivedClass();
derivedObj.MyMethod("Hello"); // Output: BaseClass: Method(string)
BaseClass derivedAsBase = new DerivedClass();
derivedAsBase.MyMethod("Hello"); // Output: BaseClass: Method(string)
Keep in mind that you cannot fix this issue by using the new keyword or by marking the method in the base
class as virtual and overriding it in the DerivedClass because the parameter types are different.
The rule is not raised when the two methods have the same parameter types.
By default the storage type of an enum is Int32. In most cases it is not necessary to change this. In particular you will
not achieve any performance gain by using a smaller data type (e.g. Byte) and may limit future uses.
using System;
namespace MyLibrary
{
public enum Visibility : sbyte // Noncompliant
{
Visible = 0,
Invisible = 1,
}
}
using System;
namespace MyLibrary
{
public enum Visibility
{
Visible = 0,
Invisible = 1,
}
}
================================================
FILE: analyzers/rspec/cs/S4022.json
================================================
{
"title": "Enumerations should have \"Int32\" storage",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4022",
"sqKey": "S4022",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4023.html
================================================
Empty interfaces should be avoided as they do not provide any functional requirements for implementing classes.
Empty interfaces are either useless or used as a marker. Custom attributes are a better alternative to marker interfaces. See the How to fix it section for more information.
This rule doesn’t raise in any of the following cases:
public interface IAggregate: IComparable, IFormattable { } // Compliant: Aggregates two interfaces
An empty interface with a single base interface is compliant as long as the resulting interface binds a generic parameter or constrains it.
// Compliant: Bound to a concrete type
public interface IStringEquatable: IEquatable<string> { }
// Compliant: Specialized by type parameter constraint
public interface ICreateableEquatable<T>: IEquatable<T> where T: new() { }
An empty interface is compliant if a custom attribute is applied to the interface.
[Obsolete]
public interface ISorted { } // Compliant: An attribute is applied to the interface declaration
Do any of the following to fix the issue:
The empty interface does not add any functionality.
public interface IFoo // Noncompliant
{
}
Add members to the interface to be compliant.
public interface IFoo
{
void Bar();
}
A typical use case for marker interfaces is doing type inspection via reflection as shown below.
The IIncludeFields marker interface is used to configure the JSON serialization of an object.
// An example marker interface
public interface IIncludeFields { }
public class OptInToIncludeFields: IIncludeFields { }
Serialize(new OptInToIncludeFields());
void Serialize<T>(T o)
{
// Use reflection to check if the interface is applied to the type
var includeFields = o.GetType()
.GetInterfaces().Any(i => i == typeof(IIncludeFields));
var options = new JsonSerializerOptions()
{
// Take decisions based on the presence of the marker
IncludeFields = includeFields,
};
}
The same example can be rewritten using custom attributes. This approach is preferable because it is more fine-grained, allows parameterization, and is more flexible in type hierarchies.
// A custom attribute used as a marker
[AttributeUsage(AttributeTargets.Class)]
public sealed class IncludeFieldsAttribute: Attribute { }
[IncludeFields]
public class OptInToIncludeFields { }
Serialize(new OptInToIncludeFields());
void Serialize<T>(T o)
{
// Use reflection to check if the attribute is applied to the type
var includeFields = o.GetType()
.GetCustomAttributes(typeof(IncludeFieldsAttribute), inherit: true).Any();
var options = new JsonSerializerOptions()
{
// Take decisions based on the presence of the marker
IncludeFields = includeFields,
};
}
Having a field in a child class with a name that differs from a parent class' field only by capitalization is sure to cause confusion. Such child class fields should be renamed.
public class Fruit
{
protected string plantingSeason;
//...
}
public class Raspberry : Fruit
{
protected string plantingseason; // Noncompliant
// ...
}
public class Fruit
{
protected string plantingSeason;
//...
}
public class Raspberry : Fruit
{
protected string whenToPlant;
// ...
}
Or
public class Fruit
{
protected string plantingSeason;
//...
}
public class Raspberry : Fruit
{
// field removed; parent field will be used instead
// ...
}
This rule ignores same-name fields that are static in both the parent and child classes. It also ignores private parent
class fields, but in all other such cases, the child class field should be renamed.
It is important to inform the ResourceManager of the language used to display the resources of the neutral culture for an assembly.
This improves lookup performance for the first resource loaded.
This rule raises an issue when an assembly contains a ResX-based resource but does not have the
System.Resources.NeutralResourcesLanguageAttribute applied to it.
using System;
public class MyClass // Noncompliant
{
public static void Main()
{
string[] cultures = { "de-DE", "en-us", "fr-FR" };
Random rnd = new Random();
int index = rnd.Next(0, cultures.Length);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultures[index]);
ResourceManager rm = new ResourceManager("MyResources" ,
typeof(MyClass).Assembly);
string greeting = rm.GetString("Greeting");
Console.Write("Enter your name: ");
string name = Console.ReadLine();
Console.WriteLine("{0} {1}!", greeting, name);
}
}
using System;
[assembly:NeutralResourcesLanguageAttribute("en")]
public class MyClass
{
public static void Main()
{
string[] cultures = { "de-DE", "en-us", "fr-FR" };
Random rnd = new Random();
int index = rnd.Next(0, cultures.Length);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultures[index]);
ResourceManager rm = new ResourceManager("MyResources" ,
typeof(MyClass).Assembly);
string greeting = rm.GetString("Greeting");
Console.Write("Enter your name: ");
string name = Console.ReadLine();
Console.WriteLine("{0} {1}!", greeting, name);
}
}
================================================
FILE: analyzers/rspec/cs/S4026.json
================================================
{
"title": "Assemblies should be marked with \"NeutralResourcesLanguageAttribute\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4026",
"sqKey": "S4026",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4027.html
================================================
Exceptions types should provide the following constructors:
public MyException()public MyException(string)public MyException(string, Exception)The absence of these constructors can complicate exception handling and limit the information that can be provided when an exception is thrown.
public class MyException : Exception // Noncompliant: several constructors are missing
{
public MyException()
{
}
}
public class MyException : Exception
{
public MyException()
{
}
public MyException(string message)
: base(message)
{
}
public MyException(string message, Exception innerException)
: base(message, innerException)
{
}
}
When a class implements the IEquatable<T> interface, it enters a contract that, in effect, states "I know how to compare two
instances of type T or any type derived from T for equality.". However if that class is derived, it is very unlikely that the base class will know how
to make a meaningful comparison. Therefore that implicit contract is now broken.
Alternatively IEqualityComparer<T> provides a safer interface and is used by collections or Equals could be made
virtual.
This rule raises an issue when an unsealed, public or protected class implements IEquatable<T> and the
Equals is neither virtual nor abstract.
using System;
namespace MyLibrary
{
public class Base : IEquatable<Base> // Noncompliant
{
public bool Equals(Base other)
{
if (other == null) { return false; }
// do comparison of base properties
return true;
}
public override bool Equals(object other) => Equals(other as Base);
}
class A : Base
{
public bool Equals(A other)
{
if (other == null) { return false; }
// do comparison of A properties
return base.Equals(other);
}
public override bool Equals(object other) => Equals(other as A);
}
class B : Base
{
public bool Equals(B other)
{
if (other == null) { return false; }
// do comparison of B properties
return base.Equals(other);
}
public override bool Equals(object other) => Equals(other as B);
}
internal class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
Console.WriteLine(a.Equals(b)); // This calls the WRONG equals. This causes Base.Equals(Base)
// to be called which only compares the properties in Base and ignores the fact that
// a and b are different types. In the working example A.Equals(Object) would have been
// called and Equals would return false because it correctly recognizes that a and b are
// different types. If a and b have the same base properties they will be returned as equal.
}
}
}
using System;
namespace MyLibrary
{
public sealed class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
// Your code here
}
}
}
IEqualityComparer<T> Interface
================================================ FILE: analyzers/rspec/cs/S4035.json ================================================ { "title": "Classes implementing \"IEquatable\u003cT\u003e\" should be sealed", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "pitfall" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4035", "sqKey": "S4035", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4036.html ================================================When you run an OS command, it is always important to protect yourself against the risk of accidental or malicious replacement of the executables in the production system.
To do so, it is important to point to the specific executable that should be used.
For example, if you call git (without specifying a path), the operating system will search for the executable in the directories
specified in the PATH environment variable.
An attacker could have added, in a permissive directory covered by PATH , another executable called git, but with a
completely different behavior, for example exfiltrating data or exploiting a vulnerability in your own code.
However, by calling /usr/bin/git or ../git (relative path) directly, the operating system will always use the intended
executable.
Note that you still need to make sure that the executable is not world-writeable and potentially overwritten. This is not the scope of this
rule.
There is a risk if you answered no to this question.
If you wish to rely on the PATH environment variable to locate the OS command, make sure that each of its listed directories is fixed,
not susceptible to change, and not writable by unprivileged users.
If you determine that these folders cannot be altered, and that you are sure that the program you intended to use will be used, then you can determine that these risks are under your control.
A good practice you can use is to also hardcode the PATH variable you want to use, if you can do so in the framework you use.
If the previous recommendations cannot be followed due to their complexity or other requirements, then consider using the absolute path of the command instead.
$ whereis git git: /usr/bin/git /usr/share/man/man1/git.1.gz $ ls -l /usr/bin/git -rwxr-xr-x 1 root root 3376112 Jan 28 10:13 /usr/bin/git
Process p = new Process(); p.StartInfo.FileName = "binary"; // Sensitive
Process p = new Process(); p.StartInfo.FileName = @"C:\Apps\binary.exe";
When a base type explicitly implements a public interface method, property or event, that member is only accessible in derived types through a
reference to the current instance (namely this). If the derived type explicitly overrides that interface member, the base implementation
becomes inaccessible.
This rule raises an issue when an unsealed, externally visible type provides an explicit member implementation of an interface and
does not provide an alternate, externally visible member with the same name.
This rule does not report a violation for an explicit implementation of IDisposable.Dispose when an externally visible
Close() or System.IDisposable.Dispose(Boolean) method is provided.
Make the class sealed, change the class member to a non-explicit declaration, or provide a new class member exposing the functionality of the explicit interface member.
public interface IMyInterface
{
void MyMethod();
}
public class Foo : IMyInterface
{
void IMyInterface.MyMethod() // Noncompliant
{
MyMethod();
}
}
public interface IMyInterface
{
void MyMethod();
}
public class Foo : IMyInterface
{
void IMyInterface.MyMethod()
{
MyMethod();
}
// This method can be public or protected
protected void MyMethod()
{
// Do something ...
}
}
Certain characters, once normalized to lowercase, cannot make a round trip. That is, they can not be converted from one locale to another and then accurately restored to their original characters.
It is therefore strongly recommended to normalize characters and strings to uppercase instead.
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
var areStringEqual = "INTEGER".ToLower() == "integer"; // Noncompliant, the result is false as the ToLower will resolve to "ınteger"
var areCharEqual = char.ToLower('I') == 'i'; // Noncompliant, the result is false as the ToLower will resolve to "ı"
var incorrectRoundtrip = "İ".ToLowerInvariant().ToUpper() == "I".ToLowerInvariant().ToUpper(); // Noncompliant, because of the lower we lose the information about the correct uppercase character
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
var areStringEqual = "ınteger".ToUpperInvariant() == "ıNTEGER";
var areCharEqual = char.ToUpperInvariant('ı') == 'ı';
var correctRoundtrip = "İ".ToUpperInvariant().ToLower() != "I".ToUpperInvariant().ToLower();
When a type name matches the name of a publicly defined namespace, for instance one in the .NET framework class library, it leads to confusion and makes the library that much harder to use.
This rule raises an issue when a name of a public type matches the name of a .NET Framework namespace, or a namespace of the project assembly, in a case-insensitive comparison.
using System;
namespace MyLibrary
{
public class Text // Noncompliant: Collides with System.Text
{
}
}
using System;
namespace MyLibrary
{
public class MyText
{
}
}
================================================
FILE: analyzers/rspec/cs/S4041.json
================================================
{
"title": "Type names should not match namespaces",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4041",
"sqKey": "S4041",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4047.html
================================================
When a reference parameter (keyword ref) is used, the passed argument type must exactly match the reference parameter type. This means
that to be able to pass a derived type, it must be cast and assigned to a variable of the proper type. Use of generic methods eliminates that
cumbersome down casting and should therefore be preferred.
This rule raises an issue when a method contains a ref parameter of type System.Object.
using System;
namespace MyLibrary
{
public class Foo
{
public void Bar(ref object o1, ref object o2) // Noncompliant
{
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
public void Bar<T>(ref T ref1, ref T ref2)
{
}
}
}
================================================
FILE: analyzers/rspec/cs/S4047.json
================================================
{
"title": "Generics should be used when appropriate",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4047",
"sqKey": "S4047",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4049.html
================================================
Properties are accessed like fields which makes them easier to use.
This rule raises an issue when the name of a public or protected method starts with Get, takes no parameter,
and returns a value that is not an array.
using System;
namespace MyLibrary
{
public class Foo
{
private string name;
public string GetName() // Noncompliant
{
return name;
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
private string name;
public string Name
{
get
{
return name;
}
}
}
}
The rule doesn’t raise an issue when the method:
overrideasyncTask, Task<T>GetEnumerator, GetAwaiterWhen overloading some arithmetic operator overloads, it is very important to make sure that all related operators and methods are consistent in their implementation.
The following guidelines should be followed:
operator ==, != you should also provide Equals(Object) and GetHashCode().operator +, -, *, / or % you should also provide operator ==, respecting the previous
guideline.This rule raises an issue when any of these guidelines are not followed on a publicly-visible class or struct (public,
protected or protected internal).
Make sure to implement all related operators.
public class Foo // Noncompliant
{
private int left;
private int right;
public Foo(int l, int r)
{
this.left = l;
this.right = r;
}
public static Foo operator +(Foo a, Foo b)
{
return new Foo(a.left + b.left, a.right + b.right);
}
public static Foo operator -(Foo a, Foo b)
{
return new Foo(a.left - b.left, a.right - b.right);
}
}
public class Foo
{
private int left;
private int right;
public Foo(int l, int r)
{
this.left = l;
this.right = r;
}
public override bool Equals(Object obj)
{
var a = obj as Foo;
if (a == null)
return false;
return this == a;
}
public override int GetHashCode()
{
return HashCode.Combine(right, left);
}
public static Foo operator +(Foo a, Foo b)
{
return new Foo(a.left + b.left, a.right + b.right);
}
public static Foo operator -(Foo a, Foo b)
{
return new Foo(a.left - b.left, a.right - b.right);
}
public static bool operator ==(Foo a, Foo b)
{
return a.left == b.left && a.right == b.right;
}
public static bool operator !=(Foo a, Foo b)
{
return !(a == b);
}
}
With the advent of .NET Framework 2.0, certain practices and types have become obsolete.
In particular, exceptions should now extend System.Exception instead of System.ApplicationException. Similarly, generic
collections should be used instead of the older, non-generic, ones. Finally when creating an XML view, you should not extend
System.Xml.XmlDocument. This rule raises an issue when an externally visible type extends one of these types:
using System;
using System.Collections;
namespace MyLibrary
{
public class MyCollection : CollectionBase // Noncompliant
{
}
}
using System;
using System.Collections.ObjectModel;
namespace MyLibrary
{
public class MyCollection : Collection<T>
{
}
}
================================================
FILE: analyzers/rspec/cs/S4052.json
================================================
{
"title": "Types should not extend outdated base types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4052",
"sqKey": "S4052",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4055.html
================================================
String literals embedded in the source code will not be localized properly.
This rule raises an issue when a literal string is passed as a parameter or property and one or more of the following cases is true:
LocalizableAttribute attribute of the parameter or property is set to true.Console.Write or Console.WriteLine method is either "value" or
"format".
using System;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
[assembly: NeutralResourcesLanguageAttribute("en-US")]
namespace MyLibrary
{
public class Foo
{
public void SetHour(int hour)
{
if (hour < 0 || hour > 23)
{
MessageBox.Show("The valid range is 0 - 23."); // Noncompliant
}
}
}
}
using System;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;
[assembly: NeutralResourcesLanguageAttribute("en-US")]
namespace MyLibrary
{
public class Foo
{
ResourceManager rm;
public Foo()
{
rm = new ResourceManager("en-US", Assembly.GetExecutingAssembly());
}
public void SetHour(int hour)
{
if (hour < 0 || hour > 23)
{
MessageBox.Show(
rm.GetString("OutOfRangeMessage", CultureInfo.CurrentUICulture));
}
}
}
}
================================================
FILE: analyzers/rspec/cs/S4055.json
================================================
{
"title": "Literals should not be passed as localized parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"localisation",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4055",
"sqKey": "S4055",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4056.html
================================================
When a System.Globalization.CultureInfo or IFormatProvider object is not supplied, the default value that is supplied by
the overloaded member might not have the effect that you want in all locales.
You should supply culture-specific information according to the following guidelines:
CultureInfo.CurrentCulture.CultureInfo.InvariantCulture.This rule raises an issue when a method or constructor calls one or more members that have overloads that accept a
System.IFormatProvider parameter, and the method or constructor does not call the overload that takes the IFormatProvider
parameter. This rule ignores calls to .NET Framework methods that are documented as ignoring the IFormatProvider parameter as well as the
following methods:
Activator.CreateInstanceResourceManager.GetObjectResourceManager.GetString
using System;
namespace MyLibrary
{
public class Foo
{
public void Bar(String string1)
{
if(string.Compare(string1, string2, false) == 0) // Noncompliant
{
Console.WriteLine(string3.ToLower()); // Noncompliant
}
}
}
}
using System;
using System.Globalization;
namespace MyLibrary
{
public class Foo
{
public void Bar(String string1, String string2, String string3)
{
if(string.Compare(string1, string2, false,
CultureInfo.InvariantCulture) == 0)
{
Console.WriteLine(string3.ToLower(CultureInfo.CurrentCulture));
}
}
}
}
This rule will not raise an issue when the overload is marked as obsolete.
================================================ FILE: analyzers/rspec/cs/S4056.json ================================================ { "title": "Overloads with a \"CultureInfo\" or an \"IFormatProvider\" parameter should be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "localisation", "pitfall" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-4056", "sqKey": "S4056", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4057.html ================================================When you create a DataTable or DataSet, you should set the locale explicitly. By default, the locale for these types is
the current culture. For data that is stored in a database or file and is shared globally, the locale should ordinarily be set to the invariant
culture (CultureInfo.InvariantCulture).
This rule raises an issue when System.Data.DataTable or System.Data.DataSet instances are created without explicitly
setting the locale property (DataTable.Locale or DataSet.Locale).
using System;
using System.Data;
namespace MyLibrary
{
public class Foo
{
public DataTable CreateTable()
{
DataTable table = new DataTable("Customers"); // Noncompliant table.Locale not set
DataColumn key = table.Columns.Add("ID", typeof(Int32));
key.AllowDBNull = false;
key.Unique = true;
table.Columns.Add("LastName", typeof(String));
table.Columns.Add("FirstName", typeof(String));
return table;
}
}
}
using System;
using System.Data;
using System.Globalization;
namespace MyLibrary
{
public class Foo
{
public DataTable CreateTable()
{
DataTable table = new DataTable("Customers");
table.Locale = CultureInfo.InvariantCulture;
DataColumn key = table.Columns.Add("ID", typeof(Int32));
key.AllowDBNull = false;
key.Unique = true;
table.Columns.Add("LastName", typeof(String));
table.Columns.Add("FirstName", typeof(String));
return table;
}
}
}
================================================
FILE: analyzers/rspec/cs/S4057.json
================================================
{
"title": "Locales should be set for data types",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"localisation"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4057",
"sqKey": "S4057",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4058.html
================================================
Many string operations, the Compare and Equals methods in particular, provide an overload that accepts a
StringComparison enumeration value as a parameter. Calling these overloads and explicitly providing this parameter makes your code
clearer and easier to maintain.
This rule raises an issue when a string comparison operation doesn’t use the overload that takes a StringComparison parameter.
using System;
namespace MyLibrary
{
public class Foo
{
public bool HaveSameNames(string name1, string name2)
{
return string.Compare(name1, name2) == 0; // Noncompliant
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
public bool HaveSameNames(string name1, string name2)
{
return string.Compare(name1, name2, StringComparison.OrdinalIgnoreCase) == 0;
}
}
}
================================================
FILE: analyzers/rspec/cs/S4058.json
================================================
{
"title": "Overloads with a \"StringComparison\" parameter should be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4058",
"sqKey": "S4058",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4059.html
================================================
Properties and Get method should have names that makes them clearly distinguishable.
This rule raises an issue when the name of a public or protected member starts with 'Get' and otherwise matches the name of a public or protected property.
using System;
namespace MyLibrary
{
public class Foo
{
public DateTime Date
{
get { return DateTime.Today; }
}
public string GetDate() // Noncompliant
{
return this.Date.ToString();
}
}
}
using System;
namespace MyLibrary
{
public class Foo
{
public DateTime Date
{
get { return DateTime.Today; }
}
public string GetDateAsString()
{
return this.Date.ToString();
}
}
}
================================================
FILE: analyzers/rspec/cs/S4059.json
================================================
{
"title": "Property names should not match get methods",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4059",
"sqKey": "S4059",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4060.html
================================================
The .NET framework class library provides methods for retrieving custom attributes. Sealing the attribute eliminates the search through the inheritance hierarchy, and can improve performance.
This rule raises an issue when a public type inherits from System.Attribute, is not abstract, and is not sealed.
using System;
public class MyAttribute: Attribute // Noncompliant
{
public string Name { get; }
public MyAttribute(string name) =>
Name = name;
}
using System;
public sealed class MyAttribute : Attribute
{
public string Name { get; }
public MyAttribute(string name) =>
Name = name;
}
================================================
FILE: analyzers/rspec/cs/S4060.json
================================================
{
"title": "Non-abstract attributes should be sealed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4060",
"sqKey": "S4060",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4061.html
================================================
A method using the VarArgs calling convention is not Common Language Specification (CLS) compliant and might not be accessible across
programming languages, while the params keyword works the same way and is CLS compliant.
This rule raises an issue when a public or protected type contains a public or protected method
that uses the VarArgs calling convention.
using System;
namespace MyLibrary
{
public class Foo
{
public void Bar(__arglist) // Noncompliant
{
ArgIterator argumentIterator = new ArgIterator(__arglist);
for(int i = 0; i < argumentIterator.GetRemainingCount(); i++)
{
Console.WriteLine(
__refvalue(argumentIterator.GetNextArg(), string));
}
}
}
}
using System;
[assembly: CLSCompliant(true)]
namespace MyLibrary
{
public class Foo
{
public void Bar(params string[] wordList)
{
for(int i = 0; i < wordList.Length; i++)
{
Console.WriteLine(wordList[i]);
}
}
}
}
Interop methods using VarArgs calling convention do not raise an issue.
[DllImport("msvcrt40.dll")]
public static extern int printf(string format, __arglist); // Compliant
================================================
FILE: analyzers/rspec/cs/S4061.json
================================================
{
"title": "\"params\" should be used instead of \"varargs\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4061",
"sqKey": "S4061",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4069.html
================================================
Operator overloading is convenient but unfortunately not portable across languages. To be able to access the same functionality from another language you need to provide an alternate named method following the convention:
| Operator | Method Name |
|---|---|
|
|
Add |
|
|
BitwiseAnd |
|
|
BitwiseOr |
|
|
Divide |
|
|
Equals |
|
|
Xor |
|
|
Compare |
|
|
Compare |
|
|
Equals |
|
|
Compare |
|
|
Compare |
|
|
LogicalNot |
|
|
Mod |
|
|
Multiply |
|
|
OnesComplement |
|
|
Subtract |
|
|
Negate |
|
|
Plus |
This rule raises an issue when there is an operator overload without the expected named alternative method.
This rule does not raise an issue when the class implementing the comparison operators >, <, >= and
<= contains a method named CompareTo.
This rule raises an issue when an externally visible enumeration is marked with FlagsAttribute and one, or more, of its values is not
a power of 2 or a combination of the other defined values.
using System;
namespace MyLibrary
{
[Flags]
public enum Color // Noncompliant, Orange is neither a power of two, nor a combination of any of the defined values
{
None = 0,
Red = 1,
Orange = 3,
Yellow = 4
}
}
using System;
namespace MyLibrary
{
public enum Color // Compliant - no FlagsAttribute
{
None = 0,
Red = 1,
Orange = 3,
Yellow = 4
}
[Flags]
public enum Days
{
None = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 4,
Thursday = 8,
Friday = 16,
All = Monday| Tuesday | Wednesday | Thursday | Friday // Compliant - combination of other values
}
}
================================================
FILE: analyzers/rspec/cs/S4070.json
================================================
{
"title": "Non-flags enums should not be marked with \"FlagsAttribute\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4070",
"sqKey": "S4070",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4136.html
================================================
For clarity, all overloads of the same method should be grouped together. That lets both users and maintainers quickly understand all the current available options.
interface IMyInterface
{
int DoTheThing(); // Noncompliant - overloaded method declarations are not grouped together
string DoTheOtherThing();
int DoTheThing(string s);
}
interface IMyInterface
{
int DoTheThing();
int DoTheThing(string s);
string DoTheOtherThing();
}
As it is common practice to group method declarations by implemented interface, no issue will be raised for implicit and explicit interface implementations if grouped together with other members of that interface.
As it is also a common practice to group method declarations by accessibility level, no issue will be raised for method overloads having different access modifiers.
Example:
class MyClass
{
private void DoTheThing(string s) // Ok - this method is declared as private while the other one is public
{
// ...
}
private string DoTheOtherThing(string s)
{
// ...
}
public void DoTheThing()
{
// ...
}
}
================================================
FILE: analyzers/rspec/cs/S4136.json
================================================
{
"title": "Method overloads should be grouped together",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4136",
"sqKey": "S4136",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4143.html
================================================
Storing a value inside a collection at a given key or index and then unconditionally overwriting it without reading the initial value is a case of a "dead store".
list[index] = "value 1"; list[index] = "value 2"; // Noncompliant dictionary.Add(key, "value 1"); dictionary[key] = "value 2"; // Noncompliant
This practice is redundant and will cause confusion for the reader. More importantly, it is often an error and not what the developer intended to do.
================================================ FILE: analyzers/rspec/cs/S4143.json ================================================ { "title": "Collection elements should not be replaced unconditionally", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4143", "sqKey": "S4143", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4144.html ================================================Two methods having the same implementation are suspicious. It might be that something else was intended. Or the duplication is intentional, which becomes a maintenance burden.
private const string CODE = "secret";
private int callCount = 0;
public string GetCode()
{
callCount++;
return CODE;
}
public string GetName() // Noncompliant: duplicates GetCode
{
callCount++;
return CODE;
}
If the identical logic is intentional, the code should be refactored to avoid duplication. For example, by having both methods call the same method or by having one implementation invoke the other.
private const string CODE = "secret";
private int callCount = 0;
public string GetCode()
{
callCount++;
return CODE;
}
public string GetName() // Intent is clear
{
return GetCode();
}
Empty methods, methods with only one line of code and methods with the same name (overload) are ignored.
================================================ FILE: analyzers/rspec/cs/S4144.json ================================================ { "title": "Methods should not have identical implementations", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "DISTINCT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "15min" }, "tags": [ "confusing", "duplicate", "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4144", "sqKey": "S4144", "scope": "All", "quickfix": "infeasible" } ================================================ FILE: analyzers/rspec/cs/S4158.html ================================================When a collection is empty, iterating it has no effect. Doing so anyway is likely a bug; either population was accidentally omitted, or the iteration needs to be revised.
public void Method()
{
var values = new List<string>();
values.Remove("bar"); // Noncompliant
if (values.Contains("foo")) { } // Noncompliant
foreach (var str in values) { } // Noncompliant
}
public void Method()
{
var values = LoadValues();
values.Remove("bar");
if (values.Contains("foo")) { }
foreach (var str in values) { }
}
================================================
FILE: analyzers/rspec/cs/S4158.json
================================================
{
"title": "Empty collections should not be accessed or iterated",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"symbolic-execution"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4158",
"sqKey": "S4158",
"scope": "All",
"quickfix": "infeasible"
}
================================================
FILE: analyzers/rspec/cs/S4159.html
================================================
The Attributed Programming Model, also known as Attribute-oriented programming (@OP), is a programming model used to embed attributes within codes.
In this model, objects are required to conform to a specific structure so that they can be used by the Managed Extensibility Framework (MEF).
MEF provides a way to discover available components implicitly, via composition. A MEF component, called a part, declaratively specifies:
The ExportAttribute declares that a part "exports", or provides to the composition container, an object that fulfills a particular contract.
During composition, parts with imports that have matching contracts will have those dependencies filled by the exported object.
If the type doesn’t implement the interface it is exporting there will be an issue at runtime (either a cast exception or just a container not filled with the exported type) leading to unexpected behaviors/crashes.
The rule raises an issue when a class doesn’t implement or inherit the type declared in the ExportAttribute.
[Export(typeof(ISomeType))]
public class SomeType // Noncompliant: doesn't implement 'ISomeType'.
{
}
[Export(typeof(ISomeType))]
public class SomeType : ISomeType
{
}
Native methods are functions that reside in libraries outside the .NET runtime. Calling them is helpful for interoperability with applications and
libraries written in other programming languages, mainly when performing platform-specific operations. However, doing so comes with additional risks
since it means stepping out of the memory-safety model of the runtime. It is therefore highly recommended to take extra steps, like input validation,
when invoking native methods. Making the native method private and providing a wrapper that performs these additional steps is the best
way to do so.
This rule raises an issue when a native method is declared public or its wrapper is too trivial.
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
class Foo
{
[DllImport("mynativelib")]
extern public static void Bar(string s, int x); // Noncompliant
}
}
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
class Foo
{
[DllImport("mynativelib")]
extern private static void Bar(string s, int x);
public void BarWrapper(string s, int x)
{
if (s != null && x >= 0 && x < s.Length)
{
Bar(s, x);
}
}
}
}
================================================
FILE: analyzers/rspec/cs/S4200.json
================================================
{
"title": "Native methods should be wrapped",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "30min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4200",
"sqKey": "S4200",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4201.html
================================================
There’s no need to null test in conjunction with an is test. null is not an instance of anything, so a null check is
redundant.
if (x != null && x is MyClass) { ... } // Noncompliant
if (x == null || !(x is MyClass)) { ... } // Noncompliant
if (x is MyClass) { ... }
if (!(x is MyClass)) { ... }
================================================
FILE: analyzers/rspec/cs/S4201.json
================================================
{
"title": "Null checks should not be combined with \"is\" operator checks",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"redundant"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4201",
"sqKey": "S4201",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/cs/S4210.html
================================================
When an assembly uses Windows Forms (classes and interfaces from the System.Windows.Forms namespace) its entry point should be marked
with the STAThreadAttribute to indicate that the threading model should be "Single-Threaded Apartment" (STA) which is the only one
supported by Windows Forms.
This rule raises an issue when the entry point (static void Main method) of an assembly using Windows Forms is not marked as STA.
using System;
using System.Windows.Forms;
namespace MyLibrary
{
public class MyForm: Form
{
public MyForm()
{
this.Text = "Hello World!";
}
public static void Main() // Noncompliant
{
var form = new MyForm();
Application.Run(form);
}
}
}
using System;
using System.Windows.Forms;
namespace MyLibrary
{
public class MyForm: Form
{
public MyForm()
{
this.Text = "Hello World!";
}
[STAThread]
public static void Main()
{
var form = new MyForm();
Application.Run(form);
}
}
}
================================================
FILE: analyzers/rspec/cs/S4210.json
================================================
{
"title": "Windows Forms entry points should be marked with STAThread",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"winforms",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4210",
"sqKey": "S4210",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4211.html
================================================
Transparency attributes in the .NET Framework, designed to protect security-critical operations, can lead to ambiguities and vulnerabilities when declared at different levels such as both for the class and a method.
Transparency attributes can be declared at several levels. If two different attributes are declared at two different levels, the attribute that
prevails is the one in the highest level. For example, you can declare that a class is SecuritySafeCritical and that a method of this
class is SecurityCritical. In this case, the method will be SecuritySafeCritical and the SecurityCritical
attribute attached to it is ignored.
Below are some real-world scenarios that illustrate some impacts of an attacker exploiting the vulnerability.
An attacker could potentially exploit conflicting transparency attributes to perform actions with higher privileges than intended.
If a member with conflicting attributes is involved in handling sensitive data, an attacker could exploit the vulnerability to gain unauthorized access to this data. This could lead to breaches of confidentiality and potential data loss.
using System;
using System.Security;
namespace MyLibrary
{
[SecuritySafeCritical]
public class Foo
{
[SecurityCritical] // Noncompliant
public void Bar()
{
}
}
}
using System;
using System.Security;
namespace MyLibrary
{
public class Foo
{
[SecurityCritical]
public void Bar()
{
}
}
}
A class should never have class-level annotations if some functions have different permission levels. Instead, make sure every function has its own correct annotation.
If no function needs a particularly distinct security annotation in a class, just set a class-level [SecurityCritical].
This rule is deprecated, and will eventually be removed.
Because serialization constructors allocate and initialize objects, security checks that are present on regular constructors must also be present on a serialization constructor. Failure to do so would allow callers that could not otherwise create an instance to use the serialization constructor to do this.
This rule raises an issue when a type implements the System.Runtime.Serialization.ISerializable interface, is not a delegate or
interface, is declared in an assembly that allows partially trusted callers and has a constructor that takes a
System.Runtime.Serialization.SerializationInfo object and a System.Runtime.Serialization.StreamingContext object which is
not secured by a security check, but one or more of the regular constructors in the type is secured.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Permissions;
[assembly: AllowPartiallyTrustedCallersAttribute()]
namespace MyLibrary
{
[Serializable]
public class Foo : ISerializable
{
private int n;
[FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
public Foo()
{
n = -1;
}
protected Foo(SerializationInfo info, StreamingContext context) // Noncompliant
{
n = (int)info.GetValue("n", typeof(int));
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("n", n);
}
}
}
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Permissions;
[assembly: AllowPartiallyTrustedCallersAttribute()]
namespace MyLibrary
{
[Serializable]
public class Foo : ISerializable
{
private int n;
[FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
public Foo()
{
n = -1;
}
[FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
protected Foo(SerializationInfo info, StreamingContext context)
{
n = (int)info.GetValue("n", typeof(int));
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("n", n);
}
}
}
This rule is deprecated; use {rule:csharpsquid:S4200} instead.
Methods marked with the System.Runtime.InteropServices.DllImportAttribute attribute use Platform Invocation Services to access
unmanaged code and should not be exposed. Keeping them private or internal makes sure that their access is controlled and properly managed.
This rule raises an issue when a method declared with DllImport is public or protected.
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
public class Foo
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool RemoveDirectory(string name); // Noncompliant
}
}
using System;
using System.Runtime.InteropServices;
namespace MyLibrary
{
public class Foo
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool RemoveDirectory(string name);
}
}
================================================
FILE: analyzers/rspec/cs/S4214.json
================================================
{
"title": "\"P\/Invoke\" methods should not be visible",
"type": "CODE_SMELL",
"status": "deprecated",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4214",
"sqKey": "S4214",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4220.html
================================================
When raising an event, two arguments are expected by the EventHandler delegate: Sender and event-data. There are three guidelines
regarding these parameters:
null as the sender when raising a non-static event.null as the sender when raising a static event.null as the event-data. If no data should be passed, then EventArgs.Empty should be used.This rule raises an issue when any of these guidelines is not met.
using System;
namespace MyLibrary
{
class Foo
{
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
ThresholdReached?.Invoke(null, e); // Noncompliant
}
}
}
using System;
namespace MyLibrary
{
class Foo
{
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
ThresholdReached?.Invoke(this, e);
}
}
}
================================================
FILE: analyzers/rspec/cs/S4220.json
================================================
{
"title": "Events should have proper arguments",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"event",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4220",
"sqKey": "S4220",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4225.html
================================================
Creating an extension method that extends object is not recommended because it makes the method available on every type.
Extensions should be applied at the most specialized level possible, and that is very unlikely to be object.
public static class MyExtensions
{
public static void SomeExtension(this object obj) // Noncompliant
{
// ...
}
}
================================================
FILE: analyzers/rspec/cs/S4225.json
================================================
{
"title": "Extension methods should not extend \"object\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4225",
"sqKey": "S4225",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4226.html
================================================
It makes little sense to create an extension method when it is possible to just add that method to the class itself.
This rule raises an issue when an extension is declared in the same namespace as the class it is extending.
namespace MyLibrary
{
public class Foo
{
// ...
}
public static class MyExtensions
{
public static void Bar(this Foo a) // Noncompliant
{
// ...
}
}
}
Using separate namespace:
namespace MyLibrary
{
public class Foo
{
// ...
}
}
namespace Helpers
{
public static class MyExtensions
{
public static void Bar(this Foo a)
{
// ...
}
}
}
Merging the method in the class:
namespace MyLibrary
{
public class Foo
{
// ...
public void Bar()
{
// ...
}
}
}
System.CodeDom.Compiler.GeneratedCodeAttribute attribute.When creating a custom Markup Extension
that accepts parameters in WPF, the ConstructorArgument markup
must be used to identify the discrete properties that match these parameters. However since this is done via a string, the compiler won’t give you any
warning in case there are typos.
This rule raises an issue when the string argument to ConstructorArgumentAttribute doesn’t match any parameter of any constructor.
using System;
namespace MyLibrary
{
public class MyExtension : MarkupExtension
{
public MyExtension() { }
public MyExtension(object value1)
{
Value1 = value1;
}
[ConstructorArgument("value2")] // Noncompliant
public object Value1 { get; set; }
}
}
using System;
namespace MyLibrary
{
public class MyExtension : MarkupExtension
{
public MyExtension() { }
public MyExtension(object value1)
{
Value1 = value1;
}
[ConstructorArgument("value1")]
public object Value1 { get; set; }
}
}
According to the Task-based Asynchronous Pattern (TAP), methods returning either a System.Threading.Tasks.Task or a
System.Threading.Tasks.Task<TResult> are considered "asynchronous". Such methods should use the Async suffix.
Conversely methods which do not return such Tasks should not have an "Async" suffix in their names.
using System;
using System.Threading.Tasks;
namespace myLibrary
{
public class Foo
{
public Task Read(byte [] buffer, int offset, int count, // Noncompliant
CancellationToken cancellationToken)
}
}
using System;
using System.Threading.Tasks;
namespace myLibrary
{
public class Foo
{
public Task ReadAsync(byte [] buffer, int offset, int count, CancellationToken cancellationToken)
}
}
This rule doesn’t raise an issue when the method is an override or part of the implementation of an interface since it can not be renamed.
Properties provide a way to enforce encapsulation by providing
accessors that give controlled access to private fields. However, in classes with multiple fields, it is not unusual that copy-and-paste is used to quickly create the needed properties, which can result
in the wrong field being accessed by a getter or setter.
class C
{
private int x;
private int y;
public int Y => x; // Noncompliant: The returned field should be 'y'
}
This rule raises an issue in any of these cases:
For simple properties, it is better to use auto-implemented properties (C# 3.0 or later).
Field and property names are compared as case-insensitive. All underscore characters are ignored.
class A
{
private int x;
private int y;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return x; } // Noncompliant: field 'y' is not used in the return value
set { x = value; } // Noncompliant: field 'y' is not updated
}
}
class A
{
private int x;
private int y;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
Marking a class with PartCreationPolicy(CreationPolicy.Shared), which is
part of Managed Extensibility Framework (MEF), means that a single, shared
instance of the exported object will be created. Therefore it doesn’t make sense to create new instances using the constructor and it will most likely
result in unexpected behaviours.
This rule raises an issue when a constructor of a class marked shared with a PartCreationPolicyAttribute is invoked.
[Export(typeof(IFooBar))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class FooBar : IFooBar
{
}
public class Program
{
public static void Main()
{
var fooBar = new FooBar(); // Noncompliant;
}
}
[Export(typeof(IFooBar))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class FooBar : IFooBar
{
}
public class Program
{
public static void Main()
{
var fooBar = serviceProvider.GetService<IFooBar>();
}
}
Cryptographic operations often rely on unpredictable random numbers to enhance security. These random numbers are created by cryptographically secure pseudo-random number generators (CSPRNG). It is important not to use a predictable seed with these random number generators otherwise the random numbers will also become predictable.
Random number generators are often used to generate random values for cryptographic algorithms. When a random number generator is used for cryptographic purposes, the generated numbers must be as random and unpredictable as possible. When the random number generator is improperly seeded with a constant or a predictable value, its output will also be predictable.
This can have severe security implications for cryptographic operations that rely on the randomness of the generated numbers. By using a predictable seed, an attacker can potentially guess or deduce the generated numbers, compromising the security of whatever cryptographic algorithm relies on the random number generator.
It is crucial to understand that the strength of cryptographic algorithms heavily relies on the quality of the random numbers used. By improperly seeding a CSPRNG, we introduce a significant weakness that can be exploited by attackers.
One of the primary use cases for CSPRNGs is generating cryptographic keys. If an attacker can predict the seed used to initialize the random number generator, they may be able to derive the same keys. Depending on the use case, this can lead to multiple severe outcomes, such as:
Another scenario where this vulnerability can be exploited is in the generation of session tokens or nonces for secure communication protocols. If an attacker can predict the seed used to generate these tokens, they can impersonate legitimate users or intercept sensitive information.
BouncyCastle provides several random number generators implementations. Most of these will automatically create unpredictable output.
The remaining random number generators require seeding with an unpredictable value before they will produce unpredictable outputs. These should be seeded with at least 16 bytes of random data to ensure unpredictable output and that the random seed cannot be guessed using a brute-force attack.
SecureRandom instances created with GetInstance() are seeded by default. Disabling seeding results in predictable
output.
using Org.BouncyCastle.Security;
byte[] random = new byte[8];
SecureRandom sr = SecureRandom.GetInstance("SHA256PRNG", false);
sr.NextBytes(random); // Noncompliant
DigestRandomGenerator and VmpcRandomGenerator instances require seeding before use. Predictable seed values will result
in predictable outputs.
using Org.BouncyCastle.Crypto.Digest;
using Org.BouncyCastle.Crypto.Prng;
byte[] random = new byte[8];
IRandomGenerator digest = new DigestRandomGenerator(new Sha256Digest());
digest.AddSeedMaterial(Encoding.UTF8.GetBytes("predictable seed value"));
digest.NextBytes(random); // Noncompliant
IRandomGenerator vmpc = new VmpcRandomGenerator();
vmpc.AddSeedMaterial(Convert.FromBase64String("2hq9pkyqLQJkrYTrEv1eNw=="));
vmpc.NextBytes(random); // Noncompliant
When a SecureRandom is created using an unseeded DigestRandomGenerator and VmpcRandomGenerator instance, the
SecureRandom will create predictable outputs.
using Org.BouncyCastle.Crypto.Digest; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Security; byte[] random = new byte[8]; IRandomGenerator digest = new DigestRandomGenerator(new Sha256Digest()); SecureRandom sr = new SecureRandom(digest); sr.NextBytes(random); // Noncompliant
Allow SecureRandom.GetInstance() to automatically seed new SecureRandom instances.
using Org.BouncyCastle.Security;
byte[] random = new byte[8];
SecureRandom sr = SecureRandom.GetInstance("SHA256PRNG");
sr.NextBytes(random);
Use unpredictable values to seed DigestRandomGenerator and VmpcRandomGenerator instances. The
SecureRandom.GenerateSeed() method is designed for this purpose.
using Org.BouncyCastle.Crypto.Digest; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Security; byte[] random = new byte[8]; IRandomGenerator digest = new DigestRandomGenerator(new Sha256Digest()); digest.AddSeedMaterial(SecureRandom.GenerateSeed(16)); digest.NextBytes(random); IRandomGenerator vmpc = new VmpcRandomGenerator(); vmpc.AddSeedMaterial(SecureRandom.GenerateSeed(16)); vmpc.NextBytes(random);
An overload of the SecureRandom constructor will automatically seed the underlying IRandomGenerator with an unpredictable
value.
using Org.BouncyCastle.Crypto.Digest; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Security; byte[] random = new byte[8]; IRandomGenerator digest = new DigestRandomGenerator(new Sha256Digest()); SecureRandom sr = new SecureRandom(digest, 16); sr.NextBytes(random);
(visible only on this page)
When the random number generator’s output is not predictable by default:
Change this seed value to something unpredictable, or remove the seed.
When the random number generator’s output is predictable by default:
Set an unpredictable seed before generating random values.
When the random number generator’s output is not predictable by default:
When the random number generator’s output is predictable by default:
If the factory method or constructor is not already highlighted, it should become a secondary highlight.
================================================ FILE: analyzers/rspec/cs/S4347.json ================================================ { "title": "Secure random number generators should not output predictable values", "type": "VULNERABILITY", "code": { "impacts": { "SECURITY": "HIGH" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "cwe", "pitfall", "symbolic-execution" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-4347", "sqKey": "S4347", "scope": "Main", "securityStandards": { "CWE": [ 330, 332, 336, 337 ], "OWASP": [ "A6" ], "OWASP Top 10 2021": [ "A2" ], "ASVS 4.0": [ "2.3.1", "2.6.2", "2.9.2" ] }, "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4423.html ================================================This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
To provide communication security over a network, SSL and TLS are generally used. However, it is important to note that the following protocols are all considered weak by the cryptographic community, and are officially deprecated:
When these unsecured protocols are used, it is best practice to expect a breach: that a user or organization with malicious intent will perform mathematical attacks on this data after obtaining it by other means.
After retrieving encrypted data and performing cryptographic attacks on it on a given timeframe, attackers can recover the plaintext that encryption was supposed to protect.
Depending on the recovered data, the impact may vary.
Below are some real-world scenarios that illustrate the potential impact of an attacker exploiting the vulnerability.
By modifying the plaintext of the encrypted message, an attacker may be able to trigger additional vulnerabilities in the code. An attacker can
further exploit a system to obtain more information.
Encrypted values are often considered trustworthy because it would not be possible for a third party to modify them under normal circumstances.
When encrypted data contains personal or sensitive information, its retrieval by an attacker can lead to privacy violations, identity theft, financial loss, reputational damage, or unauthorized access to confidential systems.
In this scenario, the company, its employees, users, and partners could be seriously affected.
The impact is twofold, as data breaches and exposure of encrypted data can undermine trust in the organization, as customers, clients and stakeholders may lose confidence in the organization’s ability to protect their sensitive data.
In many industries and locations, there are legal and compliance requirements to protect sensitive data. If encrypted data is compromised and the plaintext can be recovered, companies face legal consequences, penalties, or violations of privacy laws.
These samples use TLSv1.0 as the default TLS algorithm, which is cryptographically weak.
using System.Net;
public void encrypt()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; // Noncompliant
}
using System.Net.Http;
using System.Security.Authentication;
public void encrypt()
{
new HttpClientHandler
{
SslProtocols = SslProtocols.Tls // Noncompliant
};
}
Using System.Net;
public void encrypt()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
}
using System.Net.Http;
using System.Security.Authentication;
public void encrypt()
{
new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12
};
}
As a rule of thumb, by default you should use the cryptographic algorithms and mechanisms that are considered strong by the cryptographic community.
The best choices at the moment are the following.
Even though TLS V1.3 is available, using TLS v1.2 is still considered good and secure practice by the cryptography community.
The use of TLS v1.2 ensures compatibility with a wide range of platforms and enables seamless communication between different systems that do not yet have TLS v1.3 support.
The only drawback depends on whether the framework used is outdated: its TLS v1.2 settings may enable older and insecure cipher suites that are deprecated as insecure.
On the other hand, TLS v1.3 removes support for older and weaker cryptographic algorithms, eliminates known vulnerabilities from previous TLS versions, and improves performance.
This vulnerability exposes encrypted data to attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
In today’s cryptography, the length of the key directly affects the security level of cryptographic algorithms.
Note that depending on the algorithm, the term key refers to a different mathematical property. For example:
If an application uses a key that is considered short and insecure, the encrypted data is exposed to attacks aimed at getting at the plaintext.
In general, it is best practice to expect a breach: that a user or organization with malicious intent will perform cryptographic attacks on this data after obtaining it by other means.
After retrieving encrypted data and performing cryptographic attacks on it on a given timeframe, attackers can recover the plaintext that encryption was supposed to protect.
Depending on the recovered data, the impact may vary.
Below are some real-world scenarios that illustrate the potential impact of an attacker exploiting the vulnerability.
By modifying the plaintext of the encrypted message, an attacker may be able to trigger additional vulnerabilities in the code. An attacker can
further exploit a system to obtain more information.
Encrypted values are often considered trustworthy because it would not be possible for a third party to modify them under normal circumstances.
When encrypted data contains personal or sensitive information, its retrieval by an attacker can lead to privacy violations, identity theft, financial loss, reputational damage, or unauthorized access to confidential systems.
In this scenario, the company, its employees, users, and partners could be seriously affected.
The impact is twofold, as data breaches and exposure of encrypted data can undermine trust in the organization, as customers, clients and stakeholders may lose confidence in the organization’s ability to protect their sensitive data.
In many industries and locations, there are legal and compliance requirements to protect sensitive data. If encrypted data is compromised and the plaintext can be recovered, companies face legal consequences, penalties, or violations of privacy laws.
The following code examples either explicitly or implicitly generate keys. Note that there are differences in the size of the keys depending on the algorithm.
Due to the mathematical properties of the algorithms, the security requirements for the key size vary depending on the algorithm.
For example, a 256-bit ECC key provides about the same level of security as a 3072-bit RSA key and a 128-bit symmetric key.
Here is an example of a private key generation with RSA:
using System;
using System.Security.Cryptography;
public void encrypt()
{
var RsaCsp = new RSACryptoServiceProvider(); // Noncompliant
}
Here is an example of a key generation with the Digital Signature Algorithm (DSA):
using System;
using System.Security.Cryptography;
public void encrypt()
{
var DsaCsp = new DSACryptoServiceProvider(); // Noncompliant
}
Here is an example of an Elliptic Curve (EC) initialization. It implicitly generates a private key whose size is indicated in the elliptic curve name:
using System;
using System.Security.Cryptography;
public void encrypt()
{
ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.brainpoolP160t1); // Noncompliant
}
using System;
using System.Security.Cryptography;
public void encrypt()
{
var RsaCsp = new RSACryptoServiceProvider(2048);
}
using System;
using System.Security.Cryptography;
public void encrypt()
{
var Dsa = new DSACng(2048);
}
using System;
using System.Security.Cryptography;
public void encrypt()
{
ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
}
As a rule of thumb, use the cryptographic algorithms and mechanisms that are considered strong by the cryptography community.
The appropriate choices are the following.
The security of these algorithms depends on the difficulty of attacks attempting to solve their underlying mathematical problem.
In general, a minimum key size of 2048 bits is recommended for both. It provides 112 bits of security. A key length of 3072 or 4096 should be preferred when possible.
Elliptic curve cryptography is also used in various algorithms, such as ECDSA, ECDH, or ECMQV. The length of keys generated with elliptic curve
algorithms is mentioned directly in their names. For example, secp256k1 generates a 256-bits long private key.
Currently, a minimum key size of 224 bits is recommended for EC-based algorithms.
Additionally, some curves that theoretically provide sufficiently long keys are still discouraged. This can be because of a flaw in the curve parameters, a bad overall design, or poor performance. It is generally advised to use a NIST-approved elliptic curve wherever possible. Such curves currently include:
The following code is invalid:
----
var RsaCsp = new RSACryptoServiceProvider();
RsaCsp.KeySize = 2048;
----
The KeySize property of CryptoServiceProviders cannot be updated because the setter simply does not exist. This means that this line will not
perform any update on KeySize, and the compiler won’t raise an Exception when compiling it. This should not be considered a workaround.
To change the key size, use one of the overloaded constructors with the desired key size instead.
Encrypted data and communications recorded today could be decrypted in the future by an attack from a quantum computer.
It is important to keep in mind that NIST-approved digital signature schemes, key agreement, and key transport may need to be replaced with secure
quantum-resistant (or "post-quantum") counterpart.
Thus, if data is to remain secure beyond 2030, proactive measures should be taken now to ensure its safety.
To customize the default behavior for an export in the Managed Extensibility
Framework (MEF), applying the PartCreationPolicyAttribute
is necessary. For the PartCreationPolicyAttribute
to be meaningful in the context of an export, the class must also be annotated with the ExportAttribute.
This rule raises an issue when a class is annotated with the PartCreationPolicyAttribute but not with the
ExportAttribute.
using System.ComponentModel.Composition;
[PartCreationPolicy(CreationPolicy.Any)] // Noncompliant
public class FooBar : IFooBar { }
using System.ComponentModel.Composition;
[Export(typeof(IFooBar))]
[PartCreationPolicy(CreationPolicy.Any)]
public class FooBar : IFooBar { }
Lightweight Directory Access Protocol (LDAP) servers provide two main authentication methods: the SASL and Simple ones. The Simple Authentication method also breaks down into three different mechanisms:
A server that accepts either the Anonymous or Unauthenticated mechanisms will accept connections from clients not providing credentials.
When configured to accept the Anonymous or Unauthenticated authentication mechanism, an LDAP server will accept connections from clients that do not provide a password or other authentication credentials. Such users will be able to read or modify part or all of the data contained in the hosted directory.
An attacker exploiting unauthenticated access to an LDAP server can access the data that is stored in the corresponding directory. The impact varies depending on the permission obtained on the directory and the type of data it stores.
If attackers get write access to the directory, they will be able to alter most of the data it stores. This might include sensitive technical data such as user passwords or asset configurations. Such an attack can typically lead to an authentication bypass on applications and systems that use the affected directory as an identity provider.
In such a case, all users configured in the directory might see their identity and privileges taken over.
If attackers get read-only access to the directory, they will be able to read the data it stores. That data might include security-sensitive pieces of information.
Typically, attackers might get access to user account lists that they can use in further intrusion steps. For example, they could use such lists to perform password spraying, or related attacks, on all systems that rely on the affected directory as an identity provider.
If the directory contains some Personally Identifiable Information, an attacker accessing it might represent a violation of regulatory requirements in some countries. For example, this kind of security event would go against the European GDPR law.
The following code indicates an anonymous LDAP authentication vulnerability because it binds to a remote server using an Anonymous Simple authentication mechanism.
DirectoryEntry myDirectoryEntry = new DirectoryEntry(adPath); myDirectoryEntry.AuthenticationType = AuthenticationTypes.None; // Noncompliant DirectoryEntry myDirectoryEntry = new DirectoryEntry(adPath, "u", "p", AuthenticationTypes.None); // Noncompliant
DirectoryEntry myDirectoryEntry = new DirectoryEntry(myADSPath); // Compliant; default DirectoryEntry.AuthenticationType property value is "Secure" since .NET Framework 2.0 DirectoryEntry myDirectoryEntry = new DirectoryEntry(myADSPath, "u", "p", AuthenticationTypes.Secure);
Because of the way yield methods are rewritten by the compiler (they become lazily evaluated state machines) any exceptions thrown
during the parameters check will happen only when the collection is iterated over. That could happen far away from the source of the buggy code.
Therefore it is recommended to split the method into two: an outer method handling the validation (no longer lazy) and an inner (lazy) method to handle the iteration.
This rule raises an issue when a method throws any exception derived from ArgumentException and contains the yield
keyword.
public static IEnumerable<TSource> TakeWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) // Noncompliant
{
if (source == null) { throw new ArgumentNullException(nameof(source)); }
if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); }
foreach (var element in source)
{
if (!predicate(element)) { break; }
yield return element;
}
}
public static async IAsyncEnumerable<TSource> TakeWhileAsync(this IEnumerable<TSource> source, Func<TSource, bool> predicate) // Noncompliant
{
if (source == null) { throw new ArgumentNullException(nameof(source)); }
if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); }
foreach (var element in source)
{
await Task.Delay(10);
if (!predicate(element)) { break; }
yield return element;
}
}
public static IEnumerable<TSource> TakeWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) { throw new ArgumentNullException(nameof(source)); }
if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); }
return TakeWhileIterator<TSource>(source, predicate);
}
private static IEnumerable<TSource> TakeWhileIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource element in source)
{
if (!predicate(element)) break;
yield return element;
}
}
public static IAsyncEnumerable<TSource> TakeWhileAsync(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) { throw new ArgumentNullException(nameof(source)); }
if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); }
return TakeWhileAsyncIterator<TSource>(source, predicate);
}
private static async IAsyncEnumerable<TSource> TakeWhileAsyncIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource element in source)
{
await Task.Delay(10);
if (!predicate(element)) break;
yield return element;
}
}
async/await methods should be wrappedBecause of the way async/await methods are rewritten by the compiler, any exceptions thrown during the parameters check
are captured in the returned Task and will only be observed when the task is awaited. That could happen far away from the source of the
buggy code or never happen for fire-and-forget tasks.
Therefore it is recommended to split the method into two: an outer method handling the parameter checks (without being
async/await) and an inner method to handle the iterator block with the async/await pattern.
This rule raises an issue when an async method throws any exception derived from ArgumentException and contains the
await keyword.
public static async Task SkipLinesAsync(this TextReader reader, int linesToSkip) // Noncompliant
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
var task = SkipLinesAsync(null, -1); // No exception - captured in the task
await task; // ArgumentNullException thrown here
public static Task SkipLinesAsync(this TextReader reader, int linesToSkip)
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
return reader.SkipLinesInternalAsync(linesToSkip);
}
private static async Task SkipLinesInternalAsync(this TextReader reader, int linesToSkip)
{
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
var task = SkipLinesAsync(null, -1); // ArgumentNullException thrown here
await task;
Making blocking calls to async methods transforms code that was intended to be asynchronous into a blocking operation. Doing so can
cause deadlocks and unexpected blocking of context threads.
According to the MSDN documentation:
The root cause of this deadlock is due to the way
awaithandles contexts. By default, when an incompleteTaskis awaited, the current “context” is captured and used to resume the method when theTaskcompletes. This “context” is the currentSynchronizationContextunless it’s null, in which case it’s the currentTaskScheduler. GUI and ASP.NET applications have aSynchronizationContextthat permits only one chunk of code to run at a time. When theawaitcompletes, it attempts to execute the remainder of theasyncmethod within the captured context. But that context already has a thread in it, which is (synchronously) waiting for theasyncmethod to complete. They’re each waiting for the other, causing a deadlock.
| To Do This … | Instead of This … | Use This |
|---|---|---|
|
Retrieve the result of a background task |
|
|
|
Wait for any task to complete |
|
|
|
Retrieve the results of multiple tasks |
|
|
|
Wait a period of time |
|
|
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait(); // Noncompliant
}
}
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
public static async Task TestAsync()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
await delayTask;
}
}
Thread.Sleep is also ignored when it is used in a non-async method.Task.Run or Task.Factory.StartNew are ignored because they don’t suffer from this deadlock
issuePrivate fields which are written but never read are a case of "dead store". Changing the value of such a field is useless and most probably indicates an error in the code.
public class Rectangle
{
private readonly int length;
private readonly int width; // Noncompliant: width is written but never read
public Rectangle(int length, int width)
{
this.length = length;
this.width = width;
}
public int Surface
{
get
{
return length * length;
}
}
}
Remove this field if it doesn’t need to be read, or fix the code to read it.
public class Rectangle
{
private readonly int length;
private readonly int width;
public Rectangle(int length, int width)
{
this.length = length;
this.width = width;
}
public int Surface
{
get
{
return length * width;
}
}
}
A cross-site request forgery (CSRF) attack occurs when a trusted user of a web application can be forced, by an attacker, to perform sensitive actions that he didn’t intend, such as updating his profile or sending a message, more generally anything that can change the state of the application.
The attacker can trick the user/victim to click on a link, corresponding to the privileged action, or to visit a malicious web site that embeds a hidden web request and as web browsers automatically include cookies, the actions can be authenticated and sensitive.
There is a risk if you answered yes to any of those questions.
GET which are designed to be
used only for information retrieval.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddControllersWithViews(options => options.Filters.Add(new IgnoreAntiforgeryTokenAttribute())); // Sensitive
// ...
}
[HttpPost, IgnoreAntiforgeryToken] // Sensitive
public IActionResult ChangeEmail(ChangeEmailModel model) => View("~/Views/...");
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddControllersWithViews(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
// or
services.AddControllersWithViews(options => options.Filters.Add(new ValidateAntiForgeryTokenAttribute()));
// ...
}
[HttpPost]
[AutoValidateAntiforgeryToken]
public IActionResult ChangeEmail(ChangeEmailModel model) => View("~/Views/...");
Development tools and frameworks usually have options to make debugging easier for developers. Although these features are useful during development, they should never be enabled for applications deployed in production. Debug instructions or error messages can leak detailed information about the system, like the application’s path or file names.
There is a risk if you answered yes to any of those questions.
Do not enable debugging features on production servers.
The .Net Core framework offers multiple features which help during debug.
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage and
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage are two of them. Make sure that those features are disabled in
production.
Use if (env.IsDevelopment()) to disable debug code.
This rule raises issues when the following .Net Core methods are called:
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage,
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace mvcApp
{
public class Startup2
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Those calls are Sensitive because it seems that they will run in production
app.UseDeveloperExceptionPage(); // Sensitive
app.UseDatabaseErrorPage(); // Sensitive
}
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace mvcApp
{
public class Startup2
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
// The following calls are ok because they are disabled in production
app.UseDeveloperExceptionPage(); // Compliant
app.UseDatabaseErrorPage(); // Compliant
}
}
}
}
This rule does not analyze configuration files. Make sure that debug mode is not enabled by default in those files.
The switch statement is a conditional statement that executes a sequence of instructions based on patterns matching the provided value.
switch (temperatureInCelsius)
{
case < 35.0:
Console.WriteLine("Hypothermia");
break;
case >= 36.5 and <= 37.5:
Console.WriteLine("Normal");
break;
case > 37.5 and <= 40.0:
Console.WriteLine("Fever or hyperthermia");
break;
case > 40.0:
Console.WriteLine("Hyperpyrexia");
break;
}
The switch statement can optionally contain a default clause, executed when none of the case clauses are
executed (or in presence of a goto default;).
switch (gradeLetter)
{
case "A+":
case "A":
case "A-":
Console.WriteLine("Excellent");
break;
case "B+":
case "B":
Console.WriteLine("Very Good");
break;
case "B-":
case "C+":
Console.WriteLine("Good");
break;
case "C":
Console.WriteLine("Pass");
break;
case "F":
Console.WriteLine("Fail");
break;
default:
Console.WriteLine("Invalid grade letter!");
break;
}
The default clause can be defined for various reasons:
While C# allows the default clause to appear in any place within a switch statement, and while its position doesn’t alter
its behavior, it is recommended to put the default clause either at the beginning or at the end of the switch statement.
That improves readability and helps the developer to quickly find the default behavior of a switch statement.
This rule raises an issue if the default clause is neither the first nor the last one of the switch statement.
switch (param)
{
case 0:
DoSomething();
break;
default: // Noncompliant: default clause should be the first or last one
Error();
break;
case 1:
DoSomethingElse();
break;
}
switch (param)
{
case 0:
DoSomething();
break;
case 1:
DoSomethingElse();
break;
default:
Error();
break;
}
The DebuggerDisplayAttribute is used to determine how an object is displayed in the debugger window.
The DebuggerDisplayAttribute constructor takes a single mandatory argument: the string to be displayed in the value column for
instances of the type. Any text within curly braces is evaluated as the name of a field or property, or any complex expression containing method calls
and operators.
Naming a non-existent member between curly braces will result in a CS0103 error in the debug window when debugging objects. Although there is no impact on the production code, providing a wrong value can lead to difficulties when debugging the application.
This rule raises an issue when text specified between curly braces refers to members that don’t exist in the current context.
[DebuggerDisplay("Name: {Name}")] // Noncompliant - Name doesn't exist in this context
public class Person
{
public string FullName { get; private set; }
}
[DebuggerDisplay("Name: {FullName}")]
public class Person
{
public string FullName { get; private set; }
}
================================================
FILE: analyzers/rspec/cs/S4545.json
================================================
{
"title": "\"DebuggerDisplayAttribute\" strings should reference existing members",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4545",
"sqKey": "S4545",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4581.html
================================================
When the syntax new Guid() (i.e. parameterless instantiation) is used, it must be that one of three things is wanted:
Guid.Empty is clearer.Guid.NewGuid() should be used.This rule raises an issue when a parameterless instantiation of the Guid struct is found.
public void Foo()
{
var g1 = new Guid(); // Noncompliant - what's the intent?
Guid g2 = new(); // Noncompliant
var g3 = default(Guid); // Noncompliant
Guid g4 = default; // Noncompliant
}
public void Foo(byte[] bytes)
{
var g1 = Guid.Empty;
var g2 = Guid.NewGuid();
var g3 = new Guid(bytes);
}
================================================
FILE: analyzers/rspec/cs/S4581.json
================================================
{
"title": "\"new Guid()\" should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5 min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4581",
"sqKey": "S4581",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/cs/S4583.html
================================================
When calling the BeginInvoke method of a delegate,
resources are allocated that are only freed up when EndInvoke is called. Failing to pair BeginInvoke with
EndInvoke can lead to resource leaks and incomplete asynchronous calls.
This rule raises an issue in the following scenarios:
BeginInvoke method is called without any callback, and it is not paired with a call to EndInvoke in the same
block.IAsyncResult does not contain a call to EndInvoke in the same block.BeginInvoke without callback:
public delegate string AsyncMethodCaller();
public static void Main()
{
AsyncExample asyncExample = new AsyncExample();
AsyncMethodCaller caller = new AsyncMethodCaller(asyncExample.MyMethod);
// Initiate the asynchronous call.
IAsyncResult result = caller.BeginInvoke(null, null); // Noncompliant: not paired with EndInvoke
}
BeginInvoke with callback:
public delegate string AsyncMethodCaller();
public static void Main()
{
AsyncExample asyncExample = new AsyncExample();
AsyncMethodCaller caller = new AsyncMethodCaller(asyncExample.MyMethod);
IAsyncResult result = caller.BeginInvoke(
new AsyncCallback((IAsyncResult ar) => {}),
null); // Noncompliant: not paired with EndInvoke
}
BeginInvoke without callback:
public delegate string AsyncMethodCaller();
public static void Main()
{
AsyncExample asyncExample = new AsyncExample();
AsyncMethodCaller caller = new AsyncMethodCaller(asyncExample.MyMethod);
IAsyncResult result = caller.BeginInvoke(null, null);
string returnValue = caller.EndInvoke(result);
}
BeginInvoke with callback:
public delegate string AsyncMethodCaller();
public static void Main()
{
AsyncExample asyncExample = new AsyncExample();
AsyncMethodCaller caller = new AsyncMethodCaller(asyncExample.MyMethod);
IAsyncResult result = caller.BeginInvoke(
new AsyncCallback((IAsyncResult ar) =>
{
// Call EndInvoke to retrieve the results.
string returnValue = caller.EndInvoke(ar);
}), null);
}
Returning null from a non-async Task/Task<TResult> method will cause a
NullReferenceException at runtime if the method is awaited. This problem can be avoided by returning Task.CompletedTask or Task.FromResult<TResult>(null)
respectively.
public Task DoFooAsync()
{
return null; // Noncompliant: Causes a NullReferenceException if awaited.
}
public async Task Main()
{
await DoFooAsync(); // NullReferenceException
}
Instead of null Task.CompletedTask or Task.FromResult<TResult>(null)
should be returned.
A Task returning method can be fixed like so:
public Task DoFooAsync()
{
return null; // Noncompliant: Causes a NullReferenceException if awaited.
}
public Task DoFooAsync()
{
return Task.CompletedTask; // Compliant: Method can be awaited.
}
A Task<TResult> returning method can be fixed like so:
public Task<object> GetFooAsync()
{
return null; // Noncompliant: Causes a NullReferenceException if awaited.
}
public Task<object> GetFooAsync()
{
return Task.FromResult<object>(null); // Compliant: Method can be awaited.
}
Task.CompletedTask PropertyTask.FromResult<TResult>(TResult)
MethodIt is important to be careful when searching for characters within a substring. Let’s consider the following example:
if (str.SubString(startIndex).IndexOf(char1) == -1) // Noncompliant: a new string is going to be created by "Substring"
{
// ...
}
A new string object is created with every call to the Substring method. When chaining it with any of the
following methods, it creates a new object for one-time use only:
Creating a new object consumes significant system resources, especially CPU and memory.
When using Substring repeatedly, such as in a loop, it can greatly impact the overall performance of the application or program.
To mitigate the creation of new objects while searching for characters within a substring, it is possible to use an overload of the mentioned
methods with a startIndex parameter:
if (str.IndexOf(char1, startIndex) == -1) // Compliant: no new instance of string is created
{
// ...
}
Using these methods gives the same result while avoiding the creation of additional string objects.
String.Substring MethodString.IndexOf MethodString.IndexOfAny MethodString.LastIndexOf MethodString.LastIndexOfAny Method| Method | Runtime | StringSize | Mean | StdDev | Ratio | Allocated |
|---|---|---|---|---|---|---|
|
SubstringThenIndexOf |
.NET 7.0 |
10 |
11.234 ms |
0.1319 ms |
1.00 |
40000008 B |
|
IndexOfOnly |
.NET 7.0 |
10 |
2.477 ms |
0.0473 ms |
0.22 |
2 B |
|
SubstringThenIndexOf |
.NET Framework 4.6.2 |
10 |
8.634 ms |
0.4195 ms |
1.00 |
48141349 B |
|
IndexOfOnly |
.NET Framework 4.6.2 |
10 |
1.724 ms |
0.0346 ms |
0.19 |
- |
|
SubstringThenIndexOf |
.NET 7.0 |
100 |
14.651 ms |
0.2977 ms |
1.00 |
176000008 B |
|
IndexOfOnly |
.NET 7.0 |
100 |
2.464 ms |
0.0501 ms |
0.17 |
2 B |
|
SubstringThenIndexOf |
.NET Framework 4.6.2 |
100 |
13.363 ms |
0.2044 ms |
1.00 |
176518761 B |
|
IndexOfOnly |
.NET Framework 4.6.2 |
100 |
1.689 ms |
0.0290 ms |
0.13 |
- |
|
SubstringThenIndexOf |
.NET 7.0 |
1000 |
78.688 ms |
2.4727 ms |
1.00 |
1528000072 B |
|
IndexOfOnly |
.NET 7.0 |
1000 |
2.456 ms |
0.0397 ms |
0.03 |
2 B |
|
SubstringThenIndexOf |
.NET Framework 4.6.2 |
1000 |
75.994 ms |
1.2650 ms |
1.00 |
1532637240 B |
|
IndexOfOnly |
.NET Framework 4.6.2 |
1000 |
1.699 ms |
0.0216 ms |
0.02 |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
private string theString;
private int iteration = 1_000_000;
[Params(10, 100, 1_000)]
public int StringSize { get; set; }
[GlobalSetup]
public void Setup() =>
theString = new string('a', StringSize);
[Benchmark(Baseline = true)]
public void SubstringThenIndexOf()
{
for (var i = 0; i < iteration; i++)
{
_ = theString.Substring(StringSize / 4).IndexOf('a');
}
}
[Benchmark]
public void IndexOfOnly()
{
for (var i = 0; i < iteration; i++)
{
_ = theString.IndexOf('a', StringSize / 4) - StringSize / 4;
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2965/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S4635.json ================================================ { "title": "Start index should be used instead of calling Substring", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-4635", "sqKey": "S4635", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S4663.html ================================================
Empty comments, as shown in the example, hurt readability and might indicate an oversight.
// /* */ /// /** */
Some meaningful text should be added to the comment, or the comment markers should be removed.
================================================ FILE: analyzers/rspec/cs/S4663.json ================================================ { "title": "Comments should not be empty", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-4663", "sqKey": "S4663", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S4790.html ================================================Cryptographic hash algorithms such as MD2, MD4, MD5, MD6, HAVAL-128,
DSA (which uses SHA-1), RIPEMD, RIPEMD-128, RIPEMD-160and SHA-1 are no
longer considered secure, because it is possible to have collisions (little computational effort is enough to find two or more different
inputs that produce the same hash).
Message authentication code (MAC) algorithms such as HMAC-MD5 or HMAC-SHA1 use weak hash functions as building blocks.
Although they are not all proven to be weak, they are considered legacy algorithms and should be avoided.
The hashed value is used in a security context like:
There is a risk if you answered yes to any of those questions.
Safer alternatives, such as SHA-256, SHA-512, SHA-3 are recommended, and for password hashing, it’s even
better to use algorithms that do not compute too "quickly", like bcrypt, scrypt, argon2 or pbkdf2
because it slows down brute force attacks.
var hashProvider1 = new MD5CryptoServiceProvider(); // Sensitive
var hashProvider2 = (HashAlgorithm)CryptoConfig.CreateFromName("MD5"); // Sensitive
var hashProvider3 = new SHA1Managed(); // Sensitive
var hashProvider4 = HashAlgorithm.Create("SHA1"); // Sensitive
var hashProvider1 = new SHA512Managed(); // Compliant
var hashProvider2 = (HashAlgorithm)CryptoConfig.CreateFromName("SHA512Managed"); // Compliant
var hashProvider3 = HashAlgorithm.Create("SHA512Managed"); // Compliant
This rule is deprecated, and will eventually be removed.
Configuring loggers is security-sensitive. It has led in the past to the following vulnerabilities:
Logs are useful before, during and after a security incident.
Logs are also a target for attackers because they might contain sensitive information. Configuring loggers has an impact on the type of information logged and how they are logged.
This rule flags for review code that initiates loggers configuration. The goal is to guide security code reviews.
There is a risk if you answered yes to any of those questions.
Remember that configuring loggers properly doesn’t make them bullet-proof. Here is a list of recommendations explaining on how to use your logs:
.Net Core: configure programmatically
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore;
namespace MvcApp
{
public class ProgramLogging
{
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) => // Sensitive
{
// ...
})
.UseStartup<StartupLogging>();
}
public class StartupLogging
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(logging => // Sensitive
{
// ...
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
IConfiguration config = null;
LogLevel level = LogLevel.Critical;
Boolean includeScopes = false;
Func<string,Microsoft.Extensions.Logging.LogLevel,bool> filter = null;
Microsoft.Extensions.Logging.Console.IConsoleLoggerSettings consoleSettings = null;
Microsoft.Extensions.Logging.AzureAppServices.AzureAppServicesDiagnosticsSettings azureSettings = null;
Microsoft.Extensions.Logging.EventLog.EventLogSettings eventLogSettings = null;
// An issue will be raised for each call to an ILoggerFactory extension methods adding loggers.
loggerFactory.AddAzureWebAppDiagnostics(); // Sensitive
loggerFactory.AddAzureWebAppDiagnostics(azureSettings); // Sensitive
loggerFactory.AddConsole(); // Sensitive
loggerFactory.AddConsole(level); // Sensitive
loggerFactory.AddConsole(level, includeScopes); // Sensitive
loggerFactory.AddConsole(filter); // Sensitive
loggerFactory.AddConsole(filter, includeScopes); // Sensitive
loggerFactory.AddConsole(config); // Sensitive
loggerFactory.AddConsole(consoleSettings); // Sensitive
loggerFactory.AddDebug(); // Sensitive
loggerFactory.AddDebug(level); // Sensitive
loggerFactory.AddDebug(filter); // Sensitive
loggerFactory.AddEventLog(); // Sensitive
loggerFactory.AddEventLog(eventLogSettings); // Sensitive
loggerFactory.AddEventLog(level); // Sensitive
loggerFactory.AddEventSourceLogger(); // Sensitive
IEnumerable<ILoggerProvider> providers = null;
LoggerFilterOptions filterOptions1 = null;
IOptionsMonitor<LoggerFilterOptions> filterOptions2 = null;
LoggerFactory factory = new LoggerFactory(); // Sensitive
new LoggerFactory(providers); // Sensitive
new LoggerFactory(providers, filterOptions1); // Sensitive
new LoggerFactory(providers, filterOptions2); // Sensitive
}
}
}
Log4Net
using System;
using System.IO;
using System.Xml;
using log4net.Appender;
using log4net.Config;
using log4net.Repository;
namespace Logging
{
class Log4netLogging
{
void Foo(ILoggerRepository repository, XmlElement element, FileInfo configFile, Uri configUri, Stream configStream,
IAppender appender, params IAppender[] appenders) {
log4net.Config.XmlConfigurator.Configure(repository); // Sensitive
log4net.Config.XmlConfigurator.Configure(repository, element); // Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configFile); // Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configUri); // Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configStream); // Sensitive
log4net.Config.XmlConfigurator.ConfigureAndWatch(repository, configFile); // Sensitive
log4net.Config.DOMConfigurator.Configure(); // Sensitive
log4net.Config.DOMConfigurator.Configure(repository); // Sensitive
log4net.Config.DOMConfigurator.Configure(element); // Sensitive
log4net.Config.DOMConfigurator.Configure(repository, element); // Sensitive
log4net.Config.DOMConfigurator.Configure(configFile); // Sensitive
log4net.Config.DOMConfigurator.Configure(repository, configFile); // Sensitive
log4net.Config.DOMConfigurator.Configure(configStream); // Sensitive
log4net.Config.DOMConfigurator.Configure(repository, configStream); // Sensitive
log4net.Config.DOMConfigurator.ConfigureAndWatch(configFile); // Sensitive
log4net.Config.DOMConfigurator.ConfigureAndWatch(repository, configFile); // Sensitive
log4net.Config.BasicConfigurator.Configure(); // Sensitive
log4net.Config.BasicConfigurator.Configure(appender); // Sensitive
log4net.Config.BasicConfigurator.Configure(appenders); // Sensitive
log4net.Config.BasicConfigurator.Configure(repository); // Sensitive
log4net.Config.BasicConfigurator.Configure(repository, appender); // Sensitive
log4net.Config.BasicConfigurator.Configure(repository, appenders); // Sensitive
}
}
}
NLog: configure programmatically
namespace Logging
{
class NLogLogging
{
void Foo(NLog.Config.LoggingConfiguration config) {
NLog.LogManager.Configuration = config; // Sensitive, this changes the logging configuration.
}
}
}
Serilog
namespace Logging
{
class SerilogLogging
{
void Foo() {
new Serilog.LoggerConfiguration(); // Sensitive
}
}
}
This vulnerability makes it possible that an encrypted communication is intercepted.
Transport Layer Security (TLS) provides secure communication between systems over the internet by encrypting the data sent between them. Certificate validation adds an extra layer of trust and security to this process to ensure that a system is indeed the one it claims to be.
When certificate validation is disabled, the client skips a critical security check. This creates an opportunity for attackers to pose as a trusted entity and intercept, manipulate, or steal the data being transmitted.
Establishing trust in a secure way is a non-trivial task. When you disable certificate validation, you are removing a key mechanism designed to build this trust in internet communication, opening your system up to a number of potential threats.
If a system does not validate certificates, it cannot confirm the identity of the other party involved in the communication. An attacker can exploit this by creating a fake server and masquerading as a legitimate one. For example, they might set up a server that looks like your bank’s server, tricking your system into thinking it is communicating with the bank. This scenario, called identity spoofing, allows the attacker to collect any data your system sends to them, potentially leading to significant data breaches.
When TLS certificate validation is disabled, the integrity of the data you send and receive cannot be guaranteed. An attacker could modify the data in transit, and you would have no way of knowing. This could range from subtle manipulations of the data you receive to the injection of malicious code or malware into your system. The consequences of such breaches of data integrity can be severe, depending on the nature of the data and the system.
In the following example, the callback change impacts the entirety of HTTP requests made by the application.
The certificate validation gets disabled by overriding ServerCertificateValidationCallback with an empty implementation. It is highly
recommended to use the original implementation.
using System.Net;
using System.Net.Http;
public static void connect()
{
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, errors) => {
return true; // Noncompliant
};
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = httpClient.GetAsync("https://example.com").Result;
}
Addressing the vulnerability of disabled TLS certificate validation primarily involves re-enabling the default validation.
To avoid running into problems with invalid certificates, consider the following sections.
If possible, always use a certificate issued by a well-known, trusted CA for your server. Most programming environments come with a predefined list of trusted root CAs, and certificates issued by these authorities are validated automatically. This is the best practice, and it requires no additional code or configuration.
In some cases, you might need to work with a server using a self-signed certificate, or a certificate issued by a CA not included in your trusted roots. Rather than disabling certificate validation in your code, you can add the necessary certificates to your trust store.
ValueTask<TResult> provides a value type that wraps a Task<TResult> and the corresponding TResult.
It was introduced in .NET Core 2.0 to optimize
memory allocation when functions return their results synchronously.
Using ValueTask and ValueTask<TResult> in the following ways is discouraged as it could result in a race
condition:
await multiple times on a ValueTask/ValueTask<TResult>. The wrapped object may have been
reused by another operation. This differs from Task/Task<TResult>, on which you can await multiple times and always
get the same result.await concurrently on a ValueTask/ValueTask<TResult>. The underlying object is not thread
safe. What’s more, it has the same effect as awaiting multiple times a ValueTask/ValueTask<TResult>. This again
differs from Task/Task<TResult>, which support concurrent await..Result or .GetAwaiter().GetResult() without checking if the operation is completed.
IValueTaskSource/IValueTaskSource<TResult> implementations are not required to block until the operation completes.
On the other hand, Task/Task<TResult> blocks the call until the task completes.It is recommended to use ValueTask/ValueTask<TResult> either by calling await on the function
returning it, optionally calling ConfigureAwait(false) on it, or by calling .AsTask() on it.
This rule raises an issue when the following operations are performed on a ValueTask/ValueTask<TResult> instance
unless it happens in a loop:
AsTask multiple times..Result or .GetAwaiter().GetResult() multiple times.Result or .GetAwaiter().GetResult() when the operation has not yet completedValueTask<int> vt = ComputeAsync(); int result = await vt; result = await vt; // Noncompliant, variable is awaited multiple times int value = ComputeAsync()).GetAwaiter().GetResult(); // Noncompliant, uses GetAwaiter().GetResult() when it's not known to be done
ValueTask<int> vt = ComputeAsync(); int result = await vt; int value = await ComputeAsync().AsTask();
Successful Zip Bomb attacks occur when an application expands untrusted archive files without controlling the size of the expanded data, which can lead to denial of service. A Zip bomb is usually a malicious archive file of a few kilobytes of compressed data but turned into gigabytes of uncompressed data. To achieve this extreme compression ratio, attackers will compress irrelevant data (eg: a long string of repeated bytes).
Archives to expand are untrusted and:
There is a risk if you answered yes to any of those questions.
using var zipToOpen = new FileStream(@"ZipBomb.zip", FileMode.Open);
using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read);
foreach (ZipArchiveEntry entry in archive.Entries)
{
entry.ExtractToFile("./output_onlyfortesting.txt", true); // Sensitive
}
int THRESHOLD_ENTRIES = 10000;
int THRESHOLD_SIZE = 1000000000; // 1 GB
double THRESHOLD_RATIO = 10;
int totalSizeArchive = 0;
int totalEntryArchive = 0;
using var zipToOpen = new FileStream(@"ZipBomb.zip", FileMode.Open);
using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read);
foreach (ZipArchiveEntry entry in archive.Entries)
{
totalEntryArchive ++;
using (Stream st = entry.Open())
{
byte[] buffer = new byte[1024];
int totalSizeEntry = 0;
int numBytesRead = 0;
do
{
numBytesRead = st.Read(buffer, 0, 1024);
totalSizeEntry += numBytesRead;
totalSizeArchive += numBytesRead;
double compressionRatio = totalSizeEntry / entry.CompressedLength;
if(compressionRatio > THRESHOLD_RATIO) {
// ratio between compressed and uncompressed data is highly suspicious, looks like a Zip Bomb Attack
break;
}
}
while (numBytesRead > 0);
}
if(totalSizeArchive > THRESHOLD_SIZE) {
// the uncompressed data size is too much for the application resource capacity
break;
}
if(totalEntryArchive > THRESHOLD_ENTRIES) {
// too much entries in this archive, can lead to inodes exhaustion of the system
break;
}
}
Having a permissive Cross-Origin Resource Sharing policy is security-sensitive. It has led in the past to the following vulnerabilities:
Same origin policy in browsers prevents, by default and for security-reasons, a javascript frontend to perform a cross-origin HTTP request to a resource that has a different origin (domain, protocol, or port) from its own. The requested target can append additional HTTP headers in response, called CORS, that act like directives for the browser and change the access control policy / relax the same origin policy.
Access-Control-Allow-Origin: untrustedwebsite.com.Access-Control-Allow-Origin: *origin header.There is a risk if you answered yes to any of those questions.
Access-Control-Allow-Origin header should be set only for a trusted origin and for specific resources.Access-Control-Allow-Origin header. Prefer whitelisting domains over blacklisting or
allowing any domain (do not use * wildcard nor blindly return the Origin header content without any checks).ASP.NET Core MVC:
[HttpGet]
public string Get()
{
Response.Headers.Add("Access-Control-Allow-Origin", "*"); // Sensitive
Response.Headers.Add(HeaderNames.AccessControlAllowOrigin, "*"); // Sensitive
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("*"); // Sensitive
});
options.AddPolicy(name: "EnableAllPolicy", builder =>
{
builder.WithOrigins("*"); // Sensitive
});
options.AddPolicy(name: "OtherPolicy", builder =>
{
builder.AllowAnyOrigin(); // Sensitive
});
});
services.AddControllers();
}
ASP.NET MVC:
public class HomeController : ApiController
{
public HttpResponseMessage Get()
{
var response = HttpContext.Current.Response;
response.Headers.Add("Access-Control-Allow-Origin", "*"); // Sensitive
response.Headers.Add(HeaderNames.AccessControlAllowOrigin, "*"); // Sensitive
response.AppendHeader(HeaderNames.AccessControlAllowOrigin, "*"); // Sensitive
}
}
[EnableCors(origins: "*", headers: "*", methods: "GET")] // Sensitive
public HttpResponseMessage Get() => new HttpResponseMessage()
{
Content = new StringContent("content")
};
User-controlled origin:
String origin = Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin); // Sensitive
ASP.NET Core MVC:
[HttpGet]
public string Get()
{
Response.Headers.Add("Access-Control-Allow-Origin", "https://trustedwebsite.com"); // Safe
Response.Headers.Add(HeaderNames.AccessControlAllowOrigin, "https://trustedwebsite.com"); // Safe
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("https://trustedwebsite.com", "https://anothertrustedwebsite.com"); // Safe
});
options.AddPolicy(name: "EnableAllPolicy", builder =>
{
builder.WithOrigins("https://trustedwebsite.com"); // Safe
});
});
services.AddControllers();
}
ASP.Net MVC:
public class HomeController : ApiController
{
public HttpResponseMessage Get()
{
var response = HttpContext.Current.Response;
response.Headers.Add("Access-Control-Allow-Origin", "https://trustedwebsite.com");
response.Headers.Add(HeaderNames.AccessControlAllowOrigin, "https://trustedwebsite.com");
response.AppendHeader(HeaderNames.AccessControlAllowOrigin, "https://trustedwebsite.com");
}
}
[EnableCors(origins: "https://trustedwebsite.com", headers: "*", methods: "GET")]
public HttpResponseMessage Get() => new HttpResponseMessage()
{
Content = new StringContent("content")
};
User-controlled origin validated with an allow-list:
String origin = Request.Headers["Origin"];
if (trustedOrigins.Contains(origin))
{
Response.Headers.Add("Access-Control-Allow-Origin", origin);
}
Clear-text protocols such as ftp, telnet, or http lack encryption of transported data, as well as the
capability to build an authenticated connection. It means that an attacker able to sniff traffic from the network can read, modify, or corrupt the
transported content. These protocols are not secure as they expose applications to an extensive range of risks:
Even in the context of isolated networks like offline environments or segmented cloud environments, the insider threat exists. Thus, attacks involving communications being sniffed or tampered with can still happen.
For example, attackers could successfully compromise prior security layers by:
In such cases, encrypting communications would decrease the chances of attackers to successfully leak data or steal credentials from other network components. By layering various security practices (segmentation and encryption, for example), the application will follow the defense-in-depth principle.
Note that using the http protocol is being deprecated by major web browsers.
In the past, it has led to the following vulnerabilities:
There is a risk if you answered yes to any of those questions.
ssh as an alternative to telnet.sftp, scp, or ftps instead of ftp.https instead of http.SMTP over SSL/TLS or SMTP with STARTTLS instead of clear-text SMTP.It is recommended to secure all transport channels, even on local networks, as it can take a single non-secure connection to compromise an entire application or system.
var urlHttp = "http://example.com"; // Noncompliant var urlFtp = "ftp://anonymous@example.com"; // Noncompliant var urlTelnet = "telnet://anonymous@example.com"; // Noncompliant
using var smtp = new SmtpClient("host", 25); // Noncompliant, EnableSsl is not set
using var telnet = new MyTelnet.Client("host", port); // Noncompliant, rule raises Security Hotspot on any member containing "Telnet"
var urlHttps = "https://example.com"; var urlSftp = "sftp://anonymous@example.com"; var urlSsh = "ssh://anonymous@example.com";
using var smtp = new SmtpClient("host", 25) { EnableSsl = true };
using var ssh = new MySsh.Client("host", port);
No issue is reported for the following cases because they are not considered sensitive:
localhost.The improper storage of passwords poses a significant security risk to software applications. This vulnerability arises when passwords are stored in plaintext or with a fast hashing algorithm. To exploit this vulnerability, an attacker typically requires access to the stored passwords.
Attackers who would get access to the stored passwords could reuse them without further attacks or with little additional effort.
Obtaining the plaintext passwords, they could then gain unauthorized access to user accounts, potentially leading to various malicious
activities.
Plaintext or weakly hashed password storage poses a significant security risk to software applications.
When passwords are stored in plaintext or with weak hashing algorithms, an attacker who gains access to the password database can easily retrieve and use the passwords to gain unauthorized access to user accounts. This can lead to various malicious activities, such as unauthorized data access, identity theft, or even financial fraud.
Many users tend to reuse passwords across multiple platforms. If an attacker obtains plaintext or weakly hashed passwords, they can potentially use these credentials to gain unauthorized access to other accounts held by the same user. This can have far-reaching consequences, as sensitive personal information or critical systems may be compromised.
Many industries and jurisdictions have specific regulations and standards to protect user data and ensure its confidentiality. Storing passwords in plaintext or with weak hashing algorithms can lead to non-compliance with these regulations, potentially resulting in legal consequences, financial penalties, and damage to the reputation of the software application and its developers.
Using Microsoft.AspNetCore.Cryptography.KeyDerivation:
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;
string password = Request.Query["password"];
byte[] salt = RandomNumberGenerator.GetBytes(128 / 8);
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password!,
salt: salt,
prf: KeyDerivationPrf.HMACSHA256,
iterationCount: 1, // Noncompliant
numBytesRequested: 256 / 8));
Using System.Security.Cryptography:
using System.Security.Cryptography; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, 128/8); // Noncompliant string hashed = Convert.ToBase64String(kdf.GetBytes(256 / 8));
Using Microsoft.AspNetCore.Identity:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
string password = Request.Query["password"];
IOptions<PasswordHasherOptions> options = Options.Create(new PasswordHasherOptions() {
IterationCount = 1 // Noncompliant
});
PasswordHasher<User> hasher = new PasswordHasher<User>(options);
string hash = hasher.HashPassword(new User("test"), password);
Using Microsoft.AspNetCore.Cryptography.KeyDerivation:
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;
string password = Request.Query["password"];
byte[] salt = RandomNumberGenerator.GetBytes(128 / 8);
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password!,
salt: salt,
prf: KeyDerivationPrf.HMACSHA256,
iterationCount: 100_000,
numBytesRequested: 256 / 8));
Using System.Security.Cryptography
using System.Security.Cryptography; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, 100_000, HashAlgorithmName.SHA256); string hashed = Convert.ToBase64String(kdf.GetBytes(256 / 8));
Using Microsoft.AspNetCore.Identity:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
string password = Request.Query["password"];
PasswordHasher<User> hasher = new PasswordHasher<User>();
string hash = hasher.HashPassword(new User("test"), password);
If PBKDF2 must be used, be aware that default values might not be considered secure.
Depending on the algorithm used, the number of iterations should be adjusted to ensure that the derived key is secure. The following are the
recommended number of iterations for PBKDF2:
Note that PBKDF2-HMAC-SHA256 is recommended by NIST.
Iterations are also called "rounds" depending on the library used.
When recommended cost factors are too high in the context of the application or if the performance cost is unacceptable, a cost factor reduction might be considered. In that case, it should not be chosen under 100,000.
In a defense-in-depth security approach, peppering can also be used. This is a security technique where an external secret value
is added to a password before it is hashed.
This makes it more difficult for an attacker to crack the hashed passwords, as they would need to know the secret value to generate the correct
hash.
Learn more here.
using System.Security.Cryptography; RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); byte[] salt = new byte[32]; rngCsp.GetBytes(salt); Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, 100, HashAlgorithmName.SHA256); // Noncompliant string hashed = Convert.ToBase64String(kdf.GetBytes(256 / 8));
Using using Microsoft.AspNet.Identity:
using Microsoft.AspNet.Identity; string password = "NotSoSecure"; PasswordHasher hasher = new PasswordHasher(); string hash = hasher.HashPassword(password); // Noncompliant
using System.Security.Cryptography; RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); byte[] salt = new byte[32]; rngCsp.GetBytes(salt); Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, 100_000, HashAlgorithmName.SHA256); // Compliant string hashed = Convert.ToBase64String(kdf.GetBytes(256 / 8));
If PBKDF2 must be used, be aware that default values might not be considered secure.
Depending on the algorithm used, the number of iterations should be adjusted to ensure that the derived key is secure. The following are the
recommended number of iterations for PBKDF2:
Note that PBKDF2-HMAC-SHA256 is recommended by NIST.
Iterations are also called "rounds" depending on the library used.
When recommended cost factors are too high in the context of the application or if the performance cost is unacceptable, a cost factor reduction might be considered. In that case, it should not be chosen under 100,000.
In a defense-in-depth security approach, peppering can also be used. This is a security technique where an external secret value
is added to a password before it is hashed.
This makes it more difficult for an attacker to crack the hashed passwords, as they would need to know the secret value to generate the correct
hash.
Learn more here.
Using SCrypt:
using Org.BouncyCastle.Crypto.Generators; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); // divide by 8 to convert bits to bytes string hashed = Convert.ToBase64String(SCrypt.Generate(Encoding.Unicode.GetBytes(password), salt, 4, 2, 1, 32)); // Noncompliant
Using BCrypt:
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); string hashed = OpenBsdBCrypt.Generate(password.ToCharArray(), salt, 4); // Noncompliant
Using PBKDF2:
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using System.Security.Cryptography; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator(); gen.Init(Encoding.Unicode.GetBytes(password), salt, 100); // Noncompliant KeyParameter keyParam = (KeyParameter) gen.GenerateDerivedMacParameters(256); string hashed = Convert.ToBase64String(keyParam.GetKey());
Using SCrypt:
using Org.BouncyCastle.Crypto.Generators; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); // divide by 8 to convert bits to bytes string hashed = Convert.ToBase64String(SCrypt.Generate(Encoding.Unicode.GetBytes(password), salt, 1<<12, 8, 1, 32)); // Noncompliant
Using BCrypt:
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); string hashed = OpenBsdBCrypt.Generate(password.ToCharArray(), salt, 14); // Noncompliant
Using PBKDF2:
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using System.Security.Cryptography; string password = Request.Query["password"]; byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator(); gen.Init(Encoding.Unicode.GetBytes(password), salt, 100_000); // Noncompliant KeyParameter keyParam = (KeyParameter) gen.GenerateDerivedMacParameters(256); string hashed = Convert.ToBase64String(keyParam.GetKey());
When bcrypt’s hashing function is used, it is important to select a round count that is high enough to make the function slow enough to prevent brute force: More than 12 rounds.
For bcrypt’s key derivation function, the number of rounds should likewise be high enough to make the function slow enough to prevent brute force:
More than 4096 rounds (2^12).
This number is not the same coefficient as the first one because it uses a different algorithm.
If scrypt must be used, the default values of scrypt are considered secure.
Like Argon2id, scrypt has three different parameters that can be configured. N is the CPU/memory cost parameter and must be a power of two. r is the block size and p is the parallelization factor.
All three parameters affect the memory and CPU usage of the algorithm. Higher values of N, r and p result in safer hashes, but come at the cost of higher resource usage.
For scrypt, OWASP recommends to have a hash length of at least 64 bytes, and to set N, p and r to the values of one of the following rows:
| N (cost parameter) | p (parallelization factor) | r (block size) |
|---|---|---|
|
217 ( |
1 |
8 |
|
216 ( |
2 |
8 |
|
215 ( |
3 |
8 |
|
214 ( |
5 |
8 |
|
213 ( |
10 |
8 |
Every row provides the same level of defense. They only differ in the amount of CPU and RAM used: the top row has low CPU usage and high memory usage, while the bottom row has high CPU usage and low memory usage.
If PBKDF2 must be used, be aware that default values might not be considered secure.
Depending on the algorithm used, the number of iterations should be adjusted to ensure that the derived key is secure. The following are the
recommended number of iterations for PBKDF2:
Note that PBKDF2-HMAC-SHA256 is recommended by NIST.
Iterations are also called "rounds" depending on the library used.
When recommended cost factors are too high in the context of the application or if the performance cost is unacceptable, a cost factor reduction might be considered. In that case, it should not be chosen under 100,000.
In a defense-in-depth security approach, peppering can also be used. This is a security technique where an external secret value
is added to a password before it is hashed.
This makes it more difficult for an attacker to crack the hashed passwords, as they would need to know the secret value to generate the correct
hash.
Learn more here.
Operating systems have global directories where any user has write access. Those folders are mostly used as temporary storage areas like
/tmp in Linux based systems. An application manipulating files from these folders is exposed to race conditions on filenames: a malicious
user can try to create a file with a predictable name before the application does. A successful attack can result in other files being accessed,
modified, corrupted or deleted. This risk is even higher if the application runs with elevated permissions.
In the past, it has led to the following vulnerabilities:
This rule raises an issue whenever it detects a hard-coded path to a publicly writable directory like /tmp (see examples bellow). It
also detects access to environment variables that point to publicly writable directories, e.g., TMP, TMPDIR and
TEMP.
/tmp/var/tmp/usr/tmp/dev/shm/dev/mqueue/run/lock/var/run/lock/Library/Caches/Users/Shared/private/tmp/private/var/tmp\Windows\Temp\Temp\TMP%USERPROFILE%\AppData\Local\TempThere is a risk if you answered yes to any of those questions.
Out of the box, .NET is missing secure-by-design APIs to create temporary files. To overcome this, one of the following options can be used:
using var writer = new StreamWriter("/tmp/f"); // Sensitive
var tmp = Environment.GetEnvironmentVariable("TMP"); // Sensitive
var randomPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); // Creates a new file with write, non inheritable permissions which is deleted on close. using var fileStream = new FileStream(randomPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose); using var writer = new StreamWriter(fileStream);
Temporary files are considered insecurely created when the file existence check is performed separately from the actual file creation. Such a situation can occur when creating temporary files using normal file handling functions or when using dedicated temporary file handling functions that are not atomic.
Creating temporary files in a non-atomic way introduces race condition issues in the application’s behavior. Indeed, a third party can create a given file between when the application chooses its name and when it creates it.
In such a situation, the application might use a temporary file that it does not entirely control. In particular, this file’s permissions might be different than expected. This can lead to trust boundary issues.
Attackers with control over a temporary file used by a vulnerable application will be able to modify it in a way that will affect the application’s logic. By changing this file’s Access Control List or other operating system-level properties, they could prevent the file from being deleted or emptied. They may also alter the file’s content before or while the application uses it.
Depending on why and how the affected temporary files are used, the exploitation of a race condition in an application can have various consequences. They can range from sensitive information disclosure to more serious application or hosting infrastructure compromise.
Because attackers can control the permissions set on temporary files and prevent their removal, they can read what the application stores in them. This might be especially critical if this information is sensitive.
For example, an application might use temporary files to store users' session-related information. In such a case, attackers controlling those files can access session-stored information. This might allow them to take over authenticated users' identities and entitlements.
An application might use temporary files to store technical data for further reuse or as a communication channel between multiple components. In that case, it might consider those files part of the trust boundaries and use their content without additional security validation or sanitation. In such a case, an attacker controlling the file content might use it as an attack vector for further compromise.
For example, an application might store serialized data in temporary files for later use. In such a case, attackers controlling those files' content can change it in a way that will lead to an insecure deserialization exploitation. It might allow them to execute arbitrary code on the application hosting server and take it over.
The following code example is vulnerable to a race condition attack because it creates a temporary file using an unsafe API function.
using System.IO;
public void Example()
{
var tempPath = Path.GetTempFileName(); // Noncompliant
using (var writer = new StreamWriter(tempPath))
{
writer.WriteLine("content");
}
}
using System.IO;
public void Example()
{
var randomPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
using (var fileStream = new FileStream(randomPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose))
using (var writer = new StreamWriter(fileStream))
{
writer.WriteLine("content");
}
}
Applications should create temporary files so that no third party can read or modify their content. It requires that the files' name, location, and permissions are carefully chosen and set. This can be achieved in multiple ways depending on the applications' technology stacks.
Temporary files can be created using unsafe functions and API as long as strong security controls are applied. Non-temporary file-handling functions and APIs can also be used for that purpose.
In general, applications should ensure that attackers can not create a file before them. This turns into the following requirements when creating the files:
Moreover, when possible, it is recommended that applications destroy temporary files after they have finished using them.
Here the example compliant code uses the Path.GetTempPath and Path.GetRandomFileName functions to generate a unique
random file name. The file is then open with the FileMode.CreateNew option that will ensure the creation fails if the file already
exists. The FileShare.None option will additionally prevent the file from being opened again by any process. To finish, this code ensures
the file will get destroyed once the application has finished using it with the FileOptions.DeleteOnClose option.
This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
For AES, the weakest mode is ECB (Electronic Codebook). Repeated blocks of data are encrypted to the same value, making them easy to identify and reducing the difficulty of recovering the original cleartext.
Unauthenticated modes such as CBC (Cipher Block Chaining) may be used but are prone to attacks that manipulate the ciphertext. They must be used with caution.
For RSA, the weakest algorithms are either using it without padding or using the PKCS1v1.5 padding scheme.
The cleartext of an encrypted message might be recoverable. Additionally, it might be possible to modify the cleartext of an encrypted message.
Below are some real-world scenarios that illustrate possible impacts of an attacker exploiting the vulnerability.
The encrypted message might contain data that is considered sensitive and should not be known to third parties.
By using a weak algorithm the likelihood that an attacker might be able to recover the cleartext drastically increases.
By modifying the cleartext of the encrypted message it might be possible for an attacker to trigger other vulnerabilities in the code. Encrypted values are often considered trusted, since under normal circumstances it would not be possible for a third party to modify them.
Example with a symmetric cipher, AES:
using System.Security.Cryptography;
public void encrypt()
{
AesManaged aes = new AesManaged
{
keysize = 128,
blocksize = 128,
mode = ciphermode.ecb, // Noncompliant
padding = paddingmode.pkcs7
};
}
Note that Microsoft has marked derived cryptographic types like AesManaged as no longer recommended for use.
Example with an asymmetric cipher, RSA:
using System.Security.Cryptography;
public void encrypt()
{
RSACryptoServiceProvider RsaCsp = new RSACryptoServiceProvider();
byte[] encryptedData = RsaCsp.Encrypt(dataToEncrypt, false); // Noncompliant
}
For the AES symmetric cipher, use the GCM mode:
using System.Security.Cryptography;
public void encrypt()
{
AesGcm aes = AesGcm(key);
}
For the RSA asymmetric cipher, use the Optimal Asymmetric Encryption Padding (OAEP):
using System.Security.Cryptography;
public void encrypt()
{
RSACryptoServiceProvider RsaCsp = new RSACryptoServiceProvider();
byte[] encryptedData = RsaCsp.Encrypt(dataToEncrypt, true);
}
As a rule of thumb, use the cryptographic algorithms and mechanisms that are considered strong by the cryptographic community.
Appropriate choices are currently the following.
The best-known authenticated encryption mode for AES is Galois/Counter mode (GCM).
GCM mode combines encryption with authentication and integrity checks using a cryptographic hash function and provides both confidentiality and authenticity of data.
Other similar modes are:
Counter with CBC-MACCipher Block Chaining with Message Authentication CodeEncrypt-and-AuthenticateInteger Authenticated Parallelizable ModeOffset Codebook ModeIt is also possible to use AES-CBC with HMAC for integrity checks. However, it is considered more straightforward to use AES-GCM directly instead.
The Optimal Asymmetric Encryption Padding scheme (OAEP) adds randomness and a secure hash function that strengthens the regular inner workings of RSA.
This vulnerability makes it possible that the cleartext of the encrypted message might be recoverable without prior knowledge of the key.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communication in various domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
The cleartext of an encrypted message might be recoverable. Additionally, it might be possible to modify the cleartext of an encrypted message.
Below are some real-world scenarios that illustrate some impacts of an attacker exploiting the vulnerability.
The encrypted message might contain data that is considered sensitive and should not be known to third parties.
By using a weak algorithm the likelihood that an attacker might be able to recover the cleartext drastically increases.
By modifying the cleartext of the encrypted message it might be possible for an attacker to trigger other vulnerabilities in the code. Encrypted values are often considered trusted, since under normal circumstances it would not be possible for a third party to modify them.
The following code contains examples of algorithms that are not considered highly resistant to cryptanalysis and thus should be avoided.
using System.Security.Cryptography;
public void encrypt()
{
var simpleDES = new DESCryptoServiceProvider(); // Noncompliant
}
using System.Security.Cryptography;
public void encrypt()
{
using (Aes aes = Aes.Create())
{
// ...
}
}
It is highly recommended to use an algorithm that is currently considered secure by the cryptographic community. A common choice for such an algorithm is the Advanced Encryption Standard (AES).
For block ciphers, it is not recommended to use algorithms with a block size that is smaller than 128 bits.
The following code contains examples of algorithms that are not considered highly resistant to cryptanalysis and thus should be avoided.
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
public void encrypt()
{
AesFastEngine aesFast = new AesFastEngine(); // Noncompliant
}
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
public void encrypt()
{
var AES = new AESEngine();
}
It is highly recommended to use an algorithm that is currently considered secure by the cryptographic community. A common choice for such an algorithm is the Advanced Encryption Standard (AES).
For block ciphers, it is not recommended to use algorithms with a block size that is smaller than 128 bits.
This vulnerability allows forging of JSON Web Tokens to impersonate other users.
JSON Web Tokens (JWTs), a popular method of securely transmitting information between parties as a JSON object, can become a significant security risk when they are not properly signed with a robust cipher algorithm, left unsigned altogether, or if the signature is not verified. This vulnerability class allows malicious actors to craft fraudulent tokens, effectively impersonating user identities. In essence, the integrity of a JWT hinges on the strength and presence of its signature.
When a JSON Web Token is not appropriately signed with a strong cipher algorithm or if the signature is not verified, it becomes a significant threat to data security and the privacy of user identities.
JWTs are commonly used to represent user authorization claims. They contain information about the user’s identity, user roles, and access rights. When these tokens are not securely signed, it allows an attacker to forge them. In essence, a weak or missing signature gives an attacker the power to craft a token that could impersonate any user. For instance, they could create a token for an administrator account, gaining access to high-level permissions and sensitive data.
When a JWT is not securely signed, it can be tampered with by an attacker, and the integrity of the data it carries cannot be trusted. An attacker can manipulate the content of the token and grant themselves permissions they should not have, leading to unauthorized data access.
The following code contains an example of JWT decoding without verification of the signature.
using JWT;
public static void decode(IJwtDecoder decoder)
{
decoder.Decode(token, secret, verify: false); // Noncompliant
}
using JWT;
public static void decode()
{
var jwt = new JwtBuilder()
.WithSecret(secret)
.Decode(token); // Noncompliant
}
using JWT;
public static void decode(IJwtDecoder decoder)
{
decoder.Decode(token, secret, verify: true);
}
When using JwtBuilder, make sure to call MustVerifySignature().
using JWT;
public static void decode()
{
var jwt = new JwtBuilder()
.WithSecret(secret)
.MustVerifySignature()
.Decode(token);
}
Resolving a vulnerability concerning the validation of JWT token signatures is mainly about incorporating a critical step into your process: validating the signature every time a token is decoded. Just having a signed token using a secure algorithm is not enough. If you are not validating signatures, they are not serving their purpose.
Every time your application receives a JWT, it needs to decode the token to extract the information contained within. It is during this decoding process that the signature of the JWT should also be checked.
To resolve the issue, follow these instructions:
By following these practices, you can ensure the security of your application’s JWT handling process, making it resistant to attacks that rely on tampering with tokens. Validation of the signature needs to be an integral and non-negotiable part of your token handling process.
Ensure that your secret keys are stored securely. They should not be hard-coded into your application code or checked into your version control system. Instead, consider using environment variables, secure key management systems, or vault services.
Even with the strongest cipher algorithms, there is a risk that your secret keys may be compromised. Therefore, it is a good practice to periodically rotate your secret keys. By doing so, you limit the amount of time that an attacker can misuse a stolen key. When you rotate keys, be sure to allow a grace period where tokens signed with the old key are still accepted to prevent service disruptions.
Rejecting requests with significant content length is a good practice to control the network traffic intensity and thus resource consumption in order to prevent DoS attacks.
There is a risk if you answered yes to any of those questions.
It is recommended to customize the rule with the limit values that correspond to the web application.
using Microsoft.AspNetCore.Mvc;
public class MyController : Controller
{
[HttpPost]
[DisableRequestSizeLimit] // Sensitive: No size limit
[RequestSizeLimit(10485760)] // Sensitive: 10485760 B = 10240 KB = 10 MB is more than the recommended limit of 8MB
public IActionResult PostRequest(Model model)
{
// ...
}
[HttpPost]
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)] // Sensitive: 10485760 B = 10240 KB = 10 MB is more than the recommended limit of 8MB
public IActionResult MultipartFormRequest(Model model)
{
// ...
}
}
In Web.config:
<configuration>
<system.web>
<httpRuntime maxRequestLength="81920" executionTimeout="3600" />
<!-- Sensitive: maxRequestLength is expressed in KB, so 81920 KB = 80 MB -->
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="83886080" />
<!-- Sensitive: maxAllowedContentLength is expressed in bytes, so 83886080 B = 81920 KB = 80 MB -->
</requestFiltering>
</security>
</system.webServer>
</configuration>
using Microsoft.AspNetCore.Mvc;
public class MyController : Controller
{
[HttpPost]
[RequestSizeLimit(8388608)] // Compliant: 8388608 B = 8192 KB = 8 MB
public IActionResult PostRequest(Model model)
{
// ...
}
[HttpPost]
[RequestFormLimits(MultipartBodyLengthLimit = 8388608)] // Compliant: 8388608 B = 8192 KB = 8 MB
public IActionResult MultipartFormRequest(Model model)
{
// ...
}
}
In Web.config:
<configuration>
<system.web>
<httpRuntime maxRequestLength="8192" executionTimeout="3600" />
<!-- Compliant: maxRequestLength is expressed in KB, so 8192 KB = 8 MB -->
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="8388608" />
<!-- Compliant: maxAllowedContentLength is expressed in bytes, so 8388608 B = 8192 KB = 8 MB -->
</requestFiltering>
</security>
</system.webServer>
</configuration>
ASP.NET 1.1+ comes with a feature called Request Validation, preventing the server to accept content containing un-encoded HTML. This feature comes as a first protection layer against Cross-Site Scripting (XSS) attacks and act as a simple Web Application Firewall (WAF) rejecting requests potentially containing malicious content.
While this feature is not a silver bullet to prevent all XSS attacks, it helps to catch basic ones. It will for example prevent <script
type="text/javascript" src="https://malicious.domain/payload.js"> to reach your Controller.
Note: Request Validation feature being only available for ASP.NET, no Security Hotspot is raised on ASP.NET Core applications.
There is a risk if you answered yes to any of those questions.
At Controller level:
[ValidateInput(false)]
public ActionResult Welcome(string name)
{
...
}
At application level, configured in the Web.config file:
<configuration>
<system.web>
<pages validateRequest="false" />
...
<httpRuntime requestValidationMode="0.0" />
</system.web>
</configuration>
At Controller level:
[ValidateInput(true)]
public ActionResult Welcome(string name)
{
...
}
or
public ActionResult Welcome(string name)
{
...
}
At application level, configured in the Web.config file:
<configuration>
<system.web>
<pages validateRequest="true" />
...
<httpRuntime requestValidationMode="4.5" />
</system.web>
</configuration>
When an object is created via deserialization, its constructor is often not executed: the object is instead built directly from its serialized
data.
This means an attacker can create a malicious object and completely bypass security checks.
Serializable class lack relevant security checks.There is a risk if you answered yes to any of those questions.
In the following examples, Serializable classes either omit validation or apply different validation logic during instantiation than
during deserialization.
For classes inheriting ISerializable:
[Serializable]
public class InternalUrl : ISerializable
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
// Special Deserialization constructor
protected InternalUrl(SerializationInfo info, StreamingContext context)
{
url = (string) info.GetValue("url", typeof(string));
// Sensitive - no validation
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("url", url);
}
}
For classes inheriting IDeserializationCallback:
[Serializable]
public class InternalUrl : IDeserializationCallback
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
void IDeserializationCallback.OnDeserialization(object sender)
{
// Sensitive - no validation
}
}
For classes inheriting from neither of previous types:
[Serializable]
public class InternalUrl
{
private string url;
public InternalUrl(string tmpUrl)
{
// Sensitive - no validation
url = tmpUrl;
}
}
For classes inheriting ISerializable:
[Serializable]
public class InternalUrl : ISerializable
{
private string url;
public InternalUrl(string tmpUrl)
{
url = tmpUrl;
validate();
}
// Special Deserialization constructor
protected InternalUrl(SerializationInfo info, StreamingContext context)
{
url = (string) info.GetValue("url", typeof(string));
validate();
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("url", url);
}
void validate()
{
if(!url.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
}
}
For classes inheriting IDeserializationCallback:
[Serializable]
public class InternalUrl : IDeserializationCallback
{
private string url;
public InternalUrl(string tmpUrl)
{
url = tmpUrl;
validate();
}
void IDeserializationCallback.OnDeserialization(object sender)
{
validate();
}
}
For classes inheriting from neither of previous types:
[Serializable]
public class InternalUrl
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
}
Deserialization is the process of converting serialized data (such as objects or data structures) back into their original form. Types allowed to be unserialized should be strictly controlled.
During the deserialization process, the state of an object will be reconstructed from the serialized data stream. By allowing unrestricted deserialization of types, the application makes it possible for attackers to use types with dangerous or otherwise sensitive behavior during the deserialization process.
When an application deserializes untrusted data without proper restrictions, an attacker can craft malicious serialized objects. Depending on the affected objects and properties, the consequences can vary.
If attackers can craft malicious serialized objects that contain executable code, this code will run within the application’s context, potentially gaining full control over the system. This can lead to unauthorized access, data breaches, or even complete system compromise.
For example, a well-known attack vector consists in serializing an object of type TempFileCollection
with arbitrary files (defined by an attacker) which will be deleted on the application deserializing this object (when the finalize() method of
the TempFileCollection object is called). These kinds of specially crafted serialized objects are called "gadgets".
Unrestricted deserialization can also enable attackers to escalate their privileges within the application. By manipulating the serialized data, an attacker can modify object properties or bypass security checks, granting them elevated privileges that they should not have. This can result in unauthorized access to sensitive data, unauthorized actions, or even administrative control over the application.
In some cases, an attacker can abuse the deserialization process to cause a denial of service (DoS) condition. By providing specially crafted serialized data, the attacker can trigger excessive resource consumption, leading to system instability or unresponsiveness. This can disrupt the availability of the application, impacting its functionality and causing inconvenience to users.
With BinaryFormatter,
NetDataContractSerializer
or SoapFormatter:
var myBinaryFormatter = new BinaryFormatter(); myBinaryFormatter.Deserialize(stream); // Noncompliant
With JavaScriptSerializer:
JavaScriptSerializer serializer1 = new JavaScriptSerializer(new SimpleTypeResolver()); // Noncompliant serializer1.Deserialize<ExpectedType>(json);
With BinaryFormatter,
NetDataContractSerializer
or SoapFormatter:
sealed class CustomBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
if (!(typeName == "type1" || typeName == "type2" || typeName == "type3"))
{
throw new SerializationException("Only type1, type2 and type3 are allowed");
}
return Assembly.Load(assemblyName).GetType(typeName);
}
}
var myBinaryFormatter = new BinaryFormatter();
myBinaryFormatter.Binder = new CustomBinder();
myBinaryFormatter.Deserialize(stream);
With JavaScriptSerializer:
public class CustomSafeTypeResolver : JavaScriptTypeResolver
{
public override Type ResolveType(string id)
{
if(id != "ExpectedType") {
throw new ArgumentNullException("Only ExpectedType is allowed during deserialization");
}
return Type.GetType(id);
}
}
JavaScriptSerializer serializer = new JavaScriptSerializer(new CustomSafeTypeResolver());
serializer.Deserialize<ExpectedType>(json);
Instead of using BinaryFormatter
and similar serializers, it is recommended to use safer alternatives in most of the cases, such as XmlSerializer or DataContractSerializer.
If it’s not possible then try to mitigate the risk by restricting the types allowed to be deserialized:
Regular expressions have their own syntax that is understood by regular expression engines. Those engines will throw an exception at runtime if they are given a regular expression that does not conform to that syntax.
To avoid syntax errors, special characters should be escaped with backslashes when they are intended to be matched literally and references to capturing groups should use the correctly spelled name or number of the group.
Negative lookaround groups cannot be combined with RegexOptions.NonBacktracking. Such combination would throw an exception during runtime.
void Regexes(string input)
{
var regex = new Regex("[A"); // Noncompliant
var match = Regex.Match(input, "[A"); // Noncompliant
var negativeLookahead = new Regex("a(?!b)", RegexOptions.NonBacktracking); // Noncompliant
var negativeLookbehind = new Regex("(?<!a)b", RegexOptions.NonBacktracking); // Noncompliant
}
void Regexes(string input)
{
var regex = new Regex("[A-Z]");
var match = Regex.Match(input, "[A-Z]");
var negativeLookahead = new Regex("a(?!b)");
var negativeLookbehind = new Regex("(?<!a)b");
}
One of the principles of a unit test is that it must have full control of the system under test. This is problematic when production code includes calls to static methods, which cannot be changed or controlled. Date/time functions are usually provided by system libraries as static methods.
This can be improved by wrapping the system calls in an object or service that can be controlled inside the unit test.
public class Foo
{
public string HelloTime() =>
$"Hello at {DateTime.UtcNow}";
}
There are different approaches to solve this problem. One of them is suggested below. There are also open source libraries (such as NodaTime) which
already implement an IClock interface and a FakeClock testing class.
public interface IClock
{
DateTime UtcNow();
}
public class Foo
{
public string HelloTime(IClock clock) =>
$"Hello at {clock.UtcNow()}";
}
public class FooTest
{
public record TestClock(DateTime now) : IClock
{
public DateTime UtcNow() => now;
}
[Fact]
public void HelloTime_Gives_CorrectTime()
{
var dateTime = new DateTime(2017, 06, 11);
Assert.Equal((new Foo()).HelloTime(new TestClock(dateTime)), $"Hello at {dateTime}");
}
}
Another possible solution is using an adaptable static class, ideally supports an IDisposable method, that not only adjusts the time behaviour for the current thread only, but also for scope of the using.
public static class Clock
{
public static DateTime UtcNow() { /* ... */ }
public static IDisposable SetTimeForCurrentThread(Func<DateTime> time) { /* ... */ }
}
public class Foo
{
public string HelloTime() =>
$"Hello at {Clock.UtcNow()}";
}
public class FooTest
{
[Fact]
public void HelloTime_Gives_CorrectTime()
{
var dateTime = new DateTime(2017, 06, 11);
using (Clock.SetTimeForCurrentThread(() => dateTime))
{
Assert.Equal((new Foo()).HelloTime(), $"Hello at {dateTime}");
}
}
}
XML signatures are a method used to ensure the integrity and authenticity of XML documents. However, if XML signatures are not validated securely, it can lead to potential vulnerabilities.
XML can be used for a wide variety of purposes. Using a signature on an XML message generally indicates this message requires authenticity and integrity. However, if the signature validation is not properly implemented this authenticity can not be guaranteed.
By not enforcing secure validation, the XML Digital Signature API is more susceptible to attacks such as signature spoofing and injections.
By disabling secure validation, the application becomes more susceptible to signature spoofing attacks. Attackers can potentially manipulate the XML signature in a way that bypasses the validation process, allowing them to forge or tamper with the signature. This can lead to the acceptance of invalid or maliciously modified signatures, compromising the integrity and authenticity of the XML documents.
Disabling secure validation can expose the application to injection attacks. Attackers can inject malicious code or entities into the XML document, taking advantage of the weakened validation process. In some cases, it can also expose the application to denial-of-service attacks. Attackers can exploit vulnerabilities in the validation process to cause excessive resource consumption or system crashes, leading to service unavailability or disruption.
The following noncompliant code example verifies an XML signature without providing a trusted public key. This code will validate the signature against the embedded public key, accepting any forged signature.
XmlDocument xmlDoc = new()
{
PreserveWhitespace = true
};
xmlDoc.Load("/data/login.xml");
SignedXml signedXml = new(xmlDoc);
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
signedXml.LoadXml((XmlElement?)nodeList[0]);
if (signedXml.CheckSignature()) {
// Process the XML content
} else {
// Raise an error
}
CspParameters cspParams = new()
{
KeyContainerName = "MY_RSA_KEY"
};
RSACryptoServiceProvider rsaKey = new(cspParams);
XmlDocument xmlDoc = new()
{
PreserveWhitespace = true
};
xmlDoc.Load("/data/login.xml");
SignedXml signedXml = new(xmlDoc);
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
signedXml.LoadXml((XmlElement?)nodeList[0]);
if (signedXml.CheckSignature(rsaKey)) {
// Process the XML content
} else {
// Raise an error
}
Here, the compliant solution provides an RSA public key to the signature validation function. This will ensure only signatures computed with the associated private key will be accepted, preventing signature forgery attacks.
Using the CheckSignature method without providing a key can be risky because it may search the AddressBook store for
certificates, which includes all trusted root CA certificates on the machine. This broad trust base can be exploited by attackers. Additionally, if
the document is not signed with an X.509 signature, the method will use the key embedded in the signature element, which can lead to accepting
signatures from untrusted sources.
Hard-coding secrets in source code or binaries makes it easy for attackers to extract sensitive information, especially in distributed or open-source applications. This practice exposes credentials and tokens, increasing the risk of unauthorized access and data breaches.
This rule detects variables/fields/properties having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a pseudorandom hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The randomness sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
Secrets should be stored in a configuration file that is not committed to the code repository, in a database, or managed by your cloud provider’s secrets management service. If a secret is exposed in the source code, it must be rotated immediately.
const string mySecret = "47828a8dd77ee1eb9dde2d5e93cb221ce8c32b37";
static readonly string mySecret = Environment.GetEnvironmentVariable("MY_APP_SECRET");
An Azure Function should be stateless as there’s no control over where and when function instances are provisioned and de-provisioned. Managing and storing data/state between requests can lead to inconsistencies. If, for any reason, you need to have a stateful function, consider using the Durable Functions extension of Azure Functions.
public static class HttpExample
{
private static readonly int port = 2000;
private static int numOfRequests = 1;
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest request, ILogger log)
{
numOfRequests += 1; // Noncompliant
log.LogInformation($"Number of POST requests is {numOfRequests}.");
string responseMessage = $"HttpRequest was made on port {port}."; // Compliant, state is only read.
return new OkObjectResult(responseMessage);
}
}
public static class HttpExample
{
private static readonly int port = 2000;
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest request, ILogger log)
{
// A compliant solution would be to manage the `numOfRequests` with an entity function or would use storage (e.g., Azure Blob storage, Azure Queue Storage)
// to share the state between functions.
string responseMessage = $"HttpRequest was made on port {port}.";
return new OkObjectResult(responseMessage);
}
}
To avoid holding more connections than necessary and to avoid potentially exhausting the number of available sockets when using
HttpClient, DocumentClient, QueueClient, ConnectionMultiplexer or Azure Storage clients,
consider:
These classes typically manage their own connections to the resource, and thus are intended to be instantiated once and reused throughout the lifetime of an application.
public class HttpExample
{
[FunctionName("HttpExample")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest request)
{
HttpClient httpClient = new HttpClient(); // Noncompliant
var response = await httpClient.GetAsync("https://example.com");
// rest of the function
}
}
public class HttpExample
{
[FunctionName("HttpExample")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest request, IHttpClientFactory clientFactory)
{
var httpClient = clientFactory.CreateClient();
var response = await httpClient.GetAsync("https://example.com");
// rest of the function
}
}
The top-most level of an Azure Function code should include a try/catch block to capture and log all errors so you can monitor the health of the application effectively. In case a retry policy has been defined for your Azure Function, you should rethrow any errors that should result in a retry.
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
// Noncompliant
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// do stuff
}
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
try
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// do stuff
}
catch (Exception ex)
{
// do stuff
}
}
Making blocking calls to async methods transforms the code into a
synchronous operation. Doing so inside an Azure Function can lead to thread pool exhaustion.
Thread pool exhaustion refers to a situation where all available threads in a thread pool are occupied, and new tasks or work items cannot be scheduled for execution due to the lack of available threads. This can lead to delayed execution and degraded performance.
class RequestParser
{
[FunctionName(nameof(ParseRequest))]
public static async Task<IActionResult> ParseRequest([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
// This can lead to thread exhaustion
string requestBody = new StreamReader(req.Body).ReadToEndAsync().Result;
// do stuff...
}
}
Instead, asynchronous mechanisms should be used:
class RequestParser
{
[FunctionName(nameof(ParseRequest))]
public static async Task<IActionResult> ParseRequest([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
// Non-blocking, asynchronous operation
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// do stuff...
}
}
This applies to multiple methods that are available when working with tasks:
| Goal | Blocking | Asynchronous |
|---|---|---|
|
Wait for the result of a task |
|
|
|
Wait for any of many task to complete |
|
|
|
Wait for all of many tasks to complete |
|
|
|
Wait a period of time |
|
|
Capturing and logging errors is critical to monitoring the health of your Azure Functions application.
Each catch block inside an Azure Function should log helpful details about the failure. Moreover, the logging should not be done at
Debug or Trace level.
Consider using the built-in integration with Application Insights for better monitoring of your Application.
[FunctionName("Foo")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
try
{
// do stuff that can fail
}
catch (Exception ex)
{
// the failure is not logged at all OR is logged at DEBUG/TRACE level
}
}
[FunctionName("Foo")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
try
{
// do stuff that can fail
}
catch (Exception ex)
{
log.LogError(ex, "Give details that will help investigations");
}
}
The recommended way to access Azure Durable Entities is through generated proxy objects with the help of interfaces.
The following restrictions, during interface design, are enforced:
If any of these rules are violated, an InvalidOperationException is thrown at runtime when the interface is used as a type argument to
IDurableEntityContext.SignalEntity<TEntityInterface>, IDurableEntityClient.SignalEntityAsync<TEntityInterface>
or IDurableOrchestrationContext.CreateEntityProxy<TEntityInterface>. The exception message explains which rule was broken.
This rule raises an issue in case any of the restrictions above is not respected.
namespace Foo // Noncompliant, must be defined in the same assembly as the entity class that implements it
{
public interface ICounter<T> // Noncompliant, interfaces cannot contain generic parameters
{
string Name { get; set; } // Noncompliant, interface must only define methods
void Add(int amount, int secondParameter); // Noncompliant, methods must not have more than one parameter
int Get(); // Noncompliant, methods must return void, Task, or Task<T>
}
}
namespace Bar
{
public class Counter : ICounter
{
// do stuff
}
public static class AddToCounterFromQueue
{
[FunctionName("AddToCounterFromQueue")]
public static Task Run(
[QueueTrigger("durable-function-trigger")] string input,
[DurableClient] IDurableEntityClient client)
{
var entityId = new EntityId("Counter", "myCounter");
int amount = int.Parse(input);
return client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Add(amount, 10));
}
}
}
namespace Bar
{
public interface ICounter
{
void Add(int amount);
Task<int> Get();
}
}
namespace Bar
{
public class Counter : ICounter
{
// do stuff
}
public static class AddToCounterFromQueue
{
[FunctionName("AddToCounterFromQueue")]
public static Task Run(
[QueueTrigger("durable-function-trigger")] string input,
[DurableClient] IDurableEntityClient client)
{
var entityId = new EntityId("Counter", "myCounter");
int amount = int.Parse(input);
return client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Add(amount));
}
}
}
Not specifying a timeout for regular expressions can lead to a Denial-of-Service attack. Pass a timeout when using
System.Text.RegularExpressions to process untrusted input because a malicious user might craft a value for which the evaluation lasts
excessively long.
There is a risk if you answered yes to any of those questions.
matchTimeout when executing a
regular expression.RegexOptions.NonBacktracking.
public void RegexPattern(string input)
{
var emailPattern = new Regex(".+@.+", RegexOptions.None);
var isNumber = Regex.IsMatch(input, "[0-9]+");
var isLetterA = Regex.IsMatch(input, "(a+)+");
}
public void RegexPattern(string input)
{
var emailPattern = new Regex(".+@.+", RegexOptions.None, TimeSpan.FromMilliseconds(100));
var isNumber = Regex.IsMatch(input, "[0-9]+", RegexOptions.None, TimeSpan.FromMilliseconds(100));
var isLetterA = Regex.IsMatch(input, "(a+)+", RegexOptions.NonBacktracking); // .Net 7 and above
AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100)); // process-wide setting
}
Locking on a local variable can undermine synchronization because two different threads running the same method in parallel will potentially lock on different instances of the same object, allowing them to access the synchronized block at the same time.
private void DoSomething()
{
object local = new object();
// Code potentially modifying the local variable ...
lock (local) // Noncompliant
{
// ...
}
}
private readonly object lockObj = new object();
private void DoSomething()
{
lock (lockObj)
{
//...
}
}
The ExcludeFromCodeCoverageAttribute is
used to exclude portions of code from code coverage
reporting. It is a bad practice to retain code that is not covered by unit tests. In .Net 5, the Justification property was added to
the ExcludeFromCodeCoverageAttribute as an opportunity to document the rationale for the exclusion. This rule raises an issue when no
such justification is given.
public struct Coordinates
{
public int X { get; }
public int Y { get; }
[ExcludeFromCodeCoverage] // Noncompliant
public override bool Equals(object obj) => obj is Coordinates coordinates && X == coordinates.X && Y == coordinates.Y;
[ExcludeFromCodeCoverage] // Noncompliant
public override int GetHashCode()
{
var hashCode = 1861411795;
hashCode = hashCode * -1521134295 + X.GetHashCode();
hashCode = hashCode * -1521134295 + Y.GetHashCode();
return hashCode;
}
}
public struct Coordinates
{
public int X { get; }
public int Y { get; }
[ExcludeFromCodeCoverage(Justification = "Code generated by Visual Studio refactoring")] // Compliant
public override bool Equals(object obj) => obj is Coordinates coordinates && X == coordinates.X && Y == coordinates.Y;
[ExcludeFromCodeCoverage(Justification = "Code generated by Visual Studio refactoring")] // Compliant
public override int GetHashCode()
{
var hashCode = 1861411795;
hashCode = hashCode * -1521134295 + X.GetHashCode();
hashCode = hashCode * -1521134295 + Y.GetHashCode();
return hashCode;
}
}
The rule targets the use of DateTime.Now call followed by some arithmetic operation.
Using DateTime.Now calls within a subtraction operation to measure elapsed time is not recommended. This property is subject to
changes such as daylight savings transitions, which can invalidate the calculation if the change occurs during the benchmark session, or when updating
a timer. Moreover, DateTime.Now is dependent on the system clock, which may have low resolution on older systems (as low as 15
milliseconds).
If the purpose is to benchmark something then, instead of the DateTime.Now property, it’s recommended to use Stopwatch,
which is not affected by changes in time such as daylight savings (DST) and automatically checks for the existence of high-precision timers. As a
bonus, the StopWatch class is also lightweight and computationally faster than DateTime.
var start = DateTime.Now; // First call, on March 26th 2:59 am
MethodToBeBenchmarked();
Console.WriteLine($"{(DateTime.Now - start).TotalMilliseconds} ms"); // Second call happens 2 minutes later but `Now` is March 26th, 4:01 am as there's a shift to summer time
var stopWatch = Stopwatch.StartNew(); // Compliant
MethodToBeBenchmarked();
stopWatch.Stop();
Console.WriteLine($"{stopWatch.ElapsedMilliseconds} ms");
If, on the other hand, the goal is to refresh a timer prefer using the DateTime.UtcNow property, which guarantees reliable results
when doing arithmetic operations during DST transitions.
if ((DateTime.Now - lastRefresh).TotalMilliseconds > MinRefreshInterval)
{
lastRefresh = DateTime.Now;
// Refresh
}
if ((DateTime.UtcNow - lastRefresh).TotalMilliseconds > MinRefreshInterval)
{
lastRefresh = DateTime.UtcNow;
// Refresh
}
Not knowing the Kind of the DateTime object that an application is using can lead to misunderstandings when displaying or
comparing them. Explicitly setting the Kind property helps the application to stay consistent, and its maintainers understand what kind
of date is being managed. To achieve this, when instantiating a new DateTime object you should always use a constructor overload that
allows you to define the Kind property.
Creating the DateTime object without specifying the property Kind will set it to the default value of
DateTimeKind.Unspecified. In this case, calling the method ToUniversalTime will assume that Kind is
DateTimeKind.Local and calling the method ToLocalTime will assume that it’s DateTimeKind.Utc. As a result, you
might have mismatched DateTime objects in your application.
To resolve this issue, use a constructor overload that lets you
specify the DateTimeKind when creating the
DateTime object. From .Net 6 onwards, use the DateOnly type if the time portion of the date is not
relevant.
void CreateNewTime()
{
var birthDate = new DateTime(1994, 7, 5, 16, 23, 42);
}
void CreateNewTime()
{
var birthDate = new DateTime(1994, 7, 5, 16, 23, 42, DateTimeKind.Utc);
// or from .Net 6 onwards, use DateOnly:
var birthDate = new DateOnly(1994, 7, 5);
}
You should avoid recording time instants with the use of property DateTime.Now. The property DateTime.Now returns the
current date and time expressed in the machine’s local time without containing any timezone-related information (for example, the offset from
Coordinated Universal Time). Not having this information means that if you need to display this DateTime object or use it for
computations in another machine placed in a different time zone, you won’t be able to reconstruct it in the second machine’s local time without
knowing the origin’s offset. This will likely lead to confusion and potential bugs.
Instead, you should record the DateTime instants in UTC, which gives you the date and time as it is in the Coordinated Universal Time.
UTC is a time standard for all time zones and is not subjected to Daylight Saving Time (DST).
Similarly, the use of the DateTime.Today property should also be avoided, as it can return different date values depending on the time
zone.
Generally, unless the purpose is to only display the Date and Time to a user on their local machine, you should always use UTC (for example, when storing dates in a datebase or using them for calculations).
You can end up with DateTime instants that have no meaning for anyone except the machine they were recorded on. Using UTC gives an
unambiguous representation of an instant, and this UTC instant can be transformed into any equivalent local time. This operation isn’t reversible as
some local times are ambiguous and can be matched to more than one UTC instant (for example, due to daylight savings).
Instead of DateTime.Now use any of the following:
DateTime.UtcNow,DateTimeOffSet.Now (as it contains offset information)DateTimeOffSet.UtcNowInstead of DateTime.Today use any of the following:
DateTime.UtcNow.Date,DateOnly.FromDateTime(DateTime.UtcNow) (.NET 6.0+)
void LogDateTime()
{
using var streamWriter = new StreamWriter("logs.txt", true);
streamWriter.WriteLine($"DateTime:{DateTime.Now.ToString("o")}"); // This log won't have any meaning if it's reconstructed in a machine in a different timezone.
}
void LogDateTime()
{
using var streamWriter = new StreamWriter("logs.txt", true);
streamWriter.WriteLine($"DateTime:{DateTime.UtcNow.ToString("o")}");
}
This rule recommends using DateTimeOffset instead of DateTime for projects targeting .NET Framework 2.0 or later.
You should use DateTimeOffset instead of DateTime as it provides all the information that the DateTime
struct has, and additionally, the offset from Coordinated Universal Time (UTC). This way you can avoid potential problems created by the lack of
timezone awareness (see the "Pitfalls" section below for more information).
However, it’s important to note that although DateTimeOffset contains more information than DateTime by storing the
offset to UTC, it isn’t tied to a specific time zone. This information must be stored separately to have a full picture of the moment in time with the
use of TimeZoneInfo.
In most cases, you can directly replace DateTime with DateTimeOffset. When hardcoding dates with local kind, remember
that the offset is timezone dependent, so it should be set according to which timezone that data represents. For more information, refer to
DateTime and DateTimeOffset documentation from Microsoft (see the "Resources" section below).
DateTime myDate = new DateTime(2008, 6, 19, 7, 0, 0, DateTimeKind.Local); // Noncompliant var now = DateTime.Now; // Noncompliant
DateTimeOffset myDate = new DateTimeOffset(2008, 6, 19, 7, 0, 0, TimeSpan.FromHours(-7)); // Compliant var now = DateTimeOffset.Now; // Compliant
Common DateTime pitfalls include:
DateTime of kind Local consider the time offset of the machine where the program is running. Not
storing the offset from UTC separately can result in meaningless data when retrieved from a different location.DateTime of kind Unknown, calling ToUniversalTime() presumes the
DateTime.Kind is local and converts to UTC, if you call the method ToLocalTime(), it assumes the
DateTime.Kind is UTC and converts it to local.DateTimes objects, the user must ensure they are within the same time zone. DateTime doesn’t consider
UTC/Local when comparing; it only cares about the number of Ticks on the objects.Since .NET 6 you don’t have to use the TimeZoneConverter library to manually do the conversion between IANA and Windows timezones. The
.NET 6.0 introduced new Time Zone enhancements, one being the TimeZoneInfo.FindSystemTimeZoneById(string timezone) method now accepts as
input both IANA and Windows time zone IDs on any operating system with installed time zone data. TimeZoneInfo.FindSystemTimeZoneById will
automatically convert its input from IANA to Windows and vice versa if the requested time zone is not found on the system.
The method TimeZoneInfo.FindSystemTimeZoneById(string timezone) can get both IANA and Windows timezones as input and automatically
convert one to the other if the requested time zone is not found on the system. Because one does not need to handle the conversion, the code will be
less complex and easier to maintain.
There’s no need to translate manually between time zones; it is enough to call TimeZoneInfo.FindSystemTimeZoneById(string timezone),
where the timezone can be IANA or Windows format. Depending on the OS, the equivalent time zone will be returned (Windows Time Zones for Windows and
IANA timezones for Linux, macOS).
// Assuming we are in Windows OS and we need to get the Tokyo Time Zone. var ianaTimeZone = "Asia/Tokyo"; var windowsTimeZone = TZConvert.IanaToWindows(ianaTimeZone); TimeZoneInfo tokyoWindowsTimeZone = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZone);
// Assuming we are in Windows OS and we need to get the Tokyo Time Zone. var ianaTimeZone = "Asia/Tokyo"; TimeZoneInfo tokyoWindowsTimeZone = TimeZoneInfo.FindSystemTimeZoneById(ianaTimeZone);
When converting a string representation of a date and time to a DateTime object or any other temporal type with one of the available
system parsing methods, you should always provide an IFormatProvider parameter.
If you try to parse a string representation of a date or time without a format provider, the method will use the machine’s
CultureInfo; if the given string does not follow it, you’ll have an object that does not match the string representation or an unexpected
runtime error.
This rule raises an issue for the following date and time string representation parsing methods:
ParseParseExactTryParseTryParseExactOf the following types:
System.DateOnlySystem.DateTimeSystem.DateTimeOffsetSystem.TimeOnlySystem.TimeSpanAlway use an overload of the parse method, where you can provide an IFormatProvider parameter.
var dateTimeString = "4/12/2023 4:05:48 PM"; // This is an en-US format string - 12 of April 2023
var dateTimeObject = DateTime.Parse(dateTimeString); // This is wrongly parsed as 4th of December, when it's read in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe)
var dateTimeString2 = "4/13/2023 4:05:48 PM"; // This is an en-US format string - 13 of April 2023
var dateTimeObject2 = DateTime.Parse(dateTimeString2); // Runtime Error, when it's parsed in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe).
var timeInSaudiArabia = new TimeOnly(16, 23).ToString(new CultureInfo("ar-SA"));
var timeObject = TimeOnly.Parse(timeInSaudiArabia); // Runtime Error, when it's parsed in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe).
var dateTimeString = "4/12/2023 4:05:48 PM"; // This is an en-US format string - 12 of April 2023
var dateTimeObject = DateTime.Parse(dateTimeString, new CultureInfo("en-US"));
var dateTimeString2 = "4/13/2023 4:05:48 PM"; // This is an en-US format string - 13 of April 2023
var dateTimeObject2 = DateTime.Parse(dateTimeString2, new CultureInfo("en-US"))
var timeInSaudiArabia = new TimeOnly(16, 23).ToString(new CultureInfo("ar-SA"));
var timeObject = TimeOnly.Parse(timeInSaudiArabia, new CultureInfo("ar-SA"));
Hardcoding the date and time format strings can lead to formats that consumers misunderstand. Also, if the same format is meant to be used in
multiple places, it is easier to make a mistake when it’s hardcoded instead of using a format provided by an IFormatProvider or using one
of the standard format strings.
If a non-conventional format is used, the formatted date and time can be misunderstood. Also, if a mistake is made in the format, the formatted date can be incomplete. For example, you might switch the place of the minutes and month parts of a date or simply forget to print the year.
Instead of hardcoding the format, provide one from the available formats through an IFormatProvider or use one of the standard format
strings.
void PrintTime()
{
Console.WriteLine(DateTime.UtcNow.ToString("dd/MM/yyyy HH:mm:ss"));
Console.WriteLine(DateTime.UtcNow.ToString("dd/mm/yyyy HH:MM:ss")); // Months and minutes have changed their places
}
void PrintTime()
{
Console.WriteLine(DateTime.UtcNow.ToString(CultureInfo.GetCultureInfo("es-MX")));
Console.WriteLine(DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)); // Better provide a well known culture, so this kind of issues do not pop up
}
With .NET Core the UnixEpoch field was introduced to DateTime and DateTimeOffset types. Using this field
clearly states that the intention is to use the beginning of the Unix epoch.
You should not use the DateTime or DateTimeOffset constructors to set the time to the 1st of January 1970 to represent
the beginning of the Unix epoch. Not everyone is familiar with what this particular date is representing and it can be misleading.
To fix this issue, use the UnixEpoch field of DateTime or DateTimeOffset instead of the constructor.
void GetEpochTime()
{
var epochTime = new DateTime(1970, 1, 1);
}
void GetEpochTime()
{
var epochTime = DateTime.UnixEpoch;
}
Both the List.Find method and the Enumerable.FirstOrDefault method can be used to locate the first element that meets a
specified condition within a collection. However, for List objects, List.Find may offer superior performance compared to
Enumerable.FirstOrDefault. While the performance difference might be negligible for small collections, it can become significant for
larger collections. This observation also holds true for ImmutableList and arrays.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
FirstOrDefault closer to the performance of collection-specific Find methods in most scenarios.
Applies to
We measured at least 2x improvement in the execution time. For more details see the Benchmarks section from the More info
tab.
The Find method is defined on the collection class, and it has the same signature as FirstOrDefault extension method. The
function can be replaced in place.
int GetValue(List<int> data) =>
data.FirstOrDefault(x => x % 2 == 0);
int GetValue(int[] data) =>
data.FirstOrDefault(x => x % 2 == 0);
int GetValue(List<int> data) =>
data.Find(x => x % 2 == 0);
int GetValue(int[] data) =>
Array.Find(data, x => x % 2 == 0);
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayFirstOrDefault |
.NET 8.0 |
Array |
10.515 μs |
0.1410 μs |
32 B |
|
ArrayFind |
.NET 8.0 |
Array |
4.417 μs |
0.0729 μs |
- |
|
ArrayFirstOrDefault |
.NET 9.0 |
Array |
2.262 μs |
0.0135 μs |
- |
|
ArrayFind |
.NET 9.0 |
Array |
3.428 μs |
0.0206 μs |
- |
|
ArrayFirstOrDefault |
.NET Framework 4.8.1 |
Array |
45.074 μs |
0.7517 μs |
32 B |
|
ArrayFind |
.NET Framework 4.8.1 |
Array |
13.948 μs |
0.1496 μs |
- |
|
ImmutableListFirstOrDefault |
.NET 8.0 |
ImmutableList<T> |
83.796 μs |
1.3199 μs |
72 B |
|
ImmutableListFind |
.NET 8.0 |
ImmutableList<T> |
59.720 μs |
1.0723 μs |
- |
|
ImmutableListFirstOrDefault |
.NET 9.0 |
ImmutableList<T> |
81.984 μs |
1.0886 μs |
72 B |
|
ImmutableListFind |
.NET 9.0 |
ImmutableList<T> |
58.288 μs |
0.8079 μs |
- |
|
ImmutableListFirstOrDefault |
.NET Framework 4.8.1 |
ImmutableList<T> |
446.893 μs |
9.8430 μs |
76 B |
|
ImmutableListFind |
.NET Framework 4.8.1 |
ImmutableList<T> |
427.476 μs |
3.3371 μs |
- |
|
ListFirstOrDefault |
.NET 8.0 |
List<T> |
14.808 μs |
0.1723 μs |
40 B |
|
ListFind |
.NET 8.0 |
List<T> |
6.040 μs |
0.1104 μs |
- |
|
ListFirstOrDefault |
.NET 9.0 |
List<T> |
2.233 μs |
0.0154 μs |
- |
|
ListFind |
.NET 9.0 |
List<T> |
4.458 μs |
0.0745 μs |
- |
|
ListFirstOrDefault |
.NET Framework 4.8.1 |
List<T> |
57.290 μs |
1.0494 μs |
40 B |
|
ListFind |
.NET Framework 4.8.1 |
List<T> |
18.476 μs |
0.0504 μs |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == 1;
private readonly static Predicate<int> ConditionPredicate = static x => x == 1;
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
public const int N = 10_000;
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public int ListFirstOrDefault() =>
list.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark]
public int ListFind() =>
list.Find(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public int ImmutableListFirstOrDefault() =>
immutableList.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public int ImmutableListFind() =>
immutableList.Find(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public int ArrayFirstOrDefault() =>
array.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public int ArrayFind() =>
Array.Find(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6602.json ================================================ { "title": "\"Find\" method should be used instead of the \"FirstOrDefault\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6602", "sqKey": "S6602", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6603.html ================================================
Both the List.TrueForAll method and the IEnumerable.All method can be used to check if all list elements satisfy a given
condition in a collection. However, List.TrueForAll can be faster than IEnumerable.All for List objects. The
performance difference may be minor for small collections, but for large collections, it can be noticeable.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
All closer to the performance of collection-specific TrueForAll methods in most scenarios.
Applies to
We measured at least 4x improvement both in execution time. For more details see the Benchmarks section from the More
info tab.
The TrueForAll method is defined on the collection class, and it has the same signature as the All extension method. The
method can be replaced in place.
public bool AreAllEven(List<int> data) =>
data.All(x => x % 2 == 0);
public bool AreAllEven(int[] data) =>
data.All(x => x % 2 == 0);
public bool AreAllEven(List<int> data) =>
data.TrueForAll(x => x % 2 == 0);
public bool AreAllEven(int[] data) =>
Array.TrueForAll(data, x => x % 2 == 0);
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayAll |
.NET 8.0 |
Array |
109.25 μs |
1.767 μs |
32 B |
|
ArrayTrueForAll |
.NET 8.0 |
Array |
45.01 μs |
0.547 μs |
- |
|
ArrayAll |
.NET 9.0 |
Array |
22.28 μs |
0.254 μs |
- |
|
ArrayTrueForAll |
.NET 9.0 |
Array |
37.60 μs |
0.382 μs |
- |
|
ArrayAll |
.NET Framework 4.8.1 |
Array |
495.90 μs |
4.342 μs |
40 B |
|
ArrayTrueForAll |
.NET Framework 4.8.1 |
Array |
164.52 μs |
2.030 μs |
- |
|
ImmutableListAll |
.NET 8.0 |
ImmutableList<T> |
940.29 μs |
5.600 μs |
72 B |
|
ImmutableListTrueForAll |
.NET 8.0 |
ImmutableList<T> |
679.46 μs |
2.371 μs |
- |
|
ImmutableListAll |
.NET 9.0 |
ImmutableList<T> |
922.43 μs |
14.564 μs |
72 B |
|
ImmutableListTrueForAll |
.NET 9.0 |
ImmutableList<T> |
692.31 μs |
8.897 μs |
- |
|
ImmutableListAll |
.NET Framework 4.8.1 |
ImmutableList<T> |
4,578.72 μs |
77.920 μs |
128 B |
|
ImmutableListTrueForAll |
.NET Framework 4.8.1 |
ImmutableList<T> |
4,393.49 μs |
122.061 μs |
- |
|
ImmutableListBuilderAll |
.NET 8.0 |
ImmutableList<T>.Builder |
970.45 μs |
13.598 μs |
73 B |
|
ImmutableListBuilderTrueForAll |
.NET 8.0 |
ImmutableList<T>.Builder |
687.82 μs |
6.142 μs |
- |
|
ImmutableListBuilderAll |
.NET 9.0 |
ImmutableList<T>.Builder |
981.17 μs |
12.966 μs |
72 B |
|
ImmutableListBuilderTrueForAll |
.NET 9.0 |
ImmutableList<T>.Builder |
710.19 μs |
16.195 μs |
- |
|
ImmutableListBuilderAll |
.NET Framework 4.8.1 |
ImmutableList<T>.Builder |
4,780.50 μs |
43.282 μs |
128 B |
|
ImmutableListBuilderTrueForAll |
.NET Framework 4.8.1 |
ImmutableList<T>.Builder |
4,493.82 μs |
76.530 μs |
- |
|
ListAll |
.NET 8.0 |
List<T> |
151.12 μs |
2.028 μs |
40 B |
|
ListTrueForAll |
.NET 8.0 |
List<T> |
58.03 μs |
0.493 μs |
- |
|
ListAll |
.NET 9.0 |
List<T> |
22.14 μs |
0.327 μs |
- |
|
ListTrueForAll |
.NET 9.0 |
List<T> |
46.01 μs |
0.327 μs |
- |
|
ListAll |
.NET Framework 4.8.1 |
List<T> |
619.86 μs |
6.037 μs |
48 B |
|
ListTrueForAll |
.NET Framework 4.8.1 |
List<T> |
208.49 μs |
2.340 μs |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == Math.Abs(x);
private List<int> list;
private ImmutableList<int> immutableList;
private ImmutableList<int>.Builder immutableListBuilder;
private int[] array;
[Params(100_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
immutableListBuilder = ImmutableList.CreateBuilder<int>();
immutableListBuilder.AddRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAll() =>
list.All(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListTrueForAll() =>
list.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAll() =>
immutableList.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListTrueForAll() =>
immutableList.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark(Baseline = true)]
public bool ImmutableListBuilderAll() =>
immutableListBuilder.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark]
public bool ImmutableListBuilderTrueForAll() =>
immutableListBuilder.TrueForAll(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAll() =>
array.All(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayTrueForAll() =>
Array.TrueForAll(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6603.json ================================================ { "title": "The collection-specific \"TrueForAll\" method should be used instead of the \"All\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6603", "sqKey": "S6603", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6605.html ================================================
Both the List.Exists method and IEnumerable.Any method can be used to find the first element that satisfies a predicate
in a collection. However, List.Exists can be faster than IEnumerable.Any for List objects, as well as requires
significantly less memory. For small collections, the performance difference may be negligible, but for large collections, it can be noticeable. The
same applies to ImmutableList and arrays too.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
Any closer to the performance of collection-specific Exists methods in most scenarios.
Applies to
We measured at least 3x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
Also, no memory allocations were needed for the Exists method, since the search is done in-place.
Since LINQ to
Entities relies a lot on System.Linq for query conversion,
this rule won’t raise when used within LINQ to Entities syntaxes.
The Exists method is defined on the collection class, and it has the same signature as Any extension method if a
predicate is used. The method can be replaced in place.
bool ContainsEven(List<int> data) =>
data.Any(x => x % 2 == 0);
bool ContainsEven(int[] data) =>
data.Any(x => x % 2 == 0);
bool ContainsEven(List<int> data) =>
data.Exists(x => x % 2 == 0);
bool ContainsEven(int[] data) =>
Array.Exists(data, x => x % 2 == 0);
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayAny |
.NET 8.0 |
Array |
1,174.0 ns |
16.44 ns |
32 B |
|
ArrayExists |
.NET 8.0 |
Array |
570.6 ns |
7.12 ns |
- |
|
ArrayAny |
.NET 9.0 |
Array |
358.5 ns |
5.57 ns |
- |
|
ArrayExists |
.NET 9.0 |
Array |
581.6 ns |
6.17 ns |
- |
|
ArrayAny |
.NET Framework 4.8.1 |
Array |
4,896.0 ns |
102.83 ns |
32 B |
|
ArrayExists |
.NET Framework 4.8.1 |
Array |
1,649.4 ns |
29.81 ns |
- |
|
ImmutableListAny |
.NET 8.0 |
ImmutableList<T> |
7,859.3 ns |
91.45 ns |
72 B |
|
ImmutableListExists |
.NET 8.0 |
ImmutableList<T> |
5,898.1 ns |
81.69 ns |
- |
|
ImmutableListAny |
.NET 9.0 |
ImmutableList<T> |
7,748.9 ns |
119.10 ns |
72 B |
|
ImmutableListExists |
.NET 9.0 |
ImmutableList<T> |
5,705.0 ns |
31.53 ns |
- |
|
ImmutableListAny |
.NET Framework 4.8.1 |
ImmutableList<T> |
45,118.5 ns |
168.72 ns |
72 B |
|
ImmutableListExists |
.NET Framework 4.8.1 |
ImmutableList<T> |
41,966.0 ns |
631.59 ns |
- |
|
ListAny |
.NET 8.0 |
List<T> |
1,643.5 ns |
13.09 ns |
40 B |
|
ListExists |
.NET 8.0 |
List<T> |
726.2 ns |
11.99 ns |
- |
|
ListAny |
.NET 9.0 |
List<T> |
398.6 ns |
8.20 ns |
- |
|
ListExists |
.NET 9.0 |
List<T> |
612.4 ns |
18.73 ns |
- |
|
ListAny |
.NET Framework 4.8.1 |
List<T> |
5,621.5 ns |
35.80 ns |
40 B |
|
ListExists |
.NET Framework 4.8.1 |
List<T> |
1,748.0 ns |
11.76 ns |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == -1 * Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == -1 * Math.Abs(x);
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
[Params(1_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAny() =>
list.Any(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListExists() =>
list.Exists(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAny() =>
immutableList.Any(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListExists() =>
immutableList.Exists(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAny() =>
array.Any(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayExists() =>
Array.Exists(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6605.json ================================================ { "title": "Collection-specific \"Exists\" method should be used instead of the \"Any\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6605", "sqKey": "S6605", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6607.html ================================================
When working with LINQ in C#, it is recommended to pay attention to the order in which methods are chained, especially when using
Where and OrderBy methods. It is advised to call the Where method before OrderBy because
Where filters the elements of the sequence based on a given condition and returns a new sequence containing only the elements that
satisfy that condition. Calling OrderBy before Where, may end up sorting elements that will be later discarded, which can
lead to inefficiency. Conversely, calling Where before OrderBy, will first filter the sequence to include only the elements
of interest, and then sort them based on the specified order.
We measured at least 2x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
The issue can be fixed by calling Where before OrderBy.
public IEnumerable<int> GetSortedFilteredList(IEnumerable<int> data) =>
data.OrderBy(x => x).Where(x => x % 2 == 0);
public IEnumerable<int> GetSortedFilteredList(IEnumerable<int> data) =>
data.Where(x => x % 2 == 0).OrderBy(x => x);
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
OrderByThenWhere |
.NET 7.0 |
175.36 ms |
5.101 ms |
|
WhereThenOrderBy |
.NET 7.0 |
85.58 ms |
1.697 ms |
The results were generated by running the following snippet with BenchmarkDotNet:
private IList<int> data;
private static readonly Random Random = new Random();
[Params(1_000_000)]
public int NumberOfEntries;
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, NumberOfEntries).Select(x => Random.Next(0, NumberOfEntries)).ToList();
[Benchmark(Baseline = true)]
public void OrderByThenWhere() =>
_ = data.OrderBy(x => x).Where(x => x % 2 == 0 ).ToList(); // OrderBy followed by Where
[Benchmark]
public void WhereThenOrderBy() =>
_ = data.Where(x => x % 2 == 0 ).OrderBy(x => x).ToList(); // Where followed by OrderBy
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S6607.json ================================================ { "title": "The collection should be filtered before sorting by using \"Where\" before \"OrderBy\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6607", "sqKey": "S6607", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6608.html ================================================
Indexes in C# provide direct access to an element at a specific position within an array or collection. When compared to Enumerable
methods, indexing can be more efficient for certain scenarios, such as iterating over a large collection, due to avoiding the overhead of checking the
underlying collection type before accessing it.
This applies to types that implement one of these interfaces:
We measured a significant improvement in execution time. For more details see the Benchmarks section from the More info
tab.
If the type you are using implements IList, IList<T> or IReadonlyList<T>, it implements
this[int index]. This means calls to First, Last, or ElementAt(index) can be replaced with
indexing at 0, Count-1 and index respectively.
int GetAt(List<int> data, int index)
=> data.ElementAt(index);
int GetFirst(List<int> data)
=> data.First();
int GetLast(List<int> data)
=> data.Last();
int GetAt(List<int> data, int index)
=> data[index];
int GetFirst(List<int> data)
=> data[0];
int GetLast(List<int> data)
=> data[data.Count-1];
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
ElementAt |
3,403.4 ns |
28.52 ns |
26.67 ns |
|
Index |
478.0 ns |
6.93 ns |
6.48 ns |
|
First |
6,160.0 ns |
57.66 ns |
53.93 ns |
|
First_Index |
485.7 ns |
5.81 ns |
5.15 ns |
|
Last |
6,034.3 ns |
20.34 ns |
16.98 ns |
|
Last_Index |
408.3 ns |
2.54 ns |
2.38 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<byte> data;
private Random random;
[Params(1_000_000)]
public int SampleSize;
[Params(1_000)]
public int LoopSize;
[GlobalSetup]
public void Setup()
{
random = new Random(42);
var bytes = new byte[SampleSize];
random.NextBytes(bytes);
data = bytes.ToList();
}
[Benchmark]
public int ElementAt()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.ElementAt(i);
}
return result;
}
[Benchmark]
public int Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[i];
}
return result;
}
[Benchmark]
public int First()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.First();
}
return result;
}
[Benchmark]
public int First_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[0];
}
return result;
}
[Benchmark]
public int Last()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.Last();
}
return result;
}
[Benchmark]
public int Last_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[data.Count - 1];
}
return result;
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.4412/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=8.0.301 [Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2 .NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S6608.json ================================================ { "title": "Prefer indexing instead of \"Enumerable\" methods on types implementing \"IList\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6608", "sqKey": "S6608", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6609.html ================================================
Both the Enumerable.Max extension method and the SortedSet<T>.Max property can be used to find the maximum value in
a SortedSet<T>. However, SortedSet<T>.Max is much faster than Enumerable.Max. For small
collections, the performance difference may be minor, but for large collections, it can be noticeable. The same applies for the Min
property as well.
Max and Min in SortedSet<T> exploit the fact that the set is implemented via a Red-Black
tree. The algorithm to find the Max/Min is "go left/right whenever possible". The operation has the time complexity
of O(h) which becomes O(ln(n)) due to the fact that the tree is balanced. This is much better than the O(n)
time complexity of extension methods.
Max and Min in ImmutableSortedSet<T> exploits a tree augmentation technique, storing the
Min, Max and Count values on each node of the data structure. The time complexity in this case is
O(1) that is significantly better than O(n) of extension methods.
Applies to:
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
The Min and Max properties are defined on the following classes, and the extension method call can be replaced by calling
the propery instead:
SortedSet<T>ImmutableSortedSet<T>ImmutableSortedSet<T>.Builder
int GetMax(SortedSet<int> data) =>
data.Max();
int GetMin(SortedSet<int> data) =>
data.Min();
int GetMax(SortedSet<int> data) =>
data.Max;
int GetMin(SortedSet<int> data) =>
data.Min;
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
MaxMethod |
.NET 7.0 |
68,961.483 us |
499.6623 us |
248063 B |
|
MaxProperty |
.NET 7.0 |
4.638 us |
0.0634 us |
- |
|
MaxMethod |
.NET Framework 4.6.2 |
85,827.359 us |
1,531.1611 us |
281259 B |
|
MaxProperty |
.NET Framework 4.6.2 |
67.682 us |
0.3757 us |
312919 B |
The results were generated by running the following snippet with BenchmarkDotNet:
private SortedSet<string> data;
[Params(1_000)]
public int Iterations;
[GlobalSetup]
public void Setup() =>
data = new SortedSet<string>(Enumerable.Range(0, Iterations).Select(x => Guid.NewGuid().ToString()));
[Benchmark(Baseline = true)]
public void MaxMethod()
{
for (var i = 0; i < Iterations; i++)
{
_ = data.Max(); // Max() extension method
}
}
[Benchmark]
public void MaxProperty()
{
for (var i = 0; i < Iterations; i++)
{
_ = data.Max; // Max property
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6609.json ================================================ { "title": "\"Min\/Max\" properties of \"Set\" types should be used instead of the \"Enumerable\" extension methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6609", "sqKey": "S6609", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6610.html ================================================
With string.StartsWith(char) and string.EndsWith(char), only the first character of the string is compared to the
provided character, whereas the string versions of those methods have to do checks about the current StringComparison and
CultureInfo. Thus, the char overloads are significantly faster for default comparison scenarios.
These overloads were introduced in .NET Core 2.0.
We measured at least 3.5x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
If you are targeting a runtime version equal or greater than .NET Core 2.0, the string.StartsWith and
string.EndsWith overloads are available, with the argument’s type being char instead of string. Thus, an
argument of char type can be provided.
bool StartsWithSlash(string s) =>
s.StartsWith("/");
bool EndsWithSlash(string s) =>
s.EndsWith("/");
bool StartsWithSlash(string s) =>
s.StartsWith('/');
bool EndsWithSlash(string s) =>
s.EndsWith('/');
| Method | Mean | Standard Deviation |
|---|---|---|
|
StartsWith_String |
30.965 ms |
3.2732 ms |
|
StartsWith_Char |
7.568 ms |
0.3235 ms |
|
EndsWith_String |
30.421 ms |
5.1136 ms |
|
EndsWith_Char |
8.067 ms |
0.7092 ms |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<string> data;
[Params(1_000_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, N).Select(_ => Guid.NewGuid().ToString()).ToList();
[Benchmark]
public void StartsWith_String()
{
_ = data.Where(guid => guid.StartsWith("d")).ToList();
}
[Benchmark]
public void StartsWith_Char()
{
_ = data.Where(guid => guid.StartsWith('d')).ToList();
}
[Benchmark]
public void EndsWith_String()
{
_ = data.Where(guid => guid.EndsWith("d")).ToList();
}
[Benchmark]
public void EndsWith_Char()
{
_ = data.Where(guid => guid.EndsWith('d')).ToList();
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S6610.json ================================================ { "title": "\"StartsWith\" and \"EndsWith\" overloads that take a \"char\" should be used instead of the ones that take a \"string\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6610", "sqKey": "S6610", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S6612.html ================================================
When using the ConcurrentDictionary, there are many overloads of the GetOrAdd and AddOrUpdate methods that
take both a TKey argument and a lambda that expects a TKey parameter. This means that the right side of the lambda can be
written using either the lambda’s parameter or the method’s argument. However, using the method’s argument leads to the lambda capturing it, and the
compiler will need to generate a class and instantiate it before the call. This means memory allocations, as well as more time spend during Garbage
Collection.
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
When you are using the ConcurrentDictionary methods GetOrAdd or AddOrUpdate, reference the key by using the
lambda’s parameter instead of the method’s one.
int UpdateValue(ConcurrentDictionary<int, int> dict, int key) =>
dict.GetOrAdd(key, _ => key + 42);
int UpdateValue(ConcurrentDictionary<int, int> dict, int key) =>
dict.GetOrAdd(key, x => x + 42);
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
Capture |
.NET 7.0 |
68.52 ms |
4.450 ms |
88000063 B |
|
Lambda |
.NET 7.0 |
39.29 ms |
3.712 ms |
50 B |
|
Capture |
.NET Framework 4.6.2 |
74.58 ms |
5.199 ms |
88259787 B |
|
Lambda |
.NET Framework 4.6.2 |
42.03 ms |
2.752 ms |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
private ConcurrentDictionary<int, string> dict;
private List<int> data;
[Params(1_000_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup()
{
dict = new ConcurrentDictionary<int, string>();
data = Enumerable.Range(0, N).OrderBy(_ => Guid.NewGuid()).ToList();
}
[Benchmark(baseline=true)]
public void Capture()
{
foreach (var guid in data)
{
dict.GetOrAdd(guid, _ => $"{guid}"); // "guid" is captured
}
}
[Benchmark]
public void Lambda()
{
foreach (var guid in data)
{
dict.GetOrAdd(guid, x => $"{x}"); // no capture
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6612.json ================================================ { "title": "The lambda parameter should be used instead of capturing arguments in \"ConcurrentDictionary\" methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6612", "sqKey": "S6612", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6613.html ================================================
Both the Enumerable.First extension method and the LinkedList<T>.First property can be used to find the first value
in a LinkedList<T>. However, LinkedList<T>.First is much faster than Enumerable.First. For small
collections, the performance difference may be minor, but for large collections, it can be noticeable. The same applies for the Last
property as well.
Applies to:
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
The First and Last properties are defined on the LinkedList class, and the extension method call can be
replaced by calling the propery instead.
int GetFirst(LinkedList<int> data) =>
data.First();
int GetLast(LinkedList<int> data) =>
data.Last();
int GetFirst(LinkedList<int> data) =>
data.First.Value;
int GetLast(LinkedList<int> data) =>
data.Last.Value;
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
LastMethod |
.NET 7.0 |
919,577,629.0 ns |
44,299,688.61 ns |
48504 B |
|
LastProperty |
.NET 7.0 |
271.8 ns |
15.63 ns |
- |
|
LastMethod |
.NET Framework 4.6.2 |
810,316,427.1 ns |
47,768,482.31 ns |
57344 B |
|
LastProperty |
.NET Framework 4.6.2 |
372.0 ns |
13.38 ns |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
private LinkedList<int> data;
private Random random = new Random();
[Params(100_000)]
public int Size { get; set; }
[Params(1_000)]
public int Runs { get; set; }
[GlobalSetup]
public void Setup() =>
data = new LinkedList<int>(Enumerable.Range(0, Size).Select(x => random.Next()));
[Benchmark(Baseline = true)]
public void LastMethod()
{
for (var i = 0; i < Runs; i++)
{
_ = data.Last(); // Enumerable.Last()
}
}
[Benchmark]
public void LastProperty()
{
for (var i = 0; i < Runs; i++)
{
_ = data.Last; // Last property
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6613.json ================================================ { "title": "\"First\" and \"Last\" properties of \"LinkedList\" should be used instead of the \"First()\" and \"Last()\" extension methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6613", "sqKey": "S6613", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S6617.html ================================================
When testing if a collection contains a specific item by simple equality, both ICollection.Contains(T item) and
IEnumerable.Any(x ⇒ x == item) can be used. However, Any searches the data structure in a linear manner using a foreach
loop, whereas Contains is considerably faster in some collection types, because of the underlying implementation. More specifically:
HashSet<T> is a hashtable, and therefore has an O(1) lookupSortedSet<T> is a red-black tree, and therefore has a O(logN) lookupList<T> is a linear search, and therefore has an O(N) lookup, but the EqualityComparer is optimized for the T
type, which is not the case for AnyFor small collections, the performance difference may be negligible, but for large collections, it can be noticeable.
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
Since LINQ to
Entities relies a lot on System.Linq for query conversion,
this rule won’t raise when used within LINQ to Entities syntaxes.
Contains is a method defined on the ICollection<T> interface and takes a T item argument.
Any is an extension method defined on the IEnumerable<T> interface and takes a predicate argument. Therefore, calls
with simple equality checks like Any(x ⇒ x == item) can be replaced by Contains(item).
This applies to the following collection types:
bool ValueExists(HashSet<int> data) =>
data.Any(x => x == 42);
bool ValueExists(List<int> data) =>
data.Any(x => x == 42);
bool ValueExists(HashSet<int> data) =>
data.Contains(42);
bool ValueExists(List<int> data) =>
data.Contains(42);
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
HashSet_Any |
.NET 7.0 |
35,388.333 us |
620.1863 us |
40132 B |
|
HashSet_Contains |
.NET 7.0 |
3.799 us |
0.1489 us |
- |
|
List_Any |
.NET 7.0 |
32,851.509 us |
667.1658 us |
40130 B |
|
List_Contains |
.NET 7.0 |
375.132 us |
8.0764 us |
- |
|
HashSet_Any |
.NET Framework 4.6.2 |
28,979.763 us |
678.0093 us |
40448 B |
|
HashSet_Contains |
.NET Framework 4.6.2 |
5.987 us |
0.1090 us |
- |
|
List_Any |
.NET Framework 4.6.2 |
25,830.221 us |
487.2470 us |
40448 B |
|
List_Contains |
.NET Framework 4.6.2 |
5,935.812 us |
57.7569 us |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
[Params(10_000)]
public int SampleSize;
[Params(1_000)]
public int Iterations;
private static HashSet<int> hashSet;
private static List<int> list;
[GlobalSetup]
public void Setup()
{
hashSet = new HashSet<int>(Enumerable.Range(0, SampleSize));
list = Enumerable.Range(0, SampleSize).ToList();
}
[Benchmark]
public void HashSet_Any() =>
CheckAny(hashSet, SampleSize / 2);
[Benchmark]
public void HashSet_Contains() =>
CheckContains(hashSet, SampleSize / 2);
[Benchmark]
public void List_Any() =>
CheckAny(list, SampleSize / 2);
[Benchmark]
public void List_Contains() =>
CheckContains(list, SampleSize / 2);
void CheckAny(IEnumerable<int> values, int target)
{
for (int i = 0; i < Iterations; i++)
{
_ = values.Any(x => x == target); // Enumerable.Any
}
}
void CheckContains(ICollection<int> values, int target)
{
for (int i = 0; i < Iterations; i++)
{
_ = values.Contains(target); // ICollection<T>.Contains
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/cs/S6617.json ================================================ { "title": "\"Contains\" should be used instead of \"Any\" for simple equality checks", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6617", "sqKey": "S6617", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6618.html ================================================
In order to produce a formatted string, both string.Create and either FormattableString.Invariant or
FormattableString.CurrentCulture can be used. However, string.Create rents array buffers from
ArrayPool<char> making it more performant, as well as preventing unnecessary allocations and future stress on the Garbage
Collector.
This applies to .NET versions after .NET 6, when these string.Create overloads were introduced.
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
Replace calls to FormattableString.CurrentCulture or FormattableString.Invariant with calls to
string.Create(CultureInfo.CurrentCulture, …) or string.Create(CultureInfo.InvariantCulture, …) respectively.
string Interpolate(string value) =>
FormattableString.Invariant($"Value: {value}");
string Interpolate(string value) =>
FormattableString.CurrentCulture($"Value: {value}");
string Interpolate(string value) =>
string.Create(CultureInfo.InvariantCulture, $"Value: {value}");
string Interpolate(string value) =>
string.Create(CultureInfo.CurrentCulture, $"Value: {value}");
The results were generated by running the following snippet with BenchmarkDotNet:
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
StringCreate |
.NET 7.0 |
152.5 ms |
3.09 ms |
83.92 MB |
|
FormattableString |
.NET 7.0 |
191.8 ms |
6.92 ms |
198.36 MB |
The results were generated by running the following snippet with BenchmarkDotNet:
int Value = 42;
DateTime Now = DateTime.UtcNow;
[Params(1_000_000)]
public int N;
[Benchmark]
public void StringCreate()
{
for (int i = 0; i < N; i++)
{
_ = string.Create(CultureInfo.InvariantCulture, $"{Now}: Value is {Value}");
}
}
[Benchmark]
public void FormattableStringInvariant()
{
for (int i = 0; i < N; i++)
{
_ = FormattableString.Invariant($"{Now}: Value is {Value}");
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2728/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S6618.json ================================================ { "title": "\"string.Create\" should be used instead of \"FormattableString\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6618", "sqKey": "S6618", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/cs/S6640.html ================================================
Using unsafe code blocks can lead to unintended security or stability risks.
unsafe code blocks allow developers to use features such as pointers, fixed buffers, function calls through pointers and manual memory
management. Such features may be necessary for interoperability with native libraries, as these often require pointers. It may also increase
performance in some critical areas, as certain bounds checks are not executed in an unsafe context.
unsafe code blocks aren’t necessarily dangerous, however, the contents of such blocks are not verified by the Common Language Runtime.
Therefore, it is up to the programmer to ensure that no bugs are introduced through manual memory management or casting. If not done correctly, then
those bugs can lead to memory corruption vulnerabilities such as stack overflows. unsafe code blocks should be used with caution because
of these security and stability risks.
unsafe code block.There is a risk if you answered yes to the question.
Unless absolutely necessary, do not use unsafe code blocks. If unsafe is used to increase performance, then the Span and Memory APIs may serve a similar purpose in a safe context.
If it is not possible to remove the code block, then it should be kept as short as possible. Doing so reduces risk, as there is less code that can
potentially introduce new bugs. Within the unsafe code block, make sure that:
public unsafe int SubarraySum(int[] array, int start, int end) // Sensitive
{
var sum = 0;
// Skip array bound checks for extra performance
fixed (int* firstNumber = array)
{
for (int i = start; i < end; i++)
sum += *(firstNumber + i);
}
return sum;
}
public int SubarraySum(int[] array, int start, int end)
{
var sum = 0;
Span<int> span = array.AsSpan();
for (int i = start; i < end; i++)
sum += span[i];
return sum;
}
A code block should not contain too many logging statements of a specific level.
Excessive logging within a code block can lead to several problems:
Only the logging statements that are directly within the code block will be counted, and any logging statements within nested blocks will count towards their own. For example consider the snippet below:
void MyMethod(List<MyObject> items)
{
logger.Debug("The operation started");
foreach(var item in items)
{
logger.Debug($"Evaluating {item.Name}");
var result = Evaluate(item);
logger.Debug($"Evaluating resulted in {result}");
}
logger.Debug("The operation ended");
}
The rule will count 2 logging statements that are within the method block (namely logger.Debug("The operation started") and
logger.Debug("The operation ended")). Any statements within nested blocks, such as the foreach block will be counted
separately. The rule considers the log level of the calls, as follows:
The most popular logging frameworks are supported:
Reduce the number of specific logging level calls within the code block by identifying and selecting essential log statements with relevant information, necessary for understanding the flow of execution or diagnosing issues.
With the default Information threshold parameter value 2:
void MyMethod(List<MyObject> items)
{
logger.Debug("The operation started");
foreach(var item in items)
{
logger.Information($"Evaluating {item.Name}"); // Noncompliant
var result = Evaluate(item);
logger.Information($"Evaluating resulted in {result}"); // Secondary 1
if (item.Name is string.Empty)
{
logger.Error("Invalid item name");
}
logger.Information("End item evaluation"); // Secondary 2
}
logger.Debug("The operation ended");
}
With the default Information threshold parameter value 2:
void MyMethod(List<MyObject> items)
{
logger.Debug("The operation started");
foreach(var item in items)
{
logger.Information($"Evaluating {item.Name}");
var result = Evaluate(item);
if (item.Name is string.Empty)
{
logger.Error("Invalid item name");
}
logger.Information($"End item evaluation with result: {result}");
}
logger.Debug("The operation ended");
}
This rule raises an issue on logging calls inside a catch clause that does not pass the raised Exception.
A log entry should contain all the relevant information about the current execution context. The Exception raised in a catch block not only provides the message but also:
Logging methods provide overloads that
accept an Exception as a parameter and logging
providers persist the Exception in a structured way to facilitate the tracking of system failures. Therefore Exceptions
should be passed to the logger.
The rule covers the following logging frameworks:
public bool Save()
{
try
{
DoSave();
return true;
}
catch(IOException)
{
logger.LogError("Saving failed."); // Noncompliant: No specifics about the error are logged
return false;
}
}
public bool Save()
{
try
{
DoSave();
return true;
}
catch(IOException exception)
{
logger.LogError(exception, "Saving failed."); // Compliant: Exception details are logged
return false;
}
}
try-catch statementSerilog001: Exception
UsageMost logging frameworks have methods that take a log level, an event ID or an exception as a separate input next to the log format and its arguments. There is a high chance that if the log level, the event ID or the exception are passed as the arguments to the message format, it was a mistake. This rule is going to raise in that scenario.
The rule covers the following logging frameworks:
Use the dedicated overload that takes the log level, event id, and/or exception as arguments.
try { }
catch (Exception ex)
{
logger.LogDebug("An exception occured {Exception} with {EventId}.", ex, eventId); // Noncompliant
}
try { }
catch (Exception ex)
{
logger.LogDebug(eventId, ex, "An exception occured.");
}
This rule will not raise an issue if one of the parameters mentioned above is passed twice, once as a separate argument to the invocation and once as an argument to the message format.
try { }
catch (Exception ex)
{
logger.LogDebug(ex, "An exception occured {Exception}.", ex); // Compliant
}
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate. This rule checks that the logger field or property name matches a provided regular expression.
The rule supports the most popular logging frameworks:
Update the name of the field or property to follow the configured naming convention. By default, the following names are considered compliant:
{logger} {_logger} {Logger} {_Logger} {log} {_log}
private readonly ILogger myLogger; // Noncompliant
public ILogger MyLogger { get; set; } // Noncompliant
private readonly ILogger logger; // Compliant
public ILogger Logger { get; set; } // Compliant
Trace.Write and Trace.WriteLine methods are writing to the underlying output stream directly, bypassing the trace formatting and filtering performed by TraceListener.TraceEvent implementations. It is preferred to use Trace.TraceError, Trace.TraceWarning, and Trace.TraceInformation methods instead because they call the TraceEvent method which filters the trace output according to the TraceEventType (Error, Warning or Information) and enhance the output with additional information.
Use the Trace.TraceError, Trace.TraceWarning, or Trace.TraceInformation methods.
try
{
var message = RetrieveMessage();
Trace.Write($"Message received: {message}"); // Noncompliant
}
catch (Exception ex)
{
Trace.WriteLine(ex); // Noncompliant
}
try
{
var message = RetrieveMessage();
Trace.TraceInformation($"Message received: {message}");
}
catch (Exception ex)
{
Trace.TraceError(ex);
}
In most logging frameworks, it’s good practice to set the logger name to match its enclosing type, as enforced by {rule:csharpsquid:S3416}.
Logging frameworks can define or use Generic interfaces for the
logger, such as ILogger<TCategoryName>.
The use of a logger of a generic type parameter A (e.g. ILogger<A>) in a type different than A, say
B, goes against the convention.
Because the instance of type A would log with a logger named after B, log items would appear as if they were logged by
B instead, resulting in confusion and logging misconfiguration:
A would not take effect for entries logged in the type AA
from entries logged in the type BFurther details and examples are provided in {rule:csharpsquid:S3416}.
This rule specifically targets the generic logging interface ILogger<TCategoryName> Interface
defined by Microsoft Extensions Logging.
Change the generic type parameter of the ILogger interface to match the enclosing type.
class EnclosingType
{
public EnclosingType(ILogger<AnotherType> logger) // Noncompliant
{
// ...
}
}
class EnclosingType
{
public EnclosingType(ILogger<EnclosingType> logger) // Compliant
{
// ...
}
}
ILogger<TCategoryName>
InterfaceThe positions of arguments in a logging call should match the positions of their message template placeholders.
The placeholders of a message template are defined by their name and their position. Log methods specify the values for the placeholder at runtime by passing them in a params array:
logger.LogError("{First} placeholder and {Second} one.", first, second);
This rule raises an issue if the position of an argument does not match the position of the corresponding placeholder:
// 'first' and 'second' are swapped
logger.LogError("{First} placeholder and {Second} one.", second, first);
// ^^^^^^ ^^^^^
Logging providers use placeholder names to create key/value pairs in the log entry. The key corresponds to the placeholder and the value is the argument passed in the log call.
If the positions of the placeholder and the argument do not match, the value is associated with the wrong key. This corrupts the logs entry and makes log analytics unreliable.
Make sure that the placeholder positions and the argument positions match. Use local variables, fields, or properties for the arguments and name the placeholders accordingly.
'path' and 'fileName' are swapped and therefore assigned to the wrong placeholders.
logger.LogError("File {FileName} not found in folder {Path}", path, fileName);
// ^^^^ ^^^^^^^^
Swap the arguments.
logger.LogError("File {FileName} not found in folder {Path}", fileName, path);
'Name' is detected but 'Folder' is not. The placeholder’s name should correspond to the name from the argument.
logger.LogError("File {Name} not found in folder {Folder}", file.DirectoryName, file.Name);
// ^^^^
Swap the arguments and rename the placeholder to 'DirectoryName'.
logger.LogError("File {Name} not found in folder {DirectoryName}", file.Name, file.DirectoryName);
Not detected: A name for the arguments can not be inferred. Use locals to support detection.
logger.LogError("Sum is {Sum} and product is {Product}", x * y, x + y); // Not detected
Help detection by using arguments with a name.
var sum = x + y;
var product = x * y;
logger.LogError("Sum is {Sum} and product is {Product}", sum, product);
A message template must conform to the specification. The rule raises an issue if the template string violates the template string grammar.
A message template needs to comply with a set of rules. Logging provider parse the template and enrich log entries with the information found in the template. An unparsable message template leads to corrupted log entries and might result in a loss of information in the logs.
The rule covers the following logging frameworks:
Follow the syntax described on https://messagetemplates.org/.
logger.LogError("Login failed for {User", user); // Noncompliant: Syntactically incorrect
logger.LogError("Login failed for {}", user); // Noncompliant: Empty placeholder
logger.LogError("Login failed for {User-Name}", user); // Noncompliant: Only letters, numbers, and underscore are allowed for placeholders
logger.LogDebug("Retry attempt {Cnt,r}", cnt); // Noncompliant: The alignment specifier must be numeric
logger.LogDebug("Retry attempt {Cnt:}", cnt); // Noncompliant: Empty format specifier is not allowed
logger.LogError("Login failed for {User}", user);
logger.LogError("Login failed for {User}", user);
logger.LogError("Login failed for {User_Name}", user);
logger.LogDebug("Retry attempt {Cnt,-5}", cnt);
logger.LogDebug("Retry attempt {Cnt:000}", cnt);
Serilog002:
Message template syntax verifierThe Trace.WriteLineIf Method from the
System.Diagnostic.Trace facility writes a trace if
the condition passed as the first parameter is true.
TraceSwitch allows trace control via
bool properties for each relevant TraceLevel, such as TraceSwitch.TraceError.
Using Trace.WriteLineIf with such properties should be avoided since it can lead to misinterpretation and produce confusion.
In particular, Trace.WriteLineIf may appear as equivalent to the level-specific tracing methods provided by Trace, such
as Trace.Error, but it is not.
The difference is that Trace.WriteLineIf(switch.TraceError, …) conditionally writes the trace, based on the switch, whereas
Trace.TraceError always writes the trace, no matter whether switch.TraceError is true or
false.
Moreover, unlike Trace.TraceError, Trace.WriteLineIf(switch.TraceError, …) would behave like
Trace.WriteLine(…) when switch.TraceError is true, writing unfiltered to the underlying trace listeners and
not categorizing the log entry by level, as described more in detail in {rule:csharpsquid:S6670}.
The fix depends on the intent behind the use of TraceSwitch levels with Trace.WriteLineIf.
If it is trace categorization, level-specific tracing methods, such as Trace.TraceError or Trace.TraceWarning, should be used
instead.
If it is trace filtering, TraceSource should be used instead.
If it is log filtering, Trace should be replaced by logging APIs, such as the ILogger API.
Modern logging APIs are also more suitable than Trace when high-performance logging is required.
Trace.WriteLineIf
MethodTraceSwitchTraceSourceTrace.WriteLine
MethodNamed placeholders in message templates should be unique. The meaning of the named placeholders is to store the value of the provided argument under that name, enabling easier log querying. Since the named placeholder is used multiple times, it cannot store the different values uniquely with each name hence not serving its original purpose. There can be different behaviours when using the same named placeholder multiple times:
_indexThe rule covers the following logging frameworks:
Assign unique names to each template placeholder.
public void Checkout(ILogger logger, User user, Order order)
{
logger.LogDebug("User {Id} purchased order {Id}", user.Id, order.Id);
}
public void Checkout(ILogger logger, User user, Order order)
{
logger.LogDebug("User {UserId} purchased order {OrderId}", user.Id, order.Id);
}
Serilog005: Unique
Property Name VerifierWithin a message template each named placeholder should be in PascalCase.
Using consistent naming conventions is important for the readability and maintainability of code. In the case of message templates, using PascalCase for named placeholders ensures consistency with structured logging conventions, where each named placeholder is used as a property name in the structured data.
The rule covers the following logging frameworks:
Use PascalCase for named placeholders.
logger.LogDebug("User {firstName} logged in", firstName); // Noncompliant
logger.LogDebug("User {FirstName} logged in", firstName); // Compliant
Secret leaks often occur when a sensitive piece of authentication data is stored with the source code of an application. Considering the source code is intended to be deployed across multiple assets, including source code repositories or application hosting servers, the secrets might get exposed to an unintended audience.
In most cases, trust boundaries are violated when a secret is exposed in a source code repository or an uncontrolled deployment environment. Unintended people who don’t need to know the secret might get access to it. They might then be able to use it to gain unwanted access to associated services or resources.
The trust issue can be more or less severe depending on the people’s role and entitlement.
If a JWT secret key leaks to an unintended audience, it can have serious security implications for the corresponding application. The secret key is used to encode and decode JWTs when using a symmetric signing algorithm, and an attacker could potentially use it to perform malicious actions.
For example, an attacker could use the secret key to create their own authentication tokens that appear to be legitimate, allowing them to bypass authentication and gain access to sensitive data or functionality.
In the worst-case scenario, an attacker could be able to execute arbitrary code on the application by abusing administrative features, and take over its hosting server.
Secrets stored in appsettings.json can be read by anyone with access to the file.
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
private readonly IConfiguration _config;
public LoginExampleController(IConfiguration config)
{
_config = config;
}
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = _config["Jwt:Key"] ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
Secrets that are hard-coded into the application can be read by anyone with access to the source code or can be decompiled from the application binaries.
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = Environment.GetEnvironmentVariable("JWT_KEY") ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
Here, the compliant solution uses an environment variable to hold the secret. Environment variables are easy to change and are not easily accessible outside of the application.
Secret vaults provide secure methods for storing and accessing secrets. They protect against the unexpected disclosure of the secrets they store.
Microsoft recommends using Azure Key Vault with .NET Core applications.
var builder = WebApplication.CreateBuilder(args);
// Get the name of the key vault
var keyVaultName = Environment.GetEnvironmentVariable("AZURE_KEYVAULT") ??
throw new ApplicationException("Azure Key Vault location is not configured.");
// Add Azure Key Vault in the configuration
builder.Configuration.AddAzureKeyVault(new Uri($"https://{keyVaultName}.vault.azure.net/"), new EnvironmentCredential());
// Get the JWT secret from Azure Key Vault
var jwtKey = builder.Configuration.GetSection("JWT-KEY").Get<string>() ??
throw new ApplicationException("JWT key is not configured.");
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey!)),
ValidateIssuerSigningKey = true,
ValidIssuer = "example.com",
ValidateIssuer = true,
ValidAudience = "example.com",
ValidateAudience = true,
ValidateLifetime = true,
};
});
Secrets stored in web.config can be read by anyone with access to the file.
public class LoginExampleController : ApiController
{
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = ConfigurationManager.AppSettings["key"] ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
Secrets that are hard-coded into the application can be read by anyone with access to the source code or can be decompiled from the application binaries.
public class LoginExampleController : ApiController
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
public class LoginExampleController : ApiController
{
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = Environment.GetEnvironmentVariable("JWT_KEY") ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
Here, the compliant solution uses an environment variable to hold the secret. Environment variables are easy to change and are not easily accessible outside of the application.
The SupplyParameterFromQuery attribute can be used to specify that a component parameter, of a routable component, comes from the query string.
Component parameters supplied from the query string support the following types:
Query parameters should have one of the supported types. Otherwise, an unhandled exception will be raised at runtime.
Unhandled exception rendering component: Querystring values cannot be parsed as type '<type>'. System.NotSupportedException: Querystring values cannot be parsed as type '<type>' ...
Change the parameter type to one of the following ones:
@page "/print"
<p> Parameter value is: @Value </p>
@code {
[Parameter]
[SupplyParameterFromQuery()]
public TimeSpan Value { get; set; } // Noncompliant
}
@page "/print"
<p> Parameter value is: @Value </p>
@code {
[Parameter]
[SupplyParameterFromQuery()]
public long Value { get; set; } // Compliant
}
In Blazor, the [JSInvokable] attribute is used
to annotate a method, enabling it to be invoked from JavaScript code. The prerequisite for this functionality is that the method must be declared as
public.
Otherwise, a runtime error will be triggered when an attempt is made to call the method from JavaScript.
To fix the issue, ensure the methods annotated with the [JSInvokable] attribute are public.
@code {
[JSInvokable]
private static void MyStaticMethod() { } // Noncompliant
[JSInvokable]
internal void MyMethod() { } // Noncompliant
}
@code {
[JSInvokable]
public static void MyStaticMethod() { } // Compliant
[JSInvokable]
public void MyMethod() { } // Compliant
}
In Blazor, when a route parameter constraint is applied, the value is automatically cast to the corresponding component parameter type. If the constraint type does not match the component parameter type, it can lead to confusion and potential runtime errors due to unsuccessful casting. Therefore, it is crucial to ensure that the types of route parameters and component parameters match to prevent such issues and maintain code clarity.
Ensure the component parameter type matches the route parameter constraint type.
| Constraint Type | .NET Type |
|---|---|
|
bool |
bool |
|
datetime |
DateTime |
|
decimal |
decimal |
|
double |
double |
|
float |
float |
|
guid |
Guid |
|
int |
int |
|
long |
long |
|
string |
string |
@page "/my-route/{Param:datetime}"
@code {
[Parameter]
public string Param { get; set; } // Noncompliant
}
@page "/my-route/{Param:datetime}"
@code {
[Parameter]
public DateTime Param { get; set; } // Compliant
}
In Blazor, using lambda expressions as event handlers when the UI elements are rendered in a loop can lead to negative user experiences and performance issues. This is particularly noticeable when rendering a large number of elements.
The reason behind this is that Blazor rebuilds all lambda expressions within the loop every time the UI elements are rendered.
Ensure to not use a delegate in elements rendered in loops, you can try:
@for (var i = 1; i < 100; i++)
{
var buttonNumber = i;
<button @onclick="@(e => DoAction(e, buttonNumber))"> @* Noncompliant *@
Button #@buttonNumber
</button>
}
@code {
private void DoAction(MouseEventArgs e, int button)
{
// Do something here
}
}
@foreach (var button in Buttons)
{
<button @key="button.Id" @onclick="button.Action"> @* Compliant *@
Button #@button.Id
</button>
}
@code {
private List<Button> Buttons { get; set; } = new();
protected override void OnInitialized()
{
for (var i = 0; i < 100; i++)
{
var button = new Button();
button.Action = (e) => DoAction(e, button);
Buttons.Add(button);
}
}
private void DoAction(MouseEventArgs e, Button button)
{
// Do something here
}
private class Button
{
public string? Id { get; } = Guid.NewGuid().ToString();
public Action<MouseEventArgs> Action { get; set; } = e => { };
}
}
@* Component.razor *@
@for (var i = 1; i < 100; i++)
{
var buttonNumber = i;
<button @onclick="@(e => DoAction(e, buttonNumber))"> @* Noncompliant *@
Button #@buttonNumber
</button>
}
@code {
private void DoAction(MouseEventArgs e, int button)
{
// Do something here
}
}
@* MyButton.razor *@
<button @onclick="OnClickCallback">
@ChildContent
</button>
@code {
[Parameter]
public int Id { get; set; }
[Parameter]
public EventCallback<int> OnClick { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private void OnClickCallback()
{
OnClick.InvokeAsync(Id);
}
}
@* Component.razor *@
@for (var i = 1; i < 100; i++)
{
var buttonNumber = i;
<MyButton Id="buttonNumber" OnClick="DoAction">
Button #@buttonNumber
</MyButton>
}
@code {
private void DoAction(int button)
{
// Do something here
}
}
The results were generated with the help of BenchmarkDotNet and Benchmark.Blazor:
| Method | NbButtonRendered | Mean | StdDev | Ratio |
|---|---|---|---|---|
|
UseDelegate |
10 |
6.603 us |
0.0483 us |
1.00 |
|
UseAction |
10 |
1.994 us |
0.0592 us |
0.29 |
|
UseDelegate |
100 |
50.666 us |
0.5449 us |
1.00 |
|
UseAction |
100 |
2.016 us |
0.0346 us |
0.04 |
|
UseDelegate |
1000 |
512.513 us |
9.7561 us |
1.000 |
|
UseAction |
1000 |
2.005 us |
0.0243 us |
0.004 |
Hardware configuration:
BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 10 (10.0.19045.3448/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores .NET SDK 8.0.100-rc.1.23463.5 [Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/cs/S6802.json ================================================ { "title": "Using lambda expressions in loops should be avoided in Blazor markup section", "type": "CODE_SMELL", "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1h" }, "tags": [ "blazor" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-6802", "sqKey": "S6802", "scope": "All", "quickfix": "infeasible", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "EFFICIENT" } } ================================================ FILE: analyzers/rspec/cs/S6803.html ================================================
This rule is deprecated, and will eventually be removed.
Component parameters can only receive query parameter values in routable components with an @page directive.
SupplyParameterFromQuery attribute is used to specify that a component parameter of a routable component comes from the query string.
In the case of non-routable components, the SupplyParameterFromQuery does not contribute to the functionality, and removing it will
not affect the behavior.
Either make the component routable or remove the SupplyParameterFromQuery attribute.
<h3>Component</h3>
@code {
[Parameter]
[SupplyParameterFromQuery] // Noncompliant
public bool Param { get; set; }
}
@page "/component"
<h3>Component</h3>
@code {
[Parameter]
[SupplyParameterFromQuery] // Compliant
public bool Param { get; set; }
}
Backslash characters (\) should be avoided in route templates.
Routing in ASP.NET MVC maps controllers and actions to paths in request URIs.
In the former syntax specification of URIs, backslash characters (\) were not allowed at all (see section "2.4.3. Excluded US-ASCII Characters" of RFC 2396). While the current
specification (RFC 3986) doesn’t include anymore the "Excluded US-ASCII Characters"
section, most URL processors still don’t support backslash properly.
For instance, a backslash in the "path" part of a URL is automatically converted to a forward slash (/) both by Chrome and Internet
Explorer (see here).
As an example, \Calculator\Evaluate?expression=3\4 is converted on the fly into /Calculator/Evaluate?expression=3\4
before the HTTP request is made to the server.
While backslashes are allowed in the "query" part of a URL, and it’s common to have them as part of a complex query expression, the route of a controller is always part of the "path".
That is why the use of backslashes in controller templates should be avoided in general.
A backslash in the route pattern of a controller would only make sense if the developer intended the backslash in the route to be explicitly
escaped by the user, using %5C.
For example, the route Something\[controller] for the HomeController would need to be called as
Something%5CHome.
The validity of such a scenario is unlikely and the resulting behavior is surprising.
[Route(@"Something\[controller]")] // Noncompliant: Replace '\' with '/'.
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index() => View();
}
[Route(@"Something/[controller]")] // '\' replaced with '/'
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index() => View();
}
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}\\{action=Index}"); // Noncompliant: Replace '\' with '/'.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}"); // '\' replaced with '/'
Route templates for ASP.NET controller
actions, defined via a RouteAttribute or any derivation of HttpMethodAttribute, should
not start with "/".
Routing in ASP.NET Core MVC maps controllers and actions to paths in request URIs. Similar routing happens in ASP.NET Framework MVC.
In ASP.NET Core MVC, when an action defines a route template starting with a "/", the route is considered absolute and the action is registered at the root of the web application.
In such a scenario, any route defined at the controller level is disregarded, as shown in the following example:
[Route("[controller]")] // This route is ignored for the routing of Index1 and Index2
public class HomeController : Controller
{
[HttpGet("/Index1")] // This action is mapped to the root of the web application
public ActionResult Index1() => View();
[Route("/Index2")] // The same applies here
public ActionResult Index2() => View();
}
The behavior can be found confusing and surprising because any relative action route is relativized to the controller route.
Therefore, in the vast majority of scenarios, controllers group all related actions not only in the source code, but also at the routing level.
In ASP.NET Framework MVC with attribute routing enabled via MapMvcAttributeRoutes,
the mere presence of an absolute route at the action level will produce an InvalidOperationException at runtime.
It is then a good practice to avoid absolute routing at the action level and move the "/" to the root level, changing the template defined in the
RouteAttribute of the controller appropriately.
The rule only applies when all route templates of all actions of the controller start with "/". Sometimes some actions may have both relative and absolute route templates, for example for backward compatibility reasons (i.e. a former route needs to be preserved). In such scenarios, it may make sense to keep the absolute route template at the action level.
[Route("[controller]")] // This route is ignored
public class ReviewsController : Controller // Noncompliant
{
// Route is /reviews
[HttpGet("/reviews")]
public ActionResult Index() { /* ... */ }
// Route is /reviews/{reviewId}
[Route("/reviews/{reviewId}")]
public ActionResult Show(int reviewId)() { /* ... */ }
}
[Route("/")] // Turns on attribute routing
public class ReviewsController : Controller
{
// Route is /reviews
[HttpGet("reviews")]
public ActionResult Index() { /* ... */ }
// Route is /reviews/{reviewId}
[Route("reviews/{reviewId}")]
public ActionResult Show(int reviewId)() { /* ... */ }
}
The HttpRequest class provides access to the raw request data through the QueryString, Headers, and
Forms properties. However, whenever possible it is recommended to use model binding instead of directly accessing the input data.
Both ASP.Net MVC implementations - Core and Framework - support model binding in a comparable fashion. Model binding streamlines the process by automatically aligning data from HTTP requests with action method parameters, providing numerous benefits compared to manually parsing raw incoming request data:
Model binding simplifies the code by automatically mapping data from HTTP requests to action method parameters. You don’t need to write any code to manually extract values from the request.
Model binding provides type safety by automatically converting the incoming data into the appropriate .NET types. If the conversion fails, the
model state becomes invalid, which you can easily check using ModelState.IsValid.
With model binding, you can easily apply validation rules to your models using data annotations. If the incoming data doesn’t comply with these rules, the model state becomes invalid.
Model binding helps protect against over-posting attacks by only including properties in the model that you explicitly bind using the
[Bind] attribute or by using view models that only contain the properties you want to update.
By using model binding, your code becomes cleaner, easier to read, and maintain. It promotes the use of strongly typed views, which can provide compile-time checking of your views.
Request.Form, Request.Form.Files, Request.Headers, Request.Query and Request.RouteValues are keyed
collections that expose data from the incoming HTTP request:
Request.Form - application/x-www-form-urlencoded form data from the HTTP request bodyRequest.Form.Files - multipart/form-data file data from the HTTP request bodyRequest.Headers - HTTP Header valuesRequest.Query - URL parameter valuesRequest.RouteValues - Values extracted from the path portion of the URLModel binding can bind these keyed collections to
To replace the keyed collection access, you can:
| Replace | with parameter binding | or complex type binding | or route binding |
|---|---|---|---|
|
|
optional |
optional |
|
|
|
|||
|
|
|
|
|
|
|
optional |
optional |
|
|
|
optional |
optional |
The Model Binding in ASP.NET Core article describes the mechanisms, conventions, and customization options for model binding in more detail. Route-based binding is described in the Routing to controller actions in ASP.NET Core document.
public IActionResult Post()
{
var name = Request.Form["name"]; // Noncompliant: Request.Form
var birthdate = DateTime.Parse(Request.Form["Birthdate"]); // Noncompliant: Request.Form
var locale = Request.Query.TryGetValue("locale", out var locales)
? locales.ToString()
: "en-US"; // Noncompliant: Request.Query
// ..
}
public record User
{
[Required, StringLength(100)]
public required string Name { get; init; }
[DataType(DataType.Date)]
public DateTime? Birthdate { get; init; }
}
public IActionResult Post(User user, [FromHeader] string origin, [FromQuery] string locale = "en-US")
{
if (ModelState.IsValid)
{
// ...
}
}
Model binding in ASP.NET Core MVC and ASP.NET MVC 4.x works by automatically mapping data from HTTP requests to action method parameters. Here’s a step-by-step breakdown of how it works:
ModelState.Errors collection. You can check ModelState.IsValid in your action method to see if any
errors occurred during model binding.ModelState.IsValid is
false, you can handle the errors in your action method and return an appropriate response.See the links in the Resources section for more information.
Request.Form and Request.QueryString are keyed collections
that expose data from the incoming HTTP request:
Request.Form - application/x-www-form-urlencoded form data from the HTTP request bodyRequest.QueryString - URL parameter valuesModel binding can bind these keyed collections to
To replace the keyed collection access, you can:
| Replace | with parameter binding | or complex type binding |
|---|---|---|
|
|
optional |
optional |
|
|
optional |
property name must match query parameter key |
public ActionResult Post()
{
var name = Request.Form["name"]; // Noncompliant: Request.Form
Debug.WriteLine(Request.Form[0]); // Compliant: Binding by index is not supported.
var birthdate = DateTime.Parse(Request.Form["Birthdate"]); // Noncompliant: Request.Form
var cultureName = Request.QueryString["locale"] ?? "en-US"; // Noncompliant: Request.QueryString
// ..
}
public class User
{
[Required, StringLength(100)]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime? Birthdate { get; set; }
}
public ActionResult Post(User user, [Bind(Prefix = "locale")] string cultureName = "en-US")
{
if (ModelState.IsValid)
{
// ...
}
}
public IActionResult Post()
{
var origin = Request.Headers[HeaderNames.Origin]; // Compliant: Access via non-constant field
var nameField = "name";
var name = Request.Form[nameField]; // Compliant: Access via local
var birthdate = DateTime.Parse(Request.Form["Birthdate"]); // Compliant: Access via constant and variable keys is mixed.
// Model binding would only work partially in the method, so we do not raise here.
return Ok();
// ..
}
Model binding in ASP.NET Core MVC and ASP.NET MVC 4.x works by automatically mapping data from HTTP requests to action method parameters. Here’s a step-by-step breakdown of how it works:
ModelState.Errors collection. You can check ModelState.IsValid in your action method to see if any
errors occurred during model binding.ModelState.IsValid is
false, you can handle the errors in your action method and return an appropriate response.See the links in the Resources section for more information.
When a route template is defined through an attribute on an action method, conventional routing for that action is disabled. To maintain good
practice, it’s recommended not to combine conventional and attribute-based routing within a single controller to avoid unpredicted behavior. As such,
the controller should exclude itself from conventional routing by applying a [Route] attribute.
In ASP.NET Core MVC, the routing middleware utilizes a series of rules and conventions to
identify the appropriate controller and action method to handle a specific HTTP request. This process, known as conventional routing, is
generally established using the MapControllerRoute
method. This method is typically configured in one central location for all controllers during the application setup.
Conversely, attribute routing allows routes to be defined at the controller or action method level. It is possible to mix both mechanisms. Although it’s permissible to employ diverse routing strategies across multiple controllers, combining both mechanisms within one controller can result in confusion and increased complexity, as illustrated below.
// Conventional mapping definition
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
public class PersonController
{
// Conventional routing:
// Matches e.g. /Person/Index/123
public IActionResult Index(int? id) => View();
// Attribute routing:
// Matches e.g. /Age/Ascending (and model binds "Age" to sortBy and "Ascending" to direction)
// but does not match /Person/List/Age/Ascending
[HttpGet(template: "{sortBy}/{direction}")]
public IActionResult List(string sortBy, SortOrder direction) => View();
}
When any of the controller actions are annotated with a HttpMethodAttribute with a
route template defined, you should specify a route template on the controller with the RouteAttribute as well.
public class PersonController : Controller
{
// Matches /Person/Index/123
public IActionResult Index(int? id) => View();
// Matches /Age/Ascending
[HttpGet(template: "{sortBy}/{direction}")] // Noncompliant: The "Index" and the "List" actions are
// reachable via different routing mechanisms and routes
public IActionResult List(string sortBy, SortOrder direction) => View();
}
[Route("[controller]/{action=Index}")]
public class PersonController : Controller
{
// Matches /Person/Index/123
[Route("{id?}")]
public IActionResult Index(int? id) => View();
// Matches Person/List/Age/Ascending
[HttpGet("{sortBy}/{direction}")] // Compliant: The route is relative to the controller
public IActionResult List(string sortBy, SortOrder direction) => View();
}
There are also alternative options to prevent the mixing of conventional and attribute-based routing:
// Option 1. Replace the attribute-based routing with a conventional route
app.MapControllerRoute(
name: "Lists",
pattern: "{controller}/List/{sortBy}/{direction}",
defaults: new { action = "List" } ); // Matches Person/List/Age/Ascending
// Option 2. Use a binding, that does not depend on route templates
public class PersonController : Controller
{
// Matches Person/List?sortBy=Age&direction=Ascending
[HttpGet] // Compliant: Parameters are bound from the query string
public IActionResult List(string sortBy, SortOrder direction) => View();
}
// Option 3. Use an absolute route
public class PersonController : Controller
{
// Matches Person/List/Age/Ascending
[HttpGet("/[controller]/[action]/{sortBy}/{direction}")] // Illustrate the expected route by starting with "/"
public IActionResult List(string sortBy, SortOrder direction) => View();
}
ASP.NET controllers should not have mixed responsibilities. Following the Single Responsibility Principle (SRP), they should be kept lean and focused on a single, separate concern. In short, they should have a single reason to change.
The rule identifies different responsibilities by looking at groups of actions that use different sets of services defined in the controller.
Basic services that are typically required by most controllers are not considered:
The rule currently applies to ASP.NET Core only, and doesn’t cover ASP.NET MVC 4.x.
It also only takes into account web APIs controllers, i.e. the ones marked with the ApiController attribute. MVC controllers are
not in scope.
Multiple issues can appear when the Single Responsibility Principle (SRP) is violated.
A controller violating SRP is harder to read and understand since its Cognitive Complexity is generally above average (see {rule:csharpsquid:S3776}).
For example, a controller MediaController that is in charge of both the "movies" and "photos" APIs would need to define all the
actions dealing with movies, alongside the ones dealing with photos, all defined in the same controller class.
The alternative is to define two controllers: a MovieController and a PhotoController, each in charge of a smaller
number of actions.
Such complexity makes the controller harder to maintain and modify, slowing down new development and increasing the likelihood of bugs.
For example, a change in MediaController made for the movies APIs may inadvertently have an impact on the photos APIs as well.
Because the change was made in the context of movies, tests on photos may be overlooked, resulting in bugs in production.
That would not be likely to happen when two distinct controllers, MovieController and a PhotoController, are
defined.
The controller also becomes harder to test since the test suite would need to define a set of tests for each of the responsibilities of the controller, resulting in a large and complex test suite.
For example, the MediaController introduced above would need to be tested on all movies-related actions, as well as on all
photos-related actions.
All those tests would be defined in the same test suite for MediaController, which would be affected by the same issues of
cognitive complexity as the controller under test by the suite.
A controller that has multiple responsibilities is less likely to be reusable. Lack of reuse can result in code duplication.
For example, when a new controller wants to derive from an existing one, it’s less probable that the new controller requires all the behaviors exposed by the reused controller.
Rather, it’s much more common that the new controller only needs to reuse a fraction of the existing one. When reuse is not possible, the only valid alternative is to duplicate part of the logic in the new controller.
A controller that has multiple responsibilities may end up doing more than strictly necessary, resulting in a higher likelihood of performance issues.
To understand why, it’s important to consider the difference between ASP.NET application vs non-ASP.NET applications.
In a non-ASP.NET application, controllers may be defined as Singletons via a Dependency Injection library.
In such a scenario, they would typically be instantiated only once, lazily or eagerly, at application startup.
In ASP.NET applications, however, the default is that controllers are instantiated as many times as the number of requests that are served by the web server. Each instance of the controller would need to resolve services independently.
While service instantiation is typically handled at application startup, service resolution happens every time an instance of controller needs to be built, for each service declared in the controller.
Whether the resolution is done via Dependency Injection, direct static access (in the case of a Singleton), or a Service Locator, the cost of resolution needs to be paid at every single instantiation.
For example, the movies-related APIs of the MediaController mentioned above may require the instantiation of an
IStreamingService, typically done via dependency injection. Such a service may not be relevant
for photos-related APIs.
Similarly, some of the photos-related APIs may require the instantiation of an IRedEyeRemovalService, which may not work at all
with movies.
Having a single controller would force the developer to deal with both instantiations, even though a given instance of the controller may be used only for photos, or only for movies.
A controller that deals with multiple concerns often has unnecessarily complex routing: the route template at controller level cannot factorize the route identifying the concern, so the full route template needs to be defined at the action level.
For example, the MediaController would have an empty route (or equivalent, e.g. / or ~/) and the actions
would need to define themselves the movie or photo prefix, depending on the type of media they deal with.
On the other hand, MovieController and PhotoController can factorize the movie and photo
route respectively, so that the route on the action would only contain action-specific information.
As the size and the responsibilities of the controller increase, the issues that come with such an increase will have a further impact on the code.
Alongside attribute routing, which is typical of web APIs, MVC controllers also come with [conventional routing].
In MVC, the file structure of controllers is important, since it drives conventional routing, which is specific to MVC, as well as default view mapping.
For those reasons, splitting an MVC controller into smaller pieces may break core behaviors of the web application such as routing and views, triggering a large refactor of the whole project.
Split the controller into multiple controllers, each dealing with a single responsibility.
[Route("media")]
public class MediaController( // Noncompliant: This controller has multiple responsibilities and could be split into 2 smaller units.
// Used by all actions
ILogger<MediaController> logger,
// Movie-specific dependencies
IStreamingService streamingService, ISubtitlesService subtitlesService,
// Photo-specific dependencies
IRedEyeRemovalService redEyeRemovalService, IPhotoEnhancementService photoEnhancementService) : Controller
{
[Route("movie/stream")]
public IActionResult MovieStream([FromQuery] StreamRequest request) // Belongs to responsibility #1.
{
logger.LogInformation("Requesting movie stream for {MovieId}", request.MovieId);
return File(streamingService.GetStream(request.MovieId), "video/mp4");
}
[Route("movie/subtitles")]
public IActionResult MovieSubtitles([FromQuery] SubtitlesRequest request) // Belongs to responsibility #1.
{
logger.LogInformation("Requesting movie subtitles for {MovieId}", request.MovieId);
return File(subtitlesService.GetSubtitles(request.MovieId, request.Language), "text/vtt");
}
[Route("photo/remove-red-eye")]
public IActionResult RemoveRedEye([FromQuery] RedEyeRemovalRequest request) // Belongs to responsibility #2.
{
logger.LogInformation("Removing red-eye from photo {PhotoId}", request.PhotoId);
return File(redEyeRemovalService.RemoveRedEye(request.PhotoId, request.Sensitivity), "image/jpeg");
}
[Route("photo/enhance")]
public IActionResult EnhancePhoto([FromQuery] PhotoEnhancementRequest request) // Belongs to responsibility #2.
{
logger.LogInformation("Enhancing photo {PhotoId}", request.PhotoId);
return File(photoEnhancementService.EnhancePhoto(request.PhotoId, request.ColorGrading), "image/jpeg");
}
}
[Route("media/[controller]")]
public class MovieController(
ILogger<MovieController> logger,
IStreamingService streamingService, ISubtitlesService subtitlesService) : Controller
{
[Route("stream")]
public IActionResult MovieStream([FromQuery] StreamRequest request)
{
logger.LogInformation("Requesting movie stream for {MovieId}", request.MovieId);
return File(streamingService.GetStream(request.MovieId), "video/mp4");
}
[Route("subtitles")]
public IActionResult MovieSubtitles([FromQuery] SubtitlesRequest request)
{
logger.LogInformation("Requesting movie subtitles for {MovieId}", request.MovieId);
return File(subtitlesService.GetSubtitles(request.MovieId, request.Language), "text/vtt");
}
}
[Route("media/[controller]")]
public class PhotoController(
ILogger<PhotoController> logger,
IRedEyeRemovalService redEyeRemovalService, IPhotoEnhancementService photoEnhancementService) : Controller
{
[Route("remove-red-eye")]
public IActionResult RemoveRedEye([FromQuery] RedEyeRemovalRequest request)
{
logger.LogInformation("Removing red-eye from photo {PhotoId}", request.PhotoId);
return File(redEyeRemovalService.RemoveRedEye(request.PhotoId, request.Sensitivity), "image/jpeg");
}
[Route("enhance")]
public IActionResult EnhancePhoto([FromQuery] PhotoEnhancementRequest request)
{
logger.LogInformation("Enhancing photo {PhotoId}", request.PhotoId);
return File(photoEnhancementService.EnhancePhoto(request.PhotoId, request.ColorGrading), "image/jpeg");
}
}
ApiController attributeIn ASP.NET Core, controllers usually inherit either from ControllerBase or Controller. If a controller does not use any
View-specific functionality, it is recommended to inherit from ControllerBase.
The ControllerBase class contains all the necessary functionality to handle API
requests and responses. The Controller class inherits from ControllerBase and adds support for Views, PartialViews and ViewComponents.
Inheriting from Controller when not using any View-specific functionality exposes unnecessary methods and can lead to confusion about
the intention of the class.
Furthermore, inheriting from Controller may come with a performance cost. Even though the controller might only deal with API
operations, the support for Views might introduce some overhead from the MVC framework during the request processing pipeline.
An issue is raised when:
[ApiController] attribute.Controller.[NonController] attribute.Change the base type of the controller from Controller to ControllerBase.
[ApiController]
public class MyController : Controller // Noncompliant: Inherit from ControllerBase instead of Controller.
// ^^^^^^^^^^
{
// ..
}
[ApiController]
public class MyController : ControllerBase
{
// ..
}
In frequently used code paths, such as controller actions, you should avoid using the HttpClient directly and opt for one of the IHttpClientFactory-based mechanisms instead. This way, you avoid wasting resources and creating performance overhead.
If a code path that creates and disposes of HttpClient objects is frequently used, then the following issues can occur:
The IHttpClientFactory was introduced in
ASP.NET Core 2.1 to solve these problems. It handles pooling HTTP connections to optimize performance and reliability.
There are several ways that you can use IHttpClientFactory in your application:
Alternatively, you may cache the HttpClient in a singleton or a static field. You should be aware that by default, the HttpClient doesn’t respect the DNS’s Time To Live (TTL) settings. If the IP address associated with a domain name changes, HttpClient might still use the old, cached IP address, leading to failed requests.
[ApiController]
[Route("controller")]
public class FooController : Controller
{
[HttpGet]
public async Task<string> Foo()
{
using var client = new HttpClient(); // Noncompliant
return await client.GetStringAsync(_url);
}
}
// File: Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// ...
}
}
[ApiController]
[Route("controller")]
public class FooController : Controller
{
private readonly IHttpClientFactory _clientFactory;
public FooController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
[HttpGet]
public async Task<string> Foo()
{
using var client = _clientFactory.CreateClient(); // Compliant (Basic usage)
return await client.GetStringAsync(_url);
}
}
"Under-posting" refers to a situation where a client sends less data than expected to the server during an HTTP request, for example when the client omits some properties from the request body that the server expects to receive.
One of the main issues that under-posting can cause is data inconsistency. If the client sends less data than expected, the application might fill any value type properties with their default values, leading to inaccurate or inconsistent data in your database. Additionally, there might be unexpected behavior if there are certain data expected that are not provided and even security issues; for example, if a user omits a role or permission field from a POST request, and the server fills in a default value, it could inadvertently grant more access than intended.
A model class (in this case the
Product class) can be an input of an HTTP handler method:
public class ProductsController : Controller
{
[HttpPost]
public IActionResult Create([FromBody]Product product)
{
// Process product data...
}
}
This rule does not raise an issue when properties are decorated with the following attributes:
Additionally, this rule does not raise for properties in model classes that are not in the same project as the Controller class that references them. This is due to a limitation of Roslyn (see here).
You should mark any model value-type property as nullable, required or JsonRequired. Thus when a client underposts, you ensure that the missing properties can be detected on the server side rather than being auto-filled, and therefore, incoming data meets the application’s expectations.
public class Product
{
public int Id { get; set; } // Noncompliant
public string Name { get; set; }
public int NumberOfItems { get; set; } // Noncompliant
public decimal Price { get; set; } // Noncompliant
}
If the client sends a request without setting the NumberOfItems or Price properties, they will default to 0.
In the request handler method, there’s no way to determine whether they were intentionally set to 0 or omitted by mistake.
public class Product
{
public required int Id { get; set; }
public string Name { get; set; }
public int? NumberOfItems { get; set; } // Compliant - property is optional
[JsonRequired] public decimal Price { get; set; } // Compliant - property must have a value
}
In this example, the request handler method can
NumberOfItems was filled out through the HasValue propertyPrice is not missing
public class ProductsController : Controller
{
[HttpPost]
public IActionResult Create(Product product)
{
if (!ModelState.IsValid) // if product.Price is missing then the model state will not be valid
{
return View(product);
}
if (product.NumberOfItems.HasValue)
{
// ...
}
// Process input...
}
}
When building a REST API, it’s recommended to annotate the controller actions with the available HTTP attributes to be precise about what your API supports.
You should annotate the controller actions with the available HttpMethod attributes. You can still use them in conjunction with the Route attribute, in case there are multiple templates for one action and you need to set the order. This allows you to clearly define the HTTP methods each action method should respond to, while still being able to customize your routes.
This rule does not raise if the controller or the action is annotated with [ApiExplorerSettings(IgnoreApi =
true)] or AcceptsVerbs
attribute.
[Route("Customer")] // This route conflicts with GetCustomers action route
public async Task<IResult> ChangeCustomer([FromBody] CustomerData data) // Noncompliant
{
// ...
return Results.Ok();
}
[Route("Customer")] // This route conflicts with ChangeCustomer action route
public async Task<string> GetCustomers() // Noncompliant
{
return _customerRepository.GetAll();
}
[Route("Customer")]
[HttpPost]
public async Task<IResult> ChangeCustomer([FromBody] CustomerData data) // Compliant
{
// ...
return Results.Ok();
}
[HttpGet("Customer")]
public async Task<string> GetCustomers() // Compliant
{
return _customerRepository.GetAll();
}
In an async method, any blocking operations should be avoided.
Using a synchronous method instead of its asynchronous
counterpart in an async method blocks the execution and is considered bad practice for several reasons:
Each thread consumes system resources, such as memory. When a thread is blocked, it’s not doing any useful work, but it’s still consuming these resources. This can lead to inefficient use of system resources.
Blocking threads can limit the scalability of your application. In a high-load scenario where many operations are happening concurrently, each blocked thread represents a missed opportunity to do useful work. This can prevent your application from effectively handling increased load.
Blocking threads can degrade the performance of your application. If all threads in the thread pool become blocked, new tasks can’t start executing until an existing task completes and frees up a thread. This can lead to delays and poor responsiveness.
Instead of blocking, it’s recommended to use the async operator with async methods. This
allows the system to release the current thread back to the thread pool until the awaited task is complete, improving scalability and
responsiveness.
public async Task Examples(Stream stream, DbSet<Person> dbSet)
{
stream.Read(array, 0, 1024); // Noncompliant
File.ReadAllLines("path"); // Noncompliant
dbSet.ToList(); // Noncompliant in Entity Framework Core queries
dbSet.FirstOrDefault(x => x.Age >= 18); // Noncompliant in Entity Framework Core queries
}
public async Task Examples(Stream stream, DbSet<Person> dbSet)
{
await stream.ReadAsync(array, 0, 1024);
await File.ReadAllLinesAsync("path");
await dbSet.ToListAsync();
await dbSet.FirstOrDefaultAsync(x => x.Age >= 18);
}
In the context of ASP.NET Core MVC web applications, both model binding and model validation are processes that take place prior to the execution
of a controller action. It is imperative for the application to examine the ModelState.IsValid and respond accordingly.
This rule enforces the developer to validate the model within a controller action, ensuring the application’s robustness and reliability.
Querying the ModelState.IsValid property is necessary because it checks if the submitted data in the HTTP request is valid or not.
This property evaluates all the validation attributes applied on your model properties and determines whether the data provided satisfies those
validation rules.
Skipping model validation can lead to:
Therefore, it’s highly recommended to always validate models in your application to ensure data integrity, application stability, and a good user experience.
While client-side validation enhances user experience by providing immediate feedback, it’s not sufficient due to potential manipulation of client-side code, browser compatibility issues, and dependence on JavaScript. Users can bypass or disable it, leading to invalid or malicious data being submitted. Therefore, server-side validation is essential to ensure data integrity and security, making it a best practice to use both client-side and server-side validation in your application.
ModelState.IsValid if they have the [ApiController] attribute. In that case,
an automatic HTTP 400 response containing error details is returned when model state is invalid.TryValidateModel can also be used for model validation.If ModelState.IsValid returns true, it means that the data is valid and the process can continue. If it returns false, it means that
the validation failed, indicating that the data is not in the expected format or is missing required information.
In such cases, the controller action should handle this by returning an appropriate response, such as re-displaying the form with error messages. This helps maintain the integrity of the data and provides feedback to the user, enhancing the overall user experience and security of your application.
public async Task<IActionResult> Create(Movie movie) // Noncompliant: model validity check is missing
{
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
In an ASP.NET Core Web API,
controller actions can optionally return a result value. If a controller action returns a value in the happy path, for example ControllerBase.Ok(Object),
annotating the action with one of the [ProducesResponseType]
overloads that describe the type is recommended.
If an ASP.NET Core Web API uses Swagger, the API documentation will be generated based on the input/output types of the controller actions, as well as the attributes annotating the actions. If an action returns IActionResult or IResult, Swagger cannot infer the type of the response. From the consumer’s perspective, this can be confusing and lead to unexpected results and bugs in the long run without the API provider’s awareness.
This rule raises an issue on a controller action when:
[ProducesResponseType] attribute containing the return type, either at controller or action level.[SwaggerResponse] attribute containing the return type, either at controller or action level.[ApiController] attribute.There are multiple ways to fix this issue:
[ProducesResponseType]
containing the return type.[IActionResult] or [IResult].[IActionResult] or [IResult].
[HttpGet("foo")]
// Noncompliant: Annotate this method with ProducesResponseType containing the return type for succesful responses.
public IActionResult MagicNumber() => Ok(42);
[HttpGet("foo")]
// Noncompliant: Use the ProducesResponseType overload containing the return type for succesful responses.
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult MagicNumber() => Ok(42);
[HttpGet("foo")]
[ProducesResponseType<int>(StatusCodes.Status200OK)]
public IActionResult MagicNumber() => Ok(42);
[HttpGet("foo")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
public IActionResult MagicNumber() => Ok(42);
The Content Security Policy (CSP) is a computer security standard that serves as an additional layer of protection against various types of attacks, including Cross-Site Scripting (XSS) and clickjacking. It provides a set of standard procedures for loading resources by user agents, which can help to mitigate the risk of content injection vulnerabilities.
However, it is important to note that CSP is not a primary line of defense, but rather a safety net that catches attempts to exploit vulnerabilities that exist in the system despite other protective measures. An insecure CSP does not automatically imply that the website is vulnerable, but it does mean that this additional layer of protection is weakened.
A CSP can be considered insecure if it allows potentially harmful practices, such as inline scripts or loading resources from arbitrary domains. These practices can increase the risk of content injection attacks.
An insecure Content Security Policy (CSP) can increase the potential severity of other vulnerabilities in the system. For instance, if an attacker manages to exploit a Cross-Site Scripting (XSS) vulnerability, an insecure CSP might not provide the intended additional protection.
The impact of a successful XSS attack can be severe. XSS allows an attacker to inject malicious scripts into web pages viewed by other users. These scripts can then be used to steal sensitive information like session cookies, personal data, or credit card details, leading to identity theft or financial fraud.
Moreover, XSS can be used to perform actions on behalf of the user without their consent, such as changing their email address or password, or making transactions. This can lead to unauthorized access and potential loss of control over user accounts.
In addition, an insecure CSP that allows loading resources from arbitrary domains could potentially expose sensitive user data to untrusted sources. This could lead to data breaches, which can have serious legal and reputational consequences.
using System.Web;
public async Task InvokeAsync(HttpContext context)
{
context.Response.Headers.ContentSecurityPolicy = "script-src 'self' 'unsafe-inline';"; // Noncompliant
}
using System.Web;
public async Task InvokeAsync(HttpContext context)
{
context.Response.Headers.ContentSecurityPolicy = "script-src 'self' 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=';";
}
To fix an insecure Content Security Policy (CSP), you should adhere to the principle of least privilege. This principle states that a user should be given the minimum levels of access necessary to complete their tasks. In the context of CSP, this means restricting the sources from which content can be loaded to the minimum necessary.
Here are some steps to secure your CSP:
When working with collections that are known to be non-empty, using First or Single is generally preferred over FirstOrDefault or SingleOrDefault.
Using FirstOrDefault or SingleOrDefault on collections that are known to be non-empty is an issue due to:
FirstOrDefault or SingleOrDefault, it implies that the collection might be
empty, which can be misleading if you know it is not. It can be confusing for other developers who read your code, making it harder for them to
understand the actual constraints and behavior of the collection. This leads to confusion and harder-to-maintain code.FirstOrDefault and
SingleOrDefault can lead to subtle bugs. These methods return a default value (null for reference types and
default for value types) when the collection is empty, potentially causing issues like NullReferenceException later in the
code. In contrast, First or Single will throw an InvalidOperationException immediately if the collection is
empty, making it easier to detect and address issues early in the development process.null, you introduces a condition that cannot be fully tested,
impacting the code coverage.
var items = new List<int> { 1, 2, 3 };
int firstItem = items.FirstOrDefault(); // Noncompliant, this implies the collection might be empty, when we know it is not
var items = new List<int> { 1, 2, 3 };
int firstItem = items.First(); // Compliant
SingleFirstSingleOrDefaultFirstOrDefaultWhen using ReaderWriterLock and ReaderWriterLockSlim for managing read and write locks, you should not release a read lock while holding a write lock and vice versa, otherwise you might have runtime exceptions. The locks should be always correctly paired so that the shared resource is accessed safely.
This rule raises if:
If you use the ReaderWriterLockSlim class, you will get a LockRecursionException. In the case of
ReaderWriterLock, you’ll get a runtime exception for trying to release a lock that is not owned by the calling thread.
public class Example
{
private static ReaderWriterLock rwLock = new();
public void Writer()
{
rwLock.AcquireWriterLock(2000);
try
{
// ...
}
finally
{
rwLock.ReleaseReaderLock(); // Noncompliant, will throw runtime exception
}
}
public void Reader()
{
rwLock.AcquireReaderLock(2000);
try
{
// ...
}
finally
{
rwLock.ReleaseWriterLock(); // Noncompliant, will throw runtime exception
}
}
}
public class Example
{
private static ReaderWriterLock rwLock = new();
public static void Writer()
{
rwLock.AcquireWriterLock(2000);
try
{
// ...
}
finally
{
rwLock.ReleaseWriterLock();
}
}
public static void Reader()
{
rwLock.AcquireReaderLock(2000);
try
{
// ...
}
finally
{
rwLock.ReleaseReaderLock();
}
}
}
This rule raises if you acquire a lock with one of the following methods, and do not release it within the same method.
This rule will raise an issue when the code uses the disposable pattern. This pattern makes locking easy to use and delegates the responsibility to the caller. Users should accept issues in such cases, as they should appear only once for each synchronization type.
Not releasing a lock in the same method where you acquire it, and releasing in another one, makes the code less clear and harder to maintain. You are also introducing the risk of not releasing a lock at all which can lead to deadlocks or exceptions.
public class Example
{
private static ReaderWriterLock rwLock = new();
public void AcquireWriterLock() =>
rwLock.AcquireWriterLock(2000); // Noncompliant, as the lock release is on the callers responsibility
public void DoSomething()
{
// ...
}
public void ReleaseWriterLock() =>
rwLock.ReleaseWriterLock();
}
public class Example
{
private static ReaderWriterLock rwLock = new();
public void DoSomething()
{
rwLock.AcquireWriterLock(2000); // Compliant, locks are released in the same method
try
{
// ...
}
finally
{
rwLock.ReleaseWriterLock();
}
}
}
Using upper case literal suffixes removes the potential ambiguity between "1" (digit 1) and "l" (letter el) for declaring literals.
const long b = 0l; // Noncompliant
const long b = 0L;================================================ FILE: analyzers/rspec/cs/S818.json ================================================ { "title": "Literal suffixes should be upper case", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "IDENTIFIABLE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "convention", "pitfall" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-818", "sqKey": "S818", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/cs/S8367.html ================================================
This rule raises an issue when code uses 'field' as an identifier in contexts where it conflicts with the new field contextual keyword
introduced in C# 14.
C# 14 introduces the field contextual keyword for field-backed properties. This keyword allows access to the synthesized backing
fields directly within property accessors, simplifying property implementations that need custom logic.
By using 'field' as an identifier, several problems can occur:
field within property accessors cause compiler
error CS9273. References to existing members named field within property accessors cause compiler error CS9258. See Compiler errors on property
declarations.field may be unexpectedly overshadowed by the synthesized backing
field when referenced within property accessors, leading to logic errors and unexpected runtime behavior.Using 'field' as an identifier can prevent successful compilation when upgrading to C# 14. In property accessors, local variables or parameters named 'field' will cause compilation errors, while class members named 'field' may be unexpectedly overshadowed by synthesized backing fields, leading to logic errors and unexpected runtime behavior.
Rename the identifier, escape it by prefixing with @, or qualify member access with this. or base. to avoid
conflicts with the contextual keyword. This rule applies only inside property get, set, and init accessors;
indexer and event accessors are not affected.
public class ClassFieldExample
{
private string field;
public string Message
{
get => field; // Noncompliant
set => field = value; // Noncompliant
}
}
public class LocalFunctionExample
{
public int Value
{
get
{
return LocalFunction(42);
int LocalFunction(int field) // Noncompliant
=> field;
}
}
}
public class ClassFieldExample
{
private string field;
public string Message
{
get => this.field; // or @field
set => this.field = value; // or @field = value;
}
}
public class LocalFunctionExample
{
public int Value
{
get
{
return LocalFunction(42);
int LocalFunction(int value) // Compliant - renamed
=> value;
}
}
}
field
keywordfield in a property accessor refers to synthesized backing fieldfield disallowed in a property accessor (documentation incorrectly references CS9272; the actual error code is CS9273)Identifiers named extension should be renamed or use the @ escape prefix to avoid breaking changes in C# 14 or later.
Starting with C# 14, extension is
a contextual keyword for declaring extension members and extension containers. This change can cause compilation errors in existing code that uses
the word extension as an identifier. For example, a class named 'extension' will be interpreted as an extension container declaration,
leading to parsing failures.
This rule flags any unescaped extension identifiers used in the following cases:
Note that method names, parameter names, and local variable names are not affected by this breaking change. For example, void extension() {
} remains valid in C# 14.
Code that currently compiles will fail to compile when the project is upgraded to C# 14.
Rename the identifier, or add the @ prefix to it.
class extension { }
class MyClass
{
extension field;
extension Property { get; }
}
Escape the identifier with the @ verbatim prefix:
class @extension { }
class MyClass
{
@extension field;
@extension Property { get; }
}
class extension { }
class MyClass
{
extension ReturnType() { return default; }
}
Rename the identifier:
class MyExtension { }
class MyClass
{
MyExtension ReturnType() { return default; }
}
extension treated as a contextual keyword@ prefix)Using partial as a return type identifier will cause a compilation error when upgrading to C# 14.
In C# 14, partial in a return type position is always treated as a modifier, even when a type named partial is in scope.
This causes compilation errors in code that previously compiled successfully.
The issue specifically affects method declarations where a custom type named partial is used as the return type. Other uses of the
partial type (such as in variable declarations or constructor calls) are not affected by this change.
This breaking change will cause compilation failures when upgrading existing codebases to C# 14. The compilation error prevents the application from building, requiring immediate attention to fix the syntax.
While this is not a runtime issue, it can block development and deployment processes until resolved. The fix is straightforward but must be applied to all affected method declarations.
Add the @ prefix before partial when used as a return type. This tells the compiler to treat partial as an
identifier rather than a modifier. Alternatively, rename the type to avoid the conflict.
class C
{
partial F() => new partial(); // Noncompliant
}
class partial { }
class C
{
@partial F() => new partial();
}
class partial { }
class C
{
partial F() => new partial(); // Noncompliant
}
class partial { }
class C
{
PartialItem F() => new PartialItem();
}
class PartialItem { }
partial cannot be a return type of methodsUsing scoped as a type name in lambda parameter lists will cause a compilation error when upgrading to C# 14 or later.
In C# 14, the language introduced the ability to write lambdas with parameter modifiers without specifying parameter types. As part of this
enhancement, a breaking change was implemented where scoped is always treated as a modifier in lambda parameter lists.
As a result, parenthesized lambda parameters that use scoped as an identifier or type name will fail to compile after upgrading to C#
14. In that context, the compiler interprets scoped as a modifier unless it is escaped with @.
This breaking change affects existing code that was valid in earlier C# versions, particularly when upgrading projects to C# 14. The issue is
specific to lambda expressions and doesn’t affect other contexts where the scoped type name might be used.
This issue prevents code compilation when upgrading to C# 14. Projects that previously compiled successfully will fail to build, blocking development and deployment processes until the code is updated.
Rename the type, or escape the type name scoped with the @ prefix. This tells the compiler to treat it as an identifier
rather than a contextual keyword.
Action<int> lambda1 = (scoped) => { }; // Noncompliant
var lambda2 = (scoped scoped) => { }; // Noncompliant
var lambda3 = (scoped scoped parameter) => { }; // Noncompliant
ref struct @scoped { }
Action<int> lambda1 = (@scoped) => { };
var lambda2 = (@scoped scoped) => { };
var lambda3 = (scoped @scoped parameter) => { };
ref struct @scoped { }
var lambda = (scoped scoped parameter) => { }; // Noncompliant
ref struct @scoped { }
var lambda = (scoped ScopedItem parameter) => { };
ref struct ScopedItem { }
The use of increment and decrement operators in method calls or in combination with other arithmetic operators is not recommended, because:
u8a = ++u8b + u8c--; foo = bar++ / 4;
The following sequence is clearer and therefore safer:
++u8b; u8a = u8b + u8c; u8c--; foo = bar / 4; bar++;================================================ FILE: analyzers/rspec/cs/S881.json ================================================ { "title": "Increment (++) and decrement (--) operators should not be used in a method call or mixed with other operators in an expression", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-881", "sqKey": "S881", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/cs/S907.html ================================================
goto is an unstructured control flow statement. It makes code less readable and maintainable. Structured control flow statements such
as if, for, while, continue or break should be used instead.
Parameters are part of the method signature and its identity.
Implementing a method from an interface, a base class, or a partial method and changing one of its parameters' names will confuse and impact the method’s readability.
interface IBankAccount
{
void AddMoney(int money);
}
class BankAccount : IBankAccount
{
void AddMoney(int amount) // Noncompliant: parameter's name differs from base
{
// ...
}
}
To avoid any ambiguity in the code, a parameter’s name should match the initial declaration, whether its initial declaration is from an interface, a base class, or a partial method.
interface IBankAccount
{
void AddMoney(int money);
}
class BankAccount : IBankAccount
{
void AddMoney(int money) // Compliant: parameter's name match base name
{
// ...
}
}
The rule is ignored if both the parameter defined in the initial decalaration is a generic type and the implementing member’s declaration is a non-generic type.
This allows the implementing member to be more specific and provide more information.
Shared naming conventions allow teams to collaborate efficiently.
This rule raises an issue when a class name does not match a provided regular expression.
The default configuration is the one recommended by Microsoft:
BackColorGetIDGetHtmlFor example, with the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$, the class:
Class foo ' Noncompliant End Class
should be renamed to
Class Foo End Class
Scrolling horizontally to see a full line of code lowers the code readability.
================================================ FILE: analyzers/rspec/vbnet/S103.json ================================================ { "title": "Lines should not be too long", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "convention" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-103", "sqKey": "S103", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S104.html ================================================When a source file grows too much, it can accumulate numerous responsibilities and become challenging to understand and maintain.
Above a specific threshold, refactor the file into smaller files whose code focuses on well-defined tasks. Those smaller files will be easier to understand and test.
================================================ FILE: analyzers/rspec/vbnet/S104.json ================================================ { "title": "Files should not have too many lines of code", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1h" }, "tags": [ "brain-overload" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-104", "sqKey": "S104", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1048.html ================================================The Finalize methods are used to perform any necessary final clean-up when the garbage collector is collecting a class instance. The programmer has no control over when the Finalize method is called; the garbage collector decides when to call it.
When creating a Finalize method, it should never throw an exception, as there is a high risk of having the application terminated leaving unmanaged resources without a graceful cleanup.
The rule raises an issue on throw statements used in a Finalize method.
Class Sample
Protected Overrides Sub Finalize()
Throw New NotImplementedException() ' Noncompliant: Finalize method throws an exception
End Sub
End Class
Class Sample
Protected Overrides Sub Finalize()
' Noncompliant: Finalize method does not throw an exception
End Sub
End Class
In general object finalization can be a complex and error-prone operation and should not be implemented except within the dispose pattern.
When cleaning up unmanaged resources, it is
recommended to implement the dispose pattern or, to cover uncalled Dispose method by the consumer, implement SafeHandle.
SafeHandle
ClassIDisposable.Dispose
MethodThe tab width can differ from one development environment to another. Using tabs may require other developers to configure their environment (text editor, preferences, etc.) to read source code.
That is why using spaces is preferable.
================================================ FILE: analyzers/rspec/vbnet/S105.json ================================================ { "title": "Tabulation characters should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-105", "sqKey": "S105", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1066.html ================================================Nested code - blocks of code inside blocks of code - is eventually necessary, but increases complexity. This is why keeping the code as flat as possible, by avoiding unnecessary nesting, is considered a good practice.
Merging if statements when possible will decrease the nesting of the code and improve its readability.
Code like
If condition1 Then
If condition2 Then ' Noncompliant
' ...
End If
End If
Will be more readable as
If condition1 AndAlso condition2 Then
' ...
End If
================================================
FILE: analyzers/rspec/vbnet/S1066.json
================================================
{
"title": "Mergeable \"if\" statements should be combined",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1066",
"sqKey": "S1066",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1067.html
================================================
Complex boolean expressions are hard to read and so to maintain.
With the default threshold value of 3
If ((condition1 AndAlso condition2) OrElse (condition3 AndAlso condition4)) AndAlso condition5) Then 'Noncompliant ... End If
If ((MyFirstCondition() OrElse MySecondCondition()) AndAlso MyLastCondition()) Then ... End If================================================ FILE: analyzers/rspec/vbnet/S1067.json ================================================ { "title": "Expressions should not be too complex", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "3min" }, "tags": [ "brain-overload" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-1067", "sqKey": "S1067", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S107.html ================================================
Procedures with a long parameter list are difficult to use because maintainers must figure out the role of each parameter and keep track of their position.
Sub SetCoordinates(x1 As Integer, y1 As Integer, z1 As Integer, x2 As Integer, y2 As Integer, z2 As Integer) ' Noncompliant
' ...
End Sub
The solution can be to:
' Each function does a part of what the original setCoordinates function was doing, so confusion risks are lower Sub SetOrigin(x As Integer, y As Integer, z As Integer) ' ... End Sub Sub SetSize(width As Integer, height As Integer, depth As Integer) ' ... End Sub
Class Point ' In geometry, Point is a logical structure to group data
Public x As Integer
Public y As Integer
Public z As Integer
End Class
Sub SetCoordinates(p1 As Point, p2 As Point)
' ...
End Sub
This rule raises an issue when a procedure has more parameters than the provided threshold.
The rule does not count the parameters intended for a base class constructor.
With a maximum number of 4 parameters:
Class BaseClass
Sub New(Param1 As Integer)
' ...
End Sub
End Class
Class DerivedClass
Inherits BaseClass
Public Sub New(Param1 As Integer, Param2 As Integer, Param3 As Integer, Param4 As Integer, Param5 As Long)
' Compliant by exception: Param1 is used in the base class constructor, so it does not count in the sum of the parameter list.
MyBase.New(Param1)
' ...
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S107.json
================================================
{
"title": "Procedures should not have too many parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-107",
"sqKey": "S107",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1075.html
================================================
Hard-coding a URI makes it difficult to test a program for a variety of reasons:
In addition, hard-coded URIs can contain sensitive information, like IP addresses, and they should not be stored in the code.
For all those reasons, a URI should never be hard coded. Instead, it should be replaced by a customizable parameter.
Further, even if the elements of a URI are obtained dynamically, portability can still be limited if the path delimiters are hard-coded.
This rule raises an issue when URIs or path delimiters are hard-coded.
================================================ FILE: analyzers/rspec/vbnet/S1075.json ================================================ { "title": "URIs should not be hardcoded", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "20min" }, "tags": [], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1075", "sqKey": "S1075", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S108.html ================================================An empty code block is confusing. It will require some effort from maintainers to determine if it is intentional or indicates the implementation is incomplete.
For Index As Integer = 1 To 42 ' Noncompliant: is the block empty on purpose, or is code missing? Next
Removing or filling the empty code blocks takes away ambiguity and generally results in a more straightforward and less surprising code.
The rule ignores code blocks that contain comments.
================================================ FILE: analyzers/rspec/vbnet/S108.json ================================================ { "title": "Nested blocks of code should not be left empty", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-108", "sqKey": "S108", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1110.html ================================================The use of parentheses, even those not required to enforce a desired order of operations, can clarify the intent behind a piece of code. However, redundant pairs of parentheses could be misleading and should be removed.
If a AndAlso ((x + y > 0)) Then ' Noncompliant
' ...
End If
Return ((x + 1)) ' Noncompliant
If a AndAlso x + y > 0 Then
' ...
End If
Return x + 1
================================================
FILE: analyzers/rspec/vbnet/S1110.json
================================================
{
"title": "Redundant pairs of parentheses should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1110",
"sqKey": "S1110",
"scope": "All",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/vbnet/S112.html
================================================
This rule raises an issue when a generic exception (such as Exception, SystemException,
ApplicationException, IndexOutOfRangeException, NullReferenceException, OutOfMemoryException or
ExecutionEngineException) is thrown.
Throwing general exceptions such as Exception, SystemException and ApplicationException will have a negative
impact on any code trying to catch these exceptions.
From a consumer perspective, it is generally a best practice to only catch exceptions you intend to handle. Other exceptions should ideally not be caught and let to propagate up the stack trace so that they can be dealt with appropriately. When a generic exception is thrown, it forces consumers to catch exceptions they do not intend to handle, which they then have to re-throw.
Besides, when working with a generic type of exception, the only way to distinguish between multiple exceptions is to check their message, which is error-prone and difficult to maintain. Legitimate exceptions may be unintentionally silenced and errors may be hidden.
For instance, if an exception such as StackOverflowException is caught and not re-thrown, it may prevent the program from terminating
gracefully.
When throwing an exception, it is therefore recommended to throw the most specific exception possible so that it can be handled intentionally by consumers.
Additionally, some reserved exceptions should not be thrown manually. Exceptions such as IndexOutOfRangeException,
NullReferenceException, OutOfMemoryException or ExecutionEngineException will be thrown automatically by the
runtime when the corresponding error occurs. Many of them indicate serious errors, which the application may not be able to recover from. It is
therefore recommended to avoid throwing them as well as using them as base classes.
To fix this issue, make sure to throw specific exceptions that are relevant to the context in which they arise. It is recommended to either:
Exception when one matches. For instance ArgumentException could be raised when an unexpected
argument is provided to a function.Exception or one of its subclasses.
Public Sub DoSomething(obj As Object)
If obj Is Nothing Then
' Noncompliant
Throw New NullReferenceException("obj") ' Noncompliant: This reserved exception should not be thrown manually
End If
' ...
End Sub
Public Sub DoSomething(obj As Object)
If obj Is Nothing Then
Throw New ArgumentNullException("obj") ' Compliant: this is a specific and non-reserved exception type
End If
' ...
End Sub
The Obsolete attribute can be applied with or without a message argument. Marking something Obsolete without including
advice on why it’s obsolete or what to use instead will lead maintainers to waste time trying to figure those things out.
Public Class Car
<Obsolete> ' Noncompliant
Public Sub CrankEngine(Turns As Integer)
' ...
End Sub
End Class
Public Class Car
<Obsolete("Replaced by the automatic starter")>
Public Sub CrankEngine(Turns As Integer)
' ...
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S1123.json
================================================
{
"title": "\"Obsolete\" attributes should include explanations",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"obsolete",
"bad-practice"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1123",
"sqKey": "S1123",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1125.html
================================================
A boolean literal can be represented in two different ways: True or False. They can be combined with logical operators
(Not, And, Or, =) to produce logical expressions that represent truth values. However, comparing a boolean literal to a variable or
expression that evaluates to a boolean value is unnecessary and can make the code harder to read and understand. The more complex a boolean expression
is, the harder it will be for developers to understand its meaning and expected behavior, and it will favour the introduction of new bugs.
Remove redundant boolean literals from expressions to improve readability and make the code more maintainable.
If BooleanMethod() = True Then ' Noncompliant ' ... End If If BooleanMethod() = False Then ' Noncompliant ' ... End If If BooleanMethod() OrElse False Then ' Noncompliant ' ... End If DoSomething(Not False) ' Noncompliant DoSomething(BooleanMethod() = True) ' Noncompliant Dim booleanVariable = If(BooleanMethod(), True, False) ' Noncompliant booleanVariable = If(BooleanMethod(), True, exp) ' Noncompliant booleanVariable = If(BooleanMethod(), False, exp) ' Noncompliant booleanVariable = If(BooleanMethod(), exp, True) ' Noncompliant booleanVariable = If(BooleanMethod(), exp, False) ' Noncompliant
If BooleanMethod() Then ' ... End If If Not BooleanMethod() Then ' ... End If If BooleanMethod() Then ' ... End If DoSomething(True) DoSomething(BooleanMethod()) Dim booleanVariable = BooleanMethod() booleanVariable = BooleanMethod() OrElse exp booleanVariable = Not BooleanMethod() AndAlso exp booleanVariable = Not BooleanMethod() OrElse exp booleanVariable = BooleanMethod() AndAlso exp================================================ FILE: analyzers/rspec/vbnet/S1125.json ================================================ { "title": "Boolean literals should not be redundant", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1125", "sqKey": "S1125", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1133.html ================================================
This rule is meant to be used as a way to track code which is marked as being deprecated. Deprecated code should eventually be removed.
<Obsolete> ' Noncompliant Sub Procedure() End Sub================================================ FILE: analyzers/rspec/vbnet/S1133.json ================================================ { "title": "Deprecated code should be removed", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "INFO" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "obsolete" ], "defaultSeverity": "Info", "ruleSpecification": "RSPEC-1133", "sqKey": "S1133", "scope": "All", "quickfix": "infeasible" } ================================================ FILE: analyzers/rspec/vbnet/S1134.html ================================================
FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to deal with later.
Sometimes the developer will not have the time or will simply forget to get back to that tag.
This rule is meant to track those tags and to ensure that they do not go unnoticed.
Function Divide(numerator As Integer, denominator As Integer) As Integer
Return numerator / denominator ' FIXME denominator value might be 0
End Function
Developers often use TODO tags to mark areas in the code where additional work or improvements are needed but are not implemented
immediately. However, these TODO tags sometimes get overlooked or forgotten, leading to incomplete or unfinished code. This rule aims to
identify and address unattended TODO tags to ensure a clean and maintainable codebase. This description explores why this is a problem
and how it can be fixed to improve the overall code quality.
Unattended TODO tags in code can have significant implications for the development process and the overall codebase.
Incomplete Functionality: When developers leave TODO tags without implementing the corresponding code, it results in incomplete
functionality within the software. This can lead to unexpected behavior or missing features, adversely affecting the end-user experience.
Missed Bug Fixes: If developers do not promptly address TODO tags, they might overlook critical bug fixes and security updates.
Delayed bug fixes can result in more severe issues and increase the effort required to resolve them later.
Impact on Collaboration: In team-based development environments, unattended TODO tags can hinder collaboration. Other team members
might not be aware of the intended changes, leading to conflicts or redundant efforts in the codebase.
Codebase Bloat: The accumulation of unattended TODO tags over time can clutter the codebase and make it difficult to distinguish
between work in progress and completed code. This bloat can make it challenging to maintain an organized and efficient codebase.
Addressing this code smell is essential to ensure a maintainable, readable, reliable codebase and promote effective collaboration among developers.
Sub DoSomething()
' TODO
End Sub
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate.
This rule allows to check that all interface names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^I([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Interface Foo ' Noncompliant End Interface
Interface IFoo ' Compliant End Interface================================================ FILE: analyzers/rspec/vbnet/S114.json ================================================ { "title": "Interface names should comply with a naming convention", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "IDENTIFIABLE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-114", "sqKey": "S114", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1147.html ================================================
End statements exit the control flow of the program in an unstructured way. This statement stops code execution immediately without
executing Dispose or Finalize methods, or executing Finally blocks. Therefore, it should be avoided.
Module Module1
Sub Print(ByVal str As String)
Try
...
End ' Noncompliant
Finally
' do something important here
...
End Try
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1147.json
================================================
{
"title": "\"End\" statements should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "30min"
},
"tags": [
"cwe",
"suspicious"
],
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-1147",
"sqKey": "S1147",
"scope": "Main",
"securityStandards": {
"CWE": [
382
]
},
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1151.html
================================================
The Select...Case statement should be used only to clearly define some new branches in the control flow. As soon as a case clause
contains too many statements this highly decreases the readability of the overall control flow statement. In such case, the content of the case clause
should be extracted into a dedicated procedure.
With the default threshold of 3:
Select Case number
Case 1 To 5 ' Noncompliant: 4 statements in the case
MethodCall1("")
MethodCall2("")
MethodCall3("")
MethodCall4("")
Case Else
' ...
End Select
Select Case number
Case 1 To 5
DoSomething()
Case Else
' ...
End Select
...
Sub DoSomething()
MethodCall1("")
MethodCall2("")
MethodCall3("")
MethodCall4("")
End Sub
================================================
FILE: analyzers/rspec/vbnet/S1151.json
================================================
{
"title": "\"Select...Case\" clauses should not have too many lines of code",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1151",
"sqKey": "S1151",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1155.html
================================================
Using .Count() to test for emptiness works, but using .Any() makes the intent clearer, and the code more readable.
However, there are some cases where special attention should be paid:
EntityFramework or other ORM query, calling .Count() will cause executing a potentially
massive SQL query and could put a large overhead on the application database. Calling .Any() will also connect to the database, but
will generate much more efficient SQL..Select() statements that create objects, a large amount of memory could be
unnecessarily allocated. Calling .Any() will be much more efficient because it will execute fewer iterations of the enumerable.
Private Function HasContent(Strings As IEnumerable(Of String)) As Boolean
Return Strings.Count() > 0 ' Noncompliant
End Function
Private Function HasContent2(Strings As IEnumerable(Of String)) As Boolean
Return Strings.Count() >= 1 ' Noncompliant
End Function
Private Function IsEmpty(Strings As IEnumerable(Of String)) As Boolean
Return Strings.Count() = 0 ' Noncompliant
End Function
Private Function HasContent(Strings As IEnumerable(Of String)) As Boolean
Return Strings.Any
End Function
Private Function HasContent2(Strings As IEnumerable(Of String)) As Boolean
Return Strings.Any
End Function
Private Function IsEmpty(Strings As IEnumerable(Of String)) As Boolean
Return Not Strings.Any
End Function
================================================
FILE: analyzers/rspec/vbnet/S1155.json
================================================
{
"title": "\"Any()\" should be used to test for emptiness",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1155",
"sqKey": "S1155",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1163.html
================================================
If an exception is already being thrown within the try block or caught in a catch block, throwing another exception in
the finally block will override the original exception. This means that the original exception’s message and stack trace will be lost,
potentially making it challenging to diagnose and troubleshoot the root cause of the problem.
Try
' Some work which end up throwing an exception
Throw New ArgumentException()
Finally
' Cleanup
Throw New InvalidOperationException() ' Noncompliant: will mask the ArgumentException
End Try
Try
' Some work which end up throwing an exception
Throw New ArgumentException()
Finally
' Cleanup without throwing
End Try
Local variables should be named consistently to communicate intent and improve maintainability. Rename your local variable to follow your project’s naming convention to address this issue.
A naming convention in software development is a set of guidelines for naming code elements like variables, functions, and classes.
Local variables hold the meaning of the written code. Their names should be meaningful and follow a consistent and easily recognizable pattern.
Adhering to a consistent naming convention helps to make the code more readable and understandable, which makes it easier to maintain and debug. It
also ensures consistency in the code, especially when multiple developers are working on the same project.
This rule checks that local variable names match a provided regular expression.
Inconsistent naming of local variables can lead to several issues in your code:
In summary, not adhering to a naming convention for local variables can lead to confusion, errors, and inefficiencies, making the code harder to read, understand, and maintain.
First, familiarize yourself with the particular naming convention of the project in question. Then, update the name to match the convention, as well as all usages of the name. For many IDEs, you can use built-in renaming and refactoring features to update all usages at once.
With the default regular expression ^[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$, bringing the following constraints:
Module Module1
Sub Main()
Dim Foo = 0 ' Noncompliant
End Sub
End Module
Module Module1
Sub Main()
Dim foo = 0 ' Compliant
End Sub
End Module
A typical code smell known as unused function parameters refers to parameters declared in a function but not used anywhere within the function’s body. While this might seem harmless at first glance, it can lead to confusion and potential errors in your code. Disregarding the values passed to such parameters, the function’s behavior will be the same, but the programmer’s intention won’t be clearly expressed anymore. Therefore, removing function parameters that are not being utilized is considered best practice.
This rule raises an issue when a private procedure of a Class, Module or Structure takes a
parameter without using it.
This rule doesn’t raise any issue in the following contexts:
NotImplementedException.virtual, override procedures.Having unused function parameters in your code can lead to confusion and misunderstanding of a developer’s intention. They reduce code readability and introduce the potential for errors. To avoid these problems, developers should remove unused parameters from function declarations.
Private Sub DoSomething(ByVal a As Integer, ByVal b as Integer) ' "b" is unused
Compute(a)
End Sub
Private Function DoSomething2(ByVal a As Integer, ByVal b As Integer) As Integer ' "a" is unused
Compute(b)
Return b
End Function
Private Sub DoSomething(ByVal a As Integer)
Compute(a)
End Sub
Private Function DoSomething2(ByVal b As Integer) As Integer
Compute(b)
Return b
End Function
================================================
FILE: analyzers/rspec/vbnet/S1172.json
================================================
{
"title": "Unused procedure parameters should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1172",
"sqKey": "S1172",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1186.html
================================================
An empty method is generally considered bad practice and can lead to confusion, readability, and maintenance issues. Empty methods bring no functionality and are misleading to others as they might think the method implementation fulfills a specific and identified requirement.
There are several reasons for a method not to have a body:
The following empty methods are considered compliant:
Overridable methods, as the implementation might not be required in the base classMustOverride method as the implementation is mandatory for child classSub ShouldNotBeEmpty() ' Noncompliant - method is empty End Sub Sub NotImplementedYet() ' Noncompliant - method is empty End Sub Sub WillNeverBeImplemented() ' Noncompliant - method is empty End Sub Sub EmptyOnPurpose() ' Noncompliant - method is empty End Sub
Sub ShouldNotBeEmpty()
DoSomething()
End Sub
Sub NotImplementedYet()
Throw New NotImplementedException
End Sub
Sub WillNeverBeImplemented()
Throw New NotSupportedException
End Sub
Sub EmptyOnPurpose()
' Do nothing because of X
End Sub
================================================
FILE: analyzers/rspec/vbnet/S1186.json
================================================
{
"title": "Methods should not be empty",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"suspicious"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-1186",
"sqKey": "S1186",
"scope": "All",
"quickfix": "infeasible"
}
================================================
FILE: analyzers/rspec/vbnet/S119.html
================================================
Inconsistent naming conventions can lead to confusion and errors when working in a team. This rule ensures that all generic type parameter names follow a consistent naming convention by checking them against a provided regular expression.
The default configuration follows Microsoft’s recommended convention:
To fix this issue, ensure that all generic type parameter names in your code follow the naming convention specified in the regular expression.
With the default parameter value ^T(([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?)?$:
Public Class Foo(Of tkey) ' Noncompliant End Class
Public Class Foo(Of TKey) ' Compliant End Class
Duplicated string literals make the process of refactoring complex and error-prone, as any change would need to be propagated on all occurrences.
The following are ignored:
Use constants to replace the duplicated string literals. Constants can be referenced from many places, but only need to be updated in a single place.
Public Class Foo
Private Name As String = "foobar" ' Noncompliant
Public ReadOnly Property DefaultName As String = "foobar" ' Noncompliant
Public Sub New(Optional Value As String = "foobar") ' Noncompliant
Dim Something = If(Value, "foobar") ' Noncompliant
End Sub
End Class
Public Class Foo
Private Const Foobar As String = "foobar"
Private Name As String = Foobar
Public ReadOnly Property DefaultName As String = Foobar
Public Sub New(Optional Value As String = Foobar)
Dim Something = If(Value, Foobar)
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S1192.json
================================================
{
"title": "String literals should not be duplicated",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "DISTINCT"
},
"status": "ready",
"remediation": {
"func": "Linear with offset",
"linearDesc": "per duplicate instance",
"linearOffset": "2min",
"linearFactor": "2min"
},
"tags": [
"design"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1192",
"sqKey": "S1192",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1197.html
================================================
Array designators should always be located on the type for better code readability. Otherwise, developers must look both at the type and the variable name to know whether or not a variable is an array.
Module Module1
Sub Main()
Dim foo() As String ' Noncompliant
End Sub
End Module
Module Module1
Sub Main()
Dim foo As String() ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1197.json
================================================
{
"title": "Array designators \"()\" should be on the type, not the variable",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1197",
"sqKey": "S1197",
"scope": "All",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/vbnet/S122.html
================================================
Putting multiple statements on a single line lowers the code readability and makes debugging the code more complex.
Dim a = 0 : Dim b = 0 ' Noncompliant
Write one statement per line to improve readability.
Dim a = 0 Dim b = 0================================================ FILE: analyzers/rspec/vbnet/S122.json ================================================ { "title": "Statements should be on separate lines", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "convention" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-122", "sqKey": "S122", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1226.html ================================================
While it is technically correct to assign to parameters from within method bodies, doing so before the parameter value is read is likely a bug.
Instead, initial values of parameters should be, if not treated as readonly then at least read before reassignment.
Module Module1
Sub Foo(ByVal a As Integer)
a = 42 ' Noncompliant
End Sub
End Module
Module Module1
Sub Foo(ByVal a As Integer)
Dim tmp = a
tmp = 42
End Sub
End Module
ByRef parameters are ignored.
Module Module1
Sub Foo(ByRef a As Integer)
a = 42 ' Ignored; it is a ByRef parameter
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1226.json
================================================
{
"title": "Method parameters and caught exceptions should not be reassigned",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1226",
"sqKey": "S1226",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S126.html
================================================
This rule applies whenever an If statement is followed by one or more ElseIf statements; the final ElseIf
should be followed by an Else statement.
The requirement for a final Else statement is defensive programming.
The Else statement should either take appropriate action or contain a suitable comment as to why no action is taken. This is
consistent with the requirement to have a final Case Else clause in a Select Case statement.
If x = 0 Then
DoSomething()
ElseIf x = 1 Then
DoSomethingElse()
End If
If x = 0 Then
DoSomething()
ElseIf x = 1 Then
DoSomethingElse()
Else
Throw New ArgumentException("...")
End If
None
================================================ FILE: analyzers/rspec/vbnet/S126.json ================================================ { "title": "\"If ... ElseIf\" constructs should end with \"Else\" clauses", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-126", "sqKey": "S126", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1301.html ================================================switch statements are useful when there are many different cases depending on the value of the same expression.
For just one or two cases, however, the code will be more readable with if statements.
Select Case variable
Case 0
doSomething()
Case Else
doSomethingElse()
End Select
If variable = 0 Then
doSomething()
Else
doSomethingElse()
End If
================================================
FILE: analyzers/rspec/vbnet/S1301.json
================================================
{
"title": "\"Select\" statements should have at least 3 \"Case\" clauses",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"bad-practice"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1301",
"sqKey": "S1301",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S131.html
================================================
The requirement for a final Case Else clause is defensive programming.
This clause should either take appropriate action or contain a suitable comment as to why no action is taken.
Select Case param ' Noncompliant - Case Else clause is missing
Case 0
DoSomething()
Case 1
DoSomethingElse()
End Select
Select Case param
Case 0
DoSomething()
Case 1
DoSomethingElse()
Case Else ' Compliant
DoSomethingElse()
End Select
Hardcoding IP addresses is security-sensitive. It has led in the past to the following vulnerabilities:
Today’s services have an ever-changing architecture due to their scaling and redundancy needs. It is a mistake to think that a service will always have the same IP address. When it does change, the hardcoded IP will have to be modified too. This will have an impact on the product development, delivery, and deployment:
Last but not least it has an effect on application security. Attackers might be able to decompile the code and thereby discover a potentially sensitive address. They can perform a Denial of Service attack on the service, try to get access to the system, or try to spoof the IP address to bypass security checks. Such attacks can always be possible, but in the case of a hardcoded IP address solving the issue will take more time, which will increase an attack’s impact.
The disclosed IP address is sensitive, e.g.:
There is a risk if you answered yes to any of these questions.
Don’t hard-code the IP address in the source code, instead make it configurable with environment variables, configuration files, or a similar approach. Alternatively, if confidentially is not required a domain name can be used since it allows to change the destination quickly without having to rebuild the software.
Dim ip = "192.168.12.42" ' Sensitive Dim address = IPAddress.Parse(ip)
Dim ip = ConfigurationManager.AppSettings("myapplication.ip") ' Compliant
Dim address = IPAddress.Parse(ip)
No issue is reported for the following cases because they are not considered sensitive:
2.5.<number>.<number> as they often match
Object Identifiers (OID)Nested control flow statements If, Select, For, For Each, While, Do,
and Try are often key ingredients in creating what’s known as "Spaghetti code". This code smell can make your program difficult to
understand and maintain.
When numerous control structures are placed inside one another, the code becomes a tangled, complex web. This significantly reduces the code’s readability and maintainability, and it also complicates the testing process.
The following example demonstrates the behavior of the rule with the default threshold of 3 levels of nesting:
If condition1 ' Compliant - depth = 1
' ...
If condition2 ' Compliant - depth = 2
' ...
For i = 0 to 10 ' Compliant - depth = 3, not exceeding the limit
' ...
If condition4 ' Noncompliant - depth = 4
If condition5 ' Depth = 5, exceeding the limit, but issues are only reported on depth = 4
' ...
End If
Return
End If
Next
End If
End If
================================================
FILE: analyzers/rspec/vbnet/S134.json
================================================
{
"title": "Control flow statements \"If\", \"For\", \"For Each\", \"Do\", \"While\", \"Select\" and \"Try\" should not be nested too deeply",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-134",
"sqKey": "S134",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S138.html
================================================
A procedure that grows too large tends to aggregate too many responsibilities.
Such procedures inevitably become harder to understand and therefore harder to maintain.
Above a specific threshold, it is strongly advised to refactor into smaller procedures which focus on well-defined tasks.
Those smaller procedures will not only be easier to understand but also probably easier to test.
================================================ FILE: analyzers/rspec/vbnet/S138.json ================================================ { "title": "Procedures should not have too many lines of code", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "20min" }, "tags": [ "brain-overload" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-138", "sqKey": "S138", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S139.html ================================================This rule verifies that single-line comments are not located at the ends of lines of code. The main idea behind this rule is that in order to be really readable, trailing comments would have to be properly written and formatted (correct alignment, no interference with the visual structure of the code, not too long to be visible) but most often, automatic code formatters would not handle this correctly: the code would end up less readable. Comments are far better placed on the previous empty line of code, where they will always be visible and properly formatted.
With the default comment pattern ^'\s*\S+\s*$, which ignores single word comments:
Module Module1
Sub Main()
Console.WriteLine("Hello, world!") ' Noncompliant - My first program!
Console.WriteLine("Hello, world!") ' CompliantOneWord
End Sub
End Module
Module Module1
Sub Main()
' Compliant - My first program!
Console.WriteLine("Hello, world!")
Console.WriteLine("Hello, world!") ' CompliantOneWord
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S139.json
================================================
{
"title": "Comments should not be located at the end of lines of code",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-139",
"sqKey": "S139",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1451.html
================================================
Each source file should start with a header stating file ownership and the license which must be used to distribute the application.
This rule must be fed with the header text that is expected at the beginning of every file.
The headerFormat must end with an empty line if you want to have an empty line between the file header and the first line for your
source file (using, namespace…).
For example, if you want the source file to look like this
' Copyright (c) SonarSource. All Rights Reserved. Licensed under the LGPL License. See License.txt in the project root for license information.
namespace Foo
{
}
then the headerFormat parameter should end with an empty line like this
' Copyright (c) SonarSource. All Rights Reserved. Licensed under the LGPL License. See License.txt in the project root for license information.
/* * SonarQube, open source software quality management tool. * Copyright (C) 2008-2013 SonarSource * mailto:contact AT sonarsource DOT com * * SonarQube is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * SonarQube is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */================================================ FILE: analyzers/rspec/vbnet/S1451.json ================================================ { "title": "Track lack of copyright and license headers", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "BLOCKER" }, "attribute": "LAWFUL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "convention" ], "defaultSeverity": "Blocker", "ruleSpecification": "RSPEC-1451", "sqKey": "S1451", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1479.html ================================================
When Select Case statements
have large sets of multi-line Case clauses, the code becomes hard to read and maintain.
For example, the Cognitive Complexity is going to be particularly high.
In such scenarios, it’s better to refactor the Select Case to only have single-line case clauses.
When all the Case clauses of a Select Case statement are single-line, the readability of the code is not affected.
This rule ignores:
Select Case statements over Enum argumentsReturn and Throw statements in Case clausesExtract the logic of multi-line Case clauses into separate methods.
The examples below use the "Maximum number of case" property set to 4.
Public Function MapChar(ch As Char, value As Integer) As Integer ' Noncompliant
Select Case ch
Case "a"c
Return 1
Case "b"c
Return 2
Case "c"c
Return 3
' ...
Case "-"c
If value > 10 Then
Return 42
ElseIf value < 5 AndAlso value > 1 Then
Return 21
End If
Return 99
Case Else
Return 1000
End Select
End Function
Public Function MapChar(ch As Char, value As Integer) As Integer
Select Case ch
Case "a"c
Return 1
Case "b"c
Return 2
Case "c"c
Return 3
' ...
Case "-"c
Return HandleDash(value)
Case Else
Return 1000
End Select
End Function
Private Function HandleDash(value As Integer) As Integer
If value > 10 Then
Return 42
ElseIf value < 5 AndAlso value > 1 Then
Return 21
End If
Return 99
End Function
An unused local variable is a variable that has been declared but is not used anywhere in the block of code where it is defined. It is dead code, contributing to unnecessary complexity and leading to confusion when reading the code. Therefore, it should be removed from your code to maintain clarity and efficiency.
Having unused local variables in your code can lead to several issues:
In summary, unused local variables can make your code less readable, more confusing, and harder to maintain, and they can potentially lead to bugs or inefficient memory use. Therefore, it is best to remove them.
Unused locally created resources in a Using statement are not reported.
Using t = New TestTimer() End Using
The fix for this issue is straightforward. Once you ensure the unused variable is not part of an incomplete implementation leading to bugs, you just need to remove it.
Public Function NumberOfMinutes(ByVal hours As Integer) As Integer
Dim seconds As Integer = 0 ' Noncompliant - seconds is unused
Return hours * 60
End Function
Public Function NumberOfMinutes(ByVal hours As Integer) As Integer
Return hours * 60
End Function
================================================
FILE: analyzers/rspec/vbnet/S1481.json
================================================
{
"title": "Unused local variables should be removed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"unused"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1481",
"sqKey": "S1481",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1541.html
================================================
The cyclomatic complexity of a function, procedure or property should not exceed a defined threshold. Complex code can perform poorly and will in any case be difficult to understand and therefore to maintain.
================================================ FILE: analyzers/rspec/vbnet/S1541.json ================================================ { "title": "Functions, procedures and properties should not be too complex", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "FOCUSED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "brain-overload" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-1541", "sqKey": "S1541", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1542.html ================================================Shared naming conventions allow teams to collaborate efficiently. This rule checks that all subroutine and function names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
EventArgs second parameter are not covered by this rule.With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
Module Module1
Sub bad_subroutine() ' Noncompliant
End Sub
Public Function Bad_Function() As Integer ' Noncompliant
Return 42
End Function
End Module
Module Module1
Sub GoodSubroutine() ' Compliant
End Sub
Public Function GoodFunction() As Integer ' Compliant
Return 42
End Function
End Module
================================================
FILE: analyzers/rspec/vbnet/S1542.json
================================================
{
"title": "Functions and procedures should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"convention"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1542",
"sqKey": "S1542",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1643.html
================================================
StringBuilder is more efficient than string concatenation, especially when the operator is repeated over and over as in loops.
Module Module1
Sub Main()
Dim foo = ""
foo &= "Result: " ' Compliant - outside of loop
For i = 1 To 9
foo &= i ' Noncompliant
Next
End Sub
End Module
Module Module1
Sub Main()
Dim foo = New System.Text.StringBuilder
foo.Append("Result: ") ' Compliant
For i = 1 To 9
foo.Append(i) ' Compliant
Next
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1643.json
================================================
{
"title": "Strings should not be concatenated using \"+\" or \"\u0026\" in a loop",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1643",
"sqKey": "S1643",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1645.html
================================================
Consistently using the & operator for string concatenation make the developer intentions clear.
&, unlike +, will convert its operands to strings and perform an actual concatenation.
+ on the other hand can be an addition, or a concatenation, depending on the operand types.
Module Module1
Sub Main()
Console.WriteLine("1" + 2) ' Noncompliant - will display "3"
End Sub
End Module
Module Module1
Sub Main()
Console.WriteLine(1 & 2) ' Compliant - will display "12"
Console.WriteLine(1 + 2) ' Compliant - but will display "3"
Console.WriteLine("1" & 2) ' Compliant - will display "12"
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1645.json
================================================
{
"title": "The \"\u0026\" operator should be used to concatenate strings",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"suspicious"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-1645",
"sqKey": "S1645",
"scope": "Main",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/vbnet/S1654.html
================================================
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate.
This rule allows to check that all parameter names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
Module Module1
Sub GetSomething(ByVal ID As Integer) ' Noncompliant
End Sub
End Module
Module Module1
Sub GetSomething(ByVal id As Integer) ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S1654.json
================================================
{
"title": "Method parameters should follow a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-1654",
"sqKey": "S1654",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1656.html
================================================
There is no reason to re-assign a variable to itself. Either this statement is redundant and should be removed, or the re-assignment is a mistake and some other value or variable was intended for the assignment instead.
Public Sub SetName(name As String) name = name ' Noncompliant End Sub
Public Sub SetName(name As String) Me.name = name ' Compliant End Sub================================================ FILE: analyzers/rspec/vbnet/S1656.json ================================================ { "title": "Variables should not be self-assigned", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "LOGICAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "3min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-1656", "sqKey": "S1656", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1659.html ================================================
Declaring multiple variable on one line is difficult to read.
Module Module1 Public Const AAA As Integer = 5, BBB = 42, CCC As String = "foo" ' Noncompliant End Module
Module Module1 Public Const AAA As Integer = 5 Public Const BBB = 42 Public Const CCC as String = "foo" End Module================================================ FILE: analyzers/rspec/vbnet/S1659.json ================================================ { "title": "Multiple variables should not be declared on the same line", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "FORMATTED" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1659", "sqKey": "S1659", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/vbnet/S1751.html ================================================
A loop
statement with at most one iteration is equivalent to an If statement; the following block is executed only once.
If the initial intention was to conditionally execute the block only once, an If statement should be used instead. If that was not the
initial intention, the block of the loop should be fixed so the block is executed multiple times.
A loop statement with at most one iteration can happen when a statement unconditionally transfers control, such as a jump statement or a throw statement, is misplaced inside the loop block.
This rule raises when the following statements are misplaced:
Public Function Method(items As IEnumerable(Of Object)) As Object
For i As Integer = 0 To 9
Console.WriteLine(i)
Exit For ' Noncompliant: loop only executes once
Next
For Each item As Object In items
Return item ' Noncompliant: loop only executes once
Next
Return Nothing
End Function
Public Function Method(items As IEnumerable(Of Object)) As Object
For i As Integer = 0 To 9
Console.WriteLine(i)
Next
Dim item = items.FirstOrDefault()
If item IsNot Nothing Then
Return item
End If
Return Nothing
End Function
Exit
Statement (Visual Basic)Continue Statement
(Visual Basic)Return Statement (Visual
Basic)Throw Statement (Visual
Basic)Using the same value on either side of a binary operator is almost always a mistake. In the case of logical operators, it is either a copy/paste error and therefore a bug, or it is simply wasted code, and should be simplified. In the case of most binary mathematical operators, having the same value on both sides of an operator yields predictable results, and should be simplified.
This rule ignores *, +, &, <<, and >>.
If (a = a) Then doZ() End If If a = b OrElse a = b Then doW() End If Dim j = 5 / 5 j = 5 \ 5 j = 5 Mod 5 Dim k = 5 - 5 Dim i = 42 i /= i i -= i
This rule ignores *, +, and =.
=.Nested Select Case structures are difficult to understand because you can easily confuse the cases of an inner Select
Case as belonging to an outer statement. Therefore nested Select Case statements should be avoided.
Specifically, you should structure your code to avoid the need for nested Select Case statements, but if you cannot, then consider
moving the inner Select Case to another function.
Public Sub Foo(A As Integer, B As Integer)
Select Case A
Case 0
' ...
Case 1
Select Case B ' Noncompliant; nested Select Case
Case 2
' ...
Case 3
' ...
Case 4
' ...
Case Else
' ...
End Select
Case 2
' ...
Case Else
' ...
End Select
End Sub
Public Sub Foo(A As Integer, B As Integer)
Select Case A
Case 0
' ...
Case 1
HandleB(B)
Case 2
' ...
Case Else
' ...
End Select
End Sub
================================================
FILE: analyzers/rspec/vbnet/S1821.json
================================================
{
"title": "\"Select Case\" statements should not be nested",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-1821",
"sqKey": "S1821",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S1862.html
================================================
A chain of If/ElseIf
statements is evaluated from top to bottom. At most, only one branch will be executed: the first statement with a condition that evaluates to
True. Therefore, duplicating a condition leads to unreachable code inside the duplicated condition block. Usually, this is due to a
copy/paste error.
The result of such duplication can lead to unreachable code or even to unexpected behavior.
If param = 1 Then OpenWindow() ElseIf param = 2 Then CloseWindow() ElseIf param = 1 Then ' Noncompliant: condition has already been checked MoveWindowToTheBackground() ' unreachable code End If
If param = 1 Then OpenWindow() ElseIf param = 2 Then CloseWindow() ElseIf param = 3 Then MoveWindowToTheBackground() End If
When the same code is duplicated in two or more separate branches of a conditional, it can make the code harder to understand, maintain, and can potentially introduce bugs if one instance of the code is changed but others are not.
Having two Cases in the same Select statement or branches in the same If structure with the same
implementation is at best duplicate code, and at worst a coding error.
If a >= 0 AndAlso a < 10 Then DoFirst() DoTheThing() ElseIf a >= 10 AndAlso a < 20 Then DoTheOtherThing() ElseIf a >= 20 AndAlso a < 50 ' Noncompliant; duplicates first condition DoFirst() DoTheThing() Else DoTheRest(); End If
Select i
Case 1
DoFirst()
DoSomething()
Case 2
DoSomethingDifferent()
Case 3 ' Noncompliant; duplicates case 1's implementation
DoFirst()
DoSomething()
Case Else:
DoTheRest()
End Select
If the same logic is needed for both instances, then:
If structure they should be combinedIf (a >= 0 AndAlso a < 10) OrElse (a >= 20 AndAlso a < 50) Then DoFirst() DoTheThing() ElseIf a >= 10 AndAlso a < 20 Then DoTheOtherThing() Else DoTheRest(); End If
Select, the values should be put in the Case expression list.
Select i
Case 1, 3
DoFirst()
DoSomething()
Case 2
DoSomethingDifferent()
Case Else:
DoTheRest()
End Select
Blocks in an If chain or Case clause that contain a single line of code are ignored.
If a >= 0 AndAlso a < 10 Then DoTheThing() ElseIf a >= 10 AndAlso a < 20 Then DoTheOtherThing() ElseIf a >= 20 AndAlso a < 50 ' no issue, usually this is done on purpose to increase the readability DoTheThing() End If
But this exception does not apply to If chains without Else-s, or to Select-s without Case Else
clauses when all branches have the same single line of code. In the case of If chains with Else-s, or of
Select-es with Case Else clauses, rule {rule:vbnet:S3923} raises a bug.
If a == 1 Then DoTheThing() ' Noncompliant, this might have been done on purpose but probably not ElseIf a == 2 Then DoTheThing() End If
It is needlessly complex to invert the result of a boolean comparison. The opposite comparison should be made instead.
If Not (a = 2) Then // Noncompliant Dim b as Boolean = Not (i < 10) // Noncompliant
If a <> 2 Then Dim b as Boolean = i >= 10================================================ FILE: analyzers/rspec/vbnet/S1940.json ================================================ { "title": "Boolean checks should not be inverted", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "pitfall" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-1940", "sqKey": "S1940", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S1944.html ================================================
A cast is an explicit conversion, which is a way to tell the compiler the intent to convert from one type to another.
In Visual Basic, there are two explicit conversion operators:
Public Sub Method(Value As Object)
Dim i As Integer
i = DirectCast(Value, Integer) ' Direct casting from object holding an integer type to Integer
i = CType(Value, Integer) ' Conversion from the underlying type to Integer
End Sub
In most cases, the compiler will be able to catch invalid casts between incompatible value types or reference types.
However, the compiler will not be able to detect invalid casts to interfaces.
Invalid casts will lead to unexpected behaviors or runtime errors such as InvalidCastException.
No issue is reported if the interface has no implementing class in the assembly.
To prevent an InvalidCastException from raising during an explicit conversion, it is recommended to use the TryCast operator. When the
conversion is not possible, the TryCast operator returns Nothing and will never raise an exception.
Public Interface IMyInterface
End Interface
Public Class Implementer
Implements IMyInterface
End Class
Public Class AnotherClass
End Class
Module Program
Sub Main()
Dim Another As New AnotherClass
Dim x As IMyInterface = DirectCast(Another, IMyInterface) ' Noncompliant: InvalidCastException is being thrown
End Sub
End Module
Public Interface IMyInterface
End Interface
Public Class Implementer
Implements IMyInterface
End Class
Public Class AnotherClass
End Class
Module Program
Sub Main()
Dim Another As New AnotherClass
Dim x = TryCast(Another, IMyInterface) ' Compliant: but will always be Nothing
End Sub
End Module
DirectCast
operatorCType functionTryCast
operatorThis vulnerability increases the likelihood that attackers are able to compute the cleartext of password hashes.
During the process of password hashing, an additional component, known as a "salt," is often integrated to bolster the overall security. This salt, acting as a defensive measure, primarily wards off certain types of attacks that leverage pre-computed tables to crack passwords.
However, potential risks emerge when the salt is deemed insecure. This can occur when the salt is consistently the same across all users or when it is too short or predictable. In scenarios where users share the same password and salt, their password hashes will inevitably mirror each other. Similarly, a short salt heightens the probability of multiple users unintentionally having identical salts, which can potentially lead to identical password hashes. These identical hashes streamline the process for potential attackers to recover clear-text passwords. Thus, the emphasis on implementing secure, unique, and sufficiently lengthy salts in password-hashing functions is vital.
Despite best efforts, even well-guarded systems might have vulnerabilities that could allow an attacker to gain access to the hashed passwords. This could be due to software vulnerabilities, insider threats, or even successful phishing attempts that give attackers the access they need.
Once the attacker has these hashes, they will likely attempt to crack them using a couple of methods. One is brute force, which entails trying every possible combination until the correct password is found. While this can be time-consuming, having the same salt for all users or a short salt can make the task significantly easier and faster.
If multiple users have the same password and the same salt, their password hashes would be identical. This means that if an attacker successfully cracks one hash, they have effectively cracked all identical ones, granting them access to multiple accounts at once.
A short salt, while less critical than a shared one, still increases the odds of different users having the same salt. This might create clusters of password hashes with identical salt that can then be attacked as explained before.
With short salts, the probability of a collision between two users' passwords and salts couple might be low depending on the salt size. The shorter the salt, the higher the collision probability. In any case, using longer, cryptographically secure salt should be preferred.
To securely store password hashes, it is a recommended to rely on key derivation functions that are computationally intensive. Examples of such functions are:
When they are used for password storage, using a secure, random salt is required.
However, those functions can also be used for other purposes such as master key derivation or password-based pre-shared key generation. In those cases, the implemented cryptographic protocol might require using a fixed salt to derive keys in a deterministic way. In such cases, using a fixed salt is safe and accepted.
The following code contains examples of hard-coded salts.
Imports System.Security.Cryptography
Public Sub Hash(Password As String)
Dim Salt As Byte() = Encoding.UTF8.GetBytes("salty")
Dim Hashed As New Rfc2898DeriveBytes(Password, Salt) ' Noncompliant
End Sub
Imports System.Security.Cryptography
Public Sub Hash(Password As String)
Dim Hashed As New Rfc2898DeriveBytes(Password, 16, 10000, HashAlgorithmName.SHA256)
End Sub
This code ensures that each user’s password has a unique salt value associated with it. It generates a salt randomly and with a length that provides the required security level. It uses a salt length of at least 16 bytes (128 bits), as recommended by industry standards.
In the case of the code sample, the class automatically takes care of generating a secure salt if none is specified.
Hard-coding credentials in source code or binaries makes it easy for attackers to extract sensitive information, especially in distributed or open-source applications. This practice exposes your application to significant security risks.
This rule flags instances of hard-coded credentials used in database and LDAP connections. It looks for hard-coded credentials in connection strings, and for variable names that match any of the patterns from the provided list.
In the past, it has led to the following vulnerabilities:
Credentials should be stored in a configuration file that is not committed to the code repository, in a database, or managed by your cloud provider’s secrets management service. If a password is exposed in the source code, it must be changed immediately.
Dim username As String = "admin" Dim password As String = "Password123" ' Noncompliant Dim usernamePassword As String = "user=admin&password=Password123" ' Noncompliant Dim url As String = "scheme://user:Admin123@domain.com" ' Noncompliant
Dim username As String = "admin"
Dim password As String = GetEncryptedPassword()
Dim usernamePassword As String = String.Format("user={0}&password={1}", GetEncryptedUsername(), GetEncryptedPassword())
Dim url As String = $"scheme://{username}:{password}@domain.com"
Dim url2 As String= "http://guest:guest@domain.com" ' Compliant
Const Password_Property As String = "custom.password" ' Compliant
Formatted SQL queries can be difficult to maintain, debug and can increase the risk of SQL injection when concatenating untrusted values into the query. However, this rule doesn’t detect SQL injections, the goal is only to highlight complex/formatted queries.
There is a risk if you answered yes to any of those questions.
Public Sub SqlCommands(ByVal connection As SqlConnection, ByVal query As String, ByVal param As String)
Dim sensitiveQuery As String = String.Concat(query, param)
command = New SqlCommand(sensitiveQuery) ' Sensitive
command.CommandText = sensitiveQuery ' Sensitive
Dim adapter As SqlDataAdapter
adapter = New SqlDataAdapter(sensitiveQuery, connection) ' Sensitive
End Sub
Public Sub Foo(ByVal context As DbContext, ByVal query As String, ByVal param As String)
Dim sensitiveQuery As String = String.Concat(query, param)
context.Database.ExecuteSqlCommand(sensitiveQuery) ' Sensitive
context.Query(Of User)().FromSql(sensitiveQuery) ' Sensitive
End Sub
Public Sub Foo(ByVal context As DbContext, ByVal value As String)
context.Database.ExecuteSqlCommand("SELECT * FROM mytable WHERE mycol=@p0", value) ' Compliant, the query is parameterized
End Sub
There is no good excuse for an empty class. If it’s being used simply as a common extension point, it should be replaced with an
interface. If it was stubbed in as a placeholder for future development it should be fleshed-out. In any other case, it should be
eliminated.
Public Class Empty ' Noncompliant End Class
Public Interface IEmpty End Interface
Command, Message, Event, or Query are ignored as messaging
libraries often use them.System.Exception are ignored; even an empty Exception class can provide helpful information by its type name
alone.System.Attribute and classes annotated with attributes are ignored.PageModel class used in ASP.NET Core Razor Pages — are ignored.
Imports Microsoft.AspNetCore.Mvc.RazorPages
Public Class EmptyPageModel ' Compliant - an empty PageModel can be fully functional, the VB code can be in the vbhtml file
Inherits PageModel
End Class
================================================
FILE: analyzers/rspec/vbnet/S2094.json
================================================
{
"title": "Classes should not be empty",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2094",
"sqKey": "S2094",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2166.html
================================================
Clear, communicative naming is important in code. It helps maintainers and API users understand the intentions for and uses of a unit of code.
Using "exception" in the name of a class that does not extend Exception or one of its subclasses is a clear violation of the expectation
that a class' name will indicate what it is and/or does.
Public Class FruitException ' Noncompliant - this has nothing to do with Exception
Private expected As Fruit
Private unusualCharacteristics As String
Private appropriateForCommercialExploitation As Boolean
' ...
End Class
Public Class CarException ' Noncompliant - does not derive from any Exception-based class
Public Sub New(message As String, inner As Exception)
' ...
End Sub
End Class
Public Class FruitSport ' Compliant - class name does not end with 'Exception'
Private expected As Fruit
Private unusualCharacteristics As String
Private appropriateForCommercialExploitation As Boolean
' ...
End Class
Public Class CarException Inherits Exception ' Compliant - correctly extends System.Exception
Public Sub New(message As String, inner As Exception)
MyBase.New(message, inner)
' ...
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S2166.json
================================================
{
"title": "Classes named like \"Exception\" should extend \"Exception\" or a subclass",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention",
"error-handling",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2166",
"sqKey": "S2166",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2178.html
================================================
Short-circuit evaluation is an evaluation strategy for Boolean operators, that doesn’t evaluate the second argument of the operator if it is not needed to determine the result of the operation.
VB.NET provides logical operators that implement short-circuiting evaluations AndAlso and OrElse, as well as the
non-short-circuiting versions And and Or. Unlike short-circuiting operators, the non-short-circuiting operators evaluate
both operands and afterward perform the logical operation.
For example False AndAlso FunctionCall always results in False even when the FunctionCall invocation would
raise an exception. In contrast, False And FunctionCall also evaluates FunctionCall, and results in an exception if
FunctionCall raises an exception.
Similarly, True OrElse FunctionCall always results in True, no matter what the return value of FunctionCall
would be.
The use of non-short-circuit logic in a boolean context is likely a mistake, one that could cause serious program errors as conditions are evaluated under the wrong circumstances.
If GetTrue() Or GetFalse() Then ' Noncompliant: both sides evaluated End If
If GetTrue() OrElse GetFalse() Then ' Compliant: short-circuit logic used End If
To prevent potential deadlocks in an application, it is crucial to release any locks that are acquired within a method along all possible execution paths.
Failing to release locks properly can lead to potential deadlocks, where the lock might not be released, causing issues in the application.
This rule specifically focuses on tracking the following types from the System.Threading namespace:
An issue is reported when a lock is acquired within a method but not released on all paths.
If the lock is never released within the method, no issue is raised, assuming that the callers will handle the release.
To make sure that a lock is always released correctly, you can follow one of these two methods:
SyncLock
statement with your lock object.Try-Finally
statement and put the release of your lock object within a Finally block.
Class Example
Private obj As Object = New Object()
Public Sub DoSomethingWithMonitor()
Monitor.Enter(obj) ' Noncompliant: not all paths release the lock
If IsInitialized() Then
' ...
Monitor.Exit(obj)
End If
End Sub
End Class
Class Example
Private lockObj As ReaderWriterLockSlim = New ReaderWriterLockSlim()
Public Sub DoSomethingWithReaderWriteLockSlim()
lockObj.EnterReadLock() ' Noncompliant: not all paths release the lock
If IsInitialized() Then
' ...
lockObj.ExitReadLock()
End If
End Sub
End Class
Class Example
Private obj As Object = New Object()
Public Sub DoSomethingWithMonitor()
SyncLock obj ' Compliant: the lock will be released at the end of the SyncLock block
If IsInitialized() Then
' ...
End If
End SyncLock
End Sub
End Class
Class Example
Private lockObj As ReaderWriterLockSlim = New ReaderWriterLockSlim()
Public Sub DoSomethingWithReaderWriteLockSlim()
lockObj.EnterReadLock() ' Compliant: the lock will be released in the finally block
Try
If IsInitialized() Then
' ...
End If
Finally
lockObj.ExitReadLock()
End Try
End Sub
End Class
SyncLock
statementFinally blockCalling ToString() on an object should always return a
string. Thus, overriding the ToString method should never return Nothing because it breaks the method’s implicit contract,
and as a result the consumer’s expectations.
Public Overrides Function ToString() As String
Return Nothing ' Noncompliant
End Function
Public Overrides Function ToString() As String
Return ""
End Function
Calling a procedure with argument variables whose names match the procedure parameter names but in a different order can cause confusion. It could indicate a mistake in the arguments' order, leading to unexpected results.
Public Function Divide(divisor As Integer, dividend As Integer) As Double
Return divisor / dividend
End Function
Public Sub DoTheThing()
Dim divisor = 15
Dim dividend = 5
Dim result = Divide(dividend, divisor) ' Noncompliant: arguments' order doesn't match their respective parameter names
'...
End Sub
However, matching the procedure parameters' order contributes to clearer and more readable code:
Public Function Divide(divisor As Integer, dividend As Integer) As Double
Return divisor / dividend
End Function
Public Sub DoTheThing()
Dim divisor = 15
Dim dividend = 5
Dim result = Divide(divisor, dividend) ' Compliant
'...
End Sub
================================================
FILE: analyzers/rspec/vbnet/S2234.json
================================================
{
"title": "Arguments should be passed in the same order as the procedure parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2234",
"sqKey": "S2234",
"scope": "All",
"quickfix": "targeted"
}
================================================
FILE: analyzers/rspec/vbnet/S2257.html
================================================
The use of a non-standard algorithm is dangerous because a determined attacker may be able to break the algorithm and compromise whatever data has
been protected. Standard algorithms like AES, RSA, SHA, … should be used instead.
This rule tracks custom implementation of these types from System.Security.Cryptography namespace:
AsymmetricAlgorithmAsymmetricKeyExchangeDeformatterAsymmetricKeyExchangeFormatterAsymmetricSignatureDeformatterAsymmetricSignatureFormatterDeriveBytesHashAlgorithmICryptoTransformSymmetricAlgorithm
Public Class CustomHash ' Noncompliant
Inherits HashAlgorithm
Private fResult() As Byte
Public Overrides Sub Initialize()
fResult = Nothing
End Sub
Protected Overrides Function HashFinal() As Byte()
Return fResult
End Function
Protected Overrides Sub HashCore(array() As Byte, ibStart As Integer, cbSize As Integer)
fResult = If(fResult, array.Take(8).ToArray)
End Sub
End Class
Dim mySHA256 As SHA256 = SHA256.Create()
Accessing a Nothing value will always throw a NullReferenceException most likely causing an abrupt program termination.
Such termination might expose sensitive information that a malicious third party could exploit to, for instance, bypass security measures.
In the following cases, the rule does not raise:
Calls to extension methods can still operate on Nothing values.
Imports System.Diagnostics.CodeAnalysis
Imports System.Runtime.CompilerServices
Imports System.Text.RegularExpressions
Module Program
<Extension>
Function RemoveVowels(Value As String) As String
If Value Is Nothing Then
Return Nothing
End If
Return Regex.Replace(Value, "[aeoui]*", "", RegexOptions.IgnoreCase)
End Function
Sub Main()
Dim StrValue As String = Nothing
Console.WriteLine(StrValue.RemoveVowels()) ' Compliant: 'RemoveVowels' is an extension method
End Sub
End Module
Unreachable code is not executed, thus Nothing values will never be accessed.
Public Sub Method()
Dim o As Object = Nothing
If False Then
o.ToString() ' Compliant: code is unreachable
End If
End Sub
Nullable analysis attributes enable
the developer to annotate methods with information about the null-state of its arguments. Thus, potential Nothing values validated by one
of the following attributes will not raise:
It is important to note those attributes are only available starting .NET Core 3. As a workaround, it is possible to define those attributes manually in a custom class:
Public NotInheritable Class NotNullAttribute ' The alternative name 'ValidatedNotNullAttribute' is also supported
Inherits Attribute
End Class
Public Module Guard
Public Sub CheckNotNull(Of T)(<NotNull> Value As T, Name As String)
If Value Is Nothing Then Throw New ArgumentNullException(Name)
End Sub
End Module
Public Module Utils
Public Function Normalize(Value As String) As String
CheckNotNull(Value, nameof(Value)) ' Will throw if 'Value' is Nothing
Return Value.ToUpper() ' Compliant: value is known to be not Nothing here
End Function
End Module
A value validated with Debug.Assert to not be
Nothing is safe to access.
Imports System.Diagnostics
Public Sub Method(MyObject As Object)
Debug.Assert(MyObject IsNot Nothing)
MyObject.ToString() ' Compliant: 'MyObject' is known to be not Nothing here.
End Sub
Like with null-analysis-attribute, potential Nothing values validated by one of the following IDE-specific attributes will not
raise
Imports System
Imports JetBrains.Annotations
Public Class Utils
<TerminatesProgram>
Public Sub TerminateProgram()
Environment.FailFast("A catastrophic failure has occurred.")
End Sub
Public Sub Method()
Dim MyObject As Object = Nothing
TerminateProgram()
MyObject.ToString() ' Compliant: unreachable
End Sub
End Class
To fix the issue, the access of the Nothing value needs to be prevented by either:
NothingThe variable MyObject is equal to Nothing, meaning it has no value:
Public Sub Method()
Dim MyObject As Object = Nothing
Console.WriteLine(MyObject.ToString) ' Noncompliant: 'MyObject' is always Nothing
End Sub
The parameter Input might be Nothing as suggested by the if condition:
Public Sub Method(Input As Object)
If Input Is Nothing Then
' ...
End If
Console.WriteLine(Input.ToString) ' Noncompliant: 'Input' might be Nothing
End Sub
Ensuring the variable MyObject has a value resolves the issue:
Public Sub Method()
Dim MyObject As New Object
Console.WriteLine(MyObject.ToString) ' Compliant: 'MyObject' is not Nothing
End Sub
Preventing the non-compliant code to be executed by returning early:
Public Sub Method(Input As Object)
If Input Is Nothing Then
Return
End If
Console.WriteLine(Input.ToString) ' Compliant: if 'Input' is Nothing, this part is unreachable
End Sub
Because parameter names could be changed during refactoring, they should not be spelled out literally in strings. Instead, use
NameOf(), and the string that’s output will always be correct.
This rule raises an issue when any string in the Throw statement is an exact match for the name of one of the method parameters.
Public Sub DoSomething(param As Integer, secondParam As String)
If (param < 0)
Throw New Exception("param") ' Noncompliant
End If
If secondParam is Nothing
Throw New Exception("secondParam should be valid") ' Noncompliant
End If
End Sub
Public Sub DoSomething(param As Integer, secondParam As String)
If (param < 0)
Throw New Exception(NameOf(param))
End If
If secondParam is Nothing
Throw New Exception($"{NameOf(secondParam)} should be valid")
End If
End Sub
Throw statement string, the rule will raise an issue only if the
parameter name is at least 5 characters long. This is to avoid false positives.Shared coding conventions allow teams to collaborate efficiently. This rule checks that all namespace names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression: ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?(\.([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2}))*$
Namespace foo ' Noncompliant End Namespace
Namespace Foo ' Compliant End Namespace================================================ FILE: analyzers/rspec/vbnet/S2304.json ================================================ { "title": "Namespace names should comply with a naming convention", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "IDENTIFIABLE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "10min" }, "tags": [ "convention" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-2304", "sqKey": "S2304", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S2339.html ================================================
Constant members are copied at compile time to the call sites, instead of being fetched at runtime.
As an example, say you have a library with a constant Version member set to 1.0, and a client application linked to it.
This library is then updated and Version is set to 2.0. Unfortunately, even after the old DLL is replaced by the new one,
Version will still be 1.0 for the client application. In order to see 2.0, the client application would need to
be rebuilt against the new version of the library.
This means that you should use constants to hold values that by definition will never change, such as Zero. In practice, those cases
are uncommon, and therefore it is generally better to avoid constant members.
This rule only reports issues on public constant fields, which can be reached from outside the defining assembly.
Public Class Foo
Public Const Version = 1.0 ' Noncompliant
End Class
Public Class Foo
Public Shared ReadOnly Property Version = 1.0 ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2339.json
================================================
{
"title": "Public constant members should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2339",
"sqKey": "S2339",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2340.html
================================================
A Do ... Loop without a While or Until condition must be terminated by an unstructured Exit Do
statement. It is safer and more readable to use structured loops instead.
Module Module1
Sub Main()
Dim i = 1
Do ' Non-Compliant
If i = 10 Then
Exit Do
End If
Console.WriteLine(i)
i = i + 1
Loop
End Sub
End Module
Module Module1
Sub Main()
For i = 1 To 9 ' Compliant
Console.WriteLine(i)
Next
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2340.json
================================================
{
"title": "\"Do\" loops should not be used without a \"While\" or \"Until\" condition",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2340",
"sqKey": "S2340",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2342.html
================================================
Shared naming conventions allow teams to collaborate efficiently. This rule checks that all enum names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression for non-flags enums: ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
Public Enum foo ' Noncompliant
FooValue = 0
End Enum
With the default regular expression for flags enums: ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?s$
<Flags()>
Public Enum Option ' Noncompliant
None = 0,
Option1 = 1,
Option2 = 2
End Enum
Public Enum Foo
FooValue = 0
End Enum
<Flags()>
Public Enum Options
None = 0,
Option1 = 1,
Option2 = 2
End Enum
================================================
FILE: analyzers/rspec/vbnet/S2342.json
================================================
{
"title": "Enumeration types should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2342",
"sqKey": "S2342",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2343.html
================================================
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all enumeration value names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Enum Foo
fooValue ' Noncompliant
End Enum
Enum Foo
FooValue ' Compliant
End Enum
================================================
FILE: analyzers/rspec/vbnet/S2343.json
================================================
{
"title": "Enumeration values should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2343",
"sqKey": "S2343",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2344.html
================================================
The information that an enumeration type is actually an enumeration or a set of flags should not be duplicated in its name.
Enum FooFlags ' Noncompliant
Foo = 1
Bar = 2
Baz = 4
End Enum
Enum Foo ' Compliant
Foo = 1
Bar = 2
Baz = 4
End Enum
================================================
FILE: analyzers/rspec/vbnet/S2344.json
================================================
{
"title": "Enumeration type names should not have \"Flags\" or \"Enum\" suffixes",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2344",
"sqKey": "S2344",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2345.html
================================================
When you annotate an Enum with the Flags attribute, you must not rely on the values that are automatically
set by the language to the Enum members, but you should define the enumeration constants in powers of two (1, 2, 4, 8, and so on).
Automatic value initialization will set the first member to zero and increment the value by one for each subsequent member. As a result, you won’t be
able to use the enum members with bitwise operators.
The default initialization of 0, 1, 2, 3, 4, … matches 0, 1, 2, 4, 8 … in the first three values, so no issue is
reported if the first three members of the enumeration are not initialized.
Define enumeration constants in powers of two, that is, 1, 2, 4, 8, and so on.
<Flags()>
Enum FruitType ' Non-Compliant
None
Banana
Orange
Strawberry
End Enum
Module Module1
Sub Main()
Dim bananaAndStrawberry = FruitType.Banana Or FruitType.Strawberry
Console.WriteLine(bananaAndStrawberry.ToString()) ' Will display only "Strawberry"
End Sub
End Module
<Flags()>
Enum FruitType ' Compliant
None = 0
Banana = 1
Orange = 2
Strawberry = 4
End Enum
Module Module1
Sub Main()
Dim bananaAndStrawberry = FruitType.Banana Or FruitType.Strawberry
Console.WriteLine(bananaAndStrawberry.ToString()) ' Will display "Banana, Strawberry"
End Sub
End Module
An enumeration can be decorated with the FlagsAttribute to indicate that it can be used as a bit field: a set of flags, that can be independently set and reset.
For example, the following definition of the day of the week:
<Flags()>
Enum Days
Monday = 1 ' 0b00000001
Tuesday = 2 ' 0b00000010
Wednesday = 4 ' 0b00000100
Thursday = 8 ' 0b00001000
Friday = 16 ' 0b00010000
Saturday = 32 ' 0b00100000
Sunday = 64 ' 0b01000000
End Enum
allows to define special set of days, such as WeekDays and Weekend using the Or operator:
<Flags()>
Enum Days
' ...
None = 0 ' 0b00000000
Weekdays = Monday Or Tuesday Or Wednesday Or Thursday Or Friday ' 0b00011111
Weekend = Saturday Or Sunday ' 0b01100000
All = Weekdays Or Weekend ' 0b01111111
End Enum
These can be used to write more expressive conditions, taking advantage of bitwise operators and Enum.HasFlag:
Dim someDays = Days.Wednesday | Days.Weekend ' 0b01100100 someDays.HasFlag(Days.Wednesday) ' someDays contains Wednesday Dim mondayAndWednesday = Days.Monday Or Days.Wednesday someDays.HasFlag(mondayAndWednesday) ' someDays contains Monday and Wednesday someDays.HasFlag(Days.Monday) OrElse someDays.HasFlag(Days.Wednesday) ' someDays contains Monday or Wednesday someDays And Days.Weekend <> Days.None ' someDays overlaps with the weekend someDays And Days.Weekdays = Days.Weekdays ' someDays is only made of weekdays
Consistent use of None in flag enumerations indicates that all flag values are cleared. The value 0 should not be used to indicate any
other state since there is no way to check that the bit 0 is set.
<Flags()>
Enum Days
Monday = 0 ' 0 is used to indicate Monday
Tuesday = 1
Wednesday = 2
Thursday = 4
Friday = 8
Saturday = 16
Sunday = 32
Weekdays = Monday Or Tuesday Or Wednesday Or Thursday Or Friday
Weekend = Saturday Or Sunday
All = Weekdays Or Weekend
End Enum
Dim someDays = Days.Wednesday Or Days.Thursday
someDays & Days.Tuesday = Days.Tuesday ' False, because someDays doesn't contains Tuesday
someDays & Days.Monday = Days.Monday ' True, even though someDays doesn't contains Monday!
someDays.HasFlag(Days.Monday) ' Same issue as above
<Flags()>
Enum FruitType
Void = 0 ' Non-Compliant
Banana = 1
Orange = 2
Strawberry = 4
End Enum
<Flags()>
Enum FruitType
None = 0 ' Compliant
Banana = 1
Orange = 2
Strawberry = 4
End Enum
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all even handler names match a provided regular expression.
The default configuration is:
Event handlers with a handles clause and two-parameter methods with EventArgs second parameter are covered by this
rule.
With the default regular expression ^(([a-z][a-z0-9]*)?([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?_)?([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Module Module1
Sub subject__SomeEvent() Handles X.SomeEvent ' Noncompliant - two underscores
End Sub
End Module
Module Module1
Sub subject_SomeEvent() Handles X.SomeEvent ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2347.json
================================================
{
"title": "Event handlers should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2347",
"sqKey": "S2347",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2348.html
================================================
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all even names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Class Foo
Event fooEvent() ' Noncompliant
End Class
Class Foo
Event FooEvent() ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2348.json
================================================
{
"title": "Events should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2348",
"sqKey": "S2348",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2349.html
================================================
"After" and "Before" prefixes or suffixes should not be used to indicate pre and post events. The concepts of before and after should be given to events using the present and past tense.
Class Foo
Event BeforeClose() ' Noncompliant
Event AfterClose() ' Noncompliant
End Class
Class Foo
Event Closing() ' Compliant
Event Closed() ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2349.json
================================================
{
"title": "Event names should not have \"Before\" or \"After\" as a prefix or suffix",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2349",
"sqKey": "S2349",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2352.html
================================================
Indexed properties are meant to represent access to a logical collection. When multiple parameters are required, this design guideline may be violated, and refactoring the property into a method is preferable.
Module Module1
ReadOnly Property Sum(ByVal a As Integer, ByVal b As Integer) ' Noncompliant
Get
Return a + b
End Get
End Property
End Module
Module Module1
Function Sum(ByVal a As Integer, ByVal b As Integer) ' Compliant
Return a + b
End Function
End Module
================================================
FILE: analyzers/rspec/vbnet/S2352.json
================================================
{
"title": "Indexed properties with more than one parameter should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2352",
"sqKey": "S2352",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2354.html
================================================
To improve the code readability, the explicit line continuation character, _, should not be used. Instead, it is better to break lines
after an operator.
Module Module1
Sub Main()
' Noncompliant
Console.WriteLine("Hello" _
& "world")
End Sub
End Module
Module Module1
Sub Main()
Console.WriteLine("Hello" &
"world")
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2354.json
================================================
{
"title": "Line continuations should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2354",
"sqKey": "S2354",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2355.html
================================================
Array literals are more compact than array creation expressions.
Module Module1
Sub Main()
Dim foo = New String() {"a", "b", "c"} ' Noncompliant
End Sub
End Module
Module Module1
Sub Main()
Dim foo = {"a", "b", "c"} ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2355.json
================================================
{
"title": "Array literals should be used instead of array creation expressions",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2355",
"sqKey": "S2355",
"scope": "Main",
"quickfix": "covered"
}
================================================
FILE: analyzers/rspec/vbnet/S2357.html
================================================
Fields should not be part of an API, and therefore should always be private. Indeed, they cannot be added to an interface for instance, and validation cannot be added later on without breaking backward compatibility. Instead, developers should encapsulate their fields into properties. Explicit property getters and setters can be introduced for validation purposes or to smooth the transition to a newer system.
Class Foo
Public Foo = 42 ' Noncompliant
End Class
Class Foo
Public Property Foo = 42 ' Compliant
End Class
Shared and Const fields are ignored.
The ... IsNot ... syntax is more compact and more readable than the Not ... Is ... syntax.
Module Module1
Sub Main()
Dim a = Not "a" Is Nothing ' Noncompliant
End Sub
End Module
Module Module1
Sub Main()
Dim a = "a" IsNot Nothing ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2358.json
================================================
{
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2358",
"sqKey": "S2358",
"scope": "Main",
"quickfix": "covered",
"title": "\"IsNot\" should be used instead of \"Not ... Is ...\""
}
================================================
FILE: analyzers/rspec/vbnet/S2359.html
================================================
Prefer the use of Try ... Catch blocks instead of On Error statements.
Visual Basic .NET and Visual Basic 2005 offer structured exception handling that provides a powerful, more readable alternative to the On
Error Goto error handling from previous versions of Microsoft Visual Basic. Structured exception handling is more powerful because it allows
you to nest error handlers inside other error handlers within the same procedure. Furthermore, structured exception handling uses a block syntax
similar to the If...Else...End If statement. This makes Visual Basic .NET and Visual Basic 2005 code more readable and easier to
maintain.
Sub DivideByZero()
On Error GoTo nextstep
Dim result As Integer
Dim num As Integer
num = 100
result = num / 0
nextstep:
System.Console.WriteLine("Error")
End Sub
Sub DivideByZero()
Try
Dim result As Integer
Dim num As Integer
num = 100
result = num / 0
Catch
System.Console.WriteLine("Error")
End Try
End Sub
================================================
FILE: analyzers/rspec/vbnet/S2359.json
================================================
{
"title": "\"On Error\" statements should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"bad-practice"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2359",
"sqKey": "S2359",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2360.html
================================================
The overloading mechanism should be used in place of optional parameters for several reasons:
Sub Notify(ByVal Company As String, Optional ByVal Office As String = "QJZ") ' Noncompliant End Sub
Sub Notify(ByVal Company As String) Notify(Company, "QJZ") End Sub Sub Notify(ByVal Company As String, ByVal Office As String) End Sub
The rule ignores non externally visible methods.
================================================ FILE: analyzers/rspec/vbnet/S2360.json ================================================ { "title": "Optional parameters should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "HIGH" }, "attribute": "CONVENTIONAL" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "pitfall" ], "defaultSeverity": "Critical", "ruleSpecification": "RSPEC-2360", "sqKey": "S2360", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S2362.html ================================================Shared coding conventions allow teams to collaborate efficiently. This rule checks that all Private Const field names comply with the
provided regular expression.
The default configuration is:
_foo, s_fooWith the default regular expression ^(s_|_)?[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Module Module1
Private Const Foo = 0 ' Noncompliant
End Module
Module Module1
Private Const foo = 0 ' Compliant
End Module
================================================
FILE: analyzers/rspec/vbnet/S2362.json
================================================
{
"title": "Private constants should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2362",
"sqKey": "S2362",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2363.html
================================================
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all Private Shared ReadOnly field names comply
with the provided regular expression.
The default configuration is:
_foo, s_fooWith the default regular expression ^(s_|_)?[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Class Foo
Private Shared ReadOnly Foo As Integer ' Noncompliant
End Class
Class Foo
Private Shared ReadOnly foo As Integer ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2363.json
================================================
{
"title": "\"Private Shared ReadOnly\" fields should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2363",
"sqKey": "S2363",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2364.html
================================================
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all Private field names match the provided
regular expression.
Note that this rule does not apply to Private Shared ReadOnly fields, which are checked by another rule.
The default configuration is:
_foo, s_fooWith the default regular expression ^(s_|_)?[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Class Foo
Private Foo As Integer ' Noncompliant
End Class
Class Foo
Private foo As Integer ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2364.json
================================================
{
"title": "\"Private\" fields should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2364",
"sqKey": "S2364",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2365.html
================================================
Most developers expect property access to be as efficient as field access. However, if a property returns a copy of an array or collection, it will be much slower than a simple field access, contrary to the caller’s likely expectations. Therefore, such properties should be refactored into methods so that callers are not surprised by the unexpectedly poor performance.
This rule tracks calls to the following methods inside properties:
Private fFoo As New List(Of String) From {"a", "b", "c"}
Private fBar As String() = {"a", "b", "c"}
Public ReadOnly Property Foo() As IEnumerable(Of String) ' Noncompliant: collection fFoo is copied
Get
Return fFoo.ToList()
End Get
End Property
Public ReadOnly Property Bar() As IEnumerable(Of String) ' Noncompliant: array fBar is copied
Get
Return DirectCast(fBar.Clone(), String())
End Get
End Property
Private fFoo As New List(Of String) From {"a", "b", "c"}
Private fBar As String() = {"a", "b", "c"}
Public Function GetFoo() As IEnumerable(Of String)
Return fFoo.ToList()
End Function
Public Function GetBar() As IEnumerable(Of String)
Return DirectCast(fBar.Clone(), String())
End Function
Shared coding conventions allow teams to collaborate efficiently. This rule checks that property names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Module Module1
Public Property foo As Integer ' Noncompliant
End Module
Module Module1
Public Property Foo As Integer ' Compliant
End Module
================================================
FILE: analyzers/rspec/vbnet/S2366.json
================================================
{
"title": "Properties should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2366",
"sqKey": "S2366",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2367.html
================================================
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all non-private Const field names comply with
the provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Module Module1
Public Const foo = 0 ' Noncompliant
End Module
Module Module1
Public Const Foo = 0 ' Compliant
End Module
================================================
FILE: analyzers/rspec/vbnet/S2367.json
================================================
{
"title": "Non-private constants should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2367",
"sqKey": "S2367",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2368.html
================================================
Using multidimensional and jagged arrays as method parameters in C# can be challenging for developers.
When these methods are exposed to external users, it requires advanced language knowledge for effective usage.
Determining the appropriate data to pass to these parameters may not be intuitive.
Public Class Program
Public Sub WriteMatrix(matrix As Integer()()) ' Noncompliant: data type is not intuitive
' ...
End Sub
End Class
In this example, it cannot be inferred easily what the matrix should look like. Is it a 2x2 Matrix or even a triangular Matrix?
Using a collection, data structure, or class that provides a more suitable representation of the required data is recommended instead of a multidimensional array or jagged array to enhance code readability.
Public Class Matrix2x2
' ...
End Class
Public Class Program
Public Sub WriteMatrix(matrix As Matrix2x2) ' Compliant: data type is intuitive
' ...
End Sub
End Class
As a result, avoiding exposing such methods to external users is recommended.
However, using multidimensional and jagged array method parameters internally, such as in private or internal methods or
within internal classes, is compliant since they are not publicly exposed.
Public Class FirstClass
Private Sub UpdateMatrix(matrix As Integer()()) ' Compliant: method is private
' ...
End Sub
End Class
Friend Class SecondClass
Public Sub UpdateMatrix(matrix As Integer()()) ' Compliant: class is internal
' ...
End Sub
End Class
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all non-private fields names match a provided regular expression.
Note that this rule does not apply to non-private Shared ReadOnly fields, for which there is another rule.
The default configuration is:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Class Foo
Public foo As Integer ' Noncompliant
End Class
Class Foo
Public Foo As Integer ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2369.json
================================================
{
"title": "Non-private fields should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2369",
"sqKey": "S2369",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2370.html
================================================
Shared naming conventions allow teams to collaborate efficiently. This rule checks that all non-private Shared ReadOnly fields names
match a provided regular expression.
The default configuration is:
With the default regular expression ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$:
Class Foo
Public Shared ReadOnly foo As Integer ' Noncompliant
End Class
Class Foo
Public Shared ReadOnly Foo As Integer ' Compliant
End Class
================================================
FILE: analyzers/rspec/vbnet/S2370.json
================================================
{
"title": "Non-private \"Shared ReadOnly\" fields should comply with a naming convention",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "IDENTIFIABLE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2370",
"sqKey": "S2370",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2372.html
================================================
Property getters should be simple operations that are always safe to call. If exceptions need to be thrown, it is best to convert the property to a method.
It is valid to throw exceptions from indexed property getters and from property setters, which are not detected by this rule.
Module Module1
Public Property Foo() As Integer
Get
Throw New Exception ' Non-Compliant
End Get
Set(ByVal value As Integer)
' ... some code ...
End Set
End Property
End Module
Module Module1
Sub SetFoo(ByVal value As Integer) ' Compliant
' ... some code ...
End Sub
End Module
No issue is raised when the thrown exception derives from or is of type NotImplementedException, NotSupportedException or
InvalidOperationException.
This rule is deprecated; use {rule:vbnet:S119} instead.
Shared naming conventions allow teams to collaborate efficiently. This rule checks that all generic type parameter names match a provided regular expression.
The default configuration is the one recommended by Microsoft:
With the default parameter value ^T(([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?)?$:
Public Class Foo(Of t) ' Noncompliant End Class
Public Class Foo(Of T) ' Compliant End Class================================================ FILE: analyzers/rspec/vbnet/S2373.json ================================================ { "title": "Generic type parameter names should comply with a naming convention", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "IDENTIFIABLE" }, "status": "deprecated", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-2373", "sqKey": "S2373", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S2374.html ================================================
Unsigned integers have different arithmetic operators than signed ones - operators that few developers understand. Therefore, signed types should be preferred where possible.
Module Module1
Sub Main()
Dim foo1 As UShort ' Noncompliant
Dim foo2 As UInteger ' Noncompliant
Dim foo3 As ULong ' Noncompliant
End Sub
End Module
Module Module1
Sub Main()
Dim foo1 As Short
Dim foo2 As Integer
Dim foo3 As Long
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2374.json
================================================
{
"title": "Signed types should be preferred to unsigned ones",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-2374",
"sqKey": "S2374",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2375.html
================================================
Using the With statement for a series of calls to the same object makes the code more readable.
With the default value of 6:
Module Module1
Dim product = New With {.Name = "paperclips", .RetailPrice = 1.2, .WholesalePrice = 0.6, .A = 0, .B = 0, .C = 0}
Sub Main()
product.Name = "" ' Noncompliant
product.RetailPrice = 0
product.WholesalePrice = 0
product.A = 0
product.B = 0
product.C = 0
End Sub
End Module
Module Module1
Dim product = New With {.Name = "paperclips", .RetailPrice = 1.2, .WholesalePrice = 0.6, .A = 0, .B = 0, .C = 0}
Sub Main()
With product
.Name = ""
.RetailPrice = 0
.WholesalePrice = 0
.A = 0
.B = 0
.C = 0
End With
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2375.json
================================================
{
"title": "\"With\" statements should be used for a series of calls to the same object",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2375",
"sqKey": "S2375",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2376.html
================================================
Properties with only setters are confusing and counterintuitive. Instead, a property getter should be added if possible, or the property should be replaced with a setter method.
Module Module1
WriteOnly Property Foo() As Integer ' Non-Compliant
Set(ByVal value As Integer)
' ... some code ...
End Set
End Property
End Module
Module Module1
Sub SetFoo(ByVal value As Integer) ' Compliant
' ... some code ...
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2376.json
================================================
{
"title": "Write-only properties should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "20min"
},
"tags": [
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-2376",
"sqKey": "S2376",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2387.html
================================================
This rule is deprecated, and will eventually be removed.
Having a variable with the same name in two unrelated classes is fine, but do the same thing within a class hierarchy and you’ll get confusion at best, chaos at worst.
Public Class Fruit
Protected Ripe As Season
Protected Flesh As Color
' ...
End Class
Public Class Raspberry
Inherits Fruit
Private Ripe As Boolean ' Noncompliant
Private Shared FLESH As Color ' Noncompliant
' ...
End Class
Public Class Fruit
Protected Ripe As Season
Protected Flesh As Color
' ...
End Class
Public Class Raspberry
Inherits Fruit
Private Riped As Boolean
Private Shared FLESH_COLOR As Color ' Noncompliant
' ...
End Class
This rule ignores same-name fields that are Shared in both the parent and child classes. It also ignores Private parent
class fields and fields explicitly declared as Shadows, but in all other such cases, the child class field should be renamed.
Public Class Fruit
Private Ripe As Season
Protected Flesh As Color
' ...
End Class
Public Class Raspberry
Inherits Fruit
Private Ripe As Season ' Compliant as parent field 'Ripe' is not visible from Raspberry anyway
Protected Shadows Flesh As Color ' Compliant as the intention is explicitly declared
' ...
End Class
================================================
FILE: analyzers/rspec/vbnet/S2387.json
================================================
{
"title": "Child class fields should not shadow parent class fields",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
},
"status": "deprecated",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-2387",
"sqKey": "S2387",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2429.html
================================================
The ... = {} syntax is more compact, more readable and less error-prone.
Module Module1
Sub Main()
Dim foo(1) As String ' Noncompliant
foo(0) = "foo"
foo(1) = "bar"
End Sub
End Module
Module Module1
Sub Main()
Dim foo = {"foo", "bar"} ' Compliant
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2429.json
================================================
{
"title": "Arrays should be initialized using the \"... \u003d {}\" syntax",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2429",
"sqKey": "S2429",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2437.html
================================================
Certain bitwise operations are not needed and should not be performed because their results are predictable.
Specifically, using And -1 with any value always results in the original value.
That is because the binary representation of -1 on a numeric data type
supporting negative numbers, such as Integer or Long, is based on two’s complement and made of all 1s: &B111…111.
Performing And between a value and &B111…111 means applying the And operator to each bit of the value
and the bit 1, resulting in a value equal to the provided one, bit by bit.
anyValue And -1 ' Noncompliant anyValue ' Compliant
Similarly, anyValue Or 0 always results in anyValue, because the binary representation of 0 is always
&B000…000 and the Or operator returns its first input when the second is 0.
anyValue Or 0 ' Noncompliant anyValue ' Compliant
The same applies to anyValue Xor 0: the Xor operator returns 1 when its two input bits are different
(1 and 0 or 0 and 1) and returns 0 when its two input bits are the same (both
0 or both 1). When Xor is applied with 0, the result would be 1 if the other input is
1, because the two input bits are different, and 0 if the other input bit is 0, because the two input are the
same. That results in returning anyValue.
anyValue Xor 0 ' Noncompliant anyValue ' Compliant
The instance passed to the SyncLock statement
should be a dedicated private field.
If the instance representing an exclusively acquired lock is publicly accessible, another thread in another part of the program could accidentally attempt to acquire the same lock. This increases the likelihood of deadlocks.
For example, a string should never be used for locking. When a string is interned by the runtime, it can be shared by multiple threads, breaking the
locking mechanism.
Instead, a dedicated private Lock object
instance (or object instance, for frameworks before .Net 9) should be used for locking. This minimizes access to the lock instance and
therefore prevents accidential lock sharing.
The following objects are considered potentially prone to accidental lock sharing:
Public Sub MyLockingMethod()
SyncLock Me 'Noncompliant
' ...
End SyncLock
End Sub
Private lockObj As New Object()
Public Sub MyLockingMethod()
SyncLock lockObj
' ...
End SyncLock
End Sub
Conditional expressions which are always true or false can lead to unreachable code.
In the case below, the call of Dispose() never happens.
Dim a = False
If a Then
Dispose() ' Never reached
End If
This rule will not raise an issue in either of these cases:
const bool
Const debug = False
'...
If debug Then
' Print something
End If
true or false.In these cases, it is obvious the code is as intended.
The conditions should be reviewed to decide whether:
Public Sub Sample(ByVal b As Boolean)
Dim a = False
If a Then ' Noncompliant: The true branch is never reached
DoSomething() ' Never reached
End If
If Not a OrElse b Then ' Noncompliant: "not a" is always "True" and the false branch is never reached
DoSomething()
Else
DoSomethingElse() ' Never reached
End If
Dim c = "xxx"
Dim res = If(c, "value") ' Noncompliant: d is always not Nothing, "value" is never used
End Sub
Public Sub Sample(ByVal b As Boolean)
Dim a = False
If Foo(a) Then ' Condition was updated
DoSomething()
End If
If b Then ' Parts of the condition were removed.
DoSomething()
Else
DoSomethingElse()
End If
Dim c = "xxx"
Dim res = c ' "value" was removed
End Sub
Gratuitous boolean expressions are conditions that do not change the evaluation of a program. This issue can indicate logical errors and affect the correctness of an application, as well as its maintainability.
Control flow constructs like if-statements allow the programmer to direct the flow of a program depending on a boolean expression.
However, if the condition is always true or always false, only one of the branches will ever be executed. In that case, the control flow construct and
the condition no longer serve a purpose; they become gratuitous.
The presence of gratuitous conditions can indicate a logical error. For example, the programmer intended to have the program branch into different paths but made a mistake when formulating the branching condition. In this case, this issue might result in a bug and thus affect the reliability of the application. For instance, it might lead to the computation of incorrect results.
Additionally, gratuitous conditions and control flow constructs introduce unnecessary complexity. The source code becomes harder to understand, and thus, the application becomes more difficult to maintain.
This rule looks for operands of a boolean expression never changing the result of the expression. It also applies to the null conditional operator
when one of the operands always evaluates to Nothing.
Dim d As String = Nothing Dim v1 = If(d, "value")
This rule will not raise an issue in either of these cases:
Const bool
Const debug = False
'...
If debug Then
' Print something
End If
True or False.In these cases, it is obvious the code is as intended.
Gratuitous boolean expressions are suspicious and should be carefully removed from the code.
First, the boolean expression in question should be closely inspected for logical errors. If a mistake was made, it can be corrected so the condition is no longer gratuitous.
If it becomes apparent that the condition is actually unnecessary, it can be removed. The associated control flow construct (e.g., the
if-statement containing the condition) will be adapted or even removed, leaving only the necessary branches.
Public Sub Sample(ByVal b As Boolean, ByVal c As Boolean)
Dim a = True
If a Then ' Noncompliant: "a" is always "true"
DoSomething()
End If
If b AndAlso a Then ' Noncompliant: "a" is always "true"
DoSomething()
End If
If c OrElse Not a Then ' Noncompliant: "Not a" is always "false"
DoSomething()
End If
Dim d As String = Nothing
Dim v1 = If(d, "value") ' Noncompliant: "d" is always Nothing and v1 is always "value".
Dim v2 = If(s, d) ' Noncompliant: "d" is always Nothing and v2 is always equal to s.
End Sub
Public Sub Sample(ByVal b As Boolean, ByVal c As Boolean, ByVal s As String)
Dim a = IsAllowed()
If a Then ' Compliant
DoSomething()
End If
If b AndAlso a Then ' Compliant
DoSomething()
End If
If c OrElse Not a Then ' Compliant
DoSomething()
End If
Dim d As String = GetStringData()
Dim v1 = If(d, "value") ' Compliant
Dim v2 = If(s, d) ' Compliant
End Sub
In Windows, "Everyone" group is similar and includes all members of the Authenticated Users group as well as the built-in Guest account, and several other built-in security accounts.
Granting permissions to this category can lead to unintended access to files or directories that could allow attackers to obtain sensitive information, disrupt services or elevate privileges.
When file or directory permissions grant access to all users on a system (often represented as "others" or "everyone" in permission models), attackers who gain access to any user account can read sensitive files containing credentials, configuration data, API keys, database passwords, personal information, or proprietary business data. This exposure can lead to data breaches, identity theft, compliance violations, and competitive disadvantage.
Granting write permissions to broad user categories allows any user on the system to modify or delete critical files and directories. Attackers or compromised low-privileged accounts can corrupt application data, modify configuration files to alter system behavior or disrupt services, or delete important resources, leading to service outages, system instability, data loss, and denial of service.
When executable files or scripts have overly permissive permissions, especially when combined with special permission bits that allow programs to execute with the permissions of the file owner or group rather than the executing user, attackers can replace legitimate executables with malicious code. When these modified files are executed by privileged users or processes, the attacker’s code runs with elevated privileges, potentially enabling them to escalate from a low-privileged account to root or administrator access, install backdoors, or pivot to other systems in the network.
Replace the AccessControlType.Allow with AccessControlType.Deny when creating file system access rules for the "Everyone"
group. This explicitly denies permissions rather than granting them, ensuring that broad user groups cannot access the file.
Dim unsafeAccessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow)
Dim fileSecurity = File.GetAccessControl("path")
fileSecurity.AddAccessRule(unsafeAccessRule) ' Noncompliant
File.SetAccessControl("fileName", fileSecurity)
Dim safeAccessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Deny)
Dim fileSecurity = File.GetAccessControl("path")
fileSecurity.AddAccessRule(safeAccessRule)
File.SetAccessControl("path", fileSecurity)
Use AccessControlType.Deny instead of AccessControlType.Allow when setting access rules for the "Everyone" group. This
prevents the broad group from having write or full control access to files.
Dim accessRule = new FileSystemAccessRule("Everyone", FileSystemRights.Write, AccessControlType.Allow)
Dim fileInfo = new FileInfo("path")
Dim fileSecurity = fileInfo.GetAccessControl()
fileSecurity.SetAccessRule(accessRule) ' Noncompliant
fileInfo.SetAccessControl(fileSecurity)
Dim accessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Deny)
Dim fileInfo = new FileInfo("path")
Dim fileSecurity = fileInfo.GetAccessControl()
fileSecurity.SetAccessRule(accessRule)
fileInfo.SetAccessControl(fileSecurity)
Avoid setting permissions that grant read, write, or execute access to "others" (all users). Instead, restrict permissions to the file owner or
specific groups. Use FileAccessPermissions.UserExecute or other restrictive permission flags that limit access to the owner only.
Dim fsEntry = UnixFileSystemInfo.GetFileSystemEntry("path")
fsEntry.FileAccessPermissions = FileAccessPermissions.OtherReadWriteExecute ' Noncompliant
Dim fsEntry = UnixFileSystemInfo.GetFileSystemEntry("path")
fsEntry.FileAccessPermissions = FileAccessPermissions.UserExecute
Most checks against an IndexOf value compare it with -1 because 0 is a valid index.
strings.IndexOf(someString) = -1 ' Test for "index not found" strings.IndexOf(someString) < 0 ' Test for "index not found" strings.IndexOf(someString) >= 0 ' Test for "index found"
Any checks which look for values > 0 ignore the first element, which is likely a bug. If the intent is merely to check the
inclusion of a value in a String, List, or array, consider using the Contains method instead.
strings.Contains(someString) ' Boolean result
This rule raises an issue when the output value of any of the following methods is tested against > 0:
String, list or
arrayStringString, list or
arrayStringsomeArray.IndexOf(someItem) > 0 ' Noncompliant: index 0 missing someString.IndexOfAny(charsArray) > 0 ' Noncompliant: index 0 missing someList.LastIndexOf(someItem) > 0 ' Noncompliant: index 0 missing someString.LastIndexOf(charsArray) > 0 ' Noncompliant: index 0 missing
Dim Color As String = "blue"
Dim Name As String = "ishmael"
Dim Strings As New List(Of String)
Strings.Add(Color)
Strings.Add(Name)
Dim StringArray As String() = Strings.ToArray()
If Strings.IndexOf(Color) > 0 Then ' Noncompliant
' ...
End If
If Name.IndexOf("ish") > 0 Then ' Noncompliant
' ...
End If
If Name.IndexOf("ae") > 0 Then ' Noncompliant
' ...
End If
If Array.IndexOf(StringArray, Color) > 0 Then ' Noncompliant
' ...
End If
Dim Color As String = "blue"
Dim Name As String = "ishmael"
Dim Strings As New List(Of String)
Strings.Add(Color)
Strings.Add(Name)
Dim StringArray As String() = Strings.ToArray()
If Strings.IndexOf(Color) > -1 Then
' ...
End If
If Name.IndexOf("ish") >= 0 Then
' ...
End If
If Name.Contains("ae") Then
' ...
End If
If Array.IndexOf(StringArray, Color) >= 0 Then
' ...
End If
A Catch clause that only rethrows the caught exception has the same effect as omitting the Catch altogether and letting
it bubble up automatically.
Dim s As String = ""
Try
s = File.ReadAllText(fileName)
Catch e As Exception
Throw
End Try
Such clauses should either be removed or populated with the appropriate logic.
Dim s As String = File.ReadAllText(fileName)
or
Dim s As String = ""
Try
s = File.ReadAllText(fileName)
Catch e As Exception
logger.LogError(e)
Throw
End Try
This rule will not generate issues for Catch blocks if they are followed by a Catch block for a more general exception
type that does more than just rethrowing the exception.
Dim s As String = ""
Try
s = File.ReadAllText(fileName)
Catch e As IOException 'Compliant by exception: removing it would change the logic
Throw
Catch e As Exception 'Compliant: does more than just rethrow
logger.LogError(e)
Throw
End Try
================================================
FILE: analyzers/rspec/vbnet/S2737.json
================================================
{
"title": "\"catch\" clauses should do more than rethrow",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"error-handling",
"unused",
"finding",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2737",
"sqKey": "S2737",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S2757.html
================================================
Using operator pairs (=+ or =-) that look like reversed single operators (+= or -=) is
confusing. They compile and run but do not produce the same result as their mirrored counterpart.
Dim target As Integer = -5 Dim num As Integer = 3 target =- num ' Noncompliant: target = -3. Is that the intended behavior? target =+ num ' Noncompliant: target = 3
This rule raises an issue when =+ or =- are used without any space between the operators and when there is at least one
whitespace after.
Replace the operators with a single one if that is the intention
Dim num As Integer = 3 target -= num ' target = -8
Or fix the spacing to avoid confusion
Dim num As Integer = 3 target = -num // target = -3================================================ FILE: analyzers/rspec/vbnet/S2757.json ================================================ { "title": "Non-existent operators like \"\u003d+\" should not be used", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-2757", "sqKey": "S2757", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S2761.html ================================================
The repetition of the Not operator is usually a typo. The second operator invalidates the first one:
Dim b As Boolean = False Dim c As Boolean = Not Not b 'Noncompliant: equivalent to "b"================================================ FILE: analyzers/rspec/vbnet/S2761.json ================================================ { "title": "\u0027Not\u0027 boolean operator should not be repeated", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-2761", "sqKey": "S2761", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S2925.html ================================================
Using Thread.Sleep in a test might introduce unpredictable and inconsistent results depending on the environment. Furthermore, it will
block the thread, which means the system resources are not being fully used.
<TestMethod>
Public Sub SomeTest()
Threading.Thread.Sleep(500) ' Noncompliant
' assertions...
End Sub
An alternative is a task-based asynchronous approach, using async and await.
More specifically the Task.Delay method should be used, because of the following advantages:
Thread.Sleep
<TestMethod>
Public Async Function SomeTest() As Task
Await Task.Delay(500)
' assertions...
End Function
Another scenario is when some data might need to be mocked using Moq, and a delay needs to be introduced:
<TestMethod>
Public Sub UserService_Test()
Dim UserService As New Mock(Of UserService)
Dim Expected As New User
UserService.Setup(Function(X) X.GetUserById(42)).Returns(
Function()
Threading.Thread.Sleep(500) ' Noncompliant
Return Task.FromResult(Expected)
End Function)
' assertions...
End Sub
An alternative to Thread.Sleep while mocking with Moq is to use ReturnsAsync and pass the amount of time to
delay there:
<TestMethod>
Public Sub UserService_Test()
Dim UserService As New Mock(Of UserService)
Dim Expected As New User
UserService.Setup(Function(X) X.GetUserById(42)).ReturnsAsync(Expected, TimeSpan.FromMilliseconds(500))
' assertions...
End Sub
Visual Basic .NET, unlike many other programming languages, has no "fall-through" for its Select cases. Each case already has an
implicit Exit Select as its last instruction. It therefore is redundant to explicitly add one.
Module Module1
Sub Main()
Dim x = 0
Select Case x
Case 0
Console.WriteLine("0")
Exit Select ' Noncompliant
Case Else
Console.WriteLine("Not 0")
Exit Select ' Noncompliant
End Select
End Sub
End Module
Module Module1
Sub Main()
Dim x = 0
Select Case x
Case 0 ' Compliant
Console.WriteLine("0")
Case Else ' Compliant
Console.WriteLine("Not 0")
End Select
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S2951.json
================================================
{
"title": "\"Exit Select\" statements should not be used redundantly",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"unused",
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-2951",
"sqKey": "S2951",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3011.html
================================================
Altering or bypassing the accessibility of classes, methods, or fields through reflection violates the encapsulation principle. This can break the internal contracts of the accessed target and lead to maintainability issues and runtime errors.
This rule raises an issue when reflection is used to change the visibility of a class, method or field, and when it is used to directly update a field value.
Imports System.Reflection
Dim dynClass = Type.GetType("MyInternalClass")
' Sensitive. Using BindingFlags.NonPublic will return non-public members
Dim bindingAttr As BindingFlags = BindingFlags.NonPublic Or BindingFlags.Static
Dim dynMethod As MethodInfo = dynClass.GetMethod("mymethod", bindingAttr)
Dim result = dynMethod.Invoke(dynClass, Nothing)
StringBuilder instances that never build a string clutter the code and worse are a drag on performance. Either they
should be removed, or the missing ToString() call should be added.
Public Sub DoSomething(ByVal strings As List(Of String))
Dim sb As StringBuilder = New StringBuilder() ' Noncompliant
sb.Append("Got: ")
For Each str As String In strings
sb.Append(str).Append(", ")
Next
End Sub
Public Sub DoSomething(ByVal strings As List(Of String))
For Each str As String In strings
Next
End Sub
or
Public Sub DoSomething(ByVal strings As List(Of String))
Dim sb As StringBuilder = New StringBuilder()
sb.Append("Got: ")
For Each str As String In strings
sb.Append(str).Append(", ")
Next
My.Application.Log.WriteEntry(sb.ToString())
End Sub
No issue is reported when StringBuilder is:
sb.CopyTo(), sb.GetChunks(), sb.Length, or sb(index).ToString() invocation there.Dim sb As StringBuilder = GetStringBuilder()).This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
In the mode Cipher Block Chaining (CBC), each block is used as cryptographic input for the next block. For this reason, the first block requires an initialization vector (IV), also called a "starting variable" (SV).
If the same IV is used for multiple encryption sessions or messages, each new encryption of the same plaintext input would always produce the same ciphertext output. This may allow an attacker to detect patterns in the ciphertext.
After retrieving encrypted data and performing cryptographic attacks on it on a given timeframe, attackers can recover the plaintext that encryption was supposed to protect.
Depending on the recovered data, the impact may vary.
Below are some real-world scenarios that illustrate the potential impact of an attacker exploiting the vulnerability.
By modifying the plaintext of the encrypted message, an attacker may be able to trigger additional vulnerabilities in the code. An attacker can
further exploit a system to obtain more information.
Encrypted values are often considered trustworthy because it would not be possible for a third party to modify them under normal circumstances.
When encrypted data contains personal or sensitive information, its retrieval by an attacker can lead to privacy violations, identity theft, financial loss, reputational damage, or unauthorized access to confidential systems.
In this scenario, a company, its employees, users, and partners could be seriously affected.
The impact is twofold, as data breaches and exposure of encrypted data can undermine trust in the organization, as customers, clients and stakeholders may lose confidence in the organization’s ability to protect their sensitive data.
In many industries and locations, there are legal and compliance requirements to protect sensitive data. If encrypted data is compromised and the plaintext can be recovered, companies face legal consequences, penalties, or violations of privacy laws.
Imports System.IO
Imports System.Security.Cryptography
Public Sub Encrypt(key As Byte(), dataToEncrypt As Byte(), target As MemoryStream)
Dim aes = New AesCryptoServiceProvider()
Dim iv = New Byte() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
Dim encryptor = aes.CreateEncryptor(key, iv) ' Noncompliant
Dim cryptoStream = New CryptoStream(target, encryptor, CryptoStreamMode.Write)
Dim swEncrypt = New StreamWriter(cryptoStream)
swEncrypt.Write(dataToEncrypt)
End Sub
In this example, the code implicitly uses a number generator that is considered strong, thanks to aes.IV.
Imports System.IO
Imports System.Security.Cryptography
Public Sub Encrypt(key As Byte(), dataToEncrypt As Byte(), target As MemoryStream)
Dim aes = New AesCryptoServiceProvider()
Dim encryptor = aes.CreateEncryptor(key, aes.IV)
Dim cryptoStream = New CryptoStream(target, encryptor, CryptoStreamMode.Write)
Dim swEncrypt = New StreamWriter(cryptoStream)
swEncrypt.Write(dataToEncrypt)
End Sub
To ensure high security, initialization vectors must meet two important criteria:
The IV does not need be secret, so the IV or information sufficient to determine the IV may be transmitted along with the ciphertext.
In the previous non-compliant example, the problem is not that the IV is hard-coded.
It is that the same IV is used for multiple encryption attempts.
Nested ternaries are hard to read and can make the order of operations complex to understand.
Public Function GetReadableStatus(job As Job) As String
Return If(job.IsRunning, "Running", If(job.HasErrors, "Failed", "Succeeded")) ' Noncompliant
End Function
Instead, use another line to express the nested operation in a separate statement.
Public Function GetReadableStatus(job As Job) As String
If job.IsRunning Then Return "Running"
Return If(job.HasErrors, "Failed", "Succeeded")
End Function
================================================
FILE: analyzers/rspec/vbnet/S3358.json
================================================
{
"title": "If operators should not be nested",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"confusing"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3358",
"sqKey": "S3358",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3363.html
================================================
You should only set a property of a temporal type (like DateTime or DateTimeOffset) as the primary key of a table if the
values are guaranteed to be unique.
Using temporal types as the primary key of a table is risky. When these types are used as primary keys, it usually means that each new key is
created with the use of .Now or .UtcNow properties from DateTime and DateTimeOffset classes. In
those cases, duplicate keys exceptions may occur in many ways:
DateTime type);The rule raises an issue if:
Id, <type name>Id or decorated by the [Key] or
[PrimaryKey] attribute.Either use a GUID or the auto generated ID as a primary key.
Public Class Account
Public Property Id As DateTime
Public Property Name As String
Public Property Surname As String
End Class
Public Class Account
Public Property Id As Guid
Public Property Name As String
Public Property Surname As String
End Class
or
Public Class Person
<Key>
Public Property PersonIdentifier As DateTime
Public Property Name As String
Public Property Surname As String
End Class
Public Class Person
<Key>
Public Property PersonIdentifier As Guid
Public Property Name As String
Public Property Surname As String
End Class
Exit Function, Exit Property, and Exit Sub are all poor, less-readable substitutes for a simple
Return, and if used with code that should return a value (Exit Function and in some cases Exit Property) they
could result in a NullReferenceException.
This rule raises an issue for all their usages.
Public Class Sample
Public Sub MySub(Condition As Boolean)
If Condition Then Exit Sub ' Noncompliant
' ...
End Sub
Public Function MyFunction(Condition As Boolean) As Integer
If Condition Then
MyFunction = 42
Exit Function ' Noncompliant
End If
' ...
End Function
End Class
Public Class Sample
Public Sub MySub(Condition As Boolean)
If Condition Then Return ' Noncompliant
' ...
End Sub
Public Function MyFunction(Condition As Boolean) As Integer
If Condition Then Return 42
' ...
End Function
End Class
================================================
FILE: analyzers/rspec/vbnet/S3385.json
================================================
{
"title": "\"Exit\" statements should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"brain-overload",
"bad-practice"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3385",
"sqKey": "S3385",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3431.html
================================================
It should be clear to a casual reader what code a test is testing and what results are expected. Unfortunately, that’s not usually the case with
the ExpectedException attribute since an exception could be thrown from almost any line in the method.
This rule detects MSTest and NUnit ExpectedException attribute.
This rule ignores:
Catch or Finally clause
<TestMethod>
<ExpectedException(GetType(InvalidOperationException))>
Public Sub UsingTest()
Console.ForegroundColor = ConsoleColor.Black
Try
Using alert As New ConsoleAlert()
Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor)
Throw New InvalidOperationException()
End Using
Finally
Assert.AreEqual(ConsoleColor.Black, Console.ForegroundColor) ' The exception itself is not relevant for the test.
End Try
End Sub
Public NotInheritable Class ConsoleAlert
Implements IDisposable
Private ReadOnly previous As ConsoleColor
Public Sub New()
previous = Console.ForegroundColor
Console.ForegroundColor = ConsoleColor.Red
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Console.ForegroundColor = previous
End Sub
End Class
Remove the ExpectedException attribute in favor of using the Assert.ThrowsException
assertion.
<TestMethod>
<ExpectedException(GetType(ArgumentNullException))> ' Noncompliant
Public Sub Method_NullParam()
Dim sut As New MyService()
sut.Method(Nothing)
End Sub
<TestMethod>
Public Sub Method_NullParam()
Dim sut As New MyService()
Assert.ThrowsException(Of ArgumentNullException)(Sub() sut.Method(Nothing))
End Sub
Remove the ExpectedException attribute in favor of using the Assert.Throws assertion.
<Test>
<ExpectedException(GetType(ArgumentNullException))> ' Noncompliant
Public Sub Method_NullParam()
Dim sut As New MyService()
sut.Method(Nothing)
End Sub
<Test>
Public Sub Method_NullParam()
Dim sut As New MyService()
Assert.Throws(Of ArgumentNullException)(Sub() sut.Method(Nothing))
End Sub
Numbers can be shifted with the << and >> operators, but the right operand of
the operation needs to be an int or a type that has an implicit
conversion to int. However, when the left operand is an object, the compiler’s type checking is turned off, therfore you
can pass anything to the right of a shift operator and have it compile. If the argument can’t be implicitly converted to int at runtime,
a RuntimeBinderException will be
raised.
Dim o As Object = 5 Dim x As Integer = 5 x = o >> 5 ' Noncompliant x = x << o ' Noncompliant
This rule does not raise when the left or the right expression is Nothing.
x = Nothing >> 5 x = 5 << Nothing
When a class has only a private constructor, it can’t be instantiated except within the class itself. Such classes can be considered
dead code and should be fixed
static members are also ignored because they are covered by Rule {rule:vbnet:S1118}.
Public Class [MyClass] ' Noncompliant: the class contains only private constructors
Private Sub New()
' ...
End Sub
End Class
Public Class [MyClass] ' Compliant: the class contains at least one non-private constructor
Public Sub New()
' ...
End Sub
End Class
Recursion is a technique used to define a problem in terms of the problem itself, usually in terms of a simpler version of the problem itself.
For example, the implementation of the generator for the n-th value of the Fibonacci sequence comes naturally from its mathematical definition, when recursion is used:
Function NthFibonacciNumber(ByVal n As Integer) As Integer
If n <= 1 Then
Return 1
Else
Return NthFibonacciNumber(n - 1) + NthFibonacciNumber(n - 2)
End If
End Function
As opposed to:
Function NthFibonacciNumber(ByVal n As Integer) As Integer
Dim previous As Integer = 0
Dim last As Integer = 1
For i = 0 To n - 1
Dim temp = previous
previous = last
last = last + temp
Next
Return last
End Function
The use of recursion is acceptable in methods, like the one above, where you can break out of it.
Function NthFibonacciNumber(ByVal n As Integer) As Integer
If n <= 1 Then
Return 1 ' Base case: stop the recursion
End If
' ...
End Function
It is also acceptable and makes sense in some type definitions:
Class Box
Inherits IComparable(Of Box)
Public Function CompareTo(ByVal other As Box?) As Integer
' Compare the two Box instances...
End Function
End Class
With types, some invalid recursive definitions are caught by the compiler:
Class C2(Of T) ' Error BC31447 C2(Of T) cannot reference itself in Inherits clause
Inherits C2(Of T)
End Class
Class C2(Of T)
Inherits C2(Of C2(Of T)) ' Error BC31447 C2(Of T) cannot reference itself in Inherits clause
End Class
In more complex scenarios, however, the code will compile but execution will result in a TypeLoadException if you try to instantiate the class.
Class C1(Of T)
End Class
Class C2(Of T) ' Noncompliant
Inherits C1(Of C2(Of C2(Of T)))
End Class
Dim c2 = New C2(Of Integer) ' This would result into a TypeLoadException
When optional parameter values are not passed to base method calls, the value passed in by the caller is ignored. This can cause the function to behave differently than expected, leading to errors and making the code difficult to debug.
Public Class BaseClass
Public Overridable Sub MyMethod(ByVal Optional i As Integer = 1)
Console.WriteLine(i)
End Sub
End Class
Public Class DerivedClass
Inherits BaseClass
Public Overrides Sub MyMethod(ByVal Optional i As Integer = 1)
' ...
MyBase.MyMethod() ' Noncompliant: caller's value is ignored
End Sub
Private Shared Function Main(ByVal args As String()) As Integer
Dim dc As DerivedClass = New DerivedClass()
dc.MyMethod(12) ' prints 1
End Function
End Class
Public Class BaseClass
Public Overridable Sub MyMethod(ByVal Optional i As Integer = 1)
Console.WriteLine(i)
End Sub
End Class
Public Class DerivedClass
Inherits BaseClass
Public Overrides Sub MyMethod(ByVal Optional i As Integer = 1)
' ...
MyBase.MyMethod(i)
End Sub
Private Shared Function Main(ByVal args As String()) As Integer
Dim dc As DerivedClass = New DerivedClass()
dc.MyMethod(12) ' prints 12
End Function
End Class
Microsoft Learn - Optional Arguments (Visual Basic)
================================================ FILE: analyzers/rspec/vbnet/S3466.json ================================================ { "title": "Optional parameters should be passed to \"base\" calls", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3466", "sqKey": "S3466", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S3598.html ================================================When declaring a Windows Communication Foundation (WCF) OperationContract
method as one-way,
that service method won’t return any result, not even an underlying empty confirmation message. These are fire-and-forget methods that are useful in
event-like communication. Therefore, specifying a return type has no effect and can confuse readers.
The rule doesn’t report if OperationContractAttribute.AsyncPattern
is set to true.
<ServiceContract>
Interface IMyService
<OperationContract(IsOneWay:=True)>
Function SomethingHappened(ByVal parameter As Integer) As Integer ' Noncompliant
End Interface
<ServiceContract>
Interface IMyService
<OperationContract(IsOneWay:=True)>
Sub SomethingHappened(ByVal parameter As Integer)
End Interface
Microsoft Learn - OperationContractAttribute
================================================ FILE: analyzers/rspec/vbnet/S3598.json ================================================ { "title": "One-way \"OperationContract\" methods should have \"void\" return type", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "15min" }, "tags": [], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3598", "sqKey": "S3598", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S3603.html ================================================Marking a method with the Pure
attribute indicates that the method doesn’t make any visible state changes. Therefore, a Pure method should return a result. Otherwise,
it indicates a no-operation call.
Using Pure on a void method is either by mistake or the method is not doing a meaningful task.
Class Person
Private age As Integer
<Pure> ' Noncompliant: The method makes a state change
Private Sub ConfigureAge(ByVal age As Integer)
Me.age = age
End Sub
<Pure>
Private Sub WriteAge() ' Noncompliant
Console.WriteLine(Me.age)
End Sub
End Class
Class Person
Private age As Integer
Private Sub ConfigureAge(ByVal age As Integer)
Me.age = age
End Sub
<Pure>
Private Function Age() As Integer
Return Me.age
End Function
' or remove Pure attribute from the method
Private Sub WriteAge()
Console.WriteLine(Me.age)
End Sub
End Class
Nullable value types can hold either a value or
Nothing.
The value stored in the nullable type can be accessed with the Value property or by casting it to the underlying type. Still, both
operations throw an InvalidOperationException when the value is Nothing. A nullable type should always be tested before
accessing the value to avoid raising exceptions.
Sub Sample(condition As Boolean)
Dim nullableValue As Integer? = If(condition, 42, Nothing)
Console.WriteLine(nullableValue.Value) ' Noncompliant: InvalidOperationException is raised
Dim nullableCast As Integer? = If(condition, 42, Nothing)
Console.WriteLine(CType(nullableCast, Integer)) ' Noncompliant: InvalidOperationException is raised
End Sub
Sub Sample(condition As Boolean)
Dim nullableValue As Integer? = If(condition, 42, Nothing)
If nullableValue.HasValue Then
Console.WriteLine(nullableValue.Value)
End If
Dim nullableCast As Integer? = If(condition, 42, Nothing)
If nullableCast.HasValue Then
Console.WriteLine(CType(nullableCast, Integer))
End If
End Sub
This rule raises an issue when the code cognitive complexity of a function is above a certain threshold.
Cognitive Complexity is a measure of how hard it is to understand the control flow of a unit of code. Code with high cognitive complexity is hard to read, understand, test, and modify.
As a rule of thumb, high cognitive complexity is a sign that the code should be refactored into smaller, easier-to-manage pieces.
Here are the core concepts:
The method of computation is fully detailed in the pdf linked in the resources.
Developers spend more time reading and understanding code than writing it. High cognitive complexity slows down changes and increases the cost of maintenance.
Function Abs(ByVal n As Integer) As Integer ' Noncompliant: cognitive complexity = 5
If n >= 0 Then ' +1
Return n
Else ' +2, due to nesting
If n = Integer.MinValue Then ' +1
Throw New ArgumentException("The absolute value of int.MinValue is outside of int boundaries")
Else ' +1
Return -n
End If
End If
End Function
They should be refactored to have lower complexity:
Function Abs(ByVal n As Integer) As Integer ' Compliant: cognitive complexity = 3
If n = Integer.MinValue Then ' +1
Throw New ArgumentException("The absolute value of int.MinValue is outside of int boundaries")
Else If n >= 0 Then ' +1
Return n
Else ' +1
Return -n
End If
End Function
Since Visual Studio 2010 SP1, the ByVal parameter modifier is implicitly applied, and therefore not required anymore. Removing it from
your source code will improve readability.
Sub Foo(ByVal bar As String) ' ... End Sub
Sub Foo(bar As String) ' ... End Sub================================================ FILE: analyzers/rspec/vbnet/S3860.json ================================================ { "title": "\"ByVal\" should not be used", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "clumsy" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-3860", "sqKey": "S3860", "scope": "All", "quickfix": "covered" } ================================================ FILE: analyzers/rspec/vbnet/S3866.html ================================================
Visual Basic .NET offers a non-short-circuit conditional function, IIf(), which returns either its second or third parameter based on
the expression in the first parameter. Using it is slower than using If() because each parameter is unconditionally evaluated. Further,
its use can lead to runtime exceptions because IIf always evaluates all three of its arguments.
The newer version, If(), should be used instead because it short-circuits the evaluation of its parameters.
Public Class Foo
Public Sub Bar()
Dim var As Object = IIf(Date.Now.Year = 1999, "Lets party!", "Lets party like it is 1999!") ' Noncompliant
End Sub
End Class
Public Class Foo
Public Sub Bar()
Dim var As String = If(Date.Now.Year = 1999, "Lets party!", "Lets party like it is 1999!")
End Sub
End Class
The SafeHandle.DangerousGetHandle method poses significant risks and should be used carefully. This method carries the inherent danger
of potentially returning an invalid handle, which can result in resource leaks and security vulnerabilities. Although it is technically possible to
utilize this method without encountering issues, doing so correctly requires a high level of expertise. Therefore, it is recommended to avoid using
this method altogether.
The SafeHandle.DangerousGetHandle method is potentially prone to leaks and vulnerabilities due to its nature and usage. Here are a few
reasons why:
SafeHandle class, there is an increased risk of failing to dispose system resources correctly.SafeHandle.DangerousGetHandle without proper validation can lead to security vulnerabilities that can be exploited by an attacker.Sub Dangerous(fieldInfo As System.Reflection.FieldInfo) Dim handle As SafeHandle = CType(fieldInfo.GetValue(fieldInfo), SafeHandle) Dim dangerousHandle As IntPtr = handle.DangerousGetHandle ' Noncompliant End Sub
The point of having custom exception types is to convey more information than is available in standard types. But custom exception types must be
public for that to work.
If a method throws a non-public exception, the best you can do on the caller’s side is to catch the closest public base
of the class. However, you lose all the information that the new exception type carries.
This rule will raise an issue if you directly inherit one of the following exception types in a non-public class:
Friend Class MyException ' Noncompliant
Inherits Exception
' ...
End Class
Public Class MyException
Inherits Exception
' ...
End Class
There’s no point in creating an array solely for the purpose of passing it to a ParamArray parameter. Simply pass the elements
directly. They will be consolidated into an array automatically.
Class SurroundingClass
Public Sub Base()
Method(New String() { "s1", "s2" }) ' Noncompliant: unnecessary
Method(New String(12) {}) ' Compliant
End Sub
Public Sub Method(ParamArray args As String())
' Do something
End Sub
End Class
Class SurroundingClass
Public Sub Base()
Method("s1", "s2")
Method(New String(12) {})
End Sub
Public Sub Method(ParamArray args As String())
' Do something
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S3878.json
================================================
{
"title": "Arrays should not be created for ParamArray parameters",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"clumsy"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-3878",
"sqKey": "S3878",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3884.html
================================================
This rule is deprecated, and will eventually be removed.
CoSetProxyBlanket and CoInitializeSecurity both work to set the permissions context in which the process invoked
immediately after is executed. Calling them from within that process is useless because it’s too late at that point; the permissions context has
already been set.
Specifically, these methods are meant to be called from non-managed code such as a C++ wrapper that then invokes the managed, i.e. C# or VB.NET, code.
Public Class Noncompliant
<DllImport("ole32.dll")>
Public Shared Function CoSetProxyBlanket(<MarshalAs(UnmanagedType.IUnknown)>pProxy As Object, dwAuthnSvc as UInt32, dwAuthzSvc As UInt32, <MarshalAs(UnmanagedType.LPWStr)> pServerPrincName As String, dwAuthnLevel As UInt32, dwImpLevel As UInt32, pAuthInfo As IntPtr, dwCapabilities As UInt32) As Integer
End Function
Public Enum RpcAuthnLevel
[Default] = 0
None = 1
Connect = 2
[Call] = 3
Pkt = 4
PktIntegrity = 5
PktPrivacy = 6
End Enum
Public Enum RpcImpLevel
[Default] = 0
Anonymous = 1
Identify = 2
Impersonate = 3
[Delegate] = 4
End Enum
Public Enum EoAuthnCap
None = &H00
MutualAuth = &H01
StaticCloaking = &H20
DynamicCloaking = &H40
AnyAuthority = &H80
MakeFullSIC = &H100
[Default] = &H800
SecureRefs = &H02
AccessControl = &H04
AppID = &H08
Dynamic = &H10
RequireFullSIC = &H200
AutoImpersonate = &H400
NoCustomMarshal = &H2000
DisableAAA = &H1000
End Enum
<DllImport("ole32.dll")>
Public Shared Function CoInitializeSecurity(pVoid As IntPtr, cAuthSvc As Integer, asAuthSvc As IntPtr, pReserved1 As IntPtr, level As RpcAuthnLevel, impers As RpcImpLevel, pAuthList As IntPtr, dwCapabilities As EoAuthnCap, pReserved3 As IntPtr) As Integer
End Function
Public Sub DoSomething()
Dim Hres1 As Integer = CoSetProxyBlanket(Nothing, 0, 0, Nothing, 0, 0, IntPtr.Zero, 0) ' Noncompliant
Dim Hres2 As Integer = CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, RpcAuthnLevel.None, RpcImpLevel.Impersonate, IntPtr.Zero, EoAuthnCap.None, IntPtr.Zero) ' Noncompliant
End Sub
End Class
Thread.Suspend and Thread.Resume can give unpredictable results, and both methods have been deprecated. Indeed, if
Thread.Suspend is not used very carefully, a thread can be suspended while
holding a lock, thus leading to a deadlock.
There are other synchronization mechanisms that are safer and should be used instead, such as:
Monitor provides a mechanism that synchronizes access to objects.Mutex provides a mechanism that synchronizes interprocess access to a protected resource.Semaphore provides a mechanism that allows limiting the number of threads that have access to a protected resources
concurrently.Events enable a class to notify others when something of interest occurs.If you’re using a Structure, it is likely because you’re interested in performance. But by failing to implement
IEquatable<T> you’re loosing performance when comparisons are made because without IEquatable<T>, boxing and
reflection are used to make comparisons.
Structure MyStruct ' Noncompliant
Public Property Value As Integer
End Structure
Structure MyStruct
Implements IEquatable(Of MyStruct)
Public Property Value As Integer
Public Overloads Function Equals(other As MyStruct) As Boolean Implements IEquatable(Of MyStruct).Equals
' ...
End Function
End Structure
Methods declared as Public, Protected, or Protected Friend can be accessed from other assemblies, which
means you should validate parameters to be within the expected constraints. In general, checking against Nothing is recommended in
defensive programming.
This rule raises an issue when a parameter of a publicly accessible method is not validated against Nothing before being
dereferenced.
Public Class Sample
Public Property Message As String
Public Sub PublicMethod(Arg As Exception)
Message = Arg.Message ' Noncompliant
End Sub
Protected Sub ProtectedMethod(Arg As Exception)
Message = Arg.Message ' Noncompliant
End Sub
End Class
Public Class Sample
Public Property Message As String
Public Sub PublicMethod(Arg As Exception)
If Arg IsNot Nothing Then Message = Arg.Message ' Noncompliant
End Sub
Protected Sub ProtectedMethod(Arg As Exception)
ArgumentNullException.ThrowIfNull(Arg)
Message = Arg.Message ' Noncompliant
End Sub
Private Sub PrivateMethod(Arg As Exception)
Message = Arg.Message ' Compliant: method is not publicly accessible
End Sub
End Class
Nothing via helper methods should be annotated with the [NotNull] attribute.[NotNull] Resharper code annotation
attribute are supported as well.ValidatedNotNullAttribute and mark the parameter that is
validated for null in your method declaration with it:
Imports System.Runtime.CompilerServices
<AttributeUsage(AttributeTargets.Parameter, Inherited:=False)>
Public NotInheritable Class ValidatedNotNullAttribute
Inherits Attribute
End Class
Public Module Guard
Public Sub NotNull(Of T As Class)(<ValidatedNotNullAttribute> Value As T, <CallerArgumentExpression("Value")> Optional Name As String = "")
If Value Is Nothing Then Throw New ArgumentNullException(Name)
End Sub
End Module
Public Module SampleUsage
Public Function CustomToUpper(Value As String) As String
Guard.NotNull(Value)
Return Value.ToUpper
End Function
End Module
================================================
FILE: analyzers/rspec/vbnet/S3900.json
================================================
{
"title": "Arguments of public methods should be validated against Nothing",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"convention",
"symbolic-execution"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3900",
"sqKey": "S3900",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3903.html
================================================
Types are declared in namespaces in order to prevent name collisions and as a way to organize them into the object hierarchy. Types that are defined outside any named namespace are in a global namespace that cannot be referenced in code.
Public Class Foo End Class Public Structure Bar End Structure
Namespace SomeSpace
Public Class Foo
End Class
Public Structure Bar
End Structure
End Namespace
================================================
FILE: analyzers/rspec/vbnet/S3903.json
================================================
{
"title": "Types should be defined in named namespaces",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "MODULAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3903",
"sqKey": "S3903",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3904.html
================================================
The AssemblyVersion attribute is used to specify the version number of an assembly. An assembly is a compiled unit of code, which can be marked with a version number by applying the attribute to an assembly’s source code file.
The AssemblyVersion attribute is useful for many reasons:
AssemblyVersion attribute, you can ensure that the correct version of the referenced assembly is used. This
helps avoid compatibility issues and ensures that the expected behavior and functionality are maintained.If no AssemblyVersion is provided, the same default version will be used for every build. Since the version number is used by .NET
Framework to uniquely identify an assembly, this can lead to broken dependencies.
Imports System.Reflection
<Assembly: AssemblyTitle("MyAssembly")> ' Noncompliant
Namespace MyLibrary
' ...
End Namespace
Imports System.Reflection
<Assembly: AssemblyTitle("MyAssembly")>
<Assembly: AssemblyVersion("42.1.125.0")>
Namespace MyLibrary
' ...
End Namespace
Having all branches of a Select Case or If chain with the same implementation indicates a problem.
In the following code:
Dim b As Integer = If(a > 12, 4, 4) // Noncompliant
If b = 0 Then // Noncompliant
DoTheThing()
Else
DoTheThing()
End If
Select Case i // Noncompliant
Case 1
DoSomething()
Case 2
DoSomething()
Case 3
DoSomething()
Case Else
DoSomething()
End Select
Either there is a copy-paste error that needs fixing or an unnecessary Select Case or If chain that needs removing.
This rule does not apply to If chains without Else, nor to Select Case without a Case Else
clause.
If b = 0 Then ' No issue, this could have been done on purpose to make the code more readable
DoTheThing()
ElseIf
DoTheThing()
End If
================================================
FILE: analyzers/rspec/vbnet/S3923.json
================================================
{
"title": "All branches in a conditional structure should not have exactly the same implementation",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3923",
"sqKey": "S3923",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3926.html
================================================
Fields marked with System.Runtime.Serialization.OptionalFieldAttribute are serialized just like any other field. But such fields are
ignored on deserialization, and retain the default values associated with their types. Therefore, deserialization event handlers should be declared to
set such fields during the deserialization process.
This rule raises when at least one field with the System.Runtime.Serialization.OptionalFieldAttribute attribute is declared but one
(or both) of the following event handlers System.Runtime.Serialization.OnDeserializingAttribute or
System.Runtime.Serialization.OnDeserializedAttribute are not present.
<Serializable>
Public Class Foo ' Noncompliant
<OptionalField(VersionAdded:=2)>
Private optionalField As Integer = 5
End Class
<Serializable>
Public Class Foo
<OptionalField(VersionAdded:=2)>
Private optionalField As Integer = 5
<OnDeserializing>
Private Sub OnDeserializing(ByVal context As StreamingContext)
optionalField = 5
End Sub
<OnDeserialized>
Private Sub OnDeserialized(ByVal context As StreamingContext)
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S3926.json
================================================
{
"title": "Deserialization methods should be provided for \"OptionalField\" members",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"serialization"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-3926",
"sqKey": "S3926",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S3927.html
================================================
Serialization event handlers that don’t have the correct signature will not be called, bypassing augmentations to automated serialization and deserialization events.
A method is designated a serialization event handler by applying one of the following serialization event attributes:
Serialization event handlers take a single parameter of type StreamingContext, return
void, and have private visibility.
This rule raises an issue when any of these constraints are not respected.
<Serializable>
Public Class Foo
<OnSerializing>
Public Sub OnSerializing(ByVal context As StreamingContext) ' Noncompliant: should be private
End Sub
<OnSerialized>
Private Function OnSerialized(ByVal context As StreamingContext) As Integer ' Noncompliant: should return void
End Function
<OnDeserializing>
Private Sub OnDeserializing() ' Noncompliant: should have a single parameter of type StreamingContext
End Sub
<OnSerializing>
Public Sub OnSerializing2(Of T)(ByVal context As StreamingContext) ' Noncompliant: should have no type parameters
End Sub
<OnDeserialized>
Private Sub OnDeserialized(ByVal context As StreamingContext, ByVal str As String) ' Noncompliant: should have a single parameter of type StreamingContext
End Sub
End Class
<Serializable>
Public Class Foo
<OnSerializing>
Private Sub OnSerializing(ByVal context As StreamingContext)
End Sub
<OnSerialized>
Private Sub OnSerialized(ByVal context As StreamingContext)
End Sub
<OnDeserializing>
Private Sub OnDeserializing(ByVal context As StreamingContext)
End Sub
<OnDeserialized>
Private Sub OnDeserialized(ByVal context As StreamingContext)
End Sub
End Class
Numbers are infinite, but the types that hold them are not. Each numeric type has hard upper and lower bounds. Try to calculate numbers beyond
those bounds, and the result will be an OverflowException. When the compilation is configured to remove integer overflow checking, the
value will be silently wrapped around from the expected positive value to a negative one, or vice versa.
Public Function Transform(Value As Integer) As Integer
If Value <= 0 Then Return Value
Dim Number As Integer = Integer.MaxValue
Return Number + Value ' Noncompliant
End Function
Public Function Transform(Value As Integer) As Long
If Value <= 0 Then Return Value
Dim Number As Long = Integer.MaxValue
Return Number + Value
End Function
Disposing an object twice in the same method, either with the Using statement or by calling
Dispose directly, is confusing and error-prone. For example, another developer might try to use an already-disposed object, or there can
be runtime errors for specific paths in the code.
In addition, even if the documentation explicitly states that
calling the Dispose method multiple times should not throw an exception, some implementations still do it. Thus it is safer to not
dispose of an object twice when possible.
Dim foo As New Disposable() foo.Dispose() foo.Dispose() ' Noncompliant
Using bar As New Disposable() ' Noncompliant
bar.Dispose()
End Using
Dim foo As New Disposable() foo.Dispose()
Using bar As New Disposable() ' Compliant End Using
The size of a collection and the length of an array are always greater than or equal to zero. Testing it doesn’t make sense, since the result is
always true.
If Collection.Count >= 0 Then ... 'Noncompliant always true If array.Length >= 0 Then ... 'Noncompliant always true
Similarly testing that it is less than zero will always return false.
If Enumerable.Count < 0 Then ... 'Noncompliant always false Dim result As Boolean = Array.Length >= 0 'Noncompliant always true
Fix the code to properly check for emptiness if it was the intent, or remove the redundant code to keep the current behavior.
================================================ FILE: analyzers/rspec/vbnet/S3981.json ================================================ { "title": "Collection sizes and array length comparisons should make sense", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "2min" }, "tags": [ "confusing" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3981", "sqKey": "S3981", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S3990.html ================================================Assemblies should conform with the Common Language Specification (CLS) in order to be usable across programming languages. To be compliant an
assembly has to indicate it with System.CLSCompliantAttribute.
<Assembly: CLSCompliant(True)> Namespace MyLibrary End Namespace================================================ FILE: analyzers/rspec/vbnet/S3990.json ================================================ { "title": "Assemblies should be marked as CLS compliant", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "COMPLETE" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "api-design" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3990", "sqKey": "S3990", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S3992.html ================================================
Assemblies should explicitly indicate whether they are meant to be COM visible or not. If the ComVisibleAttribute is not present, the
default is to make the content of the assembly visible to COM clients.
Note that COM visibility can be overridden for individual types and members.
Namespace MyLibrary ' Noncompliant End Namespace
<Assembly: Runtime.InteropServices.ComVisible(False)> Namespace MyLibrary End Namespace================================================ FILE: analyzers/rspec/vbnet/S3992.json ================================================ { "title": "Assemblies should explicitly specify COM visibility", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [ "api-design" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-3992", "sqKey": "S3992", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S3998.html ================================================
Objects that can be accessed across application domain boundaries are said to have weak identity. This means that these objects can be considered shared resources outside of the domain, which can be lead to them being accessed or modified by multiple threads or concurrent parts of a program, outside of the domain.
A thread acquiring a lock on such an object runs the risk of being blocked by another thread in a different application domain, leading to poor performance and potentially thread starvation and deadlocks.
Types with weak identity are:
Public Class Sample
Private ReadOnly myLock As New StackOverflowException
Public Sub Go()
SyncLock myLock ' Noncompliant
' ...
End SyncLock
End Sub
End Class
Public Class Sample
Private ReadOnly myLock As New Object
Public Sub Go()
SyncLock myLock
' ...
End SyncLock
End Sub
End Class
Having a field in a child class with a name that differs from a parent class' field only by capitalization is sure to cause confusion. Such child class fields should be renamed.
Public Class Fruit
Protected PlantingSeason As String
' ...
End Class
Public Class Raspberry
Inherits Fruit
Protected Plantingseason As String ' Noncompliant
' ...
End Class
Public Class Fruit
Protected PlantingSeason As String
' ...
End Class
Public Class Raspberry
Inherits Fruit
Protected WhenToPlant As String
' ...
End Class
Or
Public Class Fruit
Protected PlantingSeason As String
' ...
End Class
Public Class Raspberry
Inherits Fruit
' Field removed, parent field will be used instead
End Class
This rule ignores same-name fields that are Shared in both the parent and child classes. It also ignores Private parent
class fields and fields explicitly declared as Shadows, but in all other such cases, the child class field should be renamed.
When you run an OS command, it is always important to protect yourself against the risk of accidental or malicious replacement of the executables in the production system.
To do so, it is important to point to the specific executable that should be used.
For example, if you call git (without specifying a path), the operating system will search for the executable in the directories
specified in the PATH environment variable.
An attacker could have added, in a permissive directory covered by PATH , another executable called git, but with a
completely different behavior, for example exfiltrating data or exploiting a vulnerability in your own code.
However, by calling /usr/bin/git or ../git (relative path) directly, the operating system will always use the intended
executable.
Note that you still need to make sure that the executable is not world-writeable and potentially overwritten. This is not the scope of this
rule.
There is a risk if you answered no to this question.
If you wish to rely on the PATH environment variable to locate the OS command, make sure that each of its listed directories is fixed,
not susceptible to change, and not writable by unprivileged users.
If you determine that these folders cannot be altered, and that you are sure that the program you intended to use will be used, then you can determine that these risks are under your control.
A good practice you can use is to also hardcode the PATH variable you want to use, if you can do so in the framework you use.
If the previous recommendations cannot be followed due to their complexity or other requirements, then consider using the absolute path of the command instead.
$ whereis git git: /usr/bin/git /usr/share/man/man1/git.1.gz $ ls -l /usr/bin/git -rwxr-xr-x 1 root root 3376112 Jan 28 10:13 /usr/bin/git
Dim p As New Process() p.StartInfo.FileName = "binary" ' Sensitive
Dim p As New Process() p.StartInfo.FileName = "C:\Apps\binary.exe"
The .NET framework class library provides methods for retrieving custom attributes. Sealing the attribute eliminates the search through the inheritance hierarchy, and can improve performance.
This rule raises an issue when a public type inherits from System.Attribute, is not abstract, and is not sealed.
Public Class MyAttribute ' Noncompliant
Inherits Attribute
Public ReadOnly Property Name As String
Public Sub New(Name As String)
Me.Name = Name
End Sub
End Class
Public NotInheritable Class MyAttribute
Inherits Attribute
Public ReadOnly Property Name As String
Public Sub New(Name As String)
Me.Name = Name
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S4060.json
================================================
{
"title": "Non-abstract attributes should be sealed",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "EFFICIENT"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"performance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4060",
"sqKey": "S4060",
"scope": "Main",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4136.html
================================================
For clarity, all overloads of the same method should be grouped together. That lets both users and maintainers quickly understand all the current available options.
Interface IMyInterface
Function DoTheThing() As Integer
Function DoTheOtherThing() As String // Noncompliant
Function DoTheThing(ByVal Path As String) As Integer
End Interface
Interface IMyInterface
Function DoTheThing() As Integer
Function DoTheThing(ByVal Path As String) As Integer
Function DoTheOtherThing() As String
End Interface
As it is common practice to group method declarations by implemented interface, no issue will be raised for interface implementations if grouped together with other members of that interface.
As it is also a common practice to group method declarations by accessibility level, no issue will be raised for method overloads having different access modifiers.
Example:
Class MyClass
Private Sub DoTheThing(s As String) ' Ok - this method is declared as Private while the other one is Public
' ...
End Sub
Private Sub DoTheOtherThing(s As String)
' ...
End Sub
Public Sub DoTheThing()
' ...
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S4136.json
================================================
{
"title": "Method overloads should be grouped together",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FORMATTED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "1min"
},
"tags": [
"convention"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4136",
"sqKey": "S4136",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4143.html
================================================
Storing a value inside a collection at a given key or index and then unconditionally overwriting it without reading the initial value is a case of a "dead store".
towns.Item(x) = "London" towns.Item(x) = "Chicago"; // Noncompliant
This practice is redundant and will cause confusion for the reader. More importantly, it is often an error and not what the developer intended to do.
================================================ FILE: analyzers/rspec/vbnet/S4143.json ================================================ { "title": "Map values should not be replaced unconditionally", "type": "BUG", "code": { "impacts": { "RELIABILITY": "MEDIUM" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4143", "sqKey": "S4143", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S4144.html ================================================Two methods having the same implementation are suspicious. It might be that something else was intended. Or the duplication is intentional, which becomes a maintenance burden.
Private Const CODE As String = "bounteous" Private callCount As Integer = 0 Public Function GetCode() As String callCount = callCount + 1 Return CODE End Function Public Function GetName() As String ' Noncompliant: duplicates GetCode callCount = callCount + 1 Return CODE End Function
If the identical logic is intentional, the code should be refactored to avoid duplication. For example, by having both methods call the same method or by having one implementation invoke the other.
Private Const CODE As String = "bounteous" Private callCount As Integer = 0 Public Function GetCode() As String callCount = callCount + 1 Return CODE End Function Public Function GetName() As String ' Intent is clear Return GetCode() End Function
Empty methods, methods with only one line of code and methods with the same name (overload) are ignored.
================================================ FILE: analyzers/rspec/vbnet/S4144.json ================================================ { "title": "Methods should not have identical implementations", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" }, "attribute": "DISTINCT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "15min" }, "tags": [ "confusing", "duplicate", "suspicious" ], "defaultSeverity": "Major", "ruleSpecification": "RSPEC-4144", "sqKey": "S4144", "scope": "All", "quickfix": "infeasible" } ================================================ FILE: analyzers/rspec/vbnet/S4158.html ================================================When a collection is empty, iterating it has no effect. Doing so anyway is likely a bug; either population was accidentally omitted, or the iteration needs to be revised.
Public Sub Method()
Dim Values As New List(Of String)
Values.Remove("bar") ' Noncompliant
If Values.Contains("foo") Then ' Noncompliant
End If
For Each Value As String In Values ' Noncompliant
Next
End Sub
Public Sub Method()
Dim Values As List(Of String) = LoadValues()
Values.Remove("bar")
If Values.Contains("foo") Then
End If
For Each Value As String In Values
Next
End Sub
================================================
FILE: analyzers/rspec/vbnet/S4158.json
================================================
{
"title": "Empty collections should not be accessed or iterated",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"symbolic-execution"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4158",
"sqKey": "S4158",
"scope": "All",
"quickfix": "infeasible"
}
================================================
FILE: analyzers/rspec/vbnet/S4159.html
================================================
The Attributed Programming Model, also known as Attribute-oriented programming (@OP), is a programming model used to embed attributes within codes.
In this model, objects are required to conform to a specific structure so that they can be used by the Managed Extensibility Framework (MEF).
MEF provides a way to discover available components implicitly, via composition. A MEF component, called a part, declaratively specifies:
The ExportAttribute declares that a part "exports", or provides to the composition container, an object that fulfills a particular contract.
During composition, parts with imports that have matching contracts will have those dependencies filled by the exported object.
If the type doesn’t implement the interface it is exporting there will be an issue at runtime (either a cast exception or just a container not filled with the exported type) leading to unexpected behaviors/crashes.
The rule raises an issue when a class doesn’t implement or inherit the type declared in the ExportAttribute.
<Export(GetType(ISomeType))> Public Class SomeType ' Noncompliant: doesn't implement 'ISomeType'. End Class
<Export(GetType(ISomeType))>
Public Class SomeType
Inherits ISomeType
End Class
There’s no need to null test in conjunction with an TypeOf ... Is test. Nothing is not an instance of anything, so a null
check is redundant.
If (x IsNot Nothing And TypeOf x Is MyClass)
' ...
End If
If (x Is Nothing Or TypeOf x IsNot MyClass)
' ...
End If
If (TypeOf x Is MyClass)
' ...
End If
If (TypeOf x IsNot MyClass)
' ...
End If
================================================
FILE: analyzers/rspec/vbnet/S4201.json
================================================
{
"title": "Null checks should not be combined with \"TypeOf Is\" operator checks",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"redundant"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4201",
"sqKey": "S4201",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4210.html
================================================
When an assembly uses Windows Forms (classes and interfaces from the System.Windows.Forms namespace) its entry point should be marked
with the STAThreadAttribute to indicate that the threading model should be "Single-Threaded Apartment" (STA) which is the only one
supported by Windows Forms.
This rule raises an issue when the entry point (Shared Sub Main method) of an assembly using Windows Forms is not marked as STA.
Imports System.Windows.Forms
Public Class Foo
Shared Sub Main()
Dim winForm As Form = New Form
Application.Run(winForm)
End Sub
End Class
Imports System.Windows.Forms
Public Class Foo
<STAThread()> Shared Sub Main()
Dim winForm As Form = New Form
Application.Run(winForm)
End Sub
End Class
================================================
FILE: analyzers/rspec/vbnet/S4210.json
================================================
{
"title": "Windows Forms entry points should be marked with STAThread",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "2min"
},
"tags": [
"winforms",
"pitfall"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4210",
"sqKey": "S4210",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4225.html
================================================
Creating an extension method that extends Object is not recommended because it makes the method available on every type.
Extensions should be applied at the most specialized level possible, and that is very unlikely to be Object.
Imports System.Runtime.CompilerServices
Module MyExtensions
<Extension>
Sub SomeExtension(obj As Object) ' Noncompliant
' ...
End Sub
End Module
================================================
FILE: analyzers/rspec/vbnet/S4225.json
================================================
{
"title": "Extension methods should not extend \"Object\"",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW"
},
"attribute": "FOCUSED"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-4225",
"sqKey": "S4225",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4260.html
================================================
When creating a custom Markup Extension
that accepts parameters in WPF, the ConstructorArgument markup
must be used to identify the discrete properties that match these parameters. However since this is done via a string, the compiler won’t give you any
warning in case there are typos.
This rule raises an issue when the string argument to ConstructorArgumentAttribute doesn’t match any parameter of any constructor.
Imports System
Namespace MyLibrary
Public Class MyExtension
Inherits MarkupExtension
Public Sub New()
End Sub
Public Sub New(ByVal value1 As Object)
Value1 = value1
End Sub
<ConstructorArgument("value2")> ' Noncompliant
Public Property Value1 As Object
End Class
End Namespace
Imports System
Namespace MyLibrary
Public Class MyExtension
Inherits MarkupExtension
Public Sub New()
End Sub
Public Sub New(ByVal value1 As Object)
Value1 = value1
End Sub
<ConstructorArgument("value1")>
Public Property Value1 As Object
End Class
End Namespace
Properties provide a way to enforce encapsulation by providing
property procedures that give controlled access to Private fields. However, in classes with multiple fields, it is not unusual that copy-and-paste is used to quickly create the needed properties, which can result
in the wrong field being accessed by the property procedures.
Class C
Private _x As Integer
Private _Y As Integer
Public ReadOnly Property Y As Integer
Get
Return _x ' Noncompliant: The returned field should be '_y'
End Get
End Property
End Class
This rule raises an issue in any of these cases:
For simple properties, it is better to use auto-implemented properties (VB.NET 10.0 or later).
Field and property names are compared as case-insensitive. All underscore characters are ignored.
Public Class Sample
Private _x As Integer
Private _y As Integer
Public Property Y As Integer
Get
Return _x ' Noncompliant: field '_y' is not used in the return value
End Get
Set(value As Integer)
_x = value ' Noncompliant: field '_y' is not updated
End Set
End Property
End Class
Public Class Sample
Private _x As Integer
Private _y As Integer
Public Property Y As Integer
Get
Return _y
End Get
Set(value As Integer)
_y = value
End Set
End Property
End Class
Marking a class with PartCreationPolicy(CreationPolicy.Shared), which is
part of Managed Extensibility Framework (MEF), means that a single, shared
instance of the exported object will be created. Therefore it doesn’t make sense to create new instances using the constructor and it will most likely
result in unexpected behaviours.
This rule raises an issue when a constructor of a class marked shared with a PartCreationPolicyAttribute is invoked.
<Export(GetType(IFooBar))>
<PartCreationPolicy(CreationPolicy.[Shared])>
Public Class FooBar
Inherits IFooBar
End Class
Public Class Program
Public Shared Sub Main()
Dim fooBar = New FooBar() ' Noncompliant
End Sub
End Class
<Export(GetType(IFooBar))>
<PartCreationPolicy(CreationPolicy.[Shared])>
Public Class FooBar
Inherits IFooBar
End Class
Public Class Program
Public Shared Sub Main()
Dim fooBar = serviceProvider.GetService(Of IFooBar)()
End Sub
End Class
This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
To provide communication security over a network, SSL and TLS are generally used. However, it is important to note that the following protocols are all considered weak by the cryptographic community, and are officially deprecated:
When these unsecured protocols are used, it is best practice to expect a breach: that a user or organization with malicious intent will perform mathematical attacks on this data after obtaining it by other means.
After retrieving encrypted data and performing cryptographic attacks on it on a given timeframe, attackers can recover the plaintext that encryption was supposed to protect.
Depending on the recovered data, the impact may vary.
Below are some real-world scenarios that illustrate the potential impact of an attacker exploiting the vulnerability.
By modifying the plaintext of the encrypted message, an attacker may be able to trigger additional vulnerabilities in the code. An attacker can
further exploit a system to obtain more information.
Encrypted values are often considered trustworthy because it would not be possible for a third party to modify them under normal circumstances.
When encrypted data contains personal or sensitive information, its retrieval by an attacker can lead to privacy violations, identity theft, financial loss, reputational damage, or unauthorized access to confidential systems.
In this scenario, the company, its employees, users, and partners could be seriously affected.
The impact is twofold, as data breaches and exposure of encrypted data can undermine trust in the organization, as customers, clients and stakeholders may lose confidence in the organization’s ability to protect their sensitive data.
In many industries and locations, there are legal and compliance requirements to protect sensitive data. If encrypted data is compromised and the plaintext can be recovered, companies face legal consequences, penalties, or violations of privacy laws.
These samples use a default TLS algorithm, which is a weak cryptographical algorithm: TLSv1.0.
Imports System.Net
Imports System.Security.Authentication
Public Sub Encrypt()
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls ' Noncompliant
End Sub
Imports System.Net.Http
Imports System.Security.Authentication
Public Sub Encrypt()
Dim Handler As New HttpClientHandler With {
.SslProtocols = SslProtocols.Tls ' Noncompliant
}
End Sub
Imports System.Net
Imports System.Security.Authentication
Public Sub Encrypt()
ServicePointManager.SecurityProtocol = _
SecurityProtocolType.Tls12 _
Or SecurityProtocolType.Tls13
End Sub
Imports System.Net.Http
Imports System.Security.Authentication
Public Sub Encrypt()
Dim Handler As New HttpClientHandler With {
.SslProtocols = SslProtocols.Tls12
}
End Sub
As a rule of thumb, by default you should use the cryptographic algorithms and mechanisms that are considered strong by the cryptographic community.
The best choices at the moment are the following.
Even though TLS V1.3 is available, using TLS v1.2 is still considered good and secure practice by the cryptography community.
The use of TLS v1.2 ensures compatibility with a wide range of platforms and enables seamless communication between different systems that do not yet have TLS v1.3 support.
The only drawback depends on whether the framework used is outdated: its TLS v1.2 settings may enable older and insecure cipher suites that are deprecated as insecure.
On the other hand, TLS v1.3 removes support for older and weaker cryptographic algorithms, eliminates known vulnerabilities from previous TLS versions, and improves performance.
To customize the default behavior for an export in the Managed Extensibility
Framework (MEF), applying the PartCreationPolicyAttribute
is necessary. For the PartCreationPolicyAttribute
to be meaningful in the context of an export, the class must also be annotated with the ExportAttribute.
This rule raises an issue when a class is annotated with the PartCreationPolicyAttribute but not with the
ExportAttribute.
Imports System.ComponentModel.Composition
<PartCreationPolicy(CreationPolicy.Any)> ' Noncompliant
Public Class FooBar
Inherits IFooBar
End Class
Imports System.ComponentModel.Composition
<Export(GetType(IFooBar))>
<PartCreationPolicy(CreationPolicy.Any)>
Public Class FooBar
Inherits IFooBar
End Class
Development tools and frameworks usually have options to make debugging easier for developers. Although these features are useful during development, they should never be enabled for applications deployed in production. Debug instructions or error messages can leak detailed information about the system, like the application’s path or file names.
There is a risk if you answered yes to any of those questions.
Do not enable debugging features on production servers.
The .Net Core framework offers multiple features which help during debug.
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage and
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage are two of them. Make sure that those features are disabled in
production.
Use If env.IsDevelopment() to disable debug code.
This rule raises issues when the following .Net Core methods are called:
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage,
Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage.
Imports Microsoft.AspNetCore.Builder
Imports Microsoft.AspNetCore.Hosting
Namespace MyMvcApp
Public Class Startup
Public Sub Configure(ByVal app As IApplicationBuilder, ByVal env As IHostingEnvironment)
' Those calls are Sensitive because it seems that they will run in production
app.UseDeveloperExceptionPage() 'Sensitive
app.UseDatabaseErrorPage() 'Sensitive
End Sub
End Class
End Namespace
Imports Microsoft.AspNetCore.Builder
Imports Microsoft.AspNetCore.Hosting
Namespace MyMvcApp
Public Class Startup
Public Sub Configure(ByVal app As IApplicationBuilder, ByVal env As IHostingEnvironment)
If env.IsDevelopment() Then ' Compliant
' The following calls are ok because they are disabled in production
app.UseDeveloperExceptionPage()
app.UseDatabaseErrorPage()
End If
End Sub
End Class
End Namespace
The DebuggerDisplayAttribute is used to determine how an object is displayed in the debugger window.
The DebuggerDisplayAttribute constructor takes a single mandatory argument: the string to be displayed in the value column for
instances of the type. Any text within curly braces is evaluated as the name of a field or property, or any complex expression containing method calls
and operators.
Naming a non-existent member between curly braces will result in a BC30451 error in the debug window when debugging objects. Although there is no impact on the production code, providing a wrong value can lead to difficulties when debugging the application.
This rule raises an issue when text specified between curly braces refers to members that don’t exist in the current context.
<DebuggerDisplay("Name: {Name}")> ' Noncompliant - Name doesn't exist in this context
Public Class Person
Public Property FullName As String
End Class
<DebuggerDisplay("Name: {FullName}")>
Public Class Person
Public Property FullName As String
End Class
================================================
FILE: analyzers/rspec/vbnet/S4545.json
================================================
{
"title": "\"DebuggerDisplayAttribute\" strings should reference existing members",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4545",
"sqKey": "S4545",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4581.html
================================================
When the syntax new Guid() (i.e. parameterless instantiation) is used, it must be that one of three things is wanted:
Guid.Empty is clearer.Guid.NewGuid() should be used.This rule raises an issue when a parameterless instantiation of the Guid struct is found.
Public Sub Foo()
Dim G1 As New Guid ' Noncompliant - what's the intent?
Dim G2 As Guid = Nothing ' Noncompliant
End Sub
public void Foo(byte[] bytes)
Public Sub Foo(Bytes As Byte())
Dim G1 As Guid = Guid.Empty
Dim G2 As Guid = Guid.NewGuid()
Dim G3 As Guid = New Guid(Bytes)
End Sub
================================================
FILE: analyzers/rspec/vbnet/S4581.json
================================================
{
"title": "\"new Guid()\" should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5 min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-4581",
"sqKey": "S4581",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S4583.html
================================================
When calling the BeginInvoke method of a delegate,
resources are allocated that are only freed up when EndInvoke is called. Failing to pair BeginInvoke with
EndInvoke can lead to resource leaks and incomplete asynchronous calls.
This rule raises an issue in the following scenarios:
BeginInvoke method is called without any callback, and it is not paired with a call to EndInvoke in the same
block.IAsyncResult does not contain a call to EndInvoke in the same block.BeginInvoke without callback:
Public Delegate Function AsyncMethodCaller() As String
Public Class Sample
Public Sub DoSomething()
Dim Example As New AsyncExample()
Dim Caller As New AsyncMethodCaller(Example.SomeMethod)
' Initiate the asynchronous call.
Dim Result As IAsyncResult = Caller.BeginInvoke(Nothing, Nothing) ' Noncompliant: Not paired With EndInvoke
End Sub
End Class
BeginInvoke with callback:
Public Delegate Function AsyncMethodCaller() As String
Public Class Sample
Public Sub DoSomething()
Dim Example As New AsyncExample()
Dim Caller As New AsyncMethodCaller(Example.SomeMethod)
' Initiate the asynchronous call.
Dim Result As IAsyncResult = Caller.BeginInvoke(New AsyncCallback(Sub(ar)
End Sub), Nothing) ' Noncompliant: Not paired With EndInvoke
End Sub
End Class
BeginInvoke without callback:
Public Delegate Function AsyncMethodCaller() As String
Public Class Sample
Public Function DoSomething() As String
Dim Example As New AsyncExample()
Dim Caller As New AsyncMethodCaller(Example.SomeMethod)
' Initiate the asynchronous call.
Dim Result As IAsyncResult = Caller.BeginInvoke(Nothing, Nothing)
' ...
Return Caller.EndInvoke(Result)
End Function
End Class
BeginInvoke with callback:
Public Delegate Function AsyncMethodCaller() As String
Public Class Sample
Public Sub DoSomething()
Dim Example As New AsyncExample()
Dim Caller As New AsyncMethodCaller(Example.SomeMethod)
' Initiate the asynchronous call.
Dim Result As IAsyncResult = Caller.BeginInvoke(New AsyncCallback(Sub(ar)
' Call EndInvoke to retrieve the results.
Dim Ret As String = Caller.EndInvoke(ar)
' ...
End Sub), Nothing)
End Sub
End Class
Returning Nothing from a non-Async Task/Task(Of TResult) procedure will cause a
NullReferenceException at runtime if the procedure is awaited. This problem can be avoided by returning Task.CompletedTask or Task.FromResult(Of TResult)(Nothing)
respectively.
Public Function DoFooAsync() As Task
Return Nothing ' Noncompliant: Causes a NullReferenceException if awaited.
End Function
Public Async Function Main() As Task
Await DoFooAsync() ' NullReferenceException
End Function
Instead of Nothing Task.CompletedTask or Task.FromResult(Of TResult)(Nothing)
should be returned.
A Task returning procedure can be fixed like so:
Public Function DoFooAsync() As Task
Return Nothing ' Noncompliant: Causes a NullReferenceException if awaited.
End Function
Public Function DoFooAsync() As Task
Return Task.CompletedTask ' Compliant: Method can be awaited.
End Function
A Task(Of TResult) returning procedure can be fixed like so:
Public Function GetFooAsync() As Task(Of Object)
Return Nothing ' Noncompliant: Causes a NullReferenceException if awaited.
End Function
Public Function GetFooAsync() As Task(Of Object)
Return Task.FromResult(Of Object)(Nothing) ' Compliant: Method can be awaited.
End Function
Task.CompletedTask PropertyTask.FromResult<TResult>(TResult)
MethodEmpty comments, as shown in the example, hurt readability and might indicate an oversight.
' '''
Some meaningful text should be added to the comment, or the comment markers should be removed.
================================================ FILE: analyzers/rspec/vbnet/S4663.json ================================================ { "title": "Comments should not be empty", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "CLEAR" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "1min" }, "tags": [], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-4663", "sqKey": "S4663", "scope": "Main", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S4790.html ================================================Cryptographic hash algorithms such as MD2, MD4, MD5, MD6, HAVAL-128,
DSA (which uses SHA-1), RIPEMD, RIPEMD-128, RIPEMD-160and SHA-1 are no
longer considered secure, because it is possible to have collisions (little computational effort is enough to find two or more different
inputs that produce the same hash).
Message authentication code (MAC) algorithms such as HMAC-MD5 or HMAC-SHA1 use weak hash functions as building blocks.
Although they are not all proven to be weak, they are considered legacy algorithms and should be avoided.
The hashed value is used in a security context like:
There is a risk if you answered yes to any of those questions.
Safer alternatives, such as SHA-256, SHA-512, SHA-3 are recommended, and for password hashing, it’s even
better to use algorithms that do not compute too "quickly", like bcrypt, scrypt, argon2 or pbkdf2
because it slows down brute force attacks.
Imports System.Security.Cryptography
Sub ComputeHash()
' Review all instantiations of classes that inherit from HashAlgorithm, for example:
Dim hashAlgo As HashAlgorithm = HashAlgorithm.Create() ' Sensitive
Dim hashAlgo2 As HashAlgorithm = HashAlgorithm.Create("SHA1") ' Sensitive
Dim sha As SHA1 = New SHA1CryptoServiceProvider() ' Sensitive
Dim md5 As MD5 = New MD5CryptoServiceProvider() ' Sensitive
' ...
End Sub
Class MyHashAlgorithm
Inherits HashAlgorithm ' Sensitive
' ...
End Class
Imports System.Security.Cryptography
Sub ComputeHash()
Dim sha256 = New SHA256CryptoServiceProvider() ' Compliant
Dim sha384 = New SHA384CryptoServiceProvider() ' Compliant
Dim sha512 = New SHA512CryptoServiceProvider() ' Compliant
' ...
End Sub
This rule is deprecated, and will eventually be removed.
Configuring loggers is security-sensitive. It has led in the past to the following vulnerabilities:
Logs are useful before, during and after a security incident.
Logs are also a target for attackers because they might contain sensitive information. Configuring loggers has an impact on the type of information logged and how they are logged.
This rule flags for review code that initiates loggers configuration. The goal is to guide security code reviews.
There is a risk if you answered yes to any of those questions.
Remember that configuring loggers properly doesn’t make them bullet-proof. Here is a list of recommendations explaining on how to use your logs:
.Net Core: configure programmatically
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports Microsoft.AspNetCore
Imports Microsoft.AspNetCore.Builder
Imports Microsoft.AspNetCore.Hosting
Imports Microsoft.Extensions.Configuration
Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.Logging
Imports Microsoft.Extensions.Options
Namespace MvcApp
Public Class ProgramLogging
Public Shared Function CreateWebHostBuilder(args As String()) As IWebHostBuilder
WebHost.CreateDefaultBuilder(args) _
.ConfigureLogging(Function(hostingContext, Logging) ' Sensitive
' ...
End Function) _
.UseStartup(Of StartupLogging)()
'...
End Function
End Class
Public Class StartupLogging
Public Sub ConfigureServices(services As IServiceCollection)
services.AddLogging(Function(logging) ' Sensitive
'...
End Function)
End Sub
Public Sub Configure(app As IApplicationBuilder, env As IHostingEnvironment, loggerFactory As ILoggerFactory)
Dim config As IConfiguration = Nothing
Dim level As LogLevel = LogLevel.Critical
Dim includeScopes As Boolean = False
Dim filter As Func(Of String, Microsoft.Extensions.Logging.LogLevel, Boolean) = Nothing
Dim consoleSettings As Microsoft.Extensions.Logging.Console.IConsoleLoggerSettings = Nothing
Dim azureSettings As Microsoft.Extensions.Logging.AzureAppServices.AzureAppServicesDiagnosticsSettings = Nothing
Dim eventLogSettings As Microsoft.Extensions.Logging.EventLog.EventLogSettings = Nothing
' An issue will be raised for each call to an ILoggerFactory extension methods adding loggers.
loggerFactory.AddAzureWebAppDiagnostics() ' Sensitive
loggerFactory.AddAzureWebAppDiagnostics(azureSettings) ' Sensitive
loggerFactory.AddConsole() ' Sensitive
loggerFactory.AddConsole(level) ' Sensitive
loggerFactory.AddConsole(level, includeScopes) ' Sensitive
loggerFactory.AddConsole(filter) ' Sensitive
loggerFactory.AddConsole(filter, includeScopes) ' Sensitive
loggerFactory.AddConsole(config) ' Sensitive
loggerFactory.AddConsole(consoleSettings) ' Sensitive
loggerFactory.AddDebug() ' Sensitive
loggerFactory.AddDebug(level) ' Sensitive
loggerFactory.AddDebug(filter) ' Sensitive
loggerFactory.AddEventLog() ' Sensitive
loggerFactory.AddEventLog(eventLogSettings) ' Sensitive
loggerFactory.AddEventLog(level) ' Sensitive
' Only available for NET Standard 2.0 and above
'loggerFactory.AddEventSourceLogger() ' Sensitive
Dim providers As IEnumerable(Of ILoggerProvider) = Nothing
Dim filterOptions1 As LoggerFilterOptions = Nothing
Dim filterOptions2 As IOptionsMonitor(Of LoggerFilterOptions) = Nothing
Dim factory As LoggerFactory = New LoggerFactory() ' Sensitive
factory = New LoggerFactory(providers) ' Sensitive
factory = New LoggerFactory(providers, filterOptions1) ' Sensitive
factory = New LoggerFactory(providers, filterOptions2) ' Sensitive
End Sub
End Class
End Namespace
Log4Net
Imports System
Imports System.IO
Imports System.Xml
Imports log4net.Appender
Imports log4net.Config
Imports log4net.Repository
Namespace Logging
Class Log4netLogging
Private Sub Foo(ByVal repository As ILoggerRepository, ByVal element As XmlElement, ByVal configFile As FileInfo, ByVal configUri As Uri, ByVal configStream As Stream, ByVal appender As IAppender, ParamArray appenders As IAppender())
log4net.Config.XmlConfigurator.Configure(repository) ' Sensitive
log4net.Config.XmlConfigurator.Configure(repository, element) ' Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configFile) ' Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configUri) ' Sensitive
log4net.Config.XmlConfigurator.Configure(repository, configStream) ' Sensitive
log4net.Config.XmlConfigurator.ConfigureAndWatch(repository, configFile) ' Sensitive
log4net.Config.DOMConfigurator.Configure() ' Sensitive
log4net.Config.DOMConfigurator.Configure(repository) ' Sensitive
log4net.Config.DOMConfigurator.Configure(element) ' Sensitive
log4net.Config.DOMConfigurator.Configure(repository, element) ' Sensitive
log4net.Config.DOMConfigurator.Configure(configFile) ' Sensitive
log4net.Config.DOMConfigurator.Configure(repository, configFile) ' Sensitive
log4net.Config.DOMConfigurator.Configure(configStream) ' Sensitive
log4net.Config.DOMConfigurator.Configure(repository, configStream) ' Sensitive
log4net.Config.DOMConfigurator.ConfigureAndWatch(configFile) ' Sensitive
log4net.Config.DOMConfigurator.ConfigureAndWatch(repository, configFile) ' Sensitive
log4net.Config.BasicConfigurator.Configure() ' Sensitive
log4net.Config.BasicConfigurator.Configure(appender) ' Sensitive
log4net.Config.BasicConfigurator.Configure(appenders) ' Sensitive
log4net.Config.BasicConfigurator.Configure(repository) ' Sensitive
log4net.Config.BasicConfigurator.Configure(repository, appender) ' Sensitive
log4net.Config.BasicConfigurator.Configure(repository, appenders) ' Sensitive
End Sub
End Class
End Namespace
NLog: configure programmatically
Namespace Logging
Class NLogLogging
Private Sub Foo(ByVal config As NLog.Config.LoggingConfiguration)
NLog.LogManager.Configuration = config ' Sensitive
End Sub
End Class
End Namespace
Serilog
Namespace Logging
Class SerilogLogging
Private Sub Foo()
Dim config As Serilog.LoggerConfiguration = New Serilog.LoggerConfiguration() ' Sensitive
End Sub
End Class
End Namespace
This vulnerability makes it possible that an encrypted communication is intercepted.
Transport Layer Security (TLS) provides secure communication between systems over the internet by encrypting the data sent between them. Certificate validation adds an extra layer of trust and security to this process to ensure that a system is indeed the one it claims to be.
When certificate validation is disabled, the client skips a critical security check. This creates an opportunity for attackers to pose as a trusted entity and intercept, manipulate, or steal the data being transmitted.
Establishing trust in a secure way is a non-trivial task. When you disable certificate validation, you are removing a key mechanism designed to build this trust in internet communication, opening your system up to a number of potential threats.
If a system does not validate certificates, it cannot confirm the identity of the other party involved in the communication. An attacker can exploit this by creating a fake server and masquerading as a legitimate one. For example, they might set up a server that looks like your bank’s server, tricking your system into thinking it is communicating with the bank. This scenario, called identity spoofing, allows the attacker to collect any data your system sends to them, potentially leading to significant data breaches.
When TLS certificate validation is disabled, the integrity of the data you send and receive cannot be guaranteed. An attacker could modify the data in transit, and you would have no way of knowing. This could range from subtle manipulations of the data you receive to the injection of malicious code or malware into your system. The consequences of such breaches of data integrity can be severe, depending on the nature of the data and the system.
In the following example, the callback change impacts the entirety of HTTP requests made by the application.
The certificate validation gets disabled by overriding ServerCertificateValidationCallback with an empty implementation. It is highly
recommended to use the original implementation.
Imports System.Net
Public Sub Send()
ServicePointManager.ServerCertificateValidationCallback =
Function(sender, certificate, chain, errors) True ' Noncompliant
Dim request As System.Net.HttpWebRequest = System.Net.HttpWebRequest.Create(New System.Uri("https://example.com"))
request.Method = System.Net.WebRequestMethods.Http.Get
Dim response As System.Net.HttpWebResponse = request.GetResponse()
response.Close()
End Sub
Addressing the vulnerability of disabled TLS certificate validation primarily involves re-enabling the default validation.
To avoid running into problems with invalid certificates, consider the following sections.
If possible, always use a certificate issued by a well-known, trusted CA for your server. Most programming environments come with a predefined list of trusted root CAs, and certificates issued by these authorities are validated automatically. This is the best practice, and it requires no additional code or configuration.
In some cases, you might need to work with a server using a self-signed certificate, or a certificate issued by a CA not included in your trusted roots. Rather than disabling certificate validation in your code, you can add the necessary certificates to your trust store.
Successful Zip Bomb attacks occur when an application expands untrusted archive files without controlling the size of the expanded data, which can lead to denial of service. A Zip bomb is usually a malicious archive file of a few kilobytes of compressed data but turned into gigabytes of uncompressed data. To achieve this extreme compression ratio, attackers will compress irrelevant data (eg: a long string of repeated bytes).
Archives to expand are untrusted and:
There is a risk if you answered yes to any of those questions.
For Each entry As ZipArchiveEntry in archive.Entries
' entry.FullName could contain parent directory references ".." and the destinationPath variable could become outside of the desired path
string destinationPath = Path.GetFullPath(Path.Combine(path, entry.FullName))
entry.ExtractToFile(destinationPath) ' Sensitive, extracts the entry to a file
Dim stream As Stream
stream = entry.Open() ' Sensitive, the entry is about to be extracted
Next
Const ThresholdRatio As Double = 10
Const ThresholdSize As Integer = 1024 * 1024 * 1024 ' 1 GB
Const ThresholdEntries As Integer = 10000
Dim TotalSizeArchive, TotalEntryArchive, TotalEntrySize, Cnt As Integer
Dim Buffer(1023) As Byte
Using ZipToOpen As New FileStream("ZipBomb.zip", FileMode.Open), Archive As New ZipArchive(ZipToOpen, ZipArchiveMode.Read)
For Each Entry As ZipArchiveEntry In Archive.Entries
Using s As Stream = Entry.Open
TotalEntryArchive += 1
TotalEntrySize = 0
Do
Cnt = s.Read(Buffer, 0, Buffer.Length)
TotalEntrySize += Cnt
TotalSizeArchive += Cnt
If TotalEntrySize / Entry.CompressedLength > ThresholdRatio Then Exit Do ' Ratio between compressed And uncompressed data Is highly suspicious, looks Like a Zip Bomb Attack
Loop While Cnt > 0
End Using
If TotalSizeArchive > ThresholdSize Then Exit For ' The uncompressed data size Is too much for the application resource capacity
If TotalEntryArchive > ThresholdEntries Then Exit For ' Too much entries in this archive, can lead to inodes exhaustion of the system
Next
End Using
Operating systems have global directories where any user has write access. Those folders are mostly used as temporary storage areas like
/tmp in Linux based systems. An application manipulating files from these folders is exposed to race conditions on filenames: a malicious
user can try to create a file with a predictable name before the application does. A successful attack can result in other files being accessed,
modified, corrupted or deleted. This risk is even higher if the application runs with elevated permissions.
In the past, it has led to the following vulnerabilities:
This rule raises an issue whenever it detects a hard-coded path to a publicly writable directory like /tmp (see examples bellow). It
also detects access to environment variables that point to publicly writable directories, e.g., TMP, TMPDIR and
TEMP.
/tmp/var/tmp/usr/tmp/dev/shm/dev/mqueue/run/lock/var/run/lock/Library/Caches/Users/Shared/private/tmp/private/var/tmp\Windows\Temp\Temp\TMP%USERPROFILE%\AppData\Local\TempThere is a risk if you answered yes to any of those questions.
Out of the box, .NET is missing secure-by-design APIs to create temporary files. To overcome this, one of the following options can be used:
Using Writer As New StreamWriter("/tmp/f") ' Sensitive
' ...
End Using
Dim Tmp As String = Environment.GetEnvironmentVariable("TMP") ' Sensitive
Dim RandomPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())
' Creates a new file with write, non inheritable permissions which is deleted on close.
Using FileStream As New FileStream(RandomPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose)
Using Writer As New StreamWriter(FileStream) ' Sensitive
' ...
End Using
End Using
Temporary files are considered insecurely created when the file existence check is performed separately from the actual file creation. Such a situation can occur when creating temporary files using normal file handling functions or when using dedicated temporary file handling functions that are not atomic.
Creating temporary files in a non-atomic way introduces race condition issues in the application’s behavior. Indeed, a third party can create a given file between when the application chooses its name and when it creates it.
In such a situation, the application might use a temporary file that it does not entirely control. In particular, this file’s permissions might be different than expected. This can lead to trust boundary issues.
Attackers with control over a temporary file used by a vulnerable application will be able to modify it in a way that will affect the application’s logic. By changing this file’s Access Control List or other operating system-level properties, they could prevent the file from being deleted or emptied. They may also alter the file’s content before or while the application uses it.
Depending on why and how the affected temporary files are used, the exploitation of a race condition in an application can have various consequences. They can range from sensitive information disclosure to more serious application or hosting infrastructure compromise.
Because attackers can control the permissions set on temporary files and prevent their removal, they can read what the application stores in them. This might be especially critical if this information is sensitive.
For example, an application might use temporary files to store users' session-related information. In such a case, attackers controlling those files can access session-stored information. This might allow them to take over authenticated users' identities and entitlements.
An application might use temporary files to store technical data for further reuse or as a communication channel between multiple components. In that case, it might consider those files part of the trust boundaries and use their content without additional security validation or sanitation. In such a case, an attacker controlling the file content might use it as an attack vector for further compromise.
For example, an application might store serialized data in temporary files for later use. In such a case, attackers controlling those files' content can change it in a way that will lead to an insecure deserialization exploitation. It might allow them to execute arbitrary code on the application hosting server and take it over.
The following code example is vulnerable to a race condition attack because it creates a temporary file using an unsafe API function.
Imports System.IO
Sub Example()
Dim TempPath = Path.GetTempFileName() 'Noncompliant
Using Writer As New StreamWriter(TempPath)
Writer.WriteLine("content")
End Using
End Sub
Imports System.IO
Sub Example()
Dim RandomPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())
Using FileStream As New FileStream(RandomPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose)
Using Writer As New StreamWriter(FileStream)
Writer.WriteLine("content")
End Using
End Using
End Sub
Applications should create temporary files so that no third party can read or modify their content. It requires that the files' name, location, and permissions are carefully chosen and set. This can be achieved in multiple ways depending on the applications' technology stacks.
Temporary files can be created using unsafe functions and API as long as strong security controls are applied. Non-temporary file-handling functions and APIs can also be used for that purpose.
In general, applications should ensure that attackers can not create a file before them. This turns into the following requirements when creating the files:
Moreover, when possible, it is recommended that applications destroy temporary files after they have finished using them.
Here the example compliant code uses the Path.GetTempPath and Path.GetRandomFileName functions to generate a unique
random file name. The file is then open with the FileMode.CreateNew option that will ensure the creation fails if the file already
exists. The FileShare.None option will additionally prevent the file from being opened again by any process. To finish, this code ensures
the file will get destroyed once the application has finished using it with the FileOptions.DeleteOnClose option.
This vulnerability exposes encrypted data to a number of attacks whose goal is to recover the plaintext.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communications in a variety of domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
For AES, the weakest mode is ECB (Electronic Codebook). Repeated blocks of data are encrypted to the same value, making them easy to identify and reducing the difficulty of recovering the original cleartext.
Unauthenticated modes such as CBC (Cipher Block Chaining) may be used but are prone to attacks that manipulate the ciphertext. They must be used with caution.
For RSA, the weakest algorithms are either using it without padding or using the PKCS1v1.5 padding scheme.
The cleartext of an encrypted message might be recoverable. Additionally, it might be possible to modify the cleartext of an encrypted message.
Below are some real-world scenarios that illustrate possible impacts of an attacker exploiting the vulnerability.
The encrypted message might contain data that is considered sensitive and should not be known to third parties.
By using a weak algorithm the likelihood that an attacker might be able to recover the cleartext drastically increases.
By modifying the cleartext of the encrypted message it might be possible for an attacker to trigger other vulnerabilities in the code. Encrypted values are often considered trusted, since under normal circumstances it would not be possible for a third party to modify them.
Example with a symmetric cipher, AES:
Imports System.Security.Cryptography
Public Module Example
Public Sub Encrypt()
Dim Algorithm As New AesManaged() With {
.KeySize = 128,
.BlockSize = 128,
.Mode = CipherMode.ECB, ' Noncompliant
.Padding = PaddingMode.PKCS7
}
End Sub
End Module
Example with an asymmetric cipher, RSA:
Imports System.Security.Cryptography
Public Module Example
Public Sub Encrypt()
Dim data(10) As Byte
Dim RsaCsp = New RSACryptoServiceProvider()
RsaCsp.Encrypt(data, False) ' Noncompliant
End Sub
End Module
For the AES symmetric cipher, use the GCM mode:
Imports System.Security.Cryptography
Public Module Example
Public Sub Encrypt()
Dim data(10) As Byte
Dim Algorithm As New AesGcm(data)
End Sub
End Module
For the RSA asymmetric cipher, use the Optimal Asymmetric Encryption Padding (OAEP):
Imports System.Security.Cryptography
Public Module Example
Public Sub Encrypt()
Dim data(10) As Byte
Dim RsaCsp = New RSACryptoServiceProvider()
RsaCsp.Encrypt(data, True) ' Noncompliant
End Sub
End Module
As a rule of thumb, use the cryptographic algorithms and mechanisms that are considered strong by the cryptographic community.
Appropriate choices are currently the following.
The best-known authenticated encryption mode for AES is Galois/Counter mode (GCM).
GCM mode combines encryption with authentication and integrity checks using a cryptographic hash function and provides both confidentiality and authenticity of data.
Other similar modes are:
Counter with CBC-MACCipher Block Chaining with Message Authentication CodeEncrypt-and-AuthenticateInteger Authenticated Parallelizable ModeOffset Codebook ModeIt is also possible to use AES-CBC with HMAC for integrity checks. However, it is considered more straightforward to use AES-GCM directly instead.
The Optimal Asymmetric Encryption Padding scheme (OAEP) adds randomness and a secure hash function that strengthens the regular inner workings of RSA.
This vulnerability makes it possible that the cleartext of the encrypted message might be recoverable without prior knowledge of the key.
Encryption algorithms are essential for protecting sensitive information and ensuring secure communication in various domains. They are used for several important reasons:
When selecting encryption algorithms, tools, or combinations, you should also consider two things:
For these reasons, as soon as cryptography is included in a project, it is important to choose encryption algorithms that are considered strong and secure by the cryptography community.
The cleartext of an encrypted message might be recoverable. Additionally, it might be possible to modify the cleartext of an encrypted message.
Below are some real-world scenarios that illustrate some impacts of an attacker exploiting the vulnerability.
The encrypted message might contain data that is considered sensitive and should not be known to third parties.
By using a weak algorithm the likelihood that an attacker might be able to recover the cleartext drastically increases.
By modifying the cleartext of the encrypted message it might be possible for an attacker to trigger other vulnerabilities in the code. Encrypted values are often considered trusted, since under normal circumstances it would not be possible for a third party to modify them.
The following code contains examples of algorithms that are not considered highly resistant to cryptanalysis and thus should be avoided.
Imports System.Security.Cryptography
Public Sub Encrypt()
Dim SimpleDES As New DESCryptoServiceProvider() ' Noncompliant
End Sub
Imports System.Security.Cryptography
Public Sub Encrypt()
Dim AES128ECB = Aes.Create()
End Sub
It is highly recommended to use an algorithm that is currently considered secure by the cryptographic community. A common choice for such an algorithm is the Advanced Encryption Standard (AES).
For block ciphers, it is not recommended to use algorithms with a block size that is smaller than 128 bits.
The following code contains examples of algorithms that are not considered highly resistant to cryptanalysis and thus should be avoided.
```suggestion
Imports Org.BouncyCastle.Crypto.Engines
Imports Org.BouncyCastle.Crypto.Parameters
Public Sub Encrypt()
Dim AesFast As new AesFastEngine() ' Noncompliant
End Sub
Imports Org.BouncyCastle.Crypto.Engines
Imports Org.BouncyCastle.Crypto.Parameters
```suggestion
Public Sub Encrypt()
Dim AES As new AESEngine()
End Sub
It is highly recommended to use an algorithm that is currently considered secure by the cryptographic community. A common choice for such an algorithm is the Advanced Encryption Standard (AES).
For block ciphers, it is not recommended to use algorithms with a block size that is smaller than 128 bits.
This vulnerability allows forging of JSON Web Tokens to impersonate other users.
JSON Web Tokens (JWTs), a popular method of securely transmitting information between parties as a JSON object, can become a significant security risk when they are not properly signed with a robust cipher algorithm, left unsigned altogether, or if the signature is not verified. This vulnerability class allows malicious actors to craft fraudulent tokens, effectively impersonating user identities. In essence, the integrity of a JWT hinges on the strength and presence of its signature.
When a JSON Web Token is not appropriately signed with a strong cipher algorithm or if the signature is not verified, it becomes a significant threat to data security and the privacy of user identities.
JWTs are commonly used to represent user authorization claims. They contain information about the user’s identity, user roles, and access rights. When these tokens are not securely signed, it allows an attacker to forge them. In essence, a weak or missing signature gives an attacker the power to craft a token that could impersonate any user. For instance, they could create a token for an administrator account, gaining access to high-level permissions and sensitive data.
When a JWT is not securely signed, it can be tampered with by an attacker, and the integrity of the data it carries cannot be trusted. An attacker can manipulate the content of the token and grant themselves permissions they should not have, leading to unauthorized data access.
The following code contains examples of JWT encoding and decoding without a strong cipher algorithm.
Imports JWT
Public Sub Decode(decoder AS IJwtDecoder)
Dim decoded As String = decoder.Decode(token, secret, verify:= false) ' Noncompliant
End Sub
Imports JWT
Public Sub Decode()
Dim decoded As String = new JwtBuilder()
.WithSecret(secret)
.Decode(token) ' Noncompliant
End Sub
Imports JWT
Public Sub Decode(decoder AS IJwtDecoder)
Dim decoded As String = decoder.Decode(token, secret, verify:= true)
End Sub
When using JwtBuilder, make sure to call MustVerifySignature().
Imports JWT
Public Sub Decode()
Dim decoded As String = new JwtBuilder()
.WithSecret(secret)
.MustVerifySignature()
.Decode(token)
End Sub
Resolving a vulnerability concerning the validation of JWT token signatures is mainly about incorporating a critical step into your process: validating the signature every time a token is decoded. Just having a signed token using a secure algorithm is not enough. If you are not validating signatures, they are not serving their purpose.
Every time your application receives a JWT, it needs to decode the token to extract the information contained within. It is during this decoding process that the signature of the JWT should also be checked.
To resolve the issue, follow these instructions:
By following these practices, you can ensure the security of your application’s JWT handling process, making it resistant to attacks that rely on tampering with tokens. Validation of the signature needs to be an integral and non-negotiable part of your token handling process.
Ensure that your secret keys are stored securely. They should not be hard-coded into your application code or checked into your version control system. Instead, consider using environment variables, secure key management systems, or vault services.
Even with the strongest cipher algorithms, there is a risk that your secret keys may be compromised. Therefore, it is a good practice to periodically rotate your secret keys. By doing so, you limit the amount of time that an attacker can misuse a stolen key. When you rotate keys, be sure to allow a grace period where tokens signed with the old key are still accepted to prevent service disruptions.
Rejecting requests with significant content length is a good practice to control the network traffic intensity and thus resource consumption in order to prevent DoS attacks.
There is a risk if you answered yes to any of those questions.
It is recommended to customize the rule with the limit values that correspond to the web application.
Imports Microsoft.AspNetCore.Mvc
Public Class MyController
Inherits Controller
<HttpPost>
<DisableRequestSizeLimit> ' Sensitive: No size limit
<RequestSizeLimit(10485760)> ' Sensitive: 10485760 B = 10240 KB = 10 MB is more than the recommended limit of 8MB
Public Function PostRequest(Model model) As IActionResult
' ...
End Function
<HttpPost>
<RequestFormLimits(MultipartBodyLengthLimit = 10485760)> ' Sensitive: 10485760 B = 10240 KB = 10 MB is more than the recommended limit of 8MB
Public Function MultipartFormRequest(Model model) As IActionResult
' ...
End Function
End Class
Imports Microsoft.AspNetCore.Mvc
Public Class MyController
Inherits Controller
<HttpPost>
<RequestSizeLimit(8388608)> ' Compliant: 8388608 B = 8192 KB = 8 MB
Public Function PostRequest(Model model) As IActionResult
' ...
End Function
<HttpPost>
<RequestFormLimits(MultipartBodyLengthLimit = 8388608)> ' Compliant: 8388608 B = 8192 KB = 8 MB
Public Function MultipartFormRequest(Model model) AS IActionResult
' ...
End Function
End Class
ASP.NET 1.1+ comes with a feature called Request Validation, preventing the server to accept content containing un-encoded HTML. This feature comes as a first protection layer against Cross-Site Scripting (XSS) attacks and act as a simple Web Application Firewall (WAF) rejecting requests potentially containing malicious content.
While this feature is not a silver bullet to prevent all XSS attacks, it helps to catch basic ones. It will for example prevent <script
type="text/javascript" src="https://malicious.domain/payload.js"> to reach your Controller.
Note: Request Validation feature being only available for ASP.NET, no Security Hotspot is raised on ASP.NET Core applications.
There is a risk if you answered yes to any of those questions.
At Controller level:
<ValidateInput(False)> Public Function Welcome(Name As String) As ActionResult ... End Function
At application level, configured in the Web.config file:
<configuration>
<system.web>
<pages validateRequest="false" />
...
<httpRuntime requestValidationMode="0.0" />
</system.web>
</configuration>
At Controller level:
<ValidateInput(True)> Public Function Welcome(Name As String) As ActionResult ... End Function
or
Public Function Welcome(Name As String) As ActionResult ... End Function
At application level, configured in the Web.config file:
<configuration>
<system.web>
<pages validateRequest="true" />
...
<httpRuntime requestValidationMode="4.5" />
</system.web>
</configuration>
Deserialization is the process of converting serialized data (such as objects or data structures) back into their original form. Types allowed to be unserialized should be strictly controlled.
During the deserialization process, the state of an object will be reconstructed from the serialized data stream. By allowing unrestricted deserialization of types, the application makes it possible for attackers to use types with dangerous or otherwise sensitive behavior during the deserialization process.
When an application deserializes untrusted data without proper restrictions, an attacker can craft malicious serialized objects. Depending on the affected objects and properties, the consequences can vary.
If attackers can craft malicious serialized objects that contain executable code, this code will run within the application’s context, potentially gaining full control over the system. This can lead to unauthorized access, data breaches, or even complete system compromise.
For example, a well-known attack vector consists in serializing an object of type TempFileCollection
with arbitrary files (defined by an attacker) which will be deleted on the application deserializing this object (when the finalize() method of
the TempFileCollection object is called). These kinds of specially crafted serialized objects are called "gadgets".
Unrestricted deserialization can also enable attackers to escalate their privileges within the application. By manipulating the serialized data, an attacker can modify object properties or bypass security checks, granting them elevated privileges that they should not have. This can result in unauthorized access to sensitive data, unauthorized actions, or even administrative control over the application.
In some cases, an attacker can abuse the deserialization process to cause a denial of service (DoS) condition. By providing specially crafted serialized data, the attacker can trigger excessive resource consumption, leading to system instability or unresponsiveness. This can disrupt the availability of the application, impacting its functionality and causing inconvenience to users.
With BinaryFormatter,
NetDataContractSerializer
or SoapFormatter:
Dim myBinaryFormatter = New BinaryFormatter() myBinaryFormatter.Deserialize(stream) ' Noncompliant
With JavaScriptSerializer:
Dim serializer1 As JavaScriptSerializer = New JavaScriptSerializer(New SimpleTypeResolver()) ' Noncompliant: SimpleTypeResolver is insecure (every type is resolved) serializer1.Deserialize(Of ExpectedType)(json)
With BinaryFormatter,
NetDataContractSerializer
or SoapFormatter:
NotInheritable Class CustomBinder
Inherits SerializationBinder
Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
If Not (Equals(typeName, "type1") OrElse Equals(typeName, "type2") OrElse Equals(typeName, "type3")) Then
Throw New SerializationException("Only type1, type2 and type3 are allowed")
End If
Return Assembly.Load(assemblyName).[GetType](typeName)
End Function
End Class
Dim myBinaryFormatter = New BinaryFormatter()
myBinaryFormatter.Binder = New CustomBinder()
myBinaryFormatter.Deserialize(stream)
With JavaScriptSerializer:
Public Class CustomSafeTypeResolver
Inherits JavaScriptTypeResolver
Public Overrides Function ResolveType(id As String) As Type
If Not Equals(id, "ExpectedType") Then
Throw New ArgumentNullException("Only ExpectedType is allowed during deserialization")
End If
Return Type.[GetType](id)
End Function
End Class
Dim serializer As JavaScriptSerializer = New JavaScriptSerializer(New CustomSafeTypeResolver())
serializer.Deserialize(Of ExpectedType)(json)
Instead of using BinaryFormatter
and similar serializers, it is recommended to use safer alternatives in most of the cases, such as XmlSerializer or DataContractSerializer.
If it’s not possible then try to mitigate the risk by restricting the types allowed to be deserialized:
Regular expressions have their own syntax that is understood by regular expression engines. Those engines will throw an exception at runtime if they are given a regular expression that does not conform to that syntax.
To avoid syntax errors, special characters should be escaped with backslashes when they are intended to be matched literally and references to capturing groups should use the correctly spelled name or number of the group.
Negative lookaround groups cannot be combined with RegexOptions.NonBacktracking. Such combination would throw an exception during runtime.
Sub Regexes(Input As String)
Dim Rx As New Regex("[A") ' Noncompliant: unmatched "["
Dim Match = Regex.Match(Input, "[A") ' Noncompliant
Dim NegativeLookahead As New Regex("a(?!b)", RegexOptions.NonBacktracking) ' Noncompliant: negative lookahead without backtracking
Dim NegativeLookbehind As New Regex("(?<!a)b", RegexOptions.NonBacktracking) ' Noncompliant: negative lookbehind without backtracking
End Sub
Sub Regexes(Input As String)
Dim Rx As New Regex("[A-Z]")
Dim Match = Regex.Match(Input, "[A-Z]")
Dim NegativeLookahead As New Regex("a(?!b)")
Dim NegativeLookbehind As New Regex("(?<!a)b")
End Sub
Functions can return values using two different syntaxes. The modern, and correct, way to do it is to use a Return statement. The VB6
way, i.e. old way, is to assign a return value to the function’s name .
The VB6 syntax is obsolete as it was introduced to simplify migration from VB6 projects. The compiler will create a local variable which is implicitly returned when execution exits the function’s scope.
Return statement should be used instead as they are easier to read and understand.
Public Function FunctionName() As Integer
FunctionName = 42 ' Noncompliant
End Function
Public Function FunctionNameFromVariable() As Integer
Dim Value As Integer = 42
FunctionNameFromVariable = Value ' Noncompliant
End Function
Public Function FunctionName() As Integer
Return 42
End Function
Public Function FunctionNameFromVariable() As Integer
Dim Value As Integer = 42
Return Value
End Function
There are several compilations options available for Visual Basic source code and Option Strict defines compiler behavior for implicit
data type conversions. Specifying Option Strict Off will allow:
Object typeThis behavior can lead to unexpected runtime errors due to type mismatch or missing members.
Option Strict can be set in project properties or overridden in individual source files.
Option Strict Off ' Noncompliant
Public Class KnownType
Public ReadOnly Property Name As String
End Class
Public Module MainMod
Public Function DoSomething(Arg) As String ' Type for "Arg" argument is not defined.
Dim Item As KnownType = Arg ' Implicit narrowing conversion doesn't enforce "Arg" to be of type "KnownType"
Return Arg.Title ' "Title" might not exist in "Arg"
End Function
End Module
Option Strict On
Public Class KnownType
Public ReadOnly Property Name As String
End Class
Public Module MainMod
Public Function DoSomething(Arg As KnownType) As String
Dim Item As KnownType = Arg
Return Arg.Name
End Function
End Module
There are several compilations options available for Visual Basic source code and Option Explicit defines compiler behavior for
implicit variable declarations. Specifying Option Explicit Off will allow creating a variable by it’s first usage. This behavior can lead
to unexpected runtime errors due to typos in variable names.
Option Explicit can be set in project properties or overridden in individual source files.
Option Explicit Off ' Noncompliant
Module MainMod
Public Sub DoSomething(First As String, Second As String)
Parameter = Fist ' New local variable "Fist" is created and assigned to new local variable "Parameter" instead of "First" argument.
DoSomething(Parameter)
Parametr = Second ' "Second" argument is assigned to newly created variable "Parametr" instead of intended "Parameter".
DoSomething(Parameter) ' Value of "Parameter" is always Nothing
End Sub
Private Sub DoSomething(Parameter As String)
' ...
End Sub
End Module
Option Explicit On
Module MainMod
Public Sub DoSomething(First As String, Second As String)
Dim Parameter As String = First
DoSomething(Parameter)
Parameter = Second
DoSomething(Parameter)
End Sub
Private Sub DoSomething(Parameter As String)
' ...
End Sub
End Module
One of the principles of a unit test is that it must have full control of the system under test. This is problematic when production code includes calls to static methods, which cannot be changed or controlled. Date/time functions are usually provided by system libraries as static methods.
This can be improved by wrapping the system calls in an object or service that can be controlled inside the unit test.
Public Class Foo
Public Function HelloTime() As String
Return $"Hello at {DateTime.UtcNow}"
End Function
End Class
There are different approaches to solve this problem. One of them is suggested below. There are also open source libraries (such as NodaTime) which
already implement an IClock interface and a FakeClock testing class.
Public Interface IClock
Function UtcNow() As Date
End Interface
Public Class Foo
Public Function HelloTime(clock As IClock) As String
Return $"Hello at {clock.UtcNow()}"
End Function
End Class
Public Class FooTest
Public Class TestClock
Implements IClock
' implement
End Class
<Fact>
Public Sub HelloTime_Gives_CorrectTime()
Dim dateTime = New DateTime(2017, 06, 11)
Assert.Equal((New Foo()).HelloTime(New TestClock(dateTime)), $"Hello at {dateTime}")
End Sub
End Class
Another possible solution is using an adaptable module, ideally supports an IDisposable method, that not only adjusts the time behaviour for the current thread only, but also for scope of the using.
Public Module Clock
Public Function UtcNow() As Date
End Function
Public Function SetTimeForCurrentThread(time As Func(Of Date)) As IDisposable
End Function
End Module
Public Class Foo
Public Function HelloTime() As String
Return $"Hello at {Clock.UtcNow()}"
End Function
End Class
Public Class FooTest
<Fact>
Public Sub HelloTime_Gives_CorrectTime()
Dim dateTime = New DateTime(2017, 06, 11)
Using SetTimeForCurrentThread(Function() dateTime)
Assert.Equal((New Foo()).HelloTime(), $"Hello at {dateTime}")
End Using
End Sub
End Class
Hard-coding secrets in source code or binaries makes it easy for attackers to extract sensitive information, especially in distributed or open-source applications. This practice exposes credentials and tokens, increasing the risk of unauthorized access and data breaches.
This rule detects variables/fields/properties having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a pseudorandom hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The randomness sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
Secrets should be stored in a configuration file that is not committed to the code repository, in a database, or managed by your cloud provider’s secrets management service. If a secret is exposed in the source code, it must be rotated immediately.
Private MySecret As String = "47828a8dd77ee1eb9dde2d5e93cb221ce8c32b37"
Private MySecret As String = Environment.GetEnvironmentVariable("MYSECRET")
Not specifying a timeout for regular expressions can lead to a Denial-of-Service attack. Pass a timeout when using
System.Text.RegularExpressions to process untrusted input because a malicious user might craft a value for which the evaluation lasts
excessively long.
There is a risk if you answered yes to any of those questions.
matchTimeout when executing a
regular expression.RegexOptions.NonBacktracking.
Public Sub RegexPattern(Input As String)
Dim EmailPattern As New Regex(".+@.+", RegexOptions.None)
Dim IsNumber as Boolean = Regex.IsMatch(Input, "[0-9]+")
Dim IsLetterA as Boolean = Regex.IsMatch(Input, "(a+)+")
End Sub
Public Sub RegexPattern(Input As String)
Dim EmailPattern As New Regex(".+@.+", RegexOptions.None, TimeSpan.FromMilliseconds(100))
Dim IsNumber as Boolean = Regex.IsMatch(Input, "[0-9]+", RegexOptions.None, TimeSpan.FromMilliseconds(100))
Dim IsLetterA As Boolean = Regex.IsMatch(Input, "(a+)+", RegexOptions.NonBacktracking) '.Net 7 And above
AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100)) 'process-wide setting
End Sub
The ExcludeFromCodeCoverageAttribute is
used to exclude portions of code from code coverage
reporting. It is a bad practice to retain code that is not covered by unit tests. In .Net 5, the Justification property was added to
the ExcludeFromCodeCoverageAttribute as an opportunity to document the rationale for the exclusion. This rule raises an issue when no
such justification is given.
Public Structure Coordinates
Public ReadOnly Property X As Integer
Public ReadOnly Property Y As Integer
<ExcludeFromCodeCoverage> ' Noncompliant
Public Overrides Function Equals(obj As Object) As Boolean
If Not (TypeOf obj Is Coordinates) Then
Return False
End If
Dim coordinates = DirectCast(obj, Coordinates)
Return X = coordinates.X AndAlso
Y = coordinates.Y
End Function
<ExcludeFromCodeCoverage> ' Noncompliant
Public Overrides Function GetHashCode() As Integer
Dim hashCode As Long = 1861411795
hashCode = (hashCode * -1521134295 + X.GetHashCode()).GetHashCode()
hashCode = (hashCode * -1521134295 + Y.GetHashCode()).GetHashCode()
Return hashCode
End Function
End Structure
Public Structure Coordinates
Public ReadOnly Property X As Integer
Public ReadOnly Property Y As Integer
<ExcludeFromCodeCoverage(Justification:="Code generated by Visual Studio refactoring")> ' Compliant
Public Overrides Function Equals(obj As Object) As Boolean
If Not (TypeOf obj Is Coordinates) Then
Return False
End If
Dim coordinates = DirectCast(obj, Coordinates)
Return X = coordinates.X AndAlso
Y = coordinates.Y
End Function
<ExcludeFromCodeCoverage(Justification:="Code generated by Visual Studio refactoring")> ' Compliant
Public Overrides Function GetHashCode() As Integer
Dim hashCode As Long = 1861411795
hashCode = (hashCode * -1521134295 + X.GetHashCode()).GetHashCode()
hashCode = (hashCode * -1521134295 + Y.GetHashCode()).GetHashCode()
Return hashCode
End Function
End Structure
The rule targets the use of DateTime.Now call followed by some arithmetic operation.
Using DateTime.Now calls within a subtraction operation to measure elapsed time is not recommended. This property is subject to
changes such as daylight savings transitions, which can invalidate the calculation if the change occurs during the benchmark session, or when updating
a timer. Moreover, DateTime.Now is dependent on the system clock, which may have low resolution on older systems (as low as 15
milliseconds).
If the purpose is to benchmark something then, instead of the DateTime.Now property, it’s recommended to use Stopwatch,
which is not affected by changes in time such as daylight savings (DST) and automatically checks for the existence of high-precision timers. As a
bonus, the StopWatch class is also lightweight and computationally faster than DateTime.
Dim start = DateTime.Now ' First call, on March 26th 2:59 am
' Method to be benchmarked
Console.WriteLine($"{CInt((DateTime.Now - start).TotalMilliseconds)} ms") ' Second call happens 2 minutes later but `Now` is March 26th, 4:01 am as there's a shift to summer time
Dim stopWatch = Stopwatch.StartNew() ' Compliant
' Method to be benchmarked
stopWatch.Stop()
Console.WriteLine($"{stopWatch.ElapsedMilliseconds} ms")
If, on the other hand, the goal is to refresh a timer prefer using the DateTime.UtcNow property, which guarantees reliable results
when doing arithmetic operations during DST transitions.
If (Date.Now - lastRefresh).TotalMilliseconds > MinRefreshInterval Then
lastRefresh = Date.Now
' Refresh
End If
If (Date.UtcNow - lastRefresh).TotalMilliseconds > MinRefreshInterval Then
lastRefresh = Date.UtcNow
' Refresh
End If
Not knowing the Kind of the DateTime object that an application is using can lead to misunderstandings when displaying or
comparing them. Explicitly setting the Kind property helps the application to stay consistent, and its maintainers understand what kind
of date is being managed. To achieve this, when instantiating a new DateTime object you should always use a constructor overload that
allows you to define the Kind property.
Creating the DateTime object without specifying the property Kind will set it to the default value of
DateTimeKind.Unspecified. In this case, calling the method ToUniversalTime will assume that Kind is
DateTimeKind.Local and calling the method ToLocalTime will assume that it’s DateTimeKind.Utc. As a result, you
might have mismatched DateTime objects in your application.
To resolve this issue, use a constructor overload that lets you
specify the DateTimeKind when creating the
DateTime object. From .Net 6 onwards, use the DateOnly type if the time portion of the date is not
relevant.
Private Sub CreateNewTime()
Dim birthDate = New DateTime(1994, 7, 5, 16, 23, 42)
End Sub
Private Sub CreateNewTime()
Dim birthDate = New DateTime(1994, 7, 5, 16, 23, 42, DateTimeKind.Utc)
' or from .Net 6 onwards, use DateOnly:
Dim birthDate = New DateOnly(1994, 7, 5)
End Sub
You should avoid recording time instants with the use of property DateTime.Now. The property DateTime.Now returns the
current date and time expressed in the machine’s local time without containing any timezone-related information (for example, the offset from
Coordinated Universal Time). Not having this information means that if you need to display this DateTime object or use it for
computations in another machine placed in a different time zone, you won’t be able to reconstruct it in the second machine’s local time without
knowing the origin’s offset. This will likely lead to confusion and potential bugs.
Instead, you should record the DateTime instants in UTC, which gives you the date and time as it is in the Coordinated Universal Time.
UTC is a time standard for all time zones and is not subjected to Daylight Saving Time (DST).
Similarly, the use of the DateTime.Today property should also be avoided, as it can return different date values depending on the time
zone.
Generally, unless the purpose is to only display the Date and Time to a user on their local machine, you should always use UTC (for example, when storing dates in a datebase or using them for calculations).
You can end up with DateTime instants that have no meaning for anyone except the machine they were recorded on. Using UTC gives an
unambiguous representation of an instant, and this UTC instant can be transformed into any equivalent local time. This operation isn’t reversible as
some local times are ambiguous and can be matched to more than one UTC instant (for example, due to daylight savings).
Instead of DateTime.Now use any of the following:
DateTime.UtcNow,DateTimeOffSet.Now (as it contains offset information)DateTimeOffSet.UtcNowInstead of DateTime.Today use any of the following:
DateTime.UtcNow.Date,DateOnly.FromDateTime(DateTime.UtcNow) (.NET 6.0+)
Private Sub LogDateTime()
Using streamWriter = New StreamWriter("logs.txt", True)
End Using
streamWriter.WriteLine($"DateTime:{DateTime.Now.ToString("o")}") ' This log won't have any meaning if it's reconstructed in a machine in a different timezone.
End Sub
Private Sub LogDateTime()
Using streamWriter = New StreamWriter("logs.txt", True)
End Using
streamWriter.WriteLine($"DateTime:{DateTime.UtcNow.ToString("o")}")
End Sub
This rule recommends using DateTimeOffset instead of DateTime for projects targeting .NET Framework 2.0 or later.
You should use DateTimeOffset instead of DateTime as it provides all the information that the DateTime
struct has, and additionally, the offset from Coordinated Universal Time (UTC). This way you can avoid potential problems created by the lack of
timezone awareness (see the "Pitfalls" section below for more information).
However, it’s important to note that although DateTimeOffset contains more information than DateTime by storing the
offset to UTC, it isn’t tied to a specific time zone. This information must be stored separately to have a full picture of the moment in time with the
use of TimeZoneInfo.
In most cases, you can directly replace DateTime with DateTimeOffset. When hardcoding dates with local kind, remember
that the offset is timezone dependent, so it should be set according to which timezone that data represents. For more information, refer to
DateTime and DateTimeOffset documentation from Microsoft (see the "Resources" section below).
Dim myDate As DateTime = New DateTime(2008, 6, 19, 7, 0, 0, DateTimeKind.Local) ' Noncompliant Dim now = DateTime.Now ' Noncompliant
Dim myDate As DateTimeOffset = New DateTimeOffset(2008, 6, 19, 7, 0, 0, TimeSpan.FromHours(-7)) ' Compliant Dim now = DateTimeOffset.Now ' Compliant
Common DateTime pitfalls include:
DateTime of kind Local consider the time offset of the machine where the program is running. Not
storing the offset from UTC separately can result in meaningless data when retrieved from a different location.DateTime of kind Unknown, calling ToUniversalTime() presumes the
DateTime.Kind is local and converts to UTC, if you call the method ToLocalTime(), it assumes the
DateTime.Kind is UTC and converts it to local.DateTimes objects, the user must ensure they are within the same time zone. DateTime doesn’t consider
UTC/Local when comparing; it only cares about the number of Ticks on the objects.Since .NET 6 you don’t have to use the TimeZoneConverter library to manually do the conversion between IANA and Windows timezones. The
.NET 6.0 introduced new Time Zone enhancements, one being the TimeZoneInfo.FindSystemTimeZoneById(string timezone) method now accepts as
input both IANA and Windows time zone IDs on any operating system with installed time zone data. TimeZoneInfo.FindSystemTimeZoneById will
automatically convert its input from IANA to Windows and vice versa if the requested time zone is not found on the system.
The method TimeZoneInfo.FindSystemTimeZoneById(string timezone) can get both IANA and Windows timezones as input and automatically
convert one to the other if the requested time zone is not found on the system. Because one does not need to handle the conversion, the code will be
less complex and easier to maintain.
There’s no need to translate manually between time zones; it is enough to call TimeZoneInfo.FindSystemTimeZoneById(string timezone),
where the timezone can be IANA or Windows format. Depending on the OS, the equivalent time zone will be returned (Windows Time Zones for Windows and
IANA timezones for Linux, macOS).
// Assuming we are in Windows OS and we need to get the Tokyo Time Zone. Dim ianaTimeZone = "Asia/Tokyo" Dim windowsTimeZone = TZConvert.IanaToWindows(ianaTimeZone) Dim tokyoWindowsTimeZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZone)
// Assuming we are in Windows OS and we need to get the Tokyo Time Zone. Dim ianaTimeZone = "Asia/Tokyo" Dim tokyoWindowsTimeZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(ianaTimeZone)
When converting a string representation of a date and time to a DateTime object or any other temporal type with one of the available
system parsing methods, you should always provide an IFormatProvider parameter.
If you try to parse a string representation of a date or time without a format provider, the method will use the machine’s
CultureInfo; if the given string does not follow it, you’ll have an object that does not match the string representation or an unexpected
runtime error.
This rule raises an issue for the following date and time string representation parsing methods:
ParseParseExactTryParseTryParseExactOf the following types:
System.DateOnlySystem.DateTimeSystem.DateTimeOffsetSystem.TimeOnlySystem.TimeSpanAlway use an overload of the parse method, where you can provide an IFormatProvider parameter.
Dim dateTimeString = "4/12/2023 4:05:48 PM" ' This is an en-US format string - 12 of April 2023
Dim dateTimeObject = DateTime.Parse(dateTimeString) ' This is wrongly parsed as 4th of December, when it's read in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe)
Dim dateTimeString2 = "4/13/2023 4:05:48 PM" ' This is an en-US format string - 13 of April 2023
Dim dateTimeObject2 = DateTime.Parse(dateTimeString2) ' Runtime Error, when it's parsed in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe).
Dim timeInSaudiArabia = New TimeOnly(16, 23).ToString(New CultureInfo("ar-SA"))
Dim timeObject = TimeOnly.Parse(timeInSaudiArabia) ' Runtime Error, when it's parsed in a machine with "CultureInfo.CurrentCulture" en-150 (English Europe).
Dim dateTimeString = "4/12/2023 4:05:48 PM" ' This is an en-US format string - 12 of April 2023
Dim dateTimeObject = DateTime.Parse(dateTimeString, New CultureInfo("en-US"))
Dim dateTimeString2 = "4/13/2023 4:05:48 PM" ' This is an en-US format string - 13 of April 2023
Dim dateTimeObject2 = DateTime.Parse(dateTimeString2, New CultureInfo("en-US"))
Dim timeInSaudiArabia = New TimeOnly(16, 23).ToString(New CultureInfo("ar-SA"))
Dim timeObject = TimeOnly.Parse(timeInSaudiArabia, New CultureInfo("ar-SA"))
Hardcoding the date and time format strings can lead to formats that consumers misunderstand. Also, if the same format is meant to be used in
multiple places, it is easier to make a mistake when it’s hardcoded instead of using a format provided by an IFormatProvider or using one
of the standard format strings.
If a non-conventional format is used, the formatted date and time can be misunderstood. Also, if a mistake is made in the format, the formatted date can be incomplete. For example, you might switch the place of the minutes and month parts of a date or simply forget to print the year.
Instead of hardcoding the format, provide one from the available formats through an IFormatProvider or use one of the standard format
strings.
Private Sub PrintTime()
Console.WriteLine(DateTime.UtcNow.ToString("dd/MM/yyyy HH:mm:ss"))
Console.WriteLine(DateTime.UtcNow.ToString("dd/mm/yyyy HH:MM:ss")) ' Months and minutes have changed their places
End Sub
Private Sub PrintTime()
Console.WriteLine(DateTime.UtcNow.ToString(CultureInfo.GetCultureInfo("es-MX")))
Console.WriteLine(DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)) ' Better provide a well known culture, so this kind of issues do not pop up
End Sub
With .NET Core the UnixEpoch field was introduced to DateTime and DateTimeOffset types. Using this field
clearly states that the intention is to use the beginning of the Unix epoch.
You should not use the DateTime or DateTimeOffset constructors to set the time to the 1st of January 1970 to represent
the beginning of the Unix epoch. Not everyone is familiar with what this particular date is representing and it can be misleading.
To fix this issue, use the UnixEpoch field of DateTime or DateTimeOffset instead of the constructor.
Private Sub GetEpochTime()
Dim epochTime = New DateTime(1970, 1, 1)
End Sub
Private Sub GetEpochTime()
Dim epochTime = DateTime.UnixEpoch
End Sub
Both the List.Find method and the Enumerable.FirstOrDefault method can be used to locate the first element that meets a
specified condition within a collection. However, for List objects, List.Find may offer superior performance compared to
Enumerable.FirstOrDefault. While the performance difference might be negligible for small collections, it can become significant for
larger collections. This observation also holds true for ImmutableList and arrays.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
FirstOrDefault closer to the performance of collection-specific Find methods in most scenarios.
Applies to
We measured at least 2x improvement in the execution time. For more details see the Benchmarks section from the More info
tab.
The Find method is defined on the collection class, and it has the same signature as FirstOrDefault extension method. The
function can be replaced in place.
Function GetValue(data As List(Of Integer)) As Integer
Return data.FirstOrDefault(Function(x) x Mod 2 = 0)
End Function
Function GetValue(data() As Integer) As Integer
Return data.FirstOrDefault(Function(x) x Mod 2 = 0)
End Function
Function GetValue(data As List(Of Integer)) As Integer
Return data.Find(Function(x) x Mod 2 = 0)
End Function
Function GetValue(data() As Integer) As Integer
Return Array.Find(data, Function(x) x Mod 2 = 0)
End Function
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayFirstOrDefault |
.NET 8.0 |
Array |
10.515 μs |
0.1410 μs |
32 B |
|
ArrayFind |
.NET 8.0 |
Array |
4.417 μs |
0.0729 μs |
- |
|
ArrayFirstOrDefault |
.NET 9.0 |
Array |
2.262 μs |
0.0135 μs |
- |
|
ArrayFind |
.NET 9.0 |
Array |
3.428 μs |
0.0206 μs |
- |
|
ArrayFirstOrDefault |
.NET Framework 4.8.1 |
Array |
45.074 μs |
0.7517 μs |
32 B |
|
ArrayFind |
.NET Framework 4.8.1 |
Array |
13.948 μs |
0.1496 μs |
- |
|
ImmutableListFirstOrDefault |
.NET 8.0 |
ImmutableList<T> |
83.796 μs |
1.3199 μs |
72 B |
|
ImmutableListFind |
.NET 8.0 |
ImmutableList<T> |
59.720 μs |
1.0723 μs |
- |
|
ImmutableListFirstOrDefault |
.NET 9.0 |
ImmutableList<T> |
81.984 μs |
1.0886 μs |
72 B |
|
ImmutableListFind |
.NET 9.0 |
ImmutableList<T> |
58.288 μs |
0.8079 μs |
- |
|
ImmutableListFirstOrDefault |
.NET Framework 4.8.1 |
ImmutableList<T> |
446.893 μs |
9.8430 μs |
76 B |
|
ImmutableListFind |
.NET Framework 4.8.1 |
ImmutableList<T> |
427.476 μs |
3.3371 μs |
- |
|
ListFirstOrDefault |
.NET 8.0 |
List<T> |
14.808 μs |
0.1723 μs |
40 B |
|
ListFind |
.NET 8.0 |
List<T> |
6.040 μs |
0.1104 μs |
- |
|
ListFirstOrDefault |
.NET 9.0 |
List<T> |
2.233 μs |
0.0154 μs |
- |
|
ListFind |
.NET 9.0 |
List<T> |
4.458 μs |
0.0745 μs |
- |
|
ListFirstOrDefault |
.NET Framework 4.8.1 |
List<T> |
57.290 μs |
1.0494 μs |
40 B |
|
ListFind |
.NET Framework 4.8.1 |
List<T> |
18.476 μs |
0.0504 μs |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == 1;
private readonly static Predicate<int> ConditionPredicate = static x => x == 1;
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
public const int N = 10_000;
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public int ListFirstOrDefault() =>
list.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark]
public int ListFind() =>
list.Find(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public int ImmutableListFirstOrDefault() =>
immutableList.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public int ImmutableListFind() =>
immutableList.Find(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public int ArrayFirstOrDefault() =>
array.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public int ArrayFind() =>
Array.Find(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6602.json ================================================ { "title": "\"Find\" method should be used instead of the \"FirstOrDefault\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6602", "sqKey": "S6602", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6603.html ================================================
Both the List.TrueForAll method and the IEnumerable.All method can be used to check if all list elements satisfy a given
condition in a collection. However, List.TrueForAll can be faster than IEnumerable.All for List objects. The
performance difference may be minor for small collections, but for large collections, it can be noticeable.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
All closer to the performance of collection-specific TrueForAll methods in most scenarios.
Applies to
We measured at least 4x improvement both in execution time. For more details see the Benchmarks section from the More
info tab.
The TrueForAll method is defined on the collection class, and it has the same signature as the All extension method. The
method can be replaced in place.
Public Function AreAllEven(data As List(Of Integer)) As Boolean
Return data.All(Function(x) x Mod 2 = 0)
End Function
Public Function AreAllEven(data As Integer()) As Boolean
Return data.All(Function(x) x Mod 2 = 0)
End Function
Public Function AreAllEven(data As List(Of Integer)) As Boolean
Return data.TrueForAll(Function(x) x Mod 2 = 0)
End Function
Public Function AreAllEven(data As Integer()) As Boolean
Return Array.TrueForAll(data, Function(x) x Mod 2 = 0)
End Function
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayAll |
.NET 8.0 |
Array |
109.25 μs |
1.767 μs |
32 B |
|
ArrayTrueForAll |
.NET 8.0 |
Array |
45.01 μs |
0.547 μs |
- |
|
ArrayAll |
.NET 9.0 |
Array |
22.28 μs |
0.254 μs |
- |
|
ArrayTrueForAll |
.NET 9.0 |
Array |
37.60 μs |
0.382 μs |
- |
|
ArrayAll |
.NET Framework 4.8.1 |
Array |
495.90 μs |
4.342 μs |
40 B |
|
ArrayTrueForAll |
.NET Framework 4.8.1 |
Array |
164.52 μs |
2.030 μs |
- |
|
ImmutableListAll |
.NET 8.0 |
ImmutableList<T> |
940.29 μs |
5.600 μs |
72 B |
|
ImmutableListTrueForAll |
.NET 8.0 |
ImmutableList<T> |
679.46 μs |
2.371 μs |
- |
|
ImmutableListAll |
.NET 9.0 |
ImmutableList<T> |
922.43 μs |
14.564 μs |
72 B |
|
ImmutableListTrueForAll |
.NET 9.0 |
ImmutableList<T> |
692.31 μs |
8.897 μs |
- |
|
ImmutableListAll |
.NET Framework 4.8.1 |
ImmutableList<T> |
4,578.72 μs |
77.920 μs |
128 B |
|
ImmutableListTrueForAll |
.NET Framework 4.8.1 |
ImmutableList<T> |
4,393.49 μs |
122.061 μs |
- |
|
ImmutableListBuilderAll |
.NET 8.0 |
ImmutableList<T>.Builder |
970.45 μs |
13.598 μs |
73 B |
|
ImmutableListBuilderTrueForAll |
.NET 8.0 |
ImmutableList<T>.Builder |
687.82 μs |
6.142 μs |
- |
|
ImmutableListBuilderAll |
.NET 9.0 |
ImmutableList<T>.Builder |
981.17 μs |
12.966 μs |
72 B |
|
ImmutableListBuilderTrueForAll |
.NET 9.0 |
ImmutableList<T>.Builder |
710.19 μs |
16.195 μs |
- |
|
ImmutableListBuilderAll |
.NET Framework 4.8.1 |
ImmutableList<T>.Builder |
4,780.50 μs |
43.282 μs |
128 B |
|
ImmutableListBuilderTrueForAll |
.NET Framework 4.8.1 |
ImmutableList<T>.Builder |
4,493.82 μs |
76.530 μs |
- |
|
ListAll |
.NET 8.0 |
List<T> |
151.12 μs |
2.028 μs |
40 B |
|
ListTrueForAll |
.NET 8.0 |
List<T> |
58.03 μs |
0.493 μs |
- |
|
ListAll |
.NET 9.0 |
List<T> |
22.14 μs |
0.327 μs |
- |
|
ListTrueForAll |
.NET 9.0 |
List<T> |
46.01 μs |
0.327 μs |
- |
|
ListAll |
.NET Framework 4.8.1 |
List<T> |
619.86 μs |
6.037 μs |
48 B |
|
ListTrueForAll |
.NET Framework 4.8.1 |
List<T> |
208.49 μs |
2.340 μs |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == Math.Abs(x);
private List<int> list;
private ImmutableList<int> immutableList;
private ImmutableList<int>.Builder immutableListBuilder;
private int[] array;
[Params(100_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
immutableListBuilder = ImmutableList.CreateBuilder<int>();
immutableListBuilder.AddRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAll() =>
list.All(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListTrueForAll() =>
list.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAll() =>
immutableList.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListTrueForAll() =>
immutableList.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark(Baseline = true)]
public bool ImmutableListBuilderAll() =>
immutableListBuilder.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark]
public bool ImmutableListBuilderTrueForAll() =>
immutableListBuilder.TrueForAll(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAll() =>
array.All(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayTrueForAll() =>
Array.TrueForAll(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6603.json ================================================ { "title": "The collection-specific \"TrueForAll\" method should be used instead of the \"All\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6603", "sqKey": "S6603", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6605.html ================================================
Both the List.Exists method and IEnumerable.Any method can be used to find the first element that satisfies a predicate
in a collection. However, List.Exists can be faster than IEnumerable.Any for List objects, as well as requires
significantly less memory. For small collections, the performance difference may be negligible, but for large collections, it can be noticeable. The
same applies to ImmutableList and arrays too.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
Any closer to the performance of collection-specific Exists methods in most scenarios.
Applies to
We measured at least 3x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
Also, no memory allocations were needed for the Exists method, since the search is done in-place.
Since LINQ to
Entities relies a lot on System.Linq for query conversion,
this rule won’t raise when used within LINQ to Entities syntaxes.
The Exists method is defined on the collection class, and it has the same signature as Any extension method if a
predicate is used. The method can be replaced in place.
Function ContainsEven(data As List(Of Integer)) As Boolean
Return data.Any(Function(x) x Mod 2 = 0)
End Function
Function ContainsEven(data() As Integer) As Boolean
Return data.Any(Function(x) x Mod 2 = 0)
End Function
Function ContainsEven(data As List(Of Integer)) As Boolean
Return data.Exists(Function(x) x Mod 2 = 0)
End Function
Function ContainsEven(data() As Integer) As Boolean
Return Array.Exists(data, Function(x) x Mod 2 = 0)
End Function
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
|
ArrayAny |
.NET 8.0 |
Array |
1,174.0 ns |
16.44 ns |
32 B |
|
ArrayExists |
.NET 8.0 |
Array |
570.6 ns |
7.12 ns |
- |
|
ArrayAny |
.NET 9.0 |
Array |
358.5 ns |
5.57 ns |
- |
|
ArrayExists |
.NET 9.0 |
Array |
581.6 ns |
6.17 ns |
- |
|
ArrayAny |
.NET Framework 4.8.1 |
Array |
4,896.0 ns |
102.83 ns |
32 B |
|
ArrayExists |
.NET Framework 4.8.1 |
Array |
1,649.4 ns |
29.81 ns |
- |
|
ImmutableListAny |
.NET 8.0 |
ImmutableList<T> |
7,859.3 ns |
91.45 ns |
72 B |
|
ImmutableListExists |
.NET 8.0 |
ImmutableList<T> |
5,898.1 ns |
81.69 ns |
- |
|
ImmutableListAny |
.NET 9.0 |
ImmutableList<T> |
7,748.9 ns |
119.10 ns |
72 B |
|
ImmutableListExists |
.NET 9.0 |
ImmutableList<T> |
5,705.0 ns |
31.53 ns |
- |
|
ImmutableListAny |
.NET Framework 4.8.1 |
ImmutableList<T> |
45,118.5 ns |
168.72 ns |
72 B |
|
ImmutableListExists |
.NET Framework 4.8.1 |
ImmutableList<T> |
41,966.0 ns |
631.59 ns |
- |
|
ListAny |
.NET 8.0 |
List<T> |
1,643.5 ns |
13.09 ns |
40 B |
|
ListExists |
.NET 8.0 |
List<T> |
726.2 ns |
11.99 ns |
- |
|
ListAny |
.NET 9.0 |
List<T> |
398.6 ns |
8.20 ns |
- |
|
ListExists |
.NET 9.0 |
List<T> |
612.4 ns |
18.73 ns |
- |
|
ListAny |
.NET Framework 4.8.1 |
List<T> |
5,621.5 ns |
35.80 ns |
40 B |
|
ListExists |
.NET Framework 4.8.1 |
List<T> |
1,748.0 ns |
11.76 ns |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == -1 * Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == -1 * Math.Abs(x);
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
[Params(1_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAny() =>
list.Any(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListExists() =>
list.Exists(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAny() =>
immutableList.Any(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListExists() =>
immutableList.Exists(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAny() =>
array.Any(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayExists() =>
Array.Exists(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6605.json ================================================ { "title": "Collection-specific \"Exists\" method should be used instead of the \"Any\" extension", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6605", "sqKey": "S6605", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6607.html ================================================
When working with LINQ in C#, it is recommended to pay attention to the order in which methods are chained, especially when using
Where and OrderBy methods. It is advised to call the Where method before OrderBy because
Where filters the elements of the sequence based on a given condition and returns a new sequence containing only the elements that
satisfy that condition. Calling OrderBy before Where, may end up sorting elements that will be later discarded, which can
lead to inefficiency. Conversely, calling Where before OrderBy, will first filter the sequence to include only the elements
of interest, and then sort them based on the specified order.
We measured at least 2x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
The issue can be fixed by calling Where before OrderBy.
Public Function GetSortedFilteredList(data As IEnumerable(Of Integer)) As IEnumerable(Of Integer)
Return data.OrderBy(Function(x) x).Where(Function(x) x Mod 2 = 0)
End Function
Public Function GetSortedFilteredList(data As IEnumerable(Of Integer)) As IEnumerable(Of Integer)
Return data.Where(Function(x) x Mod 2 = 0).OrderBy(Function(x) x)
End Function
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
OrderByThenWhere |
.NET 7.0 |
175.36 ms |
5.101 ms |
|
WhereThenOrderBy |
.NET 7.0 |
85.58 ms |
1.697 ms |
The results were generated by running the following snippet with BenchmarkDotNet:
private IList<int> data;
private static readonly Random Random = new Random();
[Params(1_000_000)]
public int NumberOfEntries;
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, NumberOfEntries).Select(x => Random.Next(0, NumberOfEntries)).ToList();
[Benchmark(Baseline = true)]
public void OrderByThenWhere() =>
_ = data.OrderBy(x => x).Where(x => x % 2 == 0 ).ToList(); // OrderBy followed by Where
[Benchmark]
public void WhereThenOrderBy() =>
_ = data.Where(x => x % 2 == 0 ).OrderBy(x => x).ToList(); // Where followed by OrderBy
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/vbnet/S6607.json ================================================ { "title": "The collection should be filtered before sorting by using \"Where\" before \"OrderBy\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6607", "sqKey": "S6607", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6608.html ================================================
Indexes in C# provide direct access to an element at a specific position within an array or collection. When compared to Enumerable
methods, indexing can be more efficient for certain scenarios, such as iterating over a large collection, due to avoiding the overhead of checking the
underlying collection type before accessing it.
This applies to types that implement one of these interfaces:
We measured a significant improvement in execution time. For more details see the Benchmarks section from the More info
tab.
If the type you are using implements IList, IList<T> or IReadonlyList<T>, it implements
this[int index]. This means calls to First, Last, or ElementAt(index) can be replaced with
indexing at 0, Count-1 and index respectively.
Function GetAt(data As List(Of Integer), index As Integer) As Integer
Return data.ElementAt(index)
End Function
Function GetFirst(data As List(Of Integer)) As Integer
Return data.First()
End Function
Function GetLast(data As List(Of Integer)) As Integer
Return data.Last()
End Function
Function GetAt(data As List(Of Integer), index As Integer) As Integer
Return data(index)
End Function
Function GetFirst(data As List(Of Integer)) As Integer
Return data(0)
End Function
Function GetLast(data As List(Of Integer)) As Integer
Return data(data.Count-1)
End Function
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
|
ElementAt |
3,403.4 ns |
28.52 ns |
26.67 ns |
|
Index |
478.0 ns |
6.93 ns |
6.48 ns |
|
First |
6,160.0 ns |
57.66 ns |
53.93 ns |
|
First_Index |
485.7 ns |
5.81 ns |
5.15 ns |
|
Last |
6,034.3 ns |
20.34 ns |
16.98 ns |
|
Last_Index |
408.3 ns |
2.54 ns |
2.38 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<byte> data;
private Random random;
[Params(1_000_000)]
public int SampleSize;
[Params(1_000)]
public int LoopSize;
[GlobalSetup]
public void Setup()
{
random = new Random(42);
var bytes = new byte[SampleSize];
random.NextBytes(bytes);
data = bytes.ToList();
}
[Benchmark]
public int ElementAt()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.ElementAt(i);
}
return result;
}
[Benchmark]
public int Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[i];
}
return result;
}
[Benchmark]
public int First()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.First();
}
return result;
}
[Benchmark]
public int First_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[0];
}
return result;
}
[Benchmark]
public int Last()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.Last();
}
return result;
}
[Benchmark]
public int Last_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[data.Count - 1];
}
return result;
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.4412/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=8.0.301 [Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2 .NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/vbnet/S6608.json ================================================ { "title": "Prefer indexing instead of \"Enumerable\" methods on types implementing \"IList\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6608", "sqKey": "S6608", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6609.html ================================================
Both the Enumerable.Max extension method and the SortedSet<T>.Max property can be used to find the maximum value in
a SortedSet<T>. However, SortedSet<T>.Max is much faster than Enumerable.Max. For small
collections, the performance difference may be minor, but for large collections, it can be noticeable. The same applies for the Min
property as well.
Max and Min in SortedSet<T> exploit the fact that the set is implemented via a Red-Black
tree. The algorithm to find the Max/Min is "go left/right whenever possible". The operation has the time complexity
of O(h) which becomes O(ln(n)) due to the fact that the tree is balanced. This is much better than the O(n)
time complexity of extension methods.
Max and Min in ImmutableSortedSet<T> exploits a tree augmentation technique, storing the
Min, Max and Count values on each node of the data structure. The time complexity in this case is
O(1) that is significantly better than O(n) of extension methods.
Applies to:
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
The Min and Max properties are defined on the following classes, and the extension method call can be replaced by calling
the propery instead:
SortedSet<T>ImmutableSortedSet<T>ImmutableSortedSet<T>.Builder
Function GetMax(data As SortedSet(Of Integer)) As Integer
Return Enumerable.Max(data)
End Function
Function GetMin(data As SortedSet(Of Integer)) As Integer
Return Enumerable.Min(data)
End Function
Function GetMax(data As SortedSet(Of Integer)) As Integer
Return data.Max()
End Function
Function GetMin(data As SortedSet(Of Integer)) As Integer
Return data.Min()
End Function
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
MaxMethod |
.NET 7.0 |
68,961.483 us |
499.6623 us |
248063 B |
|
MaxProperty |
.NET 7.0 |
4.638 us |
0.0634 us |
- |
|
MaxMethod |
.NET Framework 4.6.2 |
85,827.359 us |
1,531.1611 us |
281259 B |
|
MaxProperty |
.NET Framework 4.6.2 |
67.682 us |
0.3757 us |
312919 B |
The results were generated by running the following snippet with BenchmarkDotNet:
private SortedSet<string> data;
[Params(1_000)]
public int Iterations;
[GlobalSetup]
public void Setup() =>
data = new SortedSet<string>(Enumerable.Range(0, Iterations).Select(x => Guid.NewGuid().ToString()));
[Benchmark(Baseline = true)]
public void MaxMethod()
{
for (var i = 0; i < Iterations; i++)
{
_ = data.Max(); // Max() extension method
}
}
[Benchmark]
public void MaxProperty()
{
for (var i = 0; i < Iterations; i++)
{
_ = data.Max; // Max property
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6609.json ================================================ { "title": "\"Min\/Max\" properties of \"Set\" types should be used instead of the \"Enumerable\" extension methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6609", "sqKey": "S6609", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6610.html ================================================
With string.StartsWith(char) and string.EndsWith(char), only the first character of the string is compared to the
provided character, whereas the string versions of those methods have to do checks about the current StringComparison and
CultureInfo. Thus, the char overloads are significantly faster for default comparison scenarios.
These overloads were introduced in .NET Core 2.0.
We measured at least 3.5x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
If you are targeting a runtime version equal or greater than .NET Core 2.0, the string.StartsWith and
string.EndsWith overloads are available, with the argument’s type being char instead of string. Thus, an
argument of char type can be provided.
Function StartsWithSlash(s As String) As Boolean
Return s.StartsWith("/")
End Function
Function EndsWithSlash(s As String) As Boolean
Return s.EndsWith("/")
End Function
Function StartsWithSlash(s As String) As Boolean
Return s.StartsWith("/"c)
End Function
Function EndsWithSlash(s As String) As Boolean
Return s.EndsWith("/"c)
End Function
| Method | Mean | Standard Deviation |
|---|---|---|
|
StartsWith_String |
30.965 ms |
3.2732 ms |
|
StartsWith_Char |
7.568 ms |
0.3235 ms |
|
EndsWith_String |
30.421 ms |
5.1136 ms |
|
EndsWith_Char |
8.067 ms |
0.7092 ms |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<string> data;
[Params(1_000_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, N).Select(_ => Guid.NewGuid().ToString()).ToList();
[Benchmark]
public void StartsWith_String()
{
_ = data.Where(guid => guid.StartsWith("d")).ToList();
}
[Benchmark]
public void StartsWith_Char()
{
_ = data.Where(guid => guid.StartsWith('d')).ToList();
}
[Benchmark]
public void EndsWith_String()
{
_ = data.Where(guid => guid.EndsWith("d")).ToList();
}
[Benchmark]
public void EndsWith_Char()
{
_ = data.Where(guid => guid.EndsWith('d')).ToList();
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2================================================ FILE: analyzers/rspec/vbnet/S6610.json ================================================ { "title": "\"StartsWith\" and \"EndsWith\" overloads that take a \"char\" should be used instead of the ones that take a \"string\"", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6610", "sqKey": "S6610", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6612.html ================================================
When using the ConcurrentDictionary, there are many overloads of the GetOrAdd and AddOrUpdate methods that
take both a TKey argument and a lambda that expects a TKey parameter. This means that the right side of the lambda can be
written using either the lambda’s parameter or the method’s argument. However, using the method’s argument leads to the lambda capturing it, and the
compiler will need to generate a class and instantiate it before the call. This means memory allocations, as well as more time spend during Garbage
Collection.
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
When you are using the ConcurrentDictionary methods GetOrAdd or AddOrUpdate, reference the key by using the
lambda’s parameter instead of the method’s one.
Function UpdateValue(dict As ConcurrentDictionary(Of Integer, Integer), key As Integer) As Integer
Return dict.GetOrAdd(key, Function(k)
Return key + 42
End Function)
End Function
Function UpdateValue(dict As ConcurrentDictionary(Of Integer, Integer), key As Integer) As Integer
Return dict.GetOrAdd(key, Function(k)
Return k + 42
End Function)
End Function
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
Capture |
.NET 7.0 |
68.52 ms |
4.450 ms |
88000063 B |
|
Lambda |
.NET 7.0 |
39.29 ms |
3.712 ms |
50 B |
|
Capture |
.NET Framework 4.6.2 |
74.58 ms |
5.199 ms |
88259787 B |
|
Lambda |
.NET Framework 4.6.2 |
42.03 ms |
2.752 ms |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
private ConcurrentDictionary<int, string> dict;
private List<int> data;
[Params(1_000_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup()
{
dict = new ConcurrentDictionary<int, string>();
data = Enumerable.Range(0, N).OrderBy(_ => Guid.NewGuid()).ToList();
}
[Benchmark(baseline=true)]
public void Capture()
{
foreach (var guid in data)
{
dict.GetOrAdd(guid, _ => $"{guid}"); // "guid" is captured
}
}
[Benchmark]
public void Lambda()
{
foreach (var guid in data)
{
dict.GetOrAdd(guid, x => $"{x}"); // no capture
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6612.json ================================================ { "title": "The lambda parameter should be used instead of capturing arguments in \"ConcurrentDictionary\" methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6612", "sqKey": "S6612", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6613.html ================================================
Both the Enumerable.First extension method and the LinkedList<T>.First property can be used to find the first value
in a LinkedList<T>. However, LinkedList<T>.First is much faster than Enumerable.First. For small
collections, the performance difference may be minor, but for large collections, it can be noticeable. The same applies for the Last
property as well.
Applies to:
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
The First and Last properties are defined on the LinkedList class, and the extension method call can be
replaced by calling the propery instead.
Function GetFirst(data As LinkedList(Of Integer)) As Integer
Return Enumerable.First(data)
End Function
Function GetLast(data As LinkedList(Of Integer)) As Integer
Return Enumerable.Last(data)
End Function
Function GetFirst(data As LinkedList(Of Integer)) As Integer
Return data.First.Value
End Function
Function GetLast(data As LinkedList(Of Integer)) As Integer
Return data.Last.Value
End Function
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
LastMethod |
.NET 7.0 |
919,577,629.0 ns |
44,299,688.61 ns |
48504 B |
|
LastProperty |
.NET 7.0 |
271.8 ns |
15.63 ns |
- |
|
LastMethod |
.NET Framework 4.6.2 |
810,316,427.1 ns |
47,768,482.31 ns |
57344 B |
|
LastProperty |
.NET Framework 4.6.2 |
372.0 ns |
13.38 ns |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
private LinkedList<int> data;
private Random random = new Random();
[Params(100_000)]
public int Size { get; set; }
[Params(1_000)]
public int Runs { get; set; }
[GlobalSetup]
public void Setup() =>
data = new LinkedList<int>(Enumerable.Range(0, Size).Select(x => random.Next()));
[Benchmark(Baseline = true)]
public void LastMethod()
{
for (var i = 0; i < Runs; i++)
{
_ = data.Last(); // Enumerable.Last()
}
}
[Benchmark]
public void LastProperty()
{
for (var i = 0; i < Runs; i++)
{
_ = data.Last; // Last property
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6613.json ================================================ { "title": "\"First\" and \"Last\" properties of \"LinkedList\" should be used instead of the \"First()\" and \"Last()\" extension methods", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6613", "sqKey": "S6613", "scope": "All", "quickfix": "unknown" } ================================================ FILE: analyzers/rspec/vbnet/S6617.html ================================================
When testing if a collection contains a specific item by simple equality, both ICollection.Contains(T item) and
IEnumerable.Any(x ⇒ x == item) can be used. However, Any searches the data structure in a linear manner using a foreach
loop, whereas Contains is considerably faster in some collection types, because of the underlying implementation. More specifically:
HashSet<T> is a hashtable, and therefore has an O(1) lookupSortedSet<T> is a red-black tree, and therefore has a O(logN) lookupList<T> is a linear search, and therefore has an O(N) lookup, but the EqualityComparer is optimized for the T
type, which is not the case for AnyFor small collections, the performance difference may be negligible, but for large collections, it can be noticeable.
We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from
the More info tab.
Since LINQ to
Entities relies a lot on System.Linq for query conversion,
this rule won’t raise when used within LINQ to Entities syntaxes.
Contains is a method defined on the ICollection<T> interface and takes a T item argument.
Any is an extension method defined on the IEnumerable<T> interface and takes a predicate argument. Therefore, calls
with simple equality checks like Any(x ⇒ x == item) can be replaced by Contains(item).
This applies to the following collection types:
Function ValueExists(data As HashSet(Of Integer)) As Boolean
Return data.Any(Function(x) x = 42)
End Function
Function ValueExists(data As List(Of Integer)) As Boolean
Return data.Any(Function(x) x = 42)
End Function
Function ValueExists(data As HashSet(Of Integer)) As Boolean
Return data.Contains(42)
End Function
Function ValueExists(data As List(Of Integer)) As Boolean
Return data.Contains(42)
End Function
| Method | Runtime | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|
|
HashSet_Any |
.NET 7.0 |
35,388.333 us |
620.1863 us |
40132 B |
|
HashSet_Contains |
.NET 7.0 |
3.799 us |
0.1489 us |
- |
|
List_Any |
.NET 7.0 |
32,851.509 us |
667.1658 us |
40130 B |
|
List_Contains |
.NET 7.0 |
375.132 us |
8.0764 us |
- |
|
HashSet_Any |
.NET Framework 4.6.2 |
28,979.763 us |
678.0093 us |
40448 B |
|
HashSet_Contains |
.NET Framework 4.6.2 |
5.987 us |
0.1090 us |
- |
|
List_Any |
.NET Framework 4.6.2 |
25,830.221 us |
487.2470 us |
40448 B |
|
List_Contains |
.NET Framework 4.6.2 |
5,935.812 us |
57.7569 us |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
[Params(10_000)]
public int SampleSize;
[Params(1_000)]
public int Iterations;
private static HashSet<int> hashSet;
private static List<int> list;
[GlobalSetup]
public void Setup()
{
hashSet = new HashSet<int>(Enumerable.Range(0, SampleSize));
list = Enumerable.Range(0, SampleSize).ToList();
}
[Benchmark]
public void HashSet_Any() =>
CheckAny(hashSet, SampleSize / 2);
[Benchmark]
public void HashSet_Contains() =>
CheckContains(hashSet, SampleSize / 2);
[Benchmark]
public void List_Any() =>
CheckAny(list, SampleSize / 2);
[Benchmark]
public void List_Contains() =>
CheckContains(list, SampleSize / 2);
void CheckAny(IEnumerable<int> values, int target)
{
for (int i = 0; i < Iterations; i++)
{
_ = values.Any(x => x == target); // Enumerable.Any
}
}
void CheckContains(ICollection<int> values, int target)
{
for (int i = 0; i < Iterations; i++)
{
_ = values.Contains(target); // ICollection<T>.Contains
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.203 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256================================================ FILE: analyzers/rspec/vbnet/S6617.json ================================================ { "title": "\"Contains\" should be used instead of \"Any\" for simple equality checks", "type": "CODE_SMELL", "code": { "impacts": { "MAINTAINABILITY": "LOW" }, "attribute": "EFFICIENT" }, "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "5min" }, "tags": [ "performance" ], "defaultSeverity": "Minor", "ruleSpecification": "RSPEC-6617", "sqKey": "S6617", "scope": "All", "quickfix": "targeted" } ================================================ FILE: analyzers/rspec/vbnet/S6930.html ================================================
Backslash characters (\) should be avoided in route templates.
Routing in ASP.NET MVC maps controllers and actions to paths in request URIs.
In the former syntax specification of URIs, backslash characters (\) were not allowed at all (see section "2.4.3. Excluded US-ASCII Characters" of RFC 2396). While the current
specification (RFC 3986) doesn’t include anymore the "Excluded US-ASCII Characters"
section, most URL processors still don’t support backslash properly.
For instance, a backslash in the "path" part of a URL is automatically converted to a forward slash (/) both by Chrome and Internet
Explorer (see here).
As an example, \Calculator\Evaluate?expression=3\4 is converted on the fly into /Calculator/Evaluate?expression=3\4
before the HTTP request is made to the server.
While backslashes are allowed in the "query" part of a URL, and it’s common to have them as part of a complex query expression, the route of a controller is always part of the "path".
That is why the use of backslashes in controller templates should be avoided in general.
A backslash in the route pattern of a controller would only make sense if the developer intended the backslash in the route to be explicitly
escaped by the user, using %5C.
For example, the route Something\[controller] for the HomeController would need to be called as
Something%5CHome.
The validity of such a scenario is unlikely and the resulting behavior is surprising.
<Route("Something\[controller]")> ' Noncompliant: Replace '\' with '/'.
Public Class HomeController
Inherits Controller
<HttpGet>
Public Function Index() As ActionResult
Return View()
End Function
End Class
<Route("Something/[controller]")> ' '\' replaced with '/'
Public Class HomeController
Inherits Controller
<HttpGet>
Public Function Index() As ActionResult
Return View()
End Function
End Class
app.MapControllerRoute(
name:="default",
pattern:="{controller=Home}\{action=Index}") ' Noncompliant: Replace '\' with '/'.
app.MapControllerRoute(
name:="default",
pattern:="{controller=Home}/{action=Index}") ' '\' replaced with '/'
Route templates for ASP.NET controller
actions, defined via a RouteAttribute or any derivation of HttpMethodAttribute, should
not start with "/".
Routing in ASP.NET Core MVC maps controllers and actions to paths in request URIs. Similar routing happens in ASP.NET Framework MVC.
In ASP.NET Core MVC, when an action defines a route template starting with a "/", the route is considered absolute and the action is registered at the root of the web application.
In such a scenario, any route defined at the controller level is disregarded, as shown in the following example:
<Route("[controller]")> ' This route is ignored for the routing of Index1 and Index2
Public Class HomeController
Inherits Controller
<HttpGet("/Index1")> ' This action is mapped to the root of the web application
Public Function Index1() As ActionResult
Return View()
End Function
<Route("/Index2")> ' The same applies here
Public Function Index2() As ActionResult
Return View()
End Function
End Class
The behavior can be found confusing and surprising because any relative action route is relativized to the controller route.
Therefore, in the vast majority of scenarios, controllers group all related actions not only in the source code, but also at the routing level.
In ASP.NET Framework MVC with attribute routing enabled via MapMvcAttributeRoutes,
the mere presence of an absolute route at the action level will produce an InvalidOperationException at runtime.
It is then a good practice to avoid absolute routing at the action level and move the "/" to the root level, changing the template defined in the
RouteAttribute of the controller appropriately.
The rule only applies when all route templates of all actions of the controller start with "/". Sometimes some actions may have both relative and absolute route templates, for example for backward compatibility reasons (i.e. a former route needs to be preserved). In such scenarios, it may make sense to keep the absolute route template at the action level.
<Route("[controller]")> ' This route is ignored
Public Class ReviewsController
Inherits Controller
' Route is /reviews
<HttpGet("/reviews")>
Public Function Index() As ActionResult
' ...
End Function
' Route is /reviews/{reviewId}
<Route("/reviews/{reviewId}")>
Public Function Show(reviewId As Integer) As ActionResult
' ...
End Function
End Class
<Route("/")> ' Turns on attribute routing
Public Class ReviewsController
Inherits Controller
' Route is /reviews
<HttpGet("reviews")>
Public Function Index() As ActionResult
' ...
End Function
' Route is /reviews/{reviewId}
<Route("reviews/{reviewId}")>
Public Function Show(reviewId As Integer) As ActionResult
' ...
End Function
End Class
When working with collections that are known to be non-empty, using First or Single is generally preferred over FirstOrDefault or SingleOrDefault.
Using FirstOrDefault or SingleOrDefault on collections that are known to be non-empty is an issue due to:
FirstOrDefault or SingleOrDefault, it implies that the collection might be
empty, which can be misleading if you know it is not. It can be confusing for other developers who read your code, making it harder for them to
understand the actual constraints and behavior of the collection. This leads to confusion and harder-to-maintain code.FirstOrDefault and
SingleOrDefault can lead to subtle bugs. These methods return a default value (null for reference types and
default for value types) when the collection is empty, potentially causing issues like NullReferenceException later in the
code. In contrast, First or Single will throw an InvalidOperationException immediately if the collection is
empty, making it easier to detect and address issues early in the development process.null, you introduces a condition that cannot be fully tested,
impacting the code coverage.
Dim Items As New list(Of Integer) From {1, 2, 3}
Dim FirstItem As Integer = Items.FirstOrDefault() ' Noncompliant, this implies the collection might be empty, when we know it is not
Dim Items As New list(Of Integer) From {1, 2, 3}
Dim FirstItem As Integer = Items.First() ' Compliant
SingleFirstSingleOrDefaultFirstOrDefaultWhen using ReaderWriterLock and ReaderWriterLockSlim for managing read and write locks, you should not release a read lock while holding a write lock and vice versa, otherwise you might have runtime exceptions. The locks should be always correctly paired so that the shared resource is accessed safely.
This rule raises if:
If you use the ReaderWriterLockSlim class, you will get a LockRecursionException. In the case of
ReaderWriterLock, you’ll get a runtime exception for trying to release a lock that is not owned by the calling thread.
Public Class Example
Private Shared rwLock As New ReaderWriterLock()
Public Sub Writer()
rwLock.AcquireWriterLock(2000)
Try
' ...
Finally
rwLock.ReleaseReaderLock() ' Noncompliant, will throw runtime exception
End Try
End Sub
Public Sub Reader()
rwLock.AcquireReaderLock(2000)
Try
' ...
Finally
rwLock.ReleaseWriterLock() ' Noncompliant, will throw runtime exception
End Try
End Sub
End Class
Public Class Example
Private Shared rwLock As New ReaderWriterLock()
Public Shared Sub Writer()
rwLock.AcquireWriterLock(2000)
Try
' ...
Finally
rwLock.ReleaseWriterLock()
End Try
End Sub
Public Shared Sub Reader()
rwLock.AcquireReaderLock(2000)
Try
' ...
Finally
rwLock.ReleaseReaderLock()
End Try
End Sub
End Class
This rule raises if you acquire a lock with one of the following methods, and do not release it within the same method.
This rule will raise an issue when the code uses the disposable pattern. This pattern makes locking easy to use and delegates the responsibility to the caller. Users should accept issues in such cases, as they should appear only once for each synchronization type.
Not releasing a lock in the same method where you acquire it, and releasing in another one, makes the code less clear and harder to maintain. You are also introducing the risk of not releasing a lock at all which can lead to deadlocks or exceptions.
Public Class Example
Private Shared rwLock As New ReaderWriterLock
Public Sub AcquireWriterLock()
rwLock.AcquireWriterLock(2000) ' Noncompliant, as the lock release is on the callers responsibility
End Sub
Public Sub DoSomething()
' ...
End Sub
Public Sub ReleaseWriterLock()
rwLock.ReleaseWriterLock()
End Sub
End Class
Public Class Example
Private Shared rwLock As New ReaderWriterLock
Public Sub DoSomething()
rwLock.AcquireWriterLock(2000) ' Compliant, locks are released in the same method
Try
' ...
Finally
rwLock.ReleaseWriterLock()
End Try
End Sub
End Class
GoTo is an unstructured control flow statement. It makes code less readable and maintainable. Structured control flow statements such
as If, For, While, or Exit should be used instead.
Sub GoToStatementDemo()
Dim number As Integer = 1
Dim sampleString As String
' Evaluate number and branch to appropriate label.
If number = 1 Then GoTo Line1 Else GoTo Line2
Line1:
sampleString = "Number equals 1"
GoTo LastLine
Line2:
' The following statement never gets executed because number = 1.
sampleString = "Number equals 2"
LastLine:
' Write "Number equals 1" in the Debug window.
Debug.WriteLine(sampleString)
End Sub
Sub GoToStatementDemo()
Dim number As Integer = 1
Dim sampleString As String
' Evaluate number and branch to appropriate label.
If number = 1 Then
sampleString = "Number equals 1"
Else
sampleString = "Number equals 2"
End If
Debug.WriteLine(sampleString)
End Sub
================================================
FILE: analyzers/rspec/vbnet/S907.json
================================================
{
"title": "\"GoTo\" statements should not be used",
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "MEDIUM"
},
"attribute": "CLEAR"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"brain-overload"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-907",
"sqKey": "S907",
"scope": "All",
"quickfix": "unknown"
}
================================================
FILE: analyzers/rspec/vbnet/S927.html
================================================
Parameters are part of the method signature and its identity.
Implementing a method from an interface, a base class, or a partial method and changing one of its parameters' names will confuse and impact the method’s readability.
Interface IBankAccount
Sub AddMoney(money As Integer)
End Interface
Class BankAccount
Implements IBankAccount
Private Sub AddMoney(amount As Integer) ' Noncompliant: parameter's name differs from base
' ...
End Sub
End Class
To avoid any ambiguity in the code, a parameter’s name should match the initial declaration, whether its initial declaration is from an interface, a base class, or a partial method.
Interface IBankAccount
Sub AddMoney(money As Integer)
End Interface
Class BankAccount
Implements IBankAccount
Private Sub AddMoney(money As Integer) ' Compliant: parameter's name match base name
' ...
End Sub
End Class
The rule is ignored if both the parameter defined in the initial decalaration is a generic type and the implementing member’s declaration is a non-generic type.
This allows the implementing member to be more specific and provide more information.