Contrôle RunUO de l'analyseur PVS-Studio

Image 1

Cet article est consacré à la vérification du projet RunUO à l'aide de l'analyseur statique PVS-Studio. RunUO est un émulateur de logiciel serveur pour Ultima Online, un jeu qui a une fois conquis le cœur de nombreux fans de MMORPG.

introduction


RunUO est un émulateur de logiciel serveur pour MMORPG Ultima Online. Le but de ce projet est de créer un logiciel stable qui pourra rivaliser avec les serveurs officiels d'EA Games. RunUO a été créé en 2002, mais il ne perd pas de sa pertinence et est activement utilisé à ce jour.

Le but de la vérification du projet est de vulgariser le sujet de l'analyse statique. Nous vérifions divers projets - jeux ( exemple ), bibliothèques ( exemple ), messagers ( exemple ), navigateurs ( exemple ) et bien plus encore ( exemple , exemple , exemple) pour attirer l'attention d'un public diversifié. Avec ces articles, nous essayons d'attirer l'attention sur l'importance d'utiliser l'analyse statique dans le développement. L'analyse statique rend le code plus fiable et plus sûr. De plus, grâce à son utilisation régulière, vous pouvez rechercher et éliminer les erreurs dès les premiers stades. Cela fait gagner du temps et des efforts aux développeurs, car personne ne veut passer 50 heures à rechercher l'erreur que l'analyseur peut trouver.

Nous aidons également la communauté open source. À travers des articles avec des erreurs trouvées, nous contribuons au développement de l'open source. Cependant, dans les articles, nous n'analysons pas tous les avertissements. Certains avertissements semblaient trop ennuyeux pour nous d'entrer dans l'article, certains se sont révélés être de faux positifs, etc. Par conséquent, nous sommes prêts à fournir une licence gratuite pour les projets open source. De plus, ce qui nous a semblé ennuyeux pour l'article peut sembler assez intéressant pour les développeurs du projet en cours de test, car les développeurs du projet sont encore plus conscients des problèmes les plus critiques.

Morceaux de code qui ont retenu l'attention lors de l'examen du rapport de l'analyseur


PVS-Studio Warning: V3010 La valeur de retour de la fonction 'Intern' doit être utilisée. BasePaintedMask.cs 49

public static string Intern( string str )
{
  if ( str == null )
    return null;
  else if ( str.Length == 0 )
    return String.Empty;

  return String.Intern( str );
}

public BasePaintedMask( string staffer, int itemid )
                            : base( itemid + Utility.Random( 2 ) )
{
  m_Staffer = staffer;

  Utility.Intern( m_Staffer );
}

La valeur de retour de la méthode Intern () n'est prise en compte nulle part, comme indiqué par l'analyseur. Il s'agit peut-être d'un bogue ou d'un code redondant.

Avertissement PVS-Studio: V3017 Un modèle a été détecté: (l'élément est BasePotion) || ((l'élément est BasePotion) && ...). L'expression est excessive ou contient une erreur logique. Cleanup.cs 137

public static bool IsBuggable( Item item )
{
  if ( item is Fists )
    return false;

  if ( item is ICommodity || item is Multis.BaseBoat
    || item is Fish || item is BigFish
    || item is BasePotion || item is Food || item is CookableFood
    || item is SpecialFishingNet || item is BaseMagicFish
    || item is Shoes || item is Sandals
    || item is Boots || item is ThighBoots
    || item is TreasureMap || item is MessageInABottle
    || item is BaseArmor || item is BaseWeapon
    || item is BaseClothing
    || ( item is BaseJewel && Core.AOS )
    || ( item is BasePotion && Core.ML )
  {
    ....
  }
}

Il existe des sous-expressions qui peuvent être simplifiées. Je vais les écrire pour que ce soit plus clair:
if (item is BasePotion || ( item is BasePotion && Core.ML ))

Supposons article est BasePotion = true , la condition est vrai, en dépit de la Core.ML . Si l' article est BasePotion = false , la condition est fausse, encore une fois quelle que soit la valeur Core.ML . Un tel code est le plus souvent simplement redondant, cependant, il y a des situations pires lorsque le programmeur a fait une erreur et a écrit la mauvaise variable dans la deuxième sous-expression.

PVS-Studio Warning: V3031 Un contrôle excessif peut être simplifié. Le '||' L'opérateur est entouré d'expressions opposées «bPlayerOnly» et «! bPlayerOnly». BaseCreature.cs 3005

public virtual double GetFightModeRanking( Mobile m,
                                           FightMode acqType,
                                           bool bPlayerOnly )
{
  if ( ( bPlayerOnly && m.Player ) ||  !bPlayerOnly )
  {
    ....
  }
  ....
}

Ce code est soit redondant soit erroné. Son problème est que des deux côtés de «||» sont différents dans le sens de la sous-expression. Si nous le coupons comme ceci:

if ( m.Player || !bPlayerOnly )

alors rien ne changera.

Avertissement PVS-Studio: V3001 Il existe des sous-expressions identiques «acte est SmallBrickHouseDeed» à gauche et à droite de «||» opérateur. RealEstateBroker.cs 132

public int ComputePriceFor( HouseDeed deed )
{
  int price = 0;

  if ( deed is SmallBrickHouseDeed ||    // <=
       deed is StonePlasterHouseDeed ||
       deed is FieldStoneHouseDeed ||
       deed is SmallBrickHouseDeed ||    // <=
       deed is WoodHouseDeed ||
       deed is WoodPlasterHouseDeed ||
       deed is ThatchedRoofCottageDeed )
      ....
}

Je pense que cela ne vaut pas la peine d'expliquer quoi que ce soit ici, le code suivant ou erroné ou redondant.

PVS-Studio Warning: V3067 Il est possible que le bloc "else" ait été oublié ou commenté, altérant ainsi les logiques de fonctionnement du programme. BaseHouse.cs 1558

private void SetLockdown( Item i, bool locked, bool checkContains )
{
  if ( m_LockDowns == null )
    return;

  #region Mondain's Legacy
  if ( i is BaseAddonContainer )
    i.Movable = false;
  else
  #endregion

  i.Movable = !locked;
  i.IsLockedDown = locked;

  ....
}

Un avertissement assez rare. L'analyseur a trouvé suspect le formatage du code après la directive #endregion. Si vous ne lisez pas le code, il ressemble à une ligne

i.Movable = !locked;

sera exécuté dans tous les cas, quelle que soit la variable i . Peut-être que les accolades ont été oubliées ici ... En général, les développeurs devraient vérifier ce code.

PVS-Studio Warning: V3043 La logique de fonctionnement du code ne correspond pas à sa mise en forme. L'instruction est en retrait vers la droite, mais elle est toujours exécutée. Il est possible que des accolades soient manquantes. Earthquake.cs 57

public override void OnCast()
{
  if ( Core.AOS )
  {
    damage = m.Hits / 2;

    if ( !m.Player )
      damage = Math.Max( Math.Min( damage, 100 ), 15 );
      damage += Utility.RandomMinMax( 0, 15 );            // <=

  }
  else
  {
    ....
  }
}

Les accolades peuvent être manquantes dans ce code. Cette conclusion peut être faite en raison de la mise en forme étrange du code dans le corps de if (! M.Player) .

Avertissement PVS-Studio: V3083 Appel non sécurisé de l'événement 'ServerStarted', NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. EventSink.cs 921

public static void InvokeServerStarted()
{
  if ( ServerStarted != null )
    ServerStarted();
}

Cette méthode utilise un appel potentiellement dangereux au gestionnaire d' événements RefreshStarted , comme l'indique l'analyseur.

Pourquoi est-ce dangereux? Imaginez cette situation. L'événement ServerStarted n'a qu'un seul abonné. Et au moment entre la vérification de null et l'appel direct du gestionnaire d'événements ServerStarted () dans un autre thread, une désinscription a été effectuée à partir de cet événement. Cela lèvera une NullReferenceException .

Le moyen le plus simple de prévenir cette situation consiste à fournir un appel d'événement sécurisé à l'aide de l'opérateur «?».

public static void InvokeServerStarted()
{
  ServerStarted?.Invoke();
}

Avertissement PVS-Studio: V3054 Verrouillage à double vérification potentiellement dangereux. Utilisez des variables volatiles ou des primitives de synchronisation pour éviter cela. Item.cs 1624
private Packet m_RemovePacket;
....
private object _rpl = new object();
public Packet RemovePacket
{
  get
  {
    if (m_RemovePacket == null)
    {
      lock (_rpl)
      {
        if (m_RemovePacket == null)
        {
          m_RemovePacket = new RemoveItem(this);
          m_RemovePacket.SetStatic();
        }
      }
    }

    return m_RemovePacket;
  }
}

L'avertissement de l'analyseur est associé à une utilisation non sûre du schéma de verrouillage à double vérification. Comme vous pouvez le voir dans le code ci-dessus, un verrou à double vérification a été appliqué pour implémenter le modèle génératif - Loners. Lorsque nous essayons d'obtenir une instance de la classe Packet en accédant à la propriété RemovePacket , le getter vérifie que le champ m_RemovePacket est égal à zéro. Si la vérification réussit, nous entrons dans le corps de l'instruction lock , où le champ m_RemovePacket est initialisé . Une situation intéressante se présente au moment où le thread principal a déjà initialisé la variable m_RemovePacket via le constructeur, mais n'a pas encore appelé la méthodeSetStatic (). Théoriquement, un autre thread peut accéder à la propriété RemovePacket au moment très gênant. La vérification de l'égalité à zéro de m_RemovePacket n'échouera plus et le thread appelant recevra un lien vers un objet incomplètement prêt à l'emploi. Pour résoudre ce problème, vous pouvez créer une variable intermédiaire de la classe Packet dans le corps de l'instruction lock , l'initialiser via un appel de constructeur et la méthode SetStatic (), puis l'affecter à la variable m_RemovePacket . Dans ce cas, le corps de l'instruction lock pourrait ressembler à ceci:

lock (_rpl)
{
  if (m_RemovePacket == null)
  {
    Packet instance = new RemoveItem(this);
    instance.SetStatic();
    m_RemovePacket = instance;
  }
}

Le problème semble être résolu et le code fonctionnera comme prévu. Mais ce n'est pas le cas.

Il y a un autre point: l'analyseur ne suggère pas seulement d'utiliser le mot-clé volatile . Dans la version finale du programme, le compilateur peut effectuer une optimisation et réorganiser les lignes de l'appel à la méthode SetStatic () et affecter la variable d' instance au champ m_RemovePacket (du point de vue du compilateur, la sémantique du programme ne sera pas violée). Et nous revenons encore au même point où nous avons commencé - la possibilité d'obtenir la variable non initialisée m_RemovePacket. Il est impossible de dire exactement quand cette réorganisation peut se produire et si elle se produira: elle peut être affectée par la version CLR, l'architecture du processeur utilisé, etc. Il vaut toujours la peine d'être protégé contre un tel scénario, et l'une des solutions (cependant, pas la plus productive) sera d'utiliser le mot clé volatile . Une variable qui sera déclarée avec le modificateur volatile ne sera pas soumise à des permutations lors de l'optimisation par le compilateur. Un code finalement fixe peut ressembler à ceci:

private volatile Packet m_RemovePacket;
....
private object _rpl = new object();
public Packet RemovePacket
{
  get
  {
    if (m_RemovePacket == null)
    {
      lock (_rpl)
      {
        if (m_RemovePacket == null)
        {
          Packet instance = new RemoveItem(this);
          instance.SetStatic();
          m_RemovePacket = instance;
        }
      }
    }

    return m_RemovePacket;
  }
}

Parfois, l'utilisation d'un champ volatil peut être indésirable en raison de la surcharge d'accès à un tel champ. Nous ne nous attarderons pas sur ce sujet en détail, notant simplement que dans l'exemple considéré, un enregistrement de champ atomique n'est nécessaire qu'une seule fois (lorsque la propriété est accédée pour la première fois), cependant, déclarer le champ comme volatile obligera le compilateur à effectuer sa lecture et son écriture atomiques qui peut ne pas être optimal en termes de performances.

Par conséquent, une autre approche pour corriger cet avertissement de l'analyseur pour éviter une surcharge supplémentaire de déclaration d'un champ volatile consiste à utiliser le type Lazy <T> pour sauvegarder le champ m_RemovePacketau lieu d'un double contrôle de verrouillage. Dans ce cas, le corps getter peut être remplacé par la méthode initializer, qui sera transmise au constructeur de l'instance Lazy <T> :

private Lazy<Packet> m_RemovePacket = new Lazy<Packet>(() =>
  {
    Packet instance = new RemoveItem(this);
    instance.SetStatic();
    return instance;
  }, LazyThreadSafetyMode.ExecutionAndPublication);

....
public Packet RemovePacket
{
  get
  {
    return m_RemovePacket.Value;
  }
}

La méthode d'initialisation sera appelée une fois, lorsque le type Lazy est accédé pour la première fois, et la sécurité du streaming en cas d'accès à la propriété à partir de plusieurs threads en même temps sera garantie par le type Lazy <T> (le mode de sécurité des threads est contrôlé par le deuxième paramètre du constructeur Lazy ).

Avertissement PVS-Studio: V3131 La compatibilité de l'expression «ciblé» avec le type «IAxe» est vérifiée, mais elle est convertie en type «Article». HarvestTarget.cs 61

protected override void OnTarget( Mobile from, object targeted )
{
  ....
  else if ( m_System is Lumberjacking &&
            targeted is IAxe && m_Tool is BaseAxe )
  {
    IAxe obj = (IAxe)targeted;
    Item item = (Item)targeted;
    ....
  }
  ....
}

La variable ciblée a été vérifiée pour l'appartenance au type IAxe , mais personne n'a vérifié l' appartenance à Item , comme le rapporte l'analyseur.

PVS-Studio Warning: V3070 La variable non initialisée 'Zero' est utilisée lors de l'initialisation de la variable 'm_LastMobile'. Serial.cs 29
public struct Serial : IComparable, IComparable<Serial>
{
  private int m_Serial;

  private static Serial m_LastMobile = Zero;                // <=
  private static Serial m_LastItem = 0x40000000;

  public static Serial LastMobile { .... }
  public static Serial LastItem { .... }

  public static readonly Serial MinusOne = new Serial( -1 );
  public static readonly Serial Zero = new Serial( 0 );     // <=
  ....
  private Serial( int serial )
  {
    m_Serial = serial;
  }
  ....
}

En tant que tel, il n'y a pas d'erreur, cependant, écrire ce n'est pas très bon. En raison de l'affectation de m_LastMobile à la valeur d'un zéro non initialisé , une structure avec le constructeur par défaut Serial () sera créée , ce qui conduira à l'initialisation de m_Serial = 0 . Et cela, à son tour, équivaut à appeler un nouveau Serial (0) . En fait, les développeurs ont de la chance que le numéro de série soit égal à 0 , s'il devait y avoir une autre valeur, cela entraînerait une erreur.

Avertissement PVS-Studio: V3063 Une partie de l'expression conditionnelle est toujours vraie si elle est évaluée: m_Serial <= 0x7FFFFFFF. Serial.cs 83

public bool IsItem
{
  get
  {
    return ( m_Serial >= 0x40000000 && m_Serial <= 0x7FFFFFFF );
  }
}

0x7FFFFFFF est la valeur maximale possible que Int32 peut contenir . Par conséquent, quelle que soit la valeur de la variable m_Serial , elle sera en tout cas inférieure ou égale à 0x7FFFFFFF .

PVS-Studio Warning: V3004 L'instruction 'then' est équivalente à l'instruction 'else'. Serialization.cs 1571

public override void WriteDeltaTime( DateTime value )
{
  ....
  try 
  { 
    d = new TimeSpan( ticks-now ); 
  }
  catch 
  {
    if( ticks < now ) 
      d = TimeSpan.MaxValue; 
    else 
      d = TimeSpan.MaxValue;
  }
  ....
}

L'analyseur met en garde contre un morceau de code suspect dans lequel les vraies et les fausses branches de l' instruction if coïncident complètement. Peut-être qu'une des branches devrait avoir TimeSpan.MinValue. Le même code a été trouvé à plusieurs endroits:

V3004 L'instruction 'then' est équivalente à l'instruction 'else'. Item.cs 2103

public virtual void Serialize( GenericWriter writer )
{
  ....
  
  if( ticks < now ) 
    d = TimeSpan.MaxValue; 
  else 
    d = TimeSpan.MaxValue;
  
  ....
}

V3004 L'instruction 'then' est équivalente à l'instruction 'else'. Serialization.cs 383

public override void WriteDeltaTime( DateTime value )
{
  ....
  
  if( ticks < now ) 
    d = TimeSpan.MaxValue; 
  else 
    d = TimeSpan.MaxValue;
  
  ....
}

J'ai utilisé "This" pour une raison, il est très probable que, entre autres choses, il ne puisse pas se passer de copier-coller, ces fragments de code semblent très douloureux.

Avertissement PVS-Studio: V3051 Une fonte de type excessive. L'objet est déjà du type 'Item'. Mobile.cs 11237

public Item Talisman
{
  get
  {
    return FindItemOnLayer( Layer.Talisman ) as Item;
  }
}
public Item FindItemOnLayer( Layer layer )
{
  ....
}

Cet avertissement de l'analyseur peut être obtenu en cas d'utilisation excessive de l'opérateur as . Il n'y a aucune erreur dans cette section du code, mais cela n'a également aucun sens de convertir l'objet en son propre type. Avertissement

PVS-Studio: V3148 La conversion de la valeur 'null' potentielle de 'toSet' en un type de valeur peut conduire à NullReferenceException. Properties.cs 502

public static string ConstructFromString( .... )
{
  object toSet;
  bool isSerial = IsSerial( type );

  if ( isSerial ) // mutate into int32
    type = m_NumericTypes[4];

  ....
  else if ( value == null )
  {
    toSet = null;
  }
  ....

  if ( isSerial ) // mutate back
    toSet = (Serial)((Int32)toSet);

  constructed = toSet;
  return null;
}

Dans cette section de code, nous nous intéressons au cas où la valeur de la variable est nulle . Ensuite, la variable toSet est également nulle . De plus, si la variable isSerial == true , toSet est converti en Int32 , ce qui conduira à NRE .

Vous pouvez corriger ce code en ajoutant, par exemple, 0 par défaut:

toSet = (Serial)((Int32)(toSet ?? 0));

PVS-Studio Warning: V3031 Un contrôle excessif peut être simplifié. Le '||' L'opérateur est entouré des expressions opposées 'pack == null' et 'pack! = null'. BODBuyGump.cs 64
public override void OnResponse(Server.Network.NetState sender, RelayInfo info)
{
  ....
  if ( (pack == null) ||
       ((pack != null) &&
        (!pack.CheckHold(
                m_From,
                item,
                true,
                true,
                0,
                item.PileWeight + item.TotalWeight)) ) )
  {
    pv.SayTo(m_From, 503204);
    m_From.SendGump(new BOBGump(m_From, m_Book, m_Page, null));
  }
  ....
}

Ce code peut être simplifié, comme le rapporte l'analyseur:

if ((pack == null) || ((pack != null) && (!pack.CheckHold(....))))

À gauche et à droite de l'opérateur '||' il y a des expressions opposées dans le sens. Ici, le check pack! = Null est redondant, car avant cela, le pack de conditions opposées == null est vérifié , et ces expressions sont séparées par l'opérateur '||'. Cette ligne peut être raccourcie comme suit:

if (pack == null || !pack.CheckHold(....))

Avertissement PVS-Studio: V3080 Déréférence nulle possible. Pensez à inspecter le «gagnant». CTF.cs 1302

private void Finish_Callback()
{
  ....
  CTFTeamInfo winner = ( teams.Count > 0 ? teams[0] : null );

  .... 

  m_Context.Finish( m_Context.Participants[winner.TeamID] as Participant );
}

Lets dire teams.Count est 0 . Alors gagnant = null. Et plus loin dans le code, la propriété winner.TeamID est accessible sans vérifier la valeur null , ce qui entraînera l'accès par une référence null.

PVS-Studio Warning: V3041 L'expression a été implicitement convertie du type 'int' en type 'double'. Pensez à utiliser un transtypage de type explicite pour éviter la perte d'une partie fractionnaire. Un exemple: double A = (double) (X) / Y;. StormsEye.cs 87

public static void Gain( Mobile from, Skill skill ) 
{
  ....
  if ( from.Player && 
     ( skills.Total / skills.Cap ) >= Utility.RandomDouble())
  ....
}

Ce fragment de code contient l'opération de division des variables skills.Total et skills.Cap , qui sont de type int , et le résultat est ensuite implicitement converti en type double , comme le rapporte l'analyseur.

Avertissement PVS-Studio: V3085 Le nom du champ 'typeofObject' dans un type imbriqué est ambigu. Le type externe contient un champ statique avec un nom identique. PropsGump.cs 744

private static Type typeofObject = typeof( object );
....
private class GroupComparer : IComparer
{
  ....
  private static Type typeofObject = typeof( Object );
  ....
}

La variable typeofObject a été créée dans cette section de code dans une classe imbriquée . Son problème est que dans la classe externe, il existe une variable du même nom et des erreurs peuvent survenir à cause de cela. Il vaut mieux ne pas le permettre afin de réduire la probabilité de telles erreurs dues à l'inattention.

Avertissement PVS-Studio: les accesseurs de propriété V3140 utilisent différents champs de support. WallBanner.cs 77

private bool m_IsRewardItem;

[CommandProperty( AccessLevel.GameMaster )]
public bool IsRewardItem
{
  get{ return m_IsRewardItem; }
  set{ m_IsRewardItem = value; InvalidateProperties(); }
}

private bool m_East;

[CommandProperty( AccessLevel.GameMaster )]
public bool East
{
  get{ return m_East; }
  set{ m_IsRewardItem = value; InvalidateProperties(); }
}

Et ici, il y a une erreur qui est apparue en raison du copier-coller. La méthode d'accès définie de la propriété East consistait à attribuer une valeur à m_East , et non à m_IsRewardItem . Avertissements

PVS-Studio:

V3012 L'opérateur '?:', Quelle que soit son expression conditionnelle, renvoie toujours une seule et même valeur: 0xe7f. TreasureChestLevel2.cs 52

V3012 L'opérateur '?:', Quelle que soit son expression conditionnelle, renvoie toujours une seule et même valeur: 0xe77. TreasureChestLevel2.cs 57

private void SetChestAppearance()
{
  bool UseFirstItemId = Utility.RandomBool();

  switch( Utility.RandomList( 0, 1, 2, 3, 4, 5, 6, 7 ) )
  {
    ....
    case 6:// Keg
      this.ItemID = ( UseFirstItemId ? 0xe7f : 0xe7f );
      this.GumpID = 0x3e;
      break;

    case 7:// Barrel
      this.ItemID = ( UseFirstItemId ? 0xe77 : 0xe77 );
      this.GumpID = 0x3e;
      break;
  }
}

Une sorte d'illusion de choix :) Quelle que soit la valeur de UseFirstItemId , this.ItemID sera soit 0xe7f dans le premier cas, soit 0xe77 dans le second.

Avertissement PVS-Studio: V3066 Ordre incorrect possible des arguments passés à la méthode 'OnSwing': 'défenseur' et 'attaquant'. BaseWeapon.cs 1188

public virtual int AbsorbDamageAOS( Mobile attacker,
                                    Mobile defender,
                                    int damage )
{
  ....
  if ( weapon != null )
  {
    defender.FixedParticles(0x3779,
                            1,
                            15,
                            0x158B,
                            0x0,
                            0x3,
                            EffectLayer.Waist);
    weapon.OnSwing( defender, attacker );
  }
  ....
}

public virtual TimeSpan OnSwing( Mobile attacker, Mobile defender )
{
  return OnSwing( attacker, defender, 1.0 );
}

Il a semblé suspect à l'analyseur que la méthode OnSwing () ait reçu des arguments dans l'ordre inverse. Cela est peut-être dû à une erreur.

PVS-Studio Warning: V3092 Les intersections de plage sont possibles dans les expressions conditionnelles. Exemple: if (A> 0 && A <5) {...} else if (A> 3 && A <9) {...}. HouseFoundation.cs 1883

public static bool IsFixture( int itemID )
{
  ....
  else if( itemID >= 0x319C && itemID < 0x31B0 ) 
    return true;
  // ML doors
  else if( itemID == 0x2D46 ||
           itemID == 0x2D48 ||
           itemID == 0x2FE2 ||
           itemID == 0x2FE4 )
    return true;
  else if( itemID >= 0x2D63 && itemID < 0x2D70 )
    return true;
  else if( itemID >= 0x319C && itemID < 0x31AF ) 
    return true;
  ....
}

Les plages testées dans des conditions se croisent. Cela semblait suspect à l'analyseur. Même si ce morceau de code fonctionne correctement, il vaut toujours la peine de le réparer. Imaginez une situation où nous devions réécrire le corps du dernier si tel est le cas, si la condition est vraie, la méthode retourne false . Si itemID est égal à, disons, 0x319C , la méthode retournera toujours true . Cela entraînera à son tour une perte de temps pour rechercher les erreurs.

Conclusion


RunUO a été créé il y a longtemps, beaucoup de travail a été fait, cependant, en utilisant l'exemple de ce projet, vous pouvez voir les avantages d'utiliser l'analyse statique sur des projets avec un historique. L'analyseur de 543 000 lignes de code de projet a émis environ 500 avertissements (sans compter le niveau bas), dont la plupart n'apparaissaient pas dans l'article en raison de leur uniformité. Pour vous familiariser avec le résultat de l'analyse plus en détail, je vous suggère d'utiliser une licence gratuite pour les projets open source .



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Ekaterina Nikiforova. RunUO Check par l'analyseur PVS-Studio .

Source: https://habr.com/ru/post/undefined/


All Articles