Penganalisa PVS-Studio RunUO memeriksa

Gambar 1

Artikel ini dikhususkan untuk memeriksa proyek RunUO menggunakan analisa statis PVS-Studio. RunUO adalah emulator perangkat lunak server untuk Ultima Online, sebuah game yang pernah memenangkan hati banyak penggemar MMORPG.

pengantar


RunUO adalah emulator perangkat lunak server untuk MMORPG Ultima Online. Tujuan dari proyek ini adalah untuk menciptakan perangkat lunak yang stabil yang akan dapat bersaing dengan server resmi EA Games. RunUO dibuat kembali pada tahun 2002, tetapi tidak kehilangan relevansi dan aktif digunakan sampai hari ini.

Tujuan dari verifikasi proyek adalah untuk mempopulerkan topik analisis statis. Kami memeriksa berbagai proyek - permainan ( contoh ), perpustakaan ( contoh ), messenger ( contoh ), browser ( contoh ) dan banyak lagi ( contoh , contoh , contoh) untuk menarik perhatian audiens yang beragam. Dengan artikel-artikel ini, kami mencoba menarik perhatian pada pentingnya menggunakan analisis statis dalam pengembangan. Analisis statis membuat kode lebih dapat diandalkan dan lebih aman. Juga, dengan penggunaan regulernya, Anda dapat menemukan dan menghilangkan kesalahan pada tahap paling awal. Ini menghemat waktu dan usaha pengembang, karena tidak ada yang mau menghabiskan 50 jam mencari kesalahan yang dapat ditemukan oleh penganalisa.

Kami juga membantu komunitas open-source. Melalui artikel dengan kesalahan ditemukan, kami berkontribusi pada pengembangan open-source. Namun, dalam artikel kami tidak menganalisis semua peringatan. Beberapa peringatan sepertinya terlalu membosankan bagi kami untuk masuk ke artikel, beberapa ternyata positif palsu, dll. Karenanya, kami siap memberikan lisensi gratis untuk proyek sumber terbuka. Terlebih lagi, apa yang menurut kami membosankan untuk artikel tersebut mungkin terlihat cukup menarik bagi pengembang proyek yang sedang diuji, karena pengembang proyek masih lebih menyadari masalah mana yang paling kritis.

Potongan kode yang menarik perhatian saat memeriksa laporan penganalisa


PVS-Studio Warning: V3010 Nilai balik fungsi 'Intern' harus digunakan. 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 );
}

Nilai pengembalian metode Intern () tidak diperhitungkan di mana pun, seperti yang ditunjukkan oleh penganalisa. Mungkin ini adalah bug atau kode redundan.

Peringatan PVS-Studio: V3017 Pola terdeteksi: (item adalah BasePotion) || ((item adalah BasePotion) && ...). Ekspresi berlebihan atau mengandung kesalahan logis. 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 )
  {
    ....
  }
}

Ada subekspresi yang dapat disederhanakan. Saya akan menuliskannya agar lebih jelas:
if (item is BasePotion || ( item is BasePotion && Core.ML ))

Misalkan, item BasePotion = true , maka kondisi benar, meskipun Core.ML . Jika item BasePotion = false , maka kondisi salah, sekali lagi terlepas dari nilai Core.ML . Kode seperti itu paling sering hanya mubazir, namun, ada situasi yang lebih buruk ketika programmer membuat kesalahan dan menulis variabel yang salah dalam sub-ekspresi kedua.

Peringatan PVS-Studio: V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' Operator dikelilingi oleh ekspresi berlawanan 'bPlayerOnly' dan '! bPlayerOnly'. BaseCreature.cs 3005

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

Kode ini bisa jadi berlebihan atau salah. Masalahnya adalah bahwa di kedua sisi '||' berbeda dalam arti subekspresi. Jika kita memotongnya seperti ini:

if ( m.Player || !bPlayerOnly )

maka tidak ada yang akan berubah.

PVS-Studio Warning: V3001 Ada sub-ekspresi yang identik 'akta adalah SmallBrickHouseDeed' di kiri dan di kanan '||' operator. 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 )
      ....
}

Saya pikir tidak ada gunanya menjelaskan apa pun di sini, kode berikutnya atau yang salah, atau berlebihan.

PVS-Studio Warning: V3067 Ada kemungkinan bahwa blok 'lain' dilupakan atau dikomentari, sehingga mengubah logika operasi program. 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;

  ....
}

Peringatan yang cukup langka. Penganalisis menemukan bahwa pemformatan kode mencurigakan setelah arahan #endregion menjadi mencurigakan. Jika Anda tidak membaca kode, itu terlihat seperti garis

i.Movable = !locked;

akan dieksekusi dalam hal apa pun, terlepas dari variabel i . Mungkin kurung kurawal dilupakan di sini ... Secara umum, pengembang harus memeriksa kode ini.

Peringatan PVS-Studio: V3043 Logika operasional kode tidak sesuai dengan pemformatannya. Pernyataan itu menjorok ke kanan, tetapi selalu dieksekusi. Ada kemungkinan kurung keriting hilang. Gempa bumi.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
  {
    ....
  }
}

Kurung kurawal mungkin tidak ada dalam kode ini. Kesimpulan ini dapat dibuat karena format kode yang aneh di tubuh if (! M.Player) .

Peringatan PVS-Studio: V3083 Doa yang tidak aman dari acara 'ServerStarted', NullReferenceException dimungkinkan. Pertimbangkan menugaskan acara ke variabel lokal sebelum menjalankannya. EventSink.cs 921

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

Metode ini menggunakan panggilan yang berpotensi tidak aman ke event handler RefreshStarted , seperti yang ditunjukkan oleh penganalisa.

Kenapa itu berbahaya? Bayangkan situasi ini. Acara ServerStarted hanya memiliki satu pelanggan. Dan pada saat antara memeriksa null dan langsung memanggil event handler ServerStarted () di utas lain, berhenti berlangganan dibuat dari acara ini. Ini akan melempar NullReferenceException .

Cara termudah untuk mencegah situasi ini adalah menyediakan panggilan acara aman menggunakan Operator '?':

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

Peringatan PVS-Studio: V3054 Penguncian ganda yang berpotensi tidak aman. Gunakan variabel yang mudah menguap atau sinkronisasi primitif untuk menghindari hal ini. 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;
  }
}

Peringatan penganalisis dikaitkan dengan penggunaan pola penguncian ganda yang tidak aman. Seperti yang dapat Anda lihat dari kode di atas, kunci periksa ganda diterapkan untuk menerapkan pola generatif - Loners. Ketika kami mencoba untuk mendapatkan turunan dari kelas Paket dengan mengakses properti RemovePacket , pengambil rajin memeriksa bidang m_RemovePacket untuk persamaan ke nol. Jika centang lolos, maka kita masuk ke tubuh pernyataan kunci , di mana bidang m_RemovePacket diinisialisasi . Situasi menarik muncul pada saat ketika utas utama telah menginisialisasi variabel m_RemovePacket melalui konstruktor, tetapi belum disebut metodeSetStatic (). Secara teoritis, utas lain dapat mengakses properti RemovePacket pada saat yang sangat tidak nyaman. Memeriksa m_RemovePacket untuk kesetaraan ke nol tidak akan lagi gagal, dan utas panggilan akan menerima tautan ke objek yang tidak siap digunakan sepenuhnya. Untuk mengatasi masalah ini, Anda bisa membuat variabel antara kelas Paket di badan pernyataan kunci , menginisialisasi melalui panggilan konstruktor dan metode SetStatic (), dan kemudian menetapkannya ke variabel m_RemovePacket . Dalam hal ini, isi pernyataan kunci mungkin terlihat seperti ini:

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

Masalahnya tampaknya sudah diperbaiki dan kode akan berfungsi seperti yang diharapkan. Tapi ini tidak benar.

Ada satu hal lagi: penganalisis tidak hanya menyarankan menggunakan kata kunci yang mudah menguap . Dalam versi rilis program, kompiler dapat melakukan optimasi dan menyusun ulang baris panggilan ke metode SetStatic () dan menetapkan variabel instan ke bidang m_RemovePacket (dari sudut pandang kompiler, semantik program tidak akan dilanggar). Dan kita kembali lagi ke titik yang sama di mana kita mulai - kemungkinan mendapatkan variabel m_RemovePacket yang tidak diinisialisasi. Tidak mungkin untuk mengatakan dengan tepat kapan penyusunan ulang ini dapat terjadi, dan apakah itu akan terjadi sama sekali: dapat dipengaruhi oleh versi CLR, arsitektur prosesor yang digunakan, dll. Masih layak dilindungi dari skenario seperti itu, dan salah satu solusi (namun, bukan yang paling produktif) adalah dengan menggunakan kata kunci yang mudah menguap . Variabel yang akan dideklarasikan dengan pengubah volatil tidak akan dikenakan permutasi selama optimisasi oleh kompiler. Kode yang akhirnya diperbaiki mungkin terlihat seperti ini:

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;
  }
}

Kadang-kadang menggunakan bidang yang mudah menguap mungkin tidak diinginkan karena overhead mengakses bidang tersebut. Kami tidak akan membahas topik ini secara terperinci, hanya mencatat bahwa dalam contoh yang sedang dipertimbangkan, catatan bidang atom hanya diperlukan sekali (ketika properti pertama kali diakses), namun, menyatakan bidang sebagai volatil akan menyebabkan kompiler melakukan pembacaan dan penulisan atomnya yang mungkin tidak optimal dalam hal kinerja.

Oleh karena itu, pendekatan lain untuk memperbaiki peringatan penganalisis ini untuk menghindari overhead tambahan dari mendeklarasikan bidang volatil adalah dengan menggunakan tipe <T> Malas untuk mendukung bidang m_RemovePacketalih-alih penguncian periksa ganda. Dalam hal ini, badan pengambil dapat diganti dengan metode initializer, yang akan diteruskan ke konstruktor dari instance <T> Malas :

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;
  }
}

Metode initializer akan dipanggil sekali, ketika jenis Malas diakses untuk pertama kalinya, dan keamanan streaming jika mengakses properti dari beberapa utas pada saat yang sama akan dijamin oleh tipe Malas <T> (mode keselamatan ulir dikendalikan oleh parameter kedua konstruktor Malas ).

Peringatan PVS-Studio: V3131 Ekspresi 'bertarget' diperiksa untuk kompatibilitas dengan tipe 'IAxe', tetapi dilemparkan ke tipe 'Item'. 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;
    ....
  }
  ....
}

Variabel yang ditargetkan diperiksa untuk milik tipe IAxe , tetapi tidak ada yang memeriksa milik Item , seperti yang dilaporkan analis.

Peringatan PVS-Studio: V3070 Variabel tidak diinisialisasi 'Nol' digunakan saat menginisialisasi variabel '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;
  }
  ....
}

Karena itu, tidak ada kesalahan, bagaimanapun, menulis ini tidak terlalu baik. Karena penugasan m_LastMobile dengan nilai Zero yang tidak diinisialisasi , struktur akan dibuat dengan Serial konstruktor default () , yang akan mengarah ke inisialisasi m_Serial = 0 . Dan ini, pada gilirannya, sama dengan memanggil Serial baru (0) . Bahkan, para pengembang beruntung bahwa serial harus sama dengan 0 , jika seharusnya ada nilai lain, ini akan menyebabkan kesalahan.

Peringatan PVS-Studio: V3063 Bagian dari ekspresi kondisional selalu benar jika dievaluasi: m_Serial <= 0x7FFFFFFF. Serial.cs 83

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

0x7FFFFFFF adalah nilai maksimum yang mungkin dimiliki Int32 . Oleh karena itu, berapapun nilai variabel m_Serial miliki , dalam hal apa pun akan kurang dari atau sama dengan 0x7FFFFFFF .

PVS-Studio Warning: V3004 Pernyataan 'then' setara dengan pernyataan '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;
  }
  ....
}

Penganalisa memperingatkan tentang sepotong kode yang mencurigakan di mana cabang benar dan salah dari pernyataan if sepenuhnya bertepatan. Mungkin salah satu cabang harus memiliki TimeSpan.MinValue. Kode yang sama ditemukan di beberapa tempat:

V3004 Pernyataan 'lalu' setara dengan pernyataan 'lain'. Item.cs 2103

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

V3004 Pernyataan 'lalu' setara dengan pernyataan 'lain'. Serialization.cs 383

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

Saya menggunakan "Ini" karena suatu alasan, sangat mungkin bahwa, di antara hal-hal lain, itu tidak dapat dilakukan tanpa copy-paste, fragmen kode ini terlihat sangat menyakitkan.

Peringatan PVS-Studio: V3051 Tipe cetakan yang berlebihan. Objek sudah dari jenis 'Item'. Mobile.cs 11237

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

Peringatan analisa ini dapat diperoleh jika ada penggunaan berlebihan sebagai operator . Tidak ada kesalahan dalam bagian kode ini, tetapi juga tidak masuk akal untuk melemparkan objek ke tipenya sendiri. Peringatan

PVS-Studio: V3148 Memberikan nilai 'null' potensial dari 'toSet' ke tipe nilai dapat menyebabkan 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;
}

Pada bagian ini kode, kita tertarik dalam kasus ketika variabel nilai adalah nol . Kemudian variabel toSet juga nol . Selanjutnya, jika variabel isSerial == true , maka toSet dilemparkan ke Int32 , yang akan mengarah ke NRE .

Anda dapat memperbaiki kode ini dengan menambahkan, misalnya, 0 secara default:

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

Peringatan PVS-Studio: V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' operator dikelilingi oleh ekspresi berlawanan 'paket == null' dan 'paket! = 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));
  }
  ....
}

Kode ini dapat disederhanakan, seperti yang dilaporkan oleh analisa:

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

Di sebelah kiri dan kanan operator '||' ada ungkapan yang berseberangan dalam makna. Di sini paket cek ! = Null adalah redundan, karena sebelum itu kondisi yang berlawanan paket == null diperiksa , dan ekspresi ini dipisahkan oleh operator '||'. Baris ini dapat disingkat sebagai berikut:

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

Peringatan PVS-Studio: V3080 Kemungkinan null dereference. Pertimbangkan untuk memeriksa 'pemenang'. 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 );
}

Mari mengatakan teams.Count adalah 0 . Kemudian pemenang = nol. Dan selanjutnya dalam kode, properti winner.TeamID diakses tanpa memeriksa nol , yang akan menghasilkan akses dengan referensi nol.

Peringatan PVS-Studio: V3041 Ungkapan itu secara implisit dilemparkan dari tipe 'int' ke tipe 'double'. Pertimbangkan untuk menggunakan pemeran tipe eksplisit untuk menghindari hilangnya bagian fraksional. Contoh: 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())
  ....
}

Fragmen kode ini berisi operasi membagi variabel skills.Total dan skills.Cap , yang bertipe int , dan hasilnya kemudian secara implisit dikonversi menjadi tipe ganda , seperti yang dilaporkan oleh analisa.

Peringatan PVS-Studio: V3085 Nama bidang 'typeofObject' dalam tipe bersarang bersifat mendua. Tipe luar berisi bidang statis dengan nama yang identik. PropsGump.cs 744

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

TypeofObject variabel dibuat di bagian kode ini di kelas bersarang . Masalahnya adalah bahwa di kelas luar ada variabel dengan nama yang sama, dan kesalahan dapat terjadi karena ini. Lebih baik tidak membiarkan ini untuk mengurangi kemungkinan kesalahan seperti itu karena kurangnya perhatian.

Peringatan PVS-Studio: V3140 Accessor properti menggunakan bidang dukungan yang berbeda. 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(); }
}

Dan di sini ada kesalahan yang muncul akibat copy-paste. Metode akses set properti East adalah untuk menetapkan nilai untuk m_East , bukan m_IsRewardItem . Peringatan

PVS-Studio:

V3012 Operator '?:', Terlepas dari ekspresi kondisionalnya, selalu mengembalikan satu dan nilai yang sama: 0xe7f. TreasureChestLevel2.cs 52

V3012 Operator '?:', Terlepas dari ekspresi kondisionalnya, selalu mengembalikan satu dan nilai yang sama: 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;
  }
}

Beberapa jenis ilusi pilihan :) Terlepas dari nilai UseFirstItemId , this.ItemID akan baik 0xe7f dalam kasus pertama, atau 0xe77 di kedua.

Peringatan PVS-Studio: V3066 Kemungkinan urutan argumen yang salah diteruskan ke metode 'OnSwing': 'bek' dan 'penyerang'. 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 );
}

Tampaknya mencurigakan bagi penganalisa bahwa metode OnSwing () diberikan argumen dalam urutan terbalik. Mungkin ini karena kesalahan.

Peringatan PVS-Studio: Perempatan V3092 dimungkinkan dalam ekspresi bersyarat. Contoh: if (A> 0 && A <5) {...} lain jika (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;
  ....
}

Rentang diuji dalam kondisi berpotongan. Ini tampak mencurigakan bagi penganalisa. Meskipun kode ini berfungsi dengan benar, masih ada baiknya Anda memperbaikinya. Bayangkan sebuah situasi di mana kita perlu menulis ulang tubuh yang terakhir jika demikian sehingga, jika kondisinya benar, metode mengembalikan false . Jika itemID sama dengan, katakanlah, 0x319C , metode masih akan mengembalikan true . Ini, pada gilirannya, akan menyebabkan hilangnya waktu untuk mencari kesalahan.

Kesimpulan


RunUO telah dibuat sejak lama, banyak pekerjaan telah dilakukan, namun, dengan menggunakan contoh proyek ini Anda dapat melihat manfaat dari menggunakan analisis statis pada proyek-proyek dengan sejarah. Penganalisa untuk 543 ribu baris kode proyek mengeluarkan sekitar 500 peringatan (tidak termasuk tingkat Rendah), yang sebagian besar tidak muncul dalam artikel karena keseragamannya. Untuk berkenalan dengan hasil analisis secara lebih rinci, saya sarankan menggunakan lisensi gratis untuk proyek sumber terbuka .



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Ekaterina Nikiforova. RunUO Check oleh PVS-Studio Analyzer .

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


All Articles