|
1 | | -# DeadRegionAnalysis |
2 | | -Roslyn tool for analyzing dead conditional regions of C# code across multiple project configurations |
| 1 | +# deadregions |
| 2 | +Tool for analyzing and cleaning up unecessary ("dead") conditional regions in C# code |
| 3 | +``` |
| 4 | +SYNTAX |
| 5 | + DeadRegions [<project> ...] [options] |
| 6 | + DeadRegions [<source file> ...] [options] |
| 7 | +
|
| 8 | +OPTIONS |
| 9 | + config <symbol list> |
| 10 | + Specify a complete symbol configuration |
| 11 | + [multiple specifications allowed] |
| 12 | +
|
| 13 | + ignore <symbol list> |
| 14 | + Ignore a list of symbols (treat as varying) |
| 15 | + [multiple specifications allowed] |
| 16 | +
|
| 17 | + define <symbol list> |
| 18 | + Define a list of symbols (treat as always true) |
| 19 | + [multiple specifications allowed] |
| 20 | +
|
| 21 | + disable <symbol list> |
| 22 | + Disable a list of symbols (treat as always disabled) |
| 23 | + [multiple specifications allowed] |
| 24 | +
|
| 25 | + default <false|true|varying> |
| 26 | + Set the default value for symbols which do not have a specified value (defaults |
| 27 | + to varying) |
| 28 | +
|
| 29 | + printdisabled |
| 30 | + Print the list of always disabled conditional regions |
| 31 | +
|
| 32 | + printenabled |
| 33 | + Print the list of always enabled conditional regions |
| 34 | +
|
| 35 | + printvarying |
| 36 | + Print the list of varying conditional regions |
| 37 | +
|
| 38 | + printsymbols |
| 39 | + Print the lists of uniquely specified preprocessor symbols, symbols visited |
| 40 | + during analysis, and symbols not encountered during analysis |
| 41 | +
|
| 42 | + print |
| 43 | + Print the entire list of conditional regions and the lists of preprocessor |
| 44 | + symbols (combination of printenabled, printdisabled, printvarying, and |
| 45 | + printsymbols) |
| 46 | +
|
| 47 | + edit |
| 48 | + Perform edits to remove always enabled and always disabled conditional regions |
| 49 | + from source files, and simplify preprocessor expressions which evaluate to |
| 50 | + 'varying' |
| 51 | +
|
| 52 | +
|
| 53 | +NOTES |
| 54 | + <symbol list> is a comma or semi-colon separated list of preprocessor symbols |
| 55 | +``` |
| 56 | + |
| 57 | +An unnecessary conditional region is one conditioned on a preprocessor expression that, across all possible build configurations for a given project, always evaluates to `true` or always evaluates to `false`. Such regions are either dead code (if conditioned on `false`) or have unnecessary preprocessor directives (if conditioned on `true`). Conversely, regions conditioned on preprocessor expressions which evaluate differently across different build configurations have meaningful preprocessor directives. |
| 58 | + |
| 59 | +This tool can analyze a given project to determine which branching preprocessor directives and regions are unnecessary, and optionally remove them. |
| 60 | + |
| 61 | +# Examples |
| 62 | + |
| 63 | +## Analyze all conditional regions in a project |
| 64 | + |
| 65 | +`> deadregions example.csproj /print` |
| 66 | + |
| 67 | +This will print out something like |
| 68 | +``` |
| 69 | +D:\example\Program.cs(2): "#if true" : Always Enabled |
| 70 | +D:\example\Program.cs(4): "#else" : Always Disabled |
| 71 | +D:\example\Program.cs(8): "#if ZERO // TODO(somebody): Re-enable this when x is fixed" : Varying |
| 72 | +D:\example\Program.cs(14): "#if DEBUG" : Varying |
| 73 | +Conditional Regions |
| 74 | + 4 found in total |
| 75 | + 1 always disabled |
| 76 | + 1 always enabled |
| 77 | + 2 varying |
| 78 | +
|
| 79 | +Symbols |
| 80 | + 0 unique symbol(s) specified: |
| 81 | + 3 unique symbol(s) visited: true;ZERO;DEBUG |
| 82 | + 0 specified symbol(s) unvisited: |
| 83 | +``` |
| 84 | + |
| 85 | +There are a few things going on here. As you would expect, the `#if true` region is determined to be always enabled, and the corresponding `#else` is always disabled. You'll also notice that you get a summary of information about the conditional regions found in the project, as well as the preprocessor symbols that were specified on the command line, found in the project. |
| 86 | + |
| 87 | +One interesting thing to note is that the `#if ZERO` and `#if DEBUG` regions are determined to be varying. That is because unlike the C# preprocessor, this tool evaluates symbols which do not have a specified value as "varying" by default. The rational behind this is that most of the time, you'll be using this tool to remove dead conditional regions, but you won't necessarily specify or even have all the data about all possible build configurations. So in order to avoid removing regions which are determined to be always disabled simply because you didn't specify a value for a symbol, the tool defaults the value to varying, which causes the region to be ignored in the clean-up pass. |
| 88 | + |
| 89 | +If you would like to get the same behavior as the C# preprocessor (default undefined symbols to `false`), just pass `/default false` |
| 90 | + |
| 91 | +``` |
| 92 | +> deadregions .\example.csproj /print /default false |
| 93 | +D:\example\Program.cs(2): "#if true" : Always Enabled |
| 94 | +D:\example\Program.cs(4): "#else" : Always Disabled |
| 95 | +D:\example\Program.cs(8): "#if ZERO // TODO(somebody): Re-enable this when x is fixed" : Always Disabled |
| 96 | +D:\example\Program.cs(14): "#if DEBUG" : Always Disabled |
| 97 | +``` |
| 98 | + |
| 99 | +Voilà. |
| 100 | + |
| 101 | +You can also provide specific values for preprocessor expressions using `/define`, `/disable` and `/ignore`. For example, |
| 102 | +``` |
| 103 | +> deadregions .\example.csproj /print /disable ZERO |
| 104 | +... |
| 105 | +D:\example\Program.cs(8): "#if ZERO // TODO(somebody): Re-enable this when x is fixed" : Always Disabled |
| 106 | +... |
| 107 | +``` |
| 108 | + |
| 109 | +## Remove all unnecessary regions in a project |
| 110 | + |
| 111 | +When you're ready to make edits to your source files based on the output of analysis (or you're using version control and you'd like to hurry up and produce a diff already), pass `/edit` |
| 112 | + |
| 113 | +`> deadregions.exe example.csproj /disable ZERO /edit` |
| 114 | + |
| 115 | +In my example, if I analyze the project again, I'm only left with varying regions as expected. |
| 116 | + |
| 117 | +``` |
| 118 | +> deadregions .\example.csproj /print |
| 119 | +D:\example\Program.cs(5): "#if DEBUG" : Varying |
| 120 | +Conditional Regions |
| 121 | + 1 found in total |
| 122 | + 1 varying |
| 123 | +
|
| 124 | +Symbols |
| 125 | + 0 unique symbol(s) specified: |
| 126 | + 1 unique symbol(s) visited: DEBUG |
| 127 | + 0 specified symbol(s) unvisited: |
| 128 | +``` |
| 129 | + |
| 130 | +## Remove all unnecessary regions conditioned with "#if false" |
| 131 | + |
| 132 | +Since you can override how literal preprocessor expressions evaluate, you can also use a similar command to remove all unnecessary regions conditioned with "#if false" |
| 133 | + |
| 134 | +`> deadregions example.csproj /disable false /edit` |
| 135 | + |
| 136 | +## Analyze a project with many different build configurations |
| 137 | + |
| 138 | +If you're working with a large codebase with many different build configurations (and sets of preprocessor symbols), chances are you won't have an easy time figuring out or specifying the values for all those symbols by hand. Enter the `/config` switch: you can use this switch to specify each of your build configurations in their entirety, and the tool will evaluate each preprocessor expression in the context of each build configuration to determine the state of each conditional region. |
| 139 | + |
| 140 | +`> deadregions hugecodebase.csproj /config A;B;C /config A;D;E;F;DEBUG /config EXPENSIVE_LOGGING;D;E` |
| 141 | + |
| 142 | +By combining this with explicit specification of symbol values (maybe you have some build configurations which are only run by certain parts of a larger team so you don't know the whole of those configurations but you know the symbols involved), you should be able to pinpoint the set of symbols and conditional regions you care about. |
| 143 | + |
| 144 | +## Use a response file to avoid a long command line |
| 145 | + |
| 146 | +Since such command lines can get relatively long, it may be useful to specify options using a *response file*. |
| 147 | + |
| 148 | +`> deadregions hugecodebase.csproj @hugecodebase.rsp` |
| 149 | + |
| 150 | +where the file `hugecodebase.rsp` contains |
| 151 | + |
| 152 | +`/config A;B;C /config A;D;E;F;DEBUG /config EXPENSIVE_LOGGING;D;E` |
| 153 | + |
| 154 | +etc. |
0 commit comments