Primi 10 bug trovati nei progetti C# nel 2021

Primi 10 bug trovati nei progetti C# nel 2021

Nel 2021 abbiamo pubblicato diversi articoli e mostrato errori riscontrati nei progetti open source. L'anno 2021 finisce, il che significa che è tempo di presentarti la tradizionale top 10 dei bug più interessanti. Divertiti!

Una piccola introduzione

Come nell'articolo del 2020, abbiamo classificato gli avvisi in base ai seguenti principi:

  • c'è un'alta probabilità che sia presente un errore nel codice;
  • questo errore deve essere interessante, raro e insolito;
  • gli avvisi nell'elenco devono essere diversi:non vuoi leggere gli stessi errori, vero?

Dobbiamo ammettere che c'erano pochi articoli sul controllo dei progetti C#. Gli avvisi in questo elenco provengono spesso dagli stessi progetti. In qualche modo è successo che la maggior parte degli avvisi sono stati presi da articoli su DNN e PeachPie.

D'altra parte, gli errori rilevati quest'anno non si assomigliano:tutti gli avvisi sono stati emessi da una diagnostica diversa!

Con il cuore pesante ho cancellato gli avvertimenti che erano buoni ma meno interessanti di altri. A volte ho dovuto cancellare gli avvertimenti per motivi di varietà. Quindi, se ti piacciono le recensioni degli avvisi dell'analizzatore, puoi guardare altri articoli. Chissà, forse rimarrai colpito da qualcosa di cui non ho scritto. Commenta con la tua top 10:li leggerò volentieri :).

10° posto. Il tempo non cambia

Iniziamo la nostra parte superiore con un avviso dall'articolo di PeachPie:

using System_DateTime = System.DateTime;

internal static System_DateTime MakeDateTime(....) { .... }

public static long mktime(....)
{
  var zone = PhpTimeZone.GetCurrentTimeZone(ctx);
  var local = MakeDateTime(hour, minute, second, month, day, year);

  switch (daylightSaving)
  {
    case -1:
      if (zone.IsDaylightSavingTime(local))
        local.AddHours(-1);                   // <=
      break;
    case 0:
      break;
    case 1:
      local.AddHours(-1);                     // <=
      break;
    default:
      PhpException.ArgumentValueNotSupported("daylightSaving", daylightSaving);
      break;
  }
  return DateTimeUtils.UtcToUnixTimeStamp(TimeZoneInfo.ConvertTime(local, 
                                                                   ....));
}

Avvisi PVS-Studio:

  • V3010 È necessario utilizzare il valore di ritorno della funzione 'AddHours'. DateTimeFunctions.cs 1232
  • V3010 È necessario utilizzare il valore di ritorno della funzione 'AddHours'. DateTimeFunctions.cs 1239

Questi avvertimenti dicono la stessa cosa, quindi ho deciso di unirli.

L'analizzatore dice che i risultati della chiamata dovrebbero essere scritti da qualche parte. Altrimenti semplicemente non hanno senso. Metodi come AddHours non cambiano l'oggetto di origine. Invece, restituiscono un nuovo oggetto, che differisce da quello di origine per il numero di ore scritte nella chiamata all'argomento. È difficile dire quanto sia grave l'errore, ma il codice funziona in modo errato.

Tali errori sono spesso legati alle stringhe, ma a volte puoi incontrarli quando lavori con altri tipi. Succedono perché gli sviluppatori fraintendono il lavoro dei metodi di "cambiamento".

9° posto. Il quarto elemento è presente, ma è meglio ottenere un'eccezione

Il 9° posto è per l'avvertimento dall'articolo di Ryujinx:

public uint this[int index]
{
  get
  {
    if (index == 0)
    {
      return element0;
    }
    else if (index == 1)
    {
      return element1;
    }
    else if (index == 2)
    {
      return element2;
    }
    else if (index == 2)   // <=
    {
      return element3;
    }

    throw new IndexOutOfRangeException();
  }
}

Avviso PVS-Studio:V3003 È stato rilevato l'uso del pattern "if (A) {…} else if (A) {…}". C'è una probabilità di presenza di un errore logico. Righe di controllo:26, 30. ZbcSetTableArguments.cs 26

Ovviamente, tutto andrà bene finché qualcuno non vorrà ottenere il terzo elemento. E se lo fanno, viene generata un'eccezione. Va bene, ma perché c'è un blocco mai eseguito con element3 ?

Sorprendentemente, le situazioni causate da errori di battitura con i numeri 0,1,2 sono frequenti nello sviluppo. C'è un intero articolo su questo:ti consiglio vivamente di leggerlo. E andiamo avanti.

8° posto. Utile chiamata Debug.WriteLine

Ho preso questo avviso dall'articolo di PeachPie menzionato sopra. È affascinante che il codice appaia del tutto normale e per nulla sospetto:

public static bool mail(....)
{
  // to and subject cannot contain newlines, replace with spaces
  to = (to != null) ? to.Replace("\r\n", " ").Replace('\n', ' ') : "";
  subject = (subject != null) ? subject.Replace("\r\n", " ").Replace('\n', ' ')
                              : "";

  Debug.WriteLine("MAILER",
                  "mail('{0}','{1}','{2}','{3}')",
                  to,
                  subject,
                  message, 
                  additional_headers);

  var config = ctx.Configuration.Core;
  
  ....
}

Che cosa c'è che non va? Tutto sembra a posto. Vengono effettuati gli incarichi, quindi un sovraccarico di Debug.WriteLine è chiamato. Come primo argomento, questo sovraccarico richiede... Aspetta! Qual è l'ordine corretto degli argomenti qui?

Bene, diamo un'occhiata alla dichiarazione Debug.WriteLine:

public static void WriteLine(string format, params object[] args);

Secondo la firma, la stringa di formato dovrebbe essere passata come primo argomento. Nel frammento di codice, il primo argomento è "MAILER" e il formato effettivo va in args array, che non ha alcun effetto.

PVS-Studio avverte che tutti gli argomenti di formattazione vengono ignorati:V3025:Formato errato. È previsto un numero diverso di elementi di formato durante la chiamata alla funzione "WriteLine". Argomenti non utilizzati:1°, 2°, 3°, 4°, 5°. Mail.cs 25

Di conseguenza, l'output sarà semplicemente "MAILER" senza altre informazioni utili. Ma ci piacerebbe vederlo :(.

7° posto. Solo un'altra domanda

Il 7° posto è ancora per l'avvertimento di PeachPie.

Spesso gli sviluppatori mancano di controlli nulli . Una situazione particolarmente interessante è quando una variabile è stata verificata in un posto e non era in un altro (dove può anche essere nulla). Forse gli sviluppatori si sono dimenticati di farlo o semplicemente l'hanno ignorato. Possiamo solo indovinare se il controllo era ridondante o se dobbiamo aggiungere un altro controllo da qualche parte nel codice. I controlli per null non richiedono sempre operatori di confronto:ad esempio, nel frammento di codice sotto lo sviluppatore ha utilizzato un operatore condizionale nullo:

public static string get_parent_class(....)
{
  if (caller.Equals(default))
  {
    return null;
  }

  var tinfo = Type.GetTypeFromHandle(caller)?.GetPhpTypeInfo();
  return tinfo.BaseType?.Name;
}

Avviso V3105:la variabile 'tinfo' è stata utilizzata dopo essere stata assegnata tramite l'operatore condizionale nullo. NullReferenceException è possibile. Objects.cs 189

Lo sviluppatore pensa che la chiamata Type.GetTypeFromHandle(caller) possa restituire null. Ecco perché hanno usato "?." per chiamare GetPhpTypeInfo. Secondo la documentazione, è possibile.

Sì, "?." salva da un'eccezione. Se la chiamata GetTypeFromHandle restituisce null, anche null viene scritto nella variabile tinfo. Tuttavia, se proviamo ad accedere alla proprietà BaseType, viene generata un'altra eccezione. Quando ho esaminato il codice, sono giunto alla conclusione che un altro "?" manca:restituire tinfo? .TipoBase?.Nome;

Tuttavia, solo gli sviluppatori possono risolvere questo problema. Questo è esattamente quello che hanno fatto dopo che abbiamo inviato loro una segnalazione di bug. Invece di un ulteriore null controlla che abbiano deciso di generare esplicitamente un'eccezione se GetTypeFromHandle restituisce null :

public static string get_parent_class(....)
{
  if (caller.Equals(default))
  {
    return null;
  }
  
  // cannot be null; caller is either default or an invalid handle
  var t =    Type.GetTypeFromHandle(caller) 
          ?? throw new ArgumentException("", nameof(caller));

  var tinfo = t.GetPhpTypeInfo();
  return tinfo.BaseType?.Name;
}

Abbiamo dovuto formattare il codice per questo articolo. Puoi trovare questo metodo seguendo il link.

6° posto. Settimana durata un giorno

A volte sembra che il tempo rallenti. Ti senti come se fosse passata un'intera settimana, ma è passato solo un giorno. Bene, al 6° posto abbiamo un avviso dall'articolo di DotNetNuke. L'analizzatore è stato attivato dal codice in cui una settimana contiene solo un giorno:

private static DateTime CalculateTime(int lapse, string measurement)
{
  var nextTime = new DateTime();
  switch (measurement)
  {
    case "s":
      nextTime = DateTime.Now.AddSeconds(lapse);
      break;
    case "m":
      nextTime = DateTime.Now.AddMinutes(lapse);
      break;
    case "h":
      nextTime = DateTime.Now.AddHours(lapse);
      break;
    case "d":
      nextTime = DateTime.Now.AddDays(lapse);   // <=
      break;
    case "w": 
      nextTime = DateTime.Now.AddDays(lapse);   // <=
      break;
    case "mo":
      nextTime = DateTime.Now.AddMonths(lapse);
      break;
    case "y":
      nextTime = DateTime.Now.AddYears(lapse);
      break;
  }
  return nextTime;
}

Avviso PVS-Studio:V3139 Due o più rami del case eseguono le stesse azioni. DotNetNuke.Tests.Core PropertyAccessTests.cs 118

Ovviamente, la funzione dovrebbe restituire DateTime che corrisponde a un momento successivo a quello attuale. In qualche modo è successo che la lettera "w" (che implica "settimana") viene elaborata allo stesso modo di "d". Se proviamo a ottenere una data, una settimana dal momento attuale, avremo il giorno successivo!

Nota che non ci sono errori con la modifica di oggetti immutabili. Tuttavia, è strano che il codice per i rami "d" e "w" sia lo stesso. Ovviamente, non esiste un metodo standard AddWeeks in DateTime, ma puoi aggiungere 7 giorni :).

5° posto. Operatori logici e null

Il 5° posto è preso da uno dei miei avvertimenti preferiti dall'articolo di PeachPie. Ti suggerisco di dare un'occhiata più da vicino a questo frammento e di trovare un errore qui.

public static bool IsAutoloadDeprecated(Version langVersion)
{
  // >= 7.2
  return    langVersion != null 
         &&    langVersion.Major > 7 
            || (langVersion.Major == 7 && langVersion.Minor >= 2);
}

Dov'è il problema?

Penso che tu abbia facilmente trovato un errore qui. Davvero facile, se sai dove cercare :). Devo ammettere che ho cercato di confonderti e ho cambiato un po' la formattazione. In effetti, la costruzione logica è stata scritta in una riga.

Ora diamo un'occhiata alla versione formattata in base alle priorità dell'operatore:

public static bool IsAutoloadDeprecated(Version langVersion)
{
  // >= 7.2
  return    langVersion != null && langVersion.Major > 7 
         || (langVersion.Major == 7 && langVersion.Minor >= 2);
}

Avviso PVS-Studio V3080:possibile dereference null. Prendi in considerazione l'ispezione di "langVersion". AnalysisFacts.cs 20

Il codice verifica che il parametro langVersion passato non sia null. Lo sviluppatore ha presupposto che null potesse essere passato quando chiamiamo IsAutoloadDeprecated metodo. L'assegno ci salva?

No. Se la variabile langVersion è null, il valore della prima parte dell'espressione è false. Quando calcoliamo la seconda parte, viene generata un'eccezione.

A giudicare dal commento, o le priorità degli operatori sono state confuse o gli sviluppatori hanno semplicemente inserito la parentesi in modo errato. A proposito, questo e altri errori sono spariti (credo):abbiamo inviato una segnalazione di bug agli sviluppatori e li hanno corretti rapidamente. Puoi vedere una nuova versione di IsAutoloadDeprecated funzione qui.

4° posto. Elaborazione di una pagina inesistente

Siamo quasi vicini ai finalisti. Ma prima ancora — il 4° posto. E qui abbiamo l'avvertimento dall'ultimo articolo su Umbraco. Cosa abbiamo qui?

public ActionResult<PagedResult<EntityBasic>> GetPagedChildren(....
                                                               int pageNumber,
                                                               ....)
{
  if (pageNumber <= 0)
  {
    return NotFound();
  }
  ....
  if (objectType.HasValue)
  {
    if (id == Constants.System.Root &&
        startNodes.Length > 0 &&
        startNodes.Contains(Constants.System.Root) == false &&
        !ignoreUserStartNodes)
    {
      if (pageNumber > 0)  // <=
      {
        return new PagedResult<EntityBasic>(0, 0, 0);
      }
      IEntitySlim[] nodes = _entityService.GetAll(objectType.Value, 
                                                  startNodes).ToArray();
      if (nodes.Length == 0)
      {
        return new PagedResult<EntityBasic>(0, 0, 0);
      }

      if (pageSize < nodes.Length)
      {
        pageSize = nodes.Length; // bah
      }

      var pr = new PagedResult<EntityBasic>(nodes.Length, pageNumber, pageSize)
      {
        Items = nodes.Select(_umbracoMapper.Map<EntityBasic>)
      };
      return pr;
    }
  }
}

Avviso PVS-Studio:l'espressione V3022 'pageNumber> 0' è sempre vera. EntityController.cs 625

Quindi, numero di pagina è un parametro che non cambia all'interno del metodo. Se il suo valore è minore o uguale a 0, usciamo dalla funzione. Più avanti, il codice controlla se pageNumber è maggiore di 0.

Qui abbiamo una domanda:quale valore dobbiamo passare a pageNumber per creare condizioni pageNumber <=0numero pagina> 0 falso?

Ovviamente, non esiste un tale valore. Se seleziona numero pagina <=0 è falso , quindi numero pagina> 0 è sempre vero. È spaventoso? Diamo un'occhiata al codice dopo il controllo sempre vero:

if (pageNumber > 0)
{
  return new PagedResult<EntityBasic>(0, 0, 0);
}

IEntitySlim[] nodes = _entityService.GetAll(objectType.Value, 
                                            startNodes).ToArray();
if (nodes.Length == 0)
{
  return new PagedResult<EntityBasic>(0, 0, 0);
}

if (pageSize < nodes.Length)
{
  pageSize = nodes.Length; // bah
}

var pr = new PagedResult<EntityBasic>(nodes.Length, pageNumber, pageSize)
{
  Items = nodes.Select(_umbracoMapper.Map<EntityBasic>)
};
return pr;

Poiché il controllo all'inizio di questo frammento è sempre vero , il metodo esce sempre. E per quanto riguarda il codice qui sotto? Contiene un sacco di operazioni significative, ma nessuna di esse viene mai eseguita!

Sembra sospetto. Se numero pagina è minore o uguale a 0 , viene restituito il risultato predefinito:NotFound() . Sembra logico. Tuttavia, se il parametro è maggiore di 0, otteniamo... qualcosa che assomiglia al risultato predefinito:new PagedResult(0, 0, 0) . E come otteniamo un risultato normale? Non chiaro :(.

3d luogo. L'errore più raro

Quindi, ecco i finalisti. Il terzo posto è per la diagnostica V3122 che non ha rilevato errori nei progetti open source per molto tempo. Infine, nel 2021 abbiamo controllato DotNetNuke e trovato anche 2 avvisi V3122!

Quindi, ti presento il posto 3d:

public static string LocalResourceDirectory
{
  get
  {
    return "App_LocalResources";
  }
}
private static bool HasLocalResources(string path)
{
  var folderInfo = new DirectoryInfo(path);

  if (path.ToLowerInvariant().EndsWith(Localization.LocalResourceDirectory))
  {
    return true;
  }
  ....
}

Avviso di PVS-Studio:V3122 La stringa minuscola 'path.ToLowerInvariant()' viene confrontata con la stringa maiuscola mista 'Localization.LocalResourceDirectory'. Dnn.PersonaBar.Extensions LanguagesController.cs 644

Gli sviluppatori convertono il valore del percorso in minuscolo. Quindi, controllano se termina con una stringa che contiene caratteri maiuscoli - "App_LocalResources" (il valore letterale restituito dalla proprietà LocalResourceDirectory). Ovviamente questo controllo restituisce sempre false e tutto sembra sospetto.

Questo avviso mi ricorda che non importa quanti errori abbiamo visto, c'è sempre qualcosa che può sorprenderci. Andiamo oltre :).

2° posto. Non c'è via di scampo

Il secondo posto è per un eccellente avvertimento tratto dall'articolo di ILSpy scritto all'inizio del 2021:

private static void WriteSimpleValue(ITextOutput output,
                                     object value, string typeName)
{
  switch (typeName)
  {
    case "string":
      output.Write(  "'"
                   + DisassemblerHelpers
                      .EscapeString(value.ToString())
                      .Replace("'", "\'")                   // <=
                   + "'");
      break;
    case "type":
    ....
  }
  ....
}

V3038 L'argomento """"" è stato passato più volte al metodo "Sostituisci". È possibile invece che venga passato un altro argomento. ICSharpCode.Decompiler ReflectionDisassembler.cs 772

Sembra che lo sviluppatore volesse sostituire tutte le occorrenze di virgolette singole con una stringa composta da due caratteri:una barra rovesciata e una virgoletta singola. Tuttavia, a causa delle particolarità delle sequenze di escape, il secondo argomento è solo un carattere di virgolette singole. Pertanto, nessuna sostituzione qui.

Mi sono venute in mente due idee:

  • gli sviluppatori hanno dimenticato di inserire il carattere "@" prima della seconda stringa. Questo carattere consentirebbe semplicemente di salvare "\" come carattere separato;
  • Avrebbero dovuto mettere un "\" aggiuntivo prima del primo nel secondo argomento. Il primo sfuggirebbe al secondo, il che significa che la stringa finale avrebbe solo un "\".

1° posto. La minaccia fantasma

Quindi, abbiamo finalmente raggiunto l'errore più interessante e insolito del 2021. Questo errore proviene dall'articolo DotNetNuke menzionato sopra.

Ciò che è ancora più interessante, l'errore è primitivo, ma l'occhio umano non rileva errori come questo senza strumenti di analisi statica. Affermazione audace? Bene, allora prova a trovare un errore qui (se ce n'è uno, ovviamente):

private void ParseTemplateInternal(...., string templatePath, ....)
{
  ....
  string path = Path.Combine(templatePath, "admin.template");
  if (!File.Exists(path))
  {
    // if the template is a merged copy of a localized templte the
    // admin.template may be one director up
    path = Path.Combine(templatePath, "..\admin.template");
  }
  ....
}

Bene, come va? Non sarò sorpreso se trovi un errore. Dopotutto, se sai che esiste, lo vedrai rapidamente. E se non l'avessi trovato, beh, nessuna sorpresa. Non è così facile vedere un errore di battitura nel commento:'template' invece di 'template' :).

Scherzando. Naturalmente c'è un vero errore che interrompe il lavoro del programma. Esaminiamo di nuovo il codice:

private void ParseTemplateInternal(...., string templatePath, ....)
{
  ....
  string path = Path.Combine(templatePath, "admin.template");
  if (!File.Exists(path))
  {
    // if the template is a merged copy of a localized templte the
    // admin.template may be one director up
    path = Path.Combine(templatePath, "..\admin.template");
  }
  ....
}

Avviso PVS-Studio:V3057 La funzione "Combina" dovrebbe ricevere una stringa di percorso valida. Esamina il secondo argomento. DotNetNuke.Library PortalController.cs 3538

Qui abbiamo due operazioni per costruire un percorso (la chiamata Path.Combine). Il primo va bene, ma il secondo no. Chiaramente, nel secondo caso, gli sviluppatori hanno voluto prendere il file 'admin.template' non dalla directory templatePath, ma da quella genitore. Ahimè! Dopo aver aggiunto ..\, il percorso è diventato non valido poiché si è formata una sequenza di escape:..\a dmin.modello.

Sembra l'avviso precedente, giusto? Non esattamente. Tuttavia, la soluzione è la stessa:aggiungi "@" prima della stringa o un "\" aggiuntivo.

0 posto. "lol" vs Visual Studio

Bene, poiché il primo elemento della raccolta ha l'indice 0, anche la nostra raccolta dovrebbe avere 0 posto!

Naturalmente, l'errore qui è speciale, andando oltre il solito top. Eppure vale la pena menzionarlo, dal momento che l'errore è stato trovato nell'amato Visual Studio 2022. E cosa c'entra l'analisi statica con esso? Bene, troviamo una risposta.

Il mio compagno di squadra, Sergey Vasiliev, ha riscontrato questo problema e lo ha descritto nell'articolo "Come Visual Studio 2022 ha consumato 100 GB di memoria e cosa c'entravano le bombe XML". Qui descriverò brevemente la situazione.

In Visual Studio 2022 Preview 3.1, un particolare file XML aggiunto a un progetto fa ritardare l'IDE. Il che significa che tutto soffrirà insieme a questo IDE. Ecco un esempio di tale file:

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
 <!ENTITY lol10 "&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;">
 <!ENTITY lol11 
   "&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;">
 <!ENTITY lol12 
   "&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;">
 <!ENTITY lol13 
   "&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;">
 <!ENTITY lol14 
   "&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;">
 <!ENTITY lol15 
   "&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;">
]>
<lolz>&lol15;</lolz>

Come si è scoperto, Visual Studio era vulnerabile a un attacco XEE. Cercando di espandere tutte queste entità lol e l'IDE si è bloccato e ha consumato un'enorme quantità di RAM. Alla fine, ha consumato tutta la memoria possibile :(.

Questo problema è stato causato dall'utilizzo di un parser XML configurato in modo non sicuro. Questo parser consente l'elaborazione DTD e non pone limiti alle entità. Il mio consiglio:non leggere file esterni da fonti sconosciute con un parser XML. Questo porterà a un attacco DoS.

L'analisi statica aiuta a trovare tali problemi. A proposito, PVS-Studio ha recentemente introdotto una nuova diagnostica per rilevare potenziali XEE — V5615.

Abbiamo inviato a Visual Studio una segnalazione di bug al riguardo e l'hanno risolto nella nuova versione. Buon lavoro, Microsoft! 🙂

Conclusione

Sfortunatamente, nel 2021 non abbiamo scritto così tanti articoli sui veri controlli di progetto. D'altra parte, abbiamo scritto una serie di altri articoli relativi a C#. Puoi trovare i link alla fine di questo articolo.

È stato facile scegliere avvertenze interessanti per questo top. Ma non è stato facile scegliere i 10 migliori perché ce n'erano molti di più.

Anche valutarli è stato un compito infernale:il massimo è soggettivo, quindi non prendere i posti troppo vicini a te :). In un modo o nell'altro, tutti questi avvertimenti sono seri e ancora una volta ci ricordano che stiamo facendo la cosa giusta.

Sei sicuro che il tuo codice non abbia tali problemi? Sei sicuro che gli errori non si nascondano tra le righe? Forse, non puoi mai esserne sicuro con un grande progetto. Tuttavia, questo articolo mostra che è possibile trovare piccoli (e non molto piccoli) errori con l'analizzatore statico. Ecco perché ti invito a provare PVS-Studio sui tuoi progetti.

Bene, questo è tutto. Buon Anno Nuovo ea presto!

Articoli interessanti nel 2021

Ho raccolto diversi articoli che puoi consultare durante le lunghe serate invernali :).

  • Tutte le segnalazioni di bug di grandine:come abbiamo ridotto il tempo di analisi del progetto dell'utente da 80 a 4 ore
  • OWASP Top Ten e Software Composition Analysis (SCA)
  • Novità di C# 10:panoramica
  • Novità di C# 9:panoramica
  • XSS:attacco, difesa e programmazione C#
  • Enumerazioni in C#:insidie ​​nascoste
  • Come la WCF si spara ai piedi con TraceSource
  • Il ?. l'operatore in foreach non proteggerà da NullReferenceException
  • Reef nascosti nel pool di stringhe o un altro motivo per pensarci due volte prima di internare le istanze della classe string in C#
  • Dobbiamo inizializzare un parametro out prima che un metodo ritorni?