التحقق من كود XMage ولماذا لا تتوفر بطاقات نادرة خاصة لمجموعة Dragon's Maze

image1.png


XMage هو تطبيق عميل / خادم للعب Magic: The Gathering (MTG). بدأ XMage في التطور مرة أخرى في أوائل عام 2010. خلال هذا الوقت ، تم إصدار 182 إصدارًا ، وتجمع جيش كامل من المساهمين ، ولا يزال المشروع يتطور بنشاط. فرصة ممتازة للمشاركة في تطويره أيضًا! لذلك ، سيتحقق وحيد القرن من PVS-Studio اليوم من قاعدة بيانات XMage ، ومن يدري ، قد يتعارض مع شخص ما في المعركة.



باختصار عن المشروع



تم تطوير XMage بنشاط لمدة 10 سنوات حتى الآن. هدفها هو إنشاء نسخة مجانية ومفتوحة المصدر عبر الإنترنت من لعبة Magic: the Gathering card الأصلية .



مميزات التطبيق:



  • الوصول إلى 19000 بطاقة فريدة تم إصدارها على مدار 20 عامًا من تاريخ MTG ؛
  • التحكم الآلي وتطبيق جميع قواعد اللعبة الحالية ؛
  • ;
  • (AI);
  • (Standard, Modern, Vintage, Commander );
  • , .




عثرت على عمل طلاب من جامعة دلفت للتكنولوجيا 2018 (دورة ماجستير في هندسة البرمجيات ). كان يتألف من حقيقة أن الرجال قاموا بدور نشط في مشاريع مفتوحة المصدر ، والتي كان يجب أن تكون معقدة للغاية وتتطور بنشاط. على مدار ثمانية أسابيع ، درس الطلاب الدورة التدريبية ومشاريع مفتوحة المصدر لفهم ووصف بنية البرنامج المحدد.



هذا كل شيء. في هذا العمل ، قام الرجال بتحليل مشروع XMage ، وكان أحد جوانب عملهم هو الحصول على مقاييس مختلفة باستخدام SonarQube (عدد سطور الكود ، التعقيد الدوري ، تكرار الكود ، روائح الكود ، الأخطاء ، نقاط الضعف ، إلخ).



جذبت انتباهي حقيقة أنه في وقت 2018 ، أظهر مسح SonarQube 700 عيب (أخطاء ، نقاط ضعف) لكل 1،000،000 سطر من التعليمات البرمجية.



بالبحث في تاريخ الأشخاص المساهمين ، اكتشفت أنه من التقرير الذي تم استلامه مع التحذيرات ، قاموا بتقديم طلب سحب لإصلاح حوالي 30 عيبًا من فئة "Blocker" أو "Critical". وماذا عن بقية التحذيرات غير معروف ولكن أتمنى ألا يتم التغاضي عنها.



لقد مرت سنتان منذ ذلك الحين ونمت قاعدة الشفرة بحوالي 250000 سطر من التعليمات البرمجية - وهذا سبب وجيه لمعرفة كيف تسير الأمور.



حول التحليل



للتحليل ، أخذت إصدار XMage - 1.4.44V0 .



كنت محظوظًا جدًا بالمشروع. تبين أن بناء XMage باستخدام Maven بسيط للغاية (كما هو مكتوب في الوثائق):



mvn clean install -DskipTests


لا شيء مطلوب مني أكثر. رائع؟



لم تكن هناك مشاكل في دمج المكون الإضافي PVS-Studio في Maven أيضًا: كل شيء كما هو الحال في الوثائق .



بعد التحليل ، تم تلقي 911 تحذيرًا ، 674 منها كانت لتحذير من مستويات الثقة 1 و 2. لأغراض هذه المقالة ، لم أفكر في تحذيرات المستوى 3 ، نظرًا لوجود نسبة عالية من الإيجابيات الكاذبة. أود أن ألفت انتباهك إلى حقيقة أنه عند استخدام محلل ثابت في معركة حقيقية ، لا يمكنك تجاهل مثل هذه التحذيرات ، لأنها يمكن أن تشير أيضًا إلى عيوب كبيرة في الكود.



بالإضافة إلى ذلك ، لم أضع في الاعتبار التحذيرات الخاصة ببعض القواعد لأن من هم على دراية بالمشروع أفضل مني:



  • V6022, /. 336 .
  • V6014, , . 73 .
  • V6021, , . 36 .
  • V6048, , . 17 .


بالإضافة إلى ذلك ، أنتجت العديد من قواعد التشخيص حوالي 20 نتيجة إيجابية زائفة واضحة من نفس النوع. مسجل في المهام!



نتيجة لذلك ، إذا طرحنا كل شيء ، فحينئذٍ حصلنا على حوالي 190 من الإيجابيات للنظر فيها.



أثناء مراجعة المشغلات ، تم تحديد العديد من العيوب البسيطة من نفس النوع ، والتي كانت إما مرتبطة بالتصحيح أو الفحص أو التشغيل بلا معنى. أيضًا ، تم ربط الكثير من الإيجابيات بقطعة غريبة جدًا من التعليمات البرمجية التي توسلت للتو لإعادة البناء.



نتيجة لذلك ، في هذه المقالة ، حددت 11 قاعدة تشخيصية وقمت بتحليل أحد أكثر المشغلات إثارة للاهتمام.



دعونا نلقي نظرة على ما حدث.



تحذير N1



V6003 تم اكتشاف استخدام "if (card! = Null) {...} else if (card! = Null) {...}". هناك احتمال وجود خطأ منطقي. TorrentialGearhulk.java (90) ، TorrentialGearhulk.java (102)



@Override
public boolean apply(Game game, Ability source) {
  ....
  Card card = game.getCard(....);
  if (card != null) {
      ....
  } else if (card != null) {
      ....
  }
  ....
}


كل شيء بسيط هنا: جسم العبارة الشرطية الثانية if (card! = Null) في if-else-if لن يتم تنفيذ البناء أبدًا ، لأن البرنامج إما لن يصل إلى هذه النقطة ، أو البطاقة! = Null ستكون دائمًا خاطئة .



تحذير N2



V6004 عبارة "then" تعادل عبارة "else". AsThoughEffectImpl.java (35) ، AsThoughEffectImpl.java (37)



@Override
public boolean applies(....) {
  // affectedControllerId = player to check
  if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
    return applies(objectId, source, playerId, game);
  } else {
    return applies(objectId, source, playerId, game);
  }
}


خطأ شائع غالبًا ما يحدث في ممارستي للتحقق من المشاريع مفتوحة المصدر. نسخ ولصق؟ أم هل فاتني شيء؟ سأفترض أنك لا تزال بحاجة إلى إعادة القيمة false في فرع else . ملاحظة: إذا كان هناك أي شيء ، فلا يوجد استدعاء متكرر ينطبق (....) ، لأن هذه طرق مختلفة. إثارة مماثلة:











  • V6004 عبارة "then" تعادل جملة "else". GuiDisplayUtil.java (194) ، GuiDisplayUtil.java (198)


تحذير N3



تعبير V6007 'filter.getMessage (). ToLowerCase (Locale.ENGLISH) .startsWith ("كل")' خاطئ دائمًا. SetPowerToughnessAllEffect.java (107)



@Override
public String getText(Mode mode) {
  StringBuilder sb = new StringBuilder();
  ....
  if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("Each ")) {
    sb.append(" has base power and toughness ");
  } else {
    sb.append(" have base power and toughness ");
  }
  ....
  return sb.toString();
}


تحظى مشغلات قاعدة التشخيص V6007 بشعبية كبيرة لكل مشروع يتم فحصه. XMage ليست استثناء (79 قطعة). يتم تفعيل القاعدة ، من حيث المبدأ ، كل شيء على الحال ، ولكن العديد من الحالات تقع على التصحيح ، ثم إعادة التأمين ، ثم على شيء آخر. بشكل عام ، هذه الإيجابيات أفضل من مشاهدتها لمؤلف الكود.



هذه العملية ، مع ذلك ، هي بالتأكيد خطأ. اعتمادًا على بداية سطر filter.getMessage () إلى sbتمت إضافة النص "لديه ..." أو "لديك ...". لكن الخطأ هو أن المطورين يتحققون من أن السلسلة تبدأ بحرف كبير ، بعد أن قاموا بتحويل هذه السلسلة إلى أحرف صغيرة قبل ذلك. وجه الفتاة. نتيجة لذلك ، سيكون السطر المضاف دائمًا "...". نتيجة الخلل ليست حرجة ، ولكنها أيضًا غير سارة: سيظهر نص مؤلف بشكل أمي في مكان ما.



المحفزات التي وجدتها الأكثر إثارة للاهتمام:



  • V6007 تعبير 't.startsWith ("-")' خطأ دائمًا. BoostSourceEffect.java (103)
  • تعبير V6007 'setNames.isEmpty ()' خاطئ دائمًا. تنزيل PicturesService.java (300)
  • تعبير V6007 "موجود في القائمة" == فارغ "دائمًا خطأ. S3Uploader.java (23)
  • تعبير V6007 '! LastRule.endsWith (".")' صحيح دائمًا. Effects.java (76)
  • تعبير V6007 'subtypesToIgnore :: contains' خاطئ دائمًا. VerifyCardDataTest.java (893)
  • تعبير V6007 'notStartedTables == 1' خطأ دائمًا. MageServerImpl.java (1330)


تحذير N4



V6008 لا يوجد مرجع لـ "saveSpecialRares". دراغونزماز جافا (230)



public final class DragonsMaze extends ExpansionSet {
  ....
  private List<CardInfo> savedSpecialRares = new ArrayList<>();
  ....
  @Override
  public List<CardInfo> getSpecialRare() {
    if (savedSpecialRares == null) {                    // <=
      CardCriteria criteria = new CardCriteria();
      criteria.setCodes("GTC").name("Breeding Pool");
      savedSpecialRares.addAll(....);                   // <=
      criteria = new CardCriteria();
      criteria.setCodes("GTC").name("Godless Shrine");
      savedSpecialRares.addAll(....);
      ....
    }
    return new ArrayList<>(savedSpecialRares);
  }
}


يشكو المحلل اللغوي من إلغاء الإشارة إلى المرجع الفارغ saveSpecialRares عندما يصل التنفيذ إلى التعبئة الأولى للمجموعة.



أول ما يتبادر إلى الذهن هو ببساطة الخلط بين saveSpecialRares == null مع saveSpecialRares! = Null. ولكن في مثل هذه الحالة ، يمكن أن يحدث NPE في مُنشئ ArrayList عند إرجاع المجموعة من الطريقة ، نظرًا لأن saveSpecialRares == null لا يزال ممكنًا. إن إصلاح الكود باستخدام الحل الأول الذي يتبادر إلى الذهن ليس خيارًا جيدًا. بعد أن فهمت الرمز قليلاً ، اكتشفت أن s avedSpecialRares يتم تحديدها على الفور من خلال مجموعة فارغة عندما يتم الإعلان عنها ولا يتم إعادة تعيينها في أي مكان آخر. هذا يخبرنا بذلكsaveSpecialRares لن تكون فارغة أبدًا ، ولن يحدث إلغاء مرجعية لمرجع فارغ ، والذي يحذر منه المحلل ، لأنه لن يصل إلى المجموعة أبدًا. نتيجة لذلك ، ستعيد الطريقة دائمًا مجموعة فارغة.



ملاحظة لإصلاحها ، تحتاج إلى استبدال saveSpecialRares == null بـ saveSpecialRares.isEmpty () .



PPS ، للأسف ، أثناء لعب XMage ، لن تتمكن من الحصول على بطاقات نادرة خاصة لمجموعة Dragon's Maze .



حالة أخرى لإلغاء مرجعية لاغية:



  • V6008 لا يوجد إشارة إلى "تطابق". TableController.java (973)


تحذير N5



V6012 "؟:" يقوم عامل التشغيل ، بغض النظر عن التعبير الشرطي الخاص به ، بإرجاع قيمة واحدة ونفس القيمة دائمًا "table.getCreateTime ()". TableManager.java (418) ، TableManager.java (418)



private void checkTableHealthState() {
  ....
  logger.debug(.... + formatter.format(table.getStartTime() == null
                                        ? table.getCreateTime()
                                        : table.getCreateTime()) + ....);
  ....
}


هنا العامل الثلاثي ؟: إرجاع نفس القيمة بغض النظر عن شرط table.getStartTime () == null . أعتقد أن إكمال الكود قد لعب مزحة قاسية على المطور. خيار التصحيح:



private void checkTableHealthState() {
  ....
  logger.debug(.... + formatter.format(table.getStartTime() == null
                                        ? table.getCreateTime()
                                        : table.getStartTime()) + ....);
  ....
}


تحذير N6



V6026 تم تخصيص هذه القيمة بالفعل للمتغير "this.loseOther". BecomesCreatureTypeTargetEffect.java (54)



public
BecomesCreatureTypeTargetEffect(final BecomesCreatureTypeTargetEffect effect) {
  super(effect);
  this.subtypes.addAll(effect.subtypes);
  this.loseOther = effect.loseOther;
  this.loseOther = effect.loseOther;
}


سلسلة مهمة مكررة. يبدو أن المطور قد تحرك قليلاً باستخدام مفاتيح الاختصار ولم يلاحظ ذلك. ولكن نظرًا لأن التأثير يحتوي على عدد كبير من الحقول ، فإن الجزء يستحق التركيز عليه.



تحذير N7



V6036 يتم استخدام القيمة من خيار "selectUser" غير المهيأ. Session.java (227)



public String connectUserHandling(String userName, String password)
{
  ....
  if (!selectUser.isPresent()) {  // user already exists
      selectUser = UserManager.instance.getUserByName(userName);
      if (selectUser.isPresent()) {
          User user = selectUser.get();
            ....
      }
  }
  User user = selectUser.get(); // <=
  ....
}


من تحذير المحلل ، يمكننا أن نستنتج أن selectUser.get () قد يرمي NoSuchElementException.



دعنا نلقي نظرة فاحصة على ما يحدث هنا.



إذا كنت تعتقد أن التعليق الذي يشير إلى وجود المستخدم بالفعل ، فلن يتم طرح أي استثناء:



....
if (!selectUser.isPresent()) {  // user already exists
  ....
}
User user = selectUser.get()
....


في هذه الحالة ، لن يدخل تنفيذ البرنامج في نص العبارة الشرطية. وكل شيء سيكون على ما يرام. لكن السؤال الذي يطرح نفسه بعد ذلك: لماذا نحتاج إلى عامل شرطي بنوع من المنطق المعقد إذا لم يتم تنفيذه مطلقًا؟



ولكن ماذا لو كان التعليق لا شيء؟



....
if (!selectUser.isPresent()) {  // user already exists
    selectUser = UserManager.instance.getUserByName(userName);
    if (selectUser.isPresent()) {
      ....
    }
}
User user = selectUser.get(); // <=
....


ثم يدخل التنفيذ في نص العبارة الشرطية ويعيد الحصول على المستخدم عبر getUserByName (). يتم التحقق من صحة المستخدم مرة أخرى ، مما يشير إلى أن selectUser قد يكون غير مهيأ. لا يوجد فرع آخر لهذه الحالة ، مما سيؤدي أيضًا إلى NoSuchElementException على سطر الكود المعني.



تحذير N8



V6042 تم التحقق من توافق التعبير مع النوع "أ" ولكن يتم تحويله إلى النوع "ب". CheckBoxList.java (586)



/**
 * sets the model - must be an instance of CheckBoxListModel
 * 
 * @param model the model to use
 * @throws IllegalArgumentException if the model is not an instance of
 *           CheckBoxListModel
 * @see CheckBoxListModel
 */
@Override
public void setModel(ListModel model) {
  if (!(model instanceof CheckBoxListModel)) {
    if (model instanceof javax.swing.DefaultListModel) {
       super.setModel((CheckBoxListModel)model);         // <=
    }
    else {
      throw new IllegalArgumentException(
          "Model must be an instance of CheckBoxListModel!");
    }
  }
  else {
    super.setModel(model);
  }
}


مؤلف الكود مرتبك بشأن شيء ما هنا: أولاً يتأكد من أن النموذج ليس CheckBoxListModel ، ونتيجة لذلك يلقي الكائن صراحةً إلى هذا النوع. لهذا السبب ، ستقوم طريقة setModel برمي ClassCastException على الفور عندما تصل إلى هناك. تمت إضافة



ملف CheckBoxList.java منذ عامين ولا يزال هذا الخطأ موجودًا في الكود منذ ذلك الحين. على ما يبدو ، لا توجد اختبارات للمعلمات غير الصحيحة ، ولا يوجد استخدام حقيقي لهذه الطريقة مع كائنات من أنواع غير مناسبة ، لذا فهي تعيش.



إذا ربط شخص ما بهذه الطريقة فجأة وقراءة Javadoc ، فسوف يتوقع استثناء IllegalArgumentException ، وليس ClassCastException... لا أعتقد أن أي شخص سيواجه هذا الاستثناء عمدًا ، لكن من يدري.



بالنظر إلى الوثائق ، يجب أن يبدو الرمز على الأرجح كما يلي:



public void setModel(ListModel model) {
  if (!(model instanceof CheckBoxListModel)) {
     throw new IllegalArgumentException(
        "Model must be an instance of CheckBoxListModel!");  
  }
  else {
    super.setModel(model);
  }
}


تحذير N9



تم استخدام إشارة "اللاعب" V6060 قبل أن يتم التحقق من أنها خالية. VigeanIntuition.java (79)، VigeanIntuition.java (78)



@Override
public boolean apply(Game game, Ability source) {
    MageObject sourceObject = game.getObject(source.getSourceId());
    Player player = game.getPlayer(source.getControllerId());
    Library library = player.getLibrary();                           // <=
    if (player != null && sourceObject != null && library != null) { // <=
        ....
    }
}


يحذر V6060 المطور بأنه يتم الوصول إلى كائن قبل التحقق من كونه فارغًا . غالبًا ما توجد محفزات هذه القاعدة في مقالات حول التحقق من المشاريع مفتوحة المصدر: عادةً ما يكون السبب في ذلك هو إعادة بناء أو تغيير عقود الأساليب غير الناجحة. إذا انتبهت إلى إعلان طريقة getPlayer () ، فسيتم وضع كل شيء في مكانه على الفور:



// Result must be checked for null.
// Possible errors search pattern: (\S*) = game.getPlayer.+\n(?!.+\1 != null)
Player getPlayer(UUID playerId);


تحذير N10



V6072 تم العثور على جزئين متشابهين من التعليمات البرمجية. ربما يكون هذا خطأ مطبعي ويجب استخدام المتغير "playerB" بدلاً من "playerA". SubTypeChangingEffectsTest.java (162) ، SubTypeChangingEffectsTest.java (158) ، SubTypeChangingEffectsTest.java (156) ، SubTypeChangingEffectsTest.java (160)



@Test
public void testArcaneAdaptationGiveType() {
    addCard(Zone.HAND, playerA, "Arcane Adaptation", 1); // Enchantment {2}{U}
    addCard(Zone.BATTLEFIELD, playerA, "Island", 3);

    addCard(Zone.HAND, playerA, "Silvercoat Lion");
    addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
    addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

    addCard(Zone.HAND, playerB, "Silvercoat Lion");
    addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
    addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

    ....

    for (Card card : playerB.getGraveyard().getCards(currentGame)) {
        if (card.isCreature()) {
            Assert.assertEquals(card.getName() + " should not have ORC type",
                    false, card.getSubtype(currentGame).contains(SubType.ORC));
            Assert.assertEquals(card.getName() + " should have CAT type",
                    true, card.getSubtype(currentGame).contains(SubType.CAT));
        }
    }
}


بعد أن رأيت أن هذا الخطأ موجود في الاختبارات ، يمكنك على الفور تقليل قيمة العيب المكتشف ، معتقدًا: "حسنًا ، هذه اختبارات." إذا كان الأمر كذلك ، فأنا أختلف معك. بعد كل شيء ، تلعب الاختبارات دورًا مهمًا إلى حد ما في التطوير (على الرغم من أنها ليست ملحوظة مثل البرمجة) ، وعندما يظهر عيب في الإصدار ، تبدأ على الفور في توجيه أصابع الاتهام إلى الاختبارات / المختبرين. لذلك ، الاختبارات المعيبة لا يمكن الدفاع عنها. لماذا إذن هذه الاختبارات ضرورية؟ لماذا نهدر الموارد عليهم؟ تختبر



طريقة testArcaneAdaptationGiveType () بطاقة "Arcane Adaptation". يتم توزيع بطاقات كل لاعب على منطقة لعب معينة. وبفضل النسخ واللصق ، حصل playerA على بطاقتي "Silvercoat Lion" متطابقتين في منطقة اللعب "Cemetery" ، و playerBلذلك لا شيء. ثم بعض السحر واختبار نفسه.



عندما يصل الاختبار إلى "مقبرة" playerB في الرسم الحالي ، فإن تنفيذ الاختبار لا يدخل الحلقة أبدًا ، لأنه لم يكن هناك شيء في "المقبرة". لقد اكتشفت ذلك باستخدام System.out.println () القديم الجيد عند بدء الاختبار .



لصق النسخ المصحح:



....
addCard(Zone.HAND, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");   // <=

addCard(Zone.HAND, playerB, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion");   // <=
....


بعد أن قمت بتعديل الكود ، عند إجراء الاختبار ، بدأ البحث عن مخلوقات في مقبرة playerB في العمل. افي ، System.out.println () !



الاختبار باللون الأخضر قبل التصحيح وبعده ، وهذا كثير من الحظ. ولكن في حالة وجود أي تعديلات غيرت منطق تنفيذ البرنامج ، فإن مثل هذا الاختبار سوف يضر بك ، ويعلمك بإكمال البرنامج بنجاح حتى لو كانت هناك أخطاء.



نفس النسخ واللصق في مكان آخر:



  • V6072 تم العثور على جزأين متشابهين في التعليمات البرمجية. ربما يكون هذا خطأ مطبعي ويجب استخدام المتغير "playerB" بدلاً من "playerA". PaintersServantTest.java (33) ، PaintersServantTest.java (29) ، PaintersServantTest.java (27) ، PaintersServantTest.java (31)
  • V6072 تم العثور على جزئين متشابهين من التعليمات البرمجية. ربما يكون هذا خطأ مطبعي ويجب استخدام المتغير "playerB" بدلاً من "playerA". SubTypeChangingEffectsTest.java (32) ، SubTypeChangingEffectsTest.java (28) ، SubTypeChangingEffectsTest.java (26) ، SubTypeChangingEffectsTest.java (30)


تحذير N11



تنسيق التعليمات البرمجية المشبوهة V6086 . الكلمة الرئيسية "else" ربما تكون مفقودة. DeckImporter.java (23)



public static DeckImporter getDeckImporter(String file) {
  if (file == null) {
    return null;
  } if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {   // <=
    return new DecDeckImporter();
  } else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) {
    return new MWSDeckImporter();
  } else if (file.toLowerCase(Locale.ENGLISH).endsWith("txt")) {
    return new TxtDeckImporter(haveSideboardSection(file));
  }
  ....
  else {
    return null;
  }
}


تقوم قاعدة التشخيص V6086 بتشخيص تنسيق if-else-if غير الصحيح ، مما يعني حذفًا لـ else .



يوضح مقتطف الشفرة هذا هذا. في هذه الحالة ، وبسبب تعبير الإرجاع ، فإن عدم الدقة في التنسيق لا يؤدي إلى أي شيء ، ولكن من الرائع العثور على مثل هذه الحالات ، حيث لا يتعين عليك ذلك.



لنفكر في حالة يمكن أن يؤدي فيها حذف الآخر إلى سلوك غير متوقع:



public SomeType smtMethod(SomeType obj) {
  ....
  if (obj == null) {
    obj = getNewObject();
  } if (obj.isSomeObject()) {
    // some logic
  } else if (obj.isOtherSomething()) {
    obj = calulateNewObject(obj);
    // some logic
  } 
  ....
  else {
    // some logic
  }
  return obj;
}


الآن ، في حالة obj == null ، سيتم تعيين بعض القيمة للكائن المعني ، وسيؤدي عنصر else المفقود إلى بدء فحص الكائن المعين حديثًا على طول سلسلة if-else-if ، بينما كان من المفترض أن يعود الكائن على الفور طريقة.



خاتمة



يعد فحص XMage مقالًا آخر يكشف عن إمكانات أجهزة التحليل الثابتة الحديثة. في التطور الحديث ، تزداد الحاجة إليهم فقط مع زيادة تعقيد البرنامج. وبغض النظر عن عدد الإصدارات والاختبارات وتعليقات المستخدمين لديك: سيجد الخطأ دائمًا ثغرة للوصول إلى قاعدة التعليمات البرمجية الخاصة بك. فلماذا لا تضيف حاجزًا آخر لدفاعك؟



كما تفهم ، فإن المحللون عرضة للإيجابيات الخاطئة (بما في ذلك PVS-Studio Java). يمكن أن يكون هذا نتيجة لعيب واضح ورمز مربك للغاية (للأسف ، لم يكتشف المحلل ذلك). تحتاج إلى معاملتهم بفهم وإلغاء الاشتراك فورًا دون تردد ، ولكن بينما تنتظر الإيجابيات الخاطئة تصحيحها ، يمكنك استخدام إحدى الطرققمع التحذيرات.



في الختام ، أقترح عليك "لمس" المحلل شخصيًا عن طريق تنزيله من موقعنا على الإنترنت.





إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فيرجى استخدام رابط الترجمة: مكسيم ستيفانوف. التحقق من كود XMage ، ولماذا لن تكون قادرًا على الحصول على البطاقات النادرة الخاصة لمجموعة Dragon's Maze .



All Articles