← Назад ко всем статьям

Практический guide по WarningsAsErrors

Опубликовано

Software warnings кажутся безобидными до того дня, когда один из них превращается в late-night outage.

В этой статье разберем, как превратить желтые squiggles в early-failing, production-saving errors с помощью family settings WarningsAsErrors.

Вы увидите, как включить switch, когда его fine-tune и что делать, если project уже drowning in warnings.

Почему один warning может утопить release

Обычная C# solution компилируется даже когда пролетают десятки diagnostics, потому что default severity для большинства compiler diagnostics и analyzer rules - warning. Teams привыкают к noise, и реальные defects прячутся на виду:

  • Nullability warnings вроде CS8602, которые позже crash application.

  • Unobserved task exceptions, masked by ignored async warnings.

  • API misuse, пойманный analyzers, но так и не исправленный.

Прямо говоря, warning - это bug, который вы запланировали на production.

Code Example

public static class Program
{
    static void Main(string[] args)
    {
        // CS0168: Variable declared but never used
        int unusedVariable;

        // CS8600: Converting null literal or possible
        // null value to non-nullable type
        string nullableStr = GetNullableString();

        // CS8602: Dereference of a possibly null reference
        Console.WriteLine($"Length: {nullableStr.Length}");
    }

    // Method that can return null
    private static string? GetNullableString()
    {
        return DateTime.Now.Second % 2 == 0 ? "Test string" : null;
    }
}

Error List показывает CS0168, CS8600 и CS8602, но build все равно успешно завершается с zero errors и three warnings.

image

TreatWarningsAsErrors

Одна строка в .csproj превращает every compiler warning в error:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
</Project>

С этого момента build fails fast.

image

Pros

  1. Нулевой шанс отправить fresh compiler warnings в production.

  2. Clear signal для каждого developer.

Cons

  1. Это может быть brutal, если в legacy codebase уже сотни diagnostics.

WarningsAsErrors

Если нужен больший control, перечислите только warning codes, которые должны treated as errors:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <WarningsAsErrors>CS8600</WarningsAsErrors>
  </PropertyGroup>
</Project>

Все остальные warnings остаются warnings и не ломают build. Чтобы включить несколько codes, разделяйте их semicolons:

<WarningsAsErrors>CS8600;CS8602</WarningsAsErrors>

image

WarningsNotAsErrors

Иногда нужен обратный подход: держать most warnings as errors, кроме нескольких noisy ones.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <WarningsNotAsErrors>CS0168</WarningsNotAsErrors>
  </PropertyGroup>
</Project>

image

Если у вас уже сотни warnings, этого approach может быть недостаточно. В одном из следующих постов я поделюсь tactics, как быстро их clean up.

Key Takeaways

  1. Warning, проигнорированный сегодня, может стать завтрашним post-mortem.

  2. TreatWarningsAsErrors - самый простой и надежный safety net.

  3. Selective lists с WarningsAsErrors и WarningsNotAsErrors позволяют внедрять rule постепенно.

  4. Автоматизируйте compiler switches, analyzers и formatting, чтобы signal оставался clean.

Закройте warnings сейчас, и production environment позже будет спать спокойнее.