تضمين تحليل ثابت في هذه العملية ، وليس البحث عن الأخطاء معها

لقد طُلب مني كتابة هذا المقال من خلال كمية كبيرة من المواد في التحليل الثابت ، والتي كثيراً ما تلفت انتباهي. أولاً ، هذه هي مدونة PVS-studio ، التي تعمل بنشاط على الترويج لـ Habré بمساعدة مراجعات الأخطاء التي تم العثور عليها بواسطة أدواتهم في مشاريع مفتوحة المصدر. في الآونة الأخيرة ، نفذت PVS-studio دعمًا لجافا ، وبالطبع لم يستطع مطورو IntelliJ IDEA ، والذين ربما يكون محللهم المدمج هو الأكثر تقدمًا لجافا اليوم ، الابتعاد .

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

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


اسئلة (المصدر: ويكيبيديا ).

ما المحللون ثابتة لا يمكن أبدا


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

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

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

التحليل الثابت ليس بحثًا عن خلل


يتبع الاستنتاج ما تقدم: التحليل الثابت ليس وسيلة لتقليل عدد العيوب في البرنامج. سأعمل على التأكيد: عندما يتم تطبيقه لأول مرة على مشروعك ، فسيجد أماكن "مشغولة" في الشفرة ، ولكن على الأرجح لن يجد أي عيوب تؤثر على جودة البرنامج.

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

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

التحليل الثابت هو أكثر من مجرد البحث عن الأخطاء


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

  • التحقق من نمط الترميز بالمعنى الواسع للكلمة. يتضمن ذلك التحقق من التنسيق ، والبحث عن استخدام الأقواس الفارغة / الإضافية ، وتحديد قيم عتبية للمقاييس مثل عدد الخطوط / التعقيد السيكلوماتيكي للطريقة ، وما إلى ذلك - كل ذلك قد يجعل من الصعب قراءة التعليمات البرمجية والحفاظ عليها. في Java هذه الأداة هي Checkstyle ، في Python - flake8. عادة ما تسمى برامج هذه الفئة باللاتر.
  • ليس فقط رمز قابل للتنفيذ يمكن تحليله. يمكن فحص ملفات الموارد مثل JSON و YAML و XML و .properties تلقائيًا للتأكد من صحتها. بعد كل شيء ، من الأفضل معرفة أنه بسبب بعض علامات الاقتباس غير المقيدة ، فإن بنية JSON تنتهك في المرحلة المبكرة من التحقق من طلب السحب التلقائي من عند تنفيذ الاختبارات أو في وقت التشغيل؟ تتوفر الأدوات ذات الصلة: على سبيل المثال ، YAMLlint ، JSONLint .
  • التجميع (أو تحليل لغات البرمجة الديناميكية) هو أيضًا شكل من أشكال التحليل الثابت. كقاعدة عامة ، يستطيع القائمون على التحويل البرمجي إصدار تحذيرات تشير إلى مشاكل في جودة الكود المصدري ، ويجب عدم تجاهلها.
  • الترجمة في بعض الأحيان ليست فقط تجميع التعليمات البرمجية القابلة للتنفيذ. على سبيل المثال ، إذا كان لديك وثائق بتنسيق AsciiDoctor ، ثم في وقت تحويله إلى HTML / PDF ، قد يعطي معالج AsciiDoctor ( Maven plugin ) تحذيرات ، على سبيل المثال ، حول الروابط الداخلية المعطلة. وهذا سبب وجيه لعدم قبول طلب السحب مع إجراء تغييرات على الوثائق.
  • التدقيق الإملائي هو أيضًا شكل من أشكال التحليل الثابت. أداة aspell قادرة على التحقق من التدقيق الإملائي ليس فقط في الوثائق ، ولكن أيضًا في أكواد مصدر البرامج (التعليقات والحرفية) بلغات البرمجة المختلفة ، بما في ذلك C / C ++ و Java و Python. خطأ إملائي في واجهة المستخدم أو الوثائق هو أيضا عيب!
  • اختبارات التكوين (لمعرفة ما هو عليه - انظر هذا وهذا التقرير) ، على الرغم من أنها تعمل في بيئة وقت التشغيل لإجراء اختبارات الوحدة مثل pytest ، إلا أنها في الواقع نوع من التحليل الثابت ، لأنها لا تنفذ أكواد المصدر أثناء تنفيذها .

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

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

خط أنابيب التسليم كمرشح متعدد المراحل وتحليل ثابت كأول سلسلة


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

  1. تحليل ثابت
  2. تجميع
  3. اختبارات الوحدة
  4. اختبارات التكامل
  5. اختبارات واجهة المستخدم
  6. الاختيار اليدوي

لا يتم نقل التغييرات المرفوضة في المرحلة الثانية من الناقل إلى المرحلة N + 1.

لماذا ذلك ، وليس غير ذلك؟ في جزء الاختبار من خط الأنابيب ، يتعرف المختبرون على هرم الاختبار المعروف.


اختبار الهرم. المصدر: مقالة مارتن فاولر.

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

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


مرشح متعدد المراحل. المصدر: ويكيميديا ​​كومنز

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

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

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

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

التنفيذ في مشروع قديم


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

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

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

الطرق التالية لإدارة البوابات عالية الجودة معروفة:

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

اسئلة


إنه يعمل بهذه الطريقة:

  1. في المرحلة الأولية ، يتم تسجيل عدد التحذيرات في الكود الذي عثر عليه المحللون في البيانات الأولية حول الإصدار. وبالتالي ، عند إنشاء الفرع الرئيسي ، لا يتم كتابة "الإصدار 7.0.2" فقط ، ولكن "الإصدار 7.0.2 ، الذي يحتوي على 100500 تحذيرات Checkstyle" إلى مدير المستودع. إذا كنت تستخدم مدير مستودعات متقدم (مثل Artifactory) ، فسيكون حفظ بيانات التعريف هذه عن الإصدار الخاص بك أمرًا سهلاً.
  2. الآن ، يقارن كل طلب سحب أثناء التجميع عدد التحذيرات المستلمة مع الرقم الموجود في الإصدار الحالي. إذا كانت العلاقات العامة تؤدي إلى زيادة في هذا الرقم ، فإن الرمز لا يمرر بوابة الجودة للتحليل الثابت. إذا انخفض عدد التحذيرات أو لم يتغير ، فإنه يمر.
  3. في الإصدار التالي ، سيتم إعادة كتابة عدد التحذيرات المحسوبة إلى بيانات تعريف الإصدار.

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

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



أستخدم نسخة معدلة من هذه الطريقة ، أحسب بشكل منفصل التحذيرات موزعة حسب وحدات المشروع وأدوات التحليل ، يبدو ملف YAML الذي تم إنشاؤه مع بيانات التعريف التجميعية كما يلي:

celesta-sql: checkstyle: 434 spotbugs: 45 celesta-core: checkstyle: 206 spotbugs: 13 celesta-maven-plugin: checkstyle: 19 spotbugs: 0 celesta-unit: checkstyle: 0 spotbugs: 0 

في أي نظام CI متقدم ، يمكن تنفيذ "السقاطة" لأي أدوات تحليل ثابتة ، دون الاعتماد على المكونات الإضافية وأدوات الطرف الثالث. يقوم كل محلل بإصدار تقريره بنسق بسيط أو بتنسيق XML ، وهو سهل التحليل. يبقى تسجيل فقط المنطق الضروري في البرنامج النصي CI. يمكنك أن ترى كيف يتم تنفيذ هذا في مشاريعنا مفتوحة المصدر على أساس جنكينز و Artifactory هنا أو هنا . يعتمد كلا المثالين على مكتبة ratchetlib : countWarnings() طريقة countWarnings() بحساب علامات xml في الملفات التي تم إنشاؤها بواسطة Checkstyle و Spotbugs ، compareWarningMaps() نفس السقاطة ، مما يؤدي إلى حدوث خطأ عند زيادة عدد التحذيرات في أي من الفئات.

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

حول أهمية إصلاح الإصدار محلل


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

الاستنتاجات


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

المراجع


  1. تسليم مستمر
  2. أ. كودريافتسيف: تحليل البرامج: كيف تفهم أنك تقرير جيد للمبرمجين حول الأساليب المختلفة لتحليل الكود (ليس فقط ثابتًا!)

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


All Articles