Avviso C26430
Il simbolo non viene testato per verificare l'nullità in tutti i percorsi.
Linee guida di base di C++: F.23: Usare un not_null<T> per indicare che "null" non è un valore valido
Se il codice controlla le variabili del puntatore per i valori Null, deve farlo in modo coerente e convalidare i puntatori in tutti i percorsi. A volte il controllo eccessivo dei valori Null è ancora migliore della possibilità di un arresto anomalo in uno dei rami complicati. Idealmente, tale codice deve essere sottoposto a refactoring per essere meno complesso (suddividendolo in più funzioni) e per basarsi su marcatori come gsl::not_null
. Questi marcatori consentono al codice di isolare parti dell'algoritmo che possono fare ipotesi sicure sui valori validi del puntatore. La regola TEST_ON_ALL_PATHS
consente di individuare le posizioni in cui i controlli Null sono incoerenti (il che significa che i presupposti possono richiedere una revisione). In alternativa, rileva i bug effettivi in cui un valore Null potenziale può ignorare i controlli Null in alcuni dei percorsi di codice.
Osservazioni:
Questa regola prevede che il codice dereferenzii una variabile puntatore in modo che un controllo Null (o l'imposizione di un valore non Null) sia giustificato. Se non è presente alcuna dereferenziazione, la regola viene sospesa.
L'implementazione corrente gestisce solo puntatori semplici (o i relativi alias) e non rileva puntatori intelligenti, anche se i controlli Null sono applicabili anche ai puntatori intelligenti.
Una variabile viene contrassegnata come selezionata per i valori Null quando viene usata nei contesti seguenti:
- come espressione di simbolo in una condizione di ramo, ad esempio in
if (p) { ... }
; - in operazioni logiche non bit per bit;
- nelle operazioni di confronto in cui un operando è un'espressione costante che restituisce zero.
I controlli Null impliciti vengono considerati quando viene assegnato un valore puntatore da:
- un'allocazione eseguita con la creazione di
operator new
; - puntatore ottenuto da un tipo contrassegnato con
gsl::not_null
.
Esempio
il test incoerente rivela un errore di logica
void merge_states(const state *left, const state *right) // C26430
{
if (*left && *right)
converge(left, right);
else
{
// ...
if (!left && !right) // Logic error!
discard(left, right);
}
}
il test incoerente rivela l'errore di logica - corretto
void merge_states(gsl::not_null<const state *> left, gsl::not_null<const state *> right)
{
if (*left && *right)
converge(left, right);
else
{
// ...
if (*left && *right)
discard(left, right);
}
}
Euristica
Quando si garantisce che una dereferenziazione di un puntatore non sia Null, questa regola non richiede che ogni dereferenziazione abbia un controllo Null precedente. Richiede invece un controllo Null prima della dereferenziazione del puntatore. La funzione seguente non attiva C26430:
void f(int* p)
{
if (p)
*p = 1;
*p = 2;
}
La funzione seguente genera l'errore C26430 perché esiste un percorso da assegnare *p
senza un controllo Null:
void f(bool b, int* p)
{
if (b && p)
*p = 1;
*p = 2;
}
Le regole C26822 e C26823 si applicano alla dereferenziazione di un puntatore Null (possibilmente).
Questa regola non esegue il rilevamento completo del flusso di dati. Può produrre risultati non corretti nei casi in cui vengono usati controlli indiretti, ad esempio quando una variabile intermedia contiene un valore Null e viene usata successivamente in un confronto.