لماذا لا يقدم برنامج PVS-Studio تعديلات تلقائية في التعليمات البرمجية

لماذا لا يقدم برنامج PVS-Studio تعديلات تلقائية في التعليمات البرمجية


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



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



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



يكتشف PVS-Studio الأخطاء ونقاط الضعف المحتملة. العديد من الأخطاء تثير الفكر وتتطلب تغييرًا في سلوك البرنامج. ويمكن للمبرمج فقط أن يقرر كيفية إصلاح هذا الخطأ أو ذاك.



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



تأمل الخطأ الذي قمت بتحليله في مقال " 31 فبراير ".



static const int kDaysInMonth[13] = {
  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

bool ValidateDateTime(const DateTime& time) {
  if (time.year < 1 || time.year > 9999 ||
      time.month < 1 || time.month > 12 ||
      time.day < 1 || time.day > 31 ||
      time.hour < 0 || time.hour > 23 ||
      time.minute < 0 || time.minute > 59 ||
      time.second < 0 || time.second > 59) {
    return false;
  }
  if (time.month == 2 && IsLeapYear(time.year)) {
    return time.month <= kDaysInMonth[time.month] + 1;
  } else {
    return time.month <= kDaysInMonth[time.month];
  }
}


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



bool ValidateDateTime(const DateTime& time) {
  if (time.year < 1 || time.year > 9999 ||
      time.month < 1 || time.month > 12 ||
      time.day < 1 || time.day > 31 ||
      time.hour < 0 || time.hour > 23 ||
      time.minute < 0 || time.minute > 59 ||
      time.second < 0 || time.second > 59) {
    return false;
  }
  if (time.month == 2 && IsLeapYear(time.year)) {
    return true;
  } else {
    return true;
  }
}


أو ، ماذا يمكننا أن نقول عن تفاهات ، دعه يقدم مثل هذا الاستبدال التلقائي:



bool ValidateDateTime(const DateTime& time) {
  if (time.year < 1 || time.year > 9999 ||
      time.month < 1 || time.month > 12 ||
      time.day < 1 || time.day > 31 ||
      time.hour < 0 || time.hour > 23 ||
      time.minute < 0 || time.minute > 59 ||
      time.second < 0 || time.second > 59) {
    return false;
  }
  return true;
}


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



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



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



static DWORD
get_rel_wait(const struct timespec *abstime)
{
  struct __timeb64 t;
  _ftime64_s(&t);
  time_t now_ms = t.time * 1000 + t.millitm;
  time_t ms = (time_t)(abstime->tv_sec * 1000 +
    abstime->tv_nsec / 1000000);

  DWORD rel_wait = (DWORD)(ms - now_ms);

  return rel_wait < 0 ? 0 : rel_wait;
}


نظرًا لأن rel_wait من النوع غير الموقعة ، فإن الفحص اللاحق لـ rel_wait <0 لا معنى له. تحذير PVS-Studio: V547 [CWE-570] التعبير "rel_wait <0" خاطئ دائمًا. قيمة النوع غير الموقعة ليست أبدًا <0. os_thread_windows.c 359 استوحى



شخص ما من المقالة وبدأ في إصلاح الأخطاء الموضحة فيها بشكل كبير: إصلاح المشكلات المختلفة التي أبلغ عنها تحليل PVS-Studio .



وكيف تم اقتراح إصلاح الكود؟ بسيط جدًا: النواة: تبسيط تنفيذ مؤقت Windows .



إصلاح التعليمات البرمجية السيئة


لكن الكود تم تبسيطه وليس إصلاحه! لوحظ هذا وبدأت مناقشة مقابلة: المشكلة: os_thread_windows.c - سيتم حظر get_rel_wait () إذا كان abstime في الماضي .



كما ترى ، حتى الأشخاص يرتكبون أخطاء في التعديلات المقترحة. لماذا تجرب الروبوتات.



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





إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فيرجى استخدام رابط الترجمة: Andrey Karpov. لماذا لا يقدم PVS-Studio إصلاحات تلقائية .



All Articles