NotOneShotInitialization

Summary
Declare local variables with one-shot initialization.
Default severity
Warning
Description
Do NOT:
- Declare a local variable and initialize it with a provisional value
- And then, depending on some condition, assign another value to that variable
For example, see the following code:
public const int MaxScore = 100;
public void Foo(int score)
{
    var s = score;
    if (s > MaxScore)
    {
        s = MaxScore;
    }
    ...
}
Walter E. Brown calls this the “let me change my mind” idiom in [1].
You should rewrite it with one-shot initialization as follows:
...
var s = (score > MaxScore) ? MaxScore : score;
...
or
...
var s = Math.Min(score, MaxScore);
...
Let's see the next example:
public void Bar(string colorName)
{
    var c = -1;
    switch (colorName)
    {
        case "black":
            c = 0;
            break;
        case "blue":
            c = 1;
            break;
        case "red":
            c = 2;
            break;
        ...
    }
    ...
}
We can rewrite it with the switch expression as follows:
...
var c = colorName switch
{
    "black" => 0,
    "blue" => 1,
    "red" => 2,
    ...
    _ => -1,
};
...
or with Dictionary:
private static readonly Dictionary<string, int> colorMap
    = new Dictionary<string, int>()
{
    ["black"] = 0,
    ["blue"] = 1,
    ["red"] = 2,
    ...
};
public void Bar(string colorName)
{
    var c = colorMap.GetValueOrDefault(colorName, -1);
    // If you cannot use the 'GetValueOrDefault' method, try the following
    // code instead:
    //
    // var c = colorMap.TryGetValue(colorName, out var found) ? found : -1;
    ...
}
In addition to the above example, you can also move the initial value to
another method that returns a value such as var s = Method();.  Or, if the
separation with another method causes a lot of parameters, you can define the
local function instead of the method;
Initializing multiple local variables
Consider the following example:
bool b = ...;
var x = 0;
var y = 1;
if (b) {
    x = 1;
    y = 3;
}
Since you must not repeat the branches depending on the same condition, you should not write the following code:
var x = b ? 1 : 0;
var y = b ? 3 : 1;
Instead, you can use a tuple as follows:
var (x, y) = b ? (1, 3) : (0, 1);
Initialization with side effects
Let's consider more practical code as follows:
public void Baz()
{
    var n = UserInput;
    if (!IsValid(n))
    {
        logger.Warn($"invalid input: {n}");
        n = DefaultValue;
    }
    else
    {
        logger.Info($"input: {n}");
    }
    ...
}
Even if you rewrite the above example with one-shot initialization, you should not write the following code:
public void Baz()
{
    var raw = UserInput;
    var isValid = IsValid(raw);
    if (!isValid)
    {
        logger.Warn($"invalid input: {raw}");
    }
    else
    {
        logger.Info($"input: {raw}");
    }
    var n = isValid ? raw : DefaultValue;
    ...
}
The branch depending on isValid should be one time as well as the original
code.  You can do so with local functions as follows:
public void Baz()
{
    int ValidUserInput(int raw)
    {
        logger.Info($"input: {raw}");
        return raw;
    }
    int InvalidUserInput(int raw)
    {
        logger.Warn($"invalid input: {raw}");
        return DefaultValue;
    }
    var raw = UserInput;
    var supplier = IsValid(raw)
        ? (Func<int, int>)ValidUserInput
        : InvalidUserInput;
    var n = supplier(raw);
    ...
}
Or more simply, you can also use a tuple containing the action of type Action
that represents side effects, as follows:
public void Baz()
{
    var raw = UserInput;
    var (n, action) = IsValid(raw)
        ? (raw, new Action(() => logger.Info($"input: {raw}")))
        : (DefaultValue, () => logger.Warn($"invalid input: {raw}"));
    action();
    ...
}
Note that this analyzer does not emit diagnostics against the code examples described in this section.
Code fix
The code fix is not provided.
Example
Diagnostic
var b = 1;
if (...) {
    b = 3;
}
var n = 0;
if (...) {
    ++n;
}
var v = 0;
switch (...)
{
    case ...:
        v = 1;
        break;
    case ...:
        v = 2;
        break;
}
References
[1] Walter E. Brown. Crazy Code, Crazy Coders, Meeting C++ 2019