هذه هي المقالة الثانية حول استخدام محلل ثابت PVS-Studio في أنظمة السحاب CI ، وهذه المرة سننظر في النظام الأساسي Azure DevOps - وهو حل سحابة CI \ CD من Microsoft. كمشروع تم تحليله هذه المرة ، فكر في ShareX.
سنحتاج ثلاثة مكونات. الأول هو محلل ثابت PVS-Studio. والثاني هو Azure DevOps ، والذي سنقوم بدمج المحلل. والثالث هو مشروع سنقوم بالتحقق منه لإظهار قدرات PVS-Studio عند العمل في السحابة. لذلك دعونا نبدأ.
PVS-Studio هو محلل أكواد ثابت للبحث عن الأخطاء والعيوب الأمنية. ينفذ تحليل الشفرة في C و C ++ و C # و Java.
أزور ديفوبس . تشتمل منصة Azure DevOps على أدوات مثل Azure Pipeline و Azure Board و Azure Artifacts وغيرها ، مما يسرع من عملية إنشاء البرامج وتحسين جودته.
ShareX هو تطبيق مجاني يتيح لك التقاط وتسجيل أي جزء من الشاشة. المشروع مكتوب بلغة C # وهو رائع لإظهار كيفية تشغيل المحلل الثابت. الكود المصدري للمشروع
متاح على جيثب .
مخرجات الأمر cloc لمشروع ShareX:
بمعنى آخر ، فإن المشروع صغير ، لكنه كافٍ لإظهار عمل PVS-Studio مع منصة سحابية.
دعونا اقامة
للبدء في Azure DevOps ، انقر على
الرابط وانقر على زر "ابدأ مجانًا مع GitHub".
منح تطبيق Microsoft حق الوصول إلى بيانات حساب GitHub.
لإكمال التسجيل ، يجب إنشاء حساب Microsoft.
بعد التسجيل ، قم بإنشاء مشروع:
بعد ذلك ، نحتاج إلى الانتقال إلى قسم "Pipelines" - "Builds" وإنشاء خط أنابيب Build جديد
إلى السؤال الذي يوجد به رمزنا ، سنجيب - GitHub.
نأذن بتطبيق Azure Pipelines ونختار المستودع بالمشروع الذي سنقوم من أجله بتهيئة إطلاق أداة التحليل الثابتة
في نافذة اختيار القالب ، حدد "خط أنابيب البدء".
يمكننا إجراء تحليل ثابت لرمز المشروع بطريقتين: استخدام الوكلاء المستضافة من قبل Microsoft أو المستضافين ذاتيًا.
في الإصدار الأول ، سوف نستخدم الوكلاء الذين تستضيفهم Microsoft. هذه العوامل هي أجهزة افتراضية عادية تبدأ عندما نبدأ تشغيل خط أنابيبنا ويتم حذفها بعد نهاية المهمة. يتيح لك استخدام مثل هذه العوامل عدم إضاعة الوقت في دعمهم وتحديثهم ، ولكنه يفرض بعض القيود ، على سبيل المثال ، استحالة تثبيت برامج إضافية تُستخدم لبناء المشروع.
استبدال التكوين الافتراضي الخاص بنا بما يلي لاستخدام وكلاء Microsoft المستضافة:
# # master- trigger: - master # # , Docker-, # Windows Server 1803 pool: vmImage: 'win1803' container: microsoft/dotnet-framework:4.7.2-sdk-windowsservercore-1803 steps: # - task: PowerShell@2 inputs: targetType: 'inline' script: 'Invoke-WebRequest -Uri https://files.viva64.com/PVS-Studio_setup.exe -OutFile PVS-Studio_setup.exe' - task: CmdLine@2 inputs: workingDirectory: $(System.DefaultWorkingDirectory) script: | # nuget restore .\ShareX.sln # , md .\PVSTestResults # PVS-Studio_setup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /COMPONENTS=Core # "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials -u $(PVS_USERNAME) -n $(PVS_KEY) # html. "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" -t .\ShareX.sln -o .\PVSTestResults\ShareX.plog "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" -t html -o .\PVSTestResults\ .\PVSTestResults\ShareX.plog # - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults
ملاحظة: وفقًا
للوثائق ، يجب تخزين الحاوية المستخدمة في صورة الجهاز الظاهري ، ولكن في وقت كتابة هذا المقال ، لا تعمل هذه المقالة ويتم تنزيل الحاوية في كل مرة تبدأ فيها المهمة ، مما يؤثر سلبًا على وقت التنفيذ.
حفظ خط الأنابيب وإنشاء المتغيرات التي سيتم استخدامها لإنشاء ملف الترخيص. للقيام بذلك ، افتح نافذة تحرير خط الأنابيب وفي الزاوية اليمنى العليا انقر على زر "المتغيرات".
أضف متغيرين -
PVS_USERNAME و
PVS_KEY ، يحتويان على اسم المستخدم ومفتاح الترخيص ، على التوالي. عند إنشاء متغير
PVS_KEY ، لا تنس التحقق من عنصر "الحفاظ على سرية هذه القيمة" لتشفير القيمة المتغيرة باستخدام مفتاح RSA 2048 بت ، بالإضافة إلى منع إخراج القيمة المتغيرة في سجل تنفيذ المهمة.
نقوم بحفظ المتغيرات وبدء تشغيل خط الأنابيب بواسطة الزر "تشغيل".
الخيار الثاني لتشغيل التحليل هو استخدام عامل مستضاف ذاتيًا. الوكلاء المستضافة ذاتياً هم وكلاء نقوم بتكوين وإدارة أنفسنا. توفر هذه العوامل المزيد من الفرص لتثبيت البرنامج ، وهو أمر ضروري لتجميع واختبار منتجنا من البرامج.
قبل استخدام هذه العوامل ، يجب تكوينها وفقًا
للتعليمات ، ويجب تثبيت محلل ثابت
وتكوينه .
لبدء المهمة على وكيل مستضاف ذاتيًا ، استبدلنا التكوين الافتراضي المقترح بما يلي:
# # master- trigger: - master # self-hosted 'MyPool' pool: 'MyPool' steps: - task: CmdLine@2 inputs: workingDirectory: $(System.DefaultWorkingDirectory) script: | # nuget restore .\ShareX.sln # , md .\PVSTestResults # html. "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" -t .\ShareX.sln -o .\PVSTestResults\ShareX.plog "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" -t html -o .\PVSTestResults\ .\PVSTestResults\ShareX.plog # - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults
بعد الانتهاء من المهمة ، يمكن تنزيل الأرشيف مع تقارير المحلل في علامة تبويب الملخص ، أو يمكننا استخدام ملحق
إرسال البريد ، الذي يسمح لك بتكوين إرسال رسائل البريد الإلكتروني ، أو البحث عن أداة أكثر ملاءمة في
السوق .
حول نتائج التحليل
الآن دعونا نلقي نظرة على بعض الأخطاء التي تم العثور عليها في المشروع المحدد - ShareX.
الشيكات الزائدةللاحماء ، لنبدأ بعيوب بسيطة في الكود ، أي بالتحققات الزائدة عن الحاجة:
private void PbThumbnail_MouseMove(object sender, MouseEventArgs e) { .... IDataObject dataObject = new DataObject(DataFormats.FileDrop, new string[] { Task.Info.FilePath }); if (dataObject != null) { Program.MainForm.AllowDrop = false; dragBoxFromMouseDown = Rectangle.Empty; pbThumbnail.DoDragDrop(dataObject, DragDropEffects.Copy | DragDropEffects.Move); Program.MainForm.AllowDrop = true; } .... }
تحذير PVS-Studio :
V3022 [CWE-571] التعبير 'dataObject! = Null' صحيح دائمًا. TaskThumbnailPanel.cs 415
الانتباه إلى التحقق من متغير
dataObject فارغة . ما هي هنا ل؟ لا يمكن ببساطة أن تكون
dataObject خالية في هذه الحالة ، حيث تتم تهيئتها بالإشارة إلى الكائن الذي تم إنشاؤه. نتيجة لذلك ، لدينا التحقق الزائد. هل هو حاسم؟ لا. تبدو موجزة؟ لا. من الواضح أن هذا الاختيار قد تمت إزالته بشكل أفضل حتى لا يحدث تشويش للرمز.
دعنا نلقي نظرة على جزء آخر من التعليمات البرمجية ، والتي يمكنك من خلالها تقديم تعليقات مماثلة:
private static Image GetDIBImage(MemoryStream ms) { .... try { .... return new Bitmap(bmp); .... } finally { if (gcHandle != IntPtr.Zero) { GCHandle.FromIntPtr(gcHandle).Free(); } } .... } private static Image GetImageAlternative() { .... using (MemoryStream ms = dataObject.GetData(format) as MemoryStream) { if (ms != null) { try { Image img = GetDIBImage(ms); if (img != null) { return img; } } catch (Exception e) { DebugHelper.WriteException(e); } } } .... }
تحذير PVS-Studio :
V3022 [CWE-571] التعبير 'img! = Null' صحيح دائمًا. ClipboardHelpers.cs 289
يتحقق الأسلوب
GetImageAlternative مرة أخرى من أن متغير
img ليس
فارغًا بعد إنشاء نسخة جديدة من فئة
الصورة النقطية . الفرق من المثال السابق هنا هو أنه لتهيئة متغير
img ، لا نستخدم المُنشئ ، ولكننا
نستخدم أسلوب
GetDIBImage . يفترض مؤلف التعليمات البرمجية أنه قد يحدث استثناء في هذه الطريقة ، لكنه يعلن فقط عن
المحاولة وأخيراً يتم حظره ، مع حذف
الصيد . لذلك ، في حالة حدوث استثناء ، لن تتلقى طريقة الاستدعاء
- GetImageAlternative - مرجعًا لكائن من نوع
الصورة النقطية ، ولكن سيتم فرض معالجة الاستثناء في
كتلة catch الخاصة به. في هذه الحالة ، لن يتم تهيئة متغير
img ، ولن يصل مؤشر ترابط التنفيذ إلى علامة الاختيار
img! = Null ، ولكنه يقع فورًا في
كتلة catch . لذلك ، يشير المحلل إلى التحقق من الصحة الزائد.
خذ بعين الاعتبار مثال التحذير التالي مع التعليمات البرمجية
V3022 :
private void btnCopyLink_Click(object sender, EventArgs e) { .... if (lvClipboardFormats.SelectedItems.Count == 0) { url = lvClipboardFormats.Items[0].SubItems[1].Text; } else if (lvClipboardFormats.SelectedItems.Count > 0) { url = lvClipboardFormats.SelectedItems[0].SubItems[1].Text; } .... }
تحذير PVS-Studio :
V3022 [CWE-571] تعبير 'lvClipboardFormats.SelectedItems.Count> 0' صحيح دائمًا. AfterUploadForm.cs 155
دعنا ننظر إلى التعبير الشرطي الثاني. هناك نتحقق من قيمة خاصية
Count للقراءة فقط. تعرض هذه الخاصية عدد العناصر في مثيل مجموعة
SelectedItems . يتم استيفاء الشرط فقط إذا كانت الخاصية
Count أكبر من الصفر. كل شيء سيكون على ما يرام ، لكنه فقط في الخارج
إذا كان البيان الذي تم بالفعل التحقق من
العد . لا يمكن أن يحتوي مثيل مجموعة
SelectedItems على عدد العناصر التي تقل عن الصفر ، وبالتالي ، فإن
العدد يأخذ قيمة إما تساوي الصفر أو أكبر من الصفر. نظرًا لأننا قمنا بالفعل بإجراء فحص في أول
عبارة إذا كان العدد هو الصفر ، واتضح أنه غير صحيح ، فليس من المنطقي أن تكتب شيكًا آخر في الفرع الآخر الذي
يكون فيه العدد أكبر من الصفر.
المثال الأخير من رقم الخطأ
V3022 هو جزء التعليمات البرمجية التالي:
private void DrawCursorGraphics(Graphics g) { .... int cursorOffsetX = 10, cursorOffsetY = 10, itemGap = 10, itemCount = 0; Size totalSize = Size.Empty; int magnifierPosition = 0; Bitmap magnifier = null; if (Options.ShowMagnifier) { if (itemCount > 0) totalSize.Height += itemGap; .... } .... }
تحذير PVS-Studio : تعبير V3022 'itemCount> 0' غير صحيح دائمًا. RegionCaptureForm.cs 1100.
لاحظ المحلل أن
عنصر الحالةالعدد> 0 سيكون دائمًا خاطئًا ، حيث يتم تنفيذ إعلان أعلى قليلاً
وتعيين متغير
itemCount على صفر في نفس الوقت. حتى الشرط ذاته ، لا يتم استخدام هذا المتغير في أي مكان ولا يتغير ، لذلك ، توصل المحلل إلى الاستنتاج الصحيح حول التعبير الشرطي ، الذي تكون قيمته خاطئة دائمًا.
حسنًا ، دعنا الآن ننظر إلى شيء مثير للاهتمام حقًا.
أفضل طريقة لفهم الخطأ هي تصور الخطأ.يبدو لنا أنه تم العثور على خطأ مثير للاهتمام في هذا المكان:
public static void Pixelate(Bitmap bmp, int pixelSize) { .... float r = 0, g = 0, b = 0, a = 0; float weightedCount = 0; for (int y2 = y; y2 < yLimit; y2++) { for (int x2 = x; x2 < xLimit; x2++) { ColorBgra color = unsafeBitmap.GetPixel(x2, y2); float pixelWeight = color.Alpha / 255; r += color.Red * pixelWeight; g += color.Green * pixelWeight; b += color.Blue * pixelWeight; a += color.Alpha * pixelWeight; weightedCount += pixelWeight; } } .... ColorBgra averageColor = new ColorBgra((byte)(b / weightedCount), (byte)(g / weightedCount), (byte)(r / weightedCount), (byte)(a / pixelCount)); .... }
لا أريد الكشف على الفور عن جميع البطاقات وإظهار ما وجده محللنا هنا ، لذلك دعونا نؤجل هذه اللحظة لفترة قصيرة.
من خلال اسم الطريقة ، من السهل تخمين ما تفعله - فأنت تقدم صورة أو جزءًا من الصورة إليها كمدخل ، وتقوم بتنفيذ البيكسل. رمز الطريقة طويل جدًا ، لذا لن نعطيه هنا بالكامل ، لكن حاول ببساطة شرح الخوارزمية الخاصة به وشرح نوع الأخطاء التي عثر عليها في PVS-Studio هنا.
تقبل هذه الطريقة معلمتين كمدخل: كائن من نوع
صورة نقطية وقيمة type
int ، مما يدل على حجم البكسل. خوارزمية العملية بسيطة للغاية:
1) نقوم بتقسيم جزء الصورة الذي تم استلامه عند الإدخال إلى مربعات مع جانب يساوي حجم البكسل. على سبيل المثال ، إذا كان لدينا حجم بكسل 15 ، فسوف نحصل على مربع يحتوي على 15x15 = 225 بكسل.
2) بعد ذلك ، ندور حول كل بكسل في هذا المربع ونجمع قيم حقول
Red و
Green و
Blue و
Alpha في متغيرات وسيطة ،
ونضرب مسبقًا قيمة اللون المقابل وقيمة قناة ألفا بواسطة متغير
pixelWeight ، الذي تم الحصول عليه بقسمة قيمة
Alpha على 255 (يحتوي متغير
Alpha على اكتب
بايت ). أيضًا ، عند اجتياز وحدات البكسل ، نجمع القيم المسجلة
بالبكسل في
الوزن في متغير يسمى
weightedCount .
مقتطف الشفرة الذي ينفذ الخطوات أعلاه كما يلي:
ColorBgra color = unsafeBitmap.GetPixel(x2, y2); float pixelWeight = color.Alpha / 255; r += color.Red * pixelWeight; g += color.Green * pixelWeight; b += color.Blue * pixelWeight; a += color.Alpha * pixelWeight; weightedCount += pixelWeight;
بالمناسبة ، لاحظ أنه إذا كانت قيمة متغير
Alpha تساوي صفرًا ، فلن تضيف
pixelWeight أي قيمة إلى المتغير
الموزون لهذا البيكسل. سنحتاج هذا في المستقبل.
3) بعد أن تجاوزنا جميع وحدات البكسل في المربع الحالي ، يمكننا تعويض اللون "المتوسط" العام لهذا المربع. التعليمة البرمجية التي تنفذ هذه الإجراءات هي كما يلي:
ColorBgra averageColor = new ColorBgra((byte)(b / weightedCount), (byte)(g / weightedCount), (byte)(r / weightedCount), (byte)(a / pixelCount));
4) الآن وبعد أن أصبح لدينا اللون النهائي
ونكتبه على mediumColor المتغير ، يمكننا مرة أخرى الالتفاف على كل بكسل في المربع وتعيين قيمة من
averageColor .
5) نعود إلى الخطوة 2 طالما لا تزال هناك مربعات خام.
مرة أخرى ، نلاحظ أن متغير
الوزن الموزون لا يساوي عدد جميع وحدات البكسل المربعة. على سبيل المثال ، في حالة حدوث بيكسل شفاف تمامًا في الصورة (القيمة تساوي صفرًا على قناة ألفا) ،
فسيكون المتغير
pixelWe يساوي الصفر بالنسبة للبكسل (
0/255 = 0) ، وبالتالي ، لن يساهم هذا البيكسل في أي مساهمة في تكوين قيمة متغير
المرجح . هذا منطقي - ليس من المنطقي مراعاة ألوان بكسل شفاف تمامًا.
يبدو كل شيء معقولًا تمامًا - يجب أن تعمل البكسل بشكل صحيح. وانها حقا تعمل بشكل صحيح. هذا ليس فقط لصور بابوا نيو غينيا التي تحتوي على بكسلات بقيم في قناة ألفا أقل من 255 وغير متساوية مع الصفر. انتبه للصورة المنقطة أدناه:
هل رأيت البكسل؟ ونحن لسنا كذلك. حسنًا ، دعنا الآن نكشف عن هذه المؤامرة الصغيرة وشرح أين تم إخفاء الأخطاء في هذه الطريقة بالضبط. الخطأ تسلل إلى السطر لحساب قيمة المتغير
pixel :
float pixelWeight = color.Alpha / 255;
والحقيقة هي أن مؤلف الكود ، معلنا
البيكسل المتغير
الوزن كنوع من
التعويم ، يعني ضمنا أنه عند قسمة حقل
ألفا على 255 ، بالإضافة إلى صفر واحد ، ينبغي الحصول على أرقام كسرية. هنا تكمن المشكلة ، نظرًا لأن متغير
Alpha هو نوع
البايت ، وعندما نقسمه على 255 ، نحصل على قيمة عدد صحيح ، وعندها فقط سيتم طرحها ضمنًا
لتطفو ، وبالتالي ، يتم فقد الجزء الكسري.
من السهل شرح عدم القدرة على تكبير صور PNG التي تحتوي على درجة من الشفافية. نظرًا لأن قيم قناة ألفا لهذه البيكسلات تقع في النطاق 0 <Alpha <255 ، عند قسمة المتغير
Alpha على 255 ، سنحصل دائمًا على 0. لذلك ، فإن قيم
pixelWeight و
r و
g و
b و
a و
weightedCount متغيرات هي أيضًا دائمًا سيكون صفر. نتيجة لذلك ، سيكون متوسط
لوننا المتوسط مع قيم صفرية في جميع القنوات: أحمر - 0 ، أزرق - 0 ، أخضر - 0 ، ألفا - 0. ملء المربع بهذا اللون ، لا نغير اللون الأصلي للبكسل ، لأن
متوسط اللون شفاف تمامًا . لإصلاح هذا الخطأ ، تحتاج فقط إلى إرسال حقل
ألفا بشكل صريح
لتعويم الكتابة. قد يبدو السطر الصحيح من التعليمات البرمجية كما يلي:
float pixelWeight = (float)color.Alpha / 255;
وقد حان الوقت للاستشهاد بالرسالة التي أعطاها PVS-Studio للرمز غير الصحيح:
تحذير PVS-Studio :
V3041 [CWE-682] تم التعبير ضمنيًا عن التعبير من النوع "int" إلى النوع "float". النظر في استخدام يلقي نوع واضح لتجنب فقدان جزء كسور. مثال: double A = (double) (X) / Y؛ ImageHelpers.cs 1119.
وللمقارنة ، نقدم لقطة شاشة لصورة منقطة حقًا تم الحصول عليها على إصدار ثابت من التطبيق:
NullReferenceException المحتملة public static bool AddMetadata(Image img, int id, string text) { .... pi.Value = bytesText; if (pi != null) { img.SetPropertyItem(pi); return true; } .... }
تحذير PVS-Studio: V3095 [CWE-476] تم استخدام كائن "pi" قبل أن يتم التحقق منه ضد قيمة خالية. خطوط التحقق: 801 ، 803. ImageHelpers.cs 801
يوضح جزء التعليمات البرمجية هذا أن مؤلفه يتوقع أن يكون متغير
pi خاليًا ، وهذا هو السبب في إجراء
pi! = Null يتم إجراء
الاختبار قبل استدعاء الأسلوب
SetPropertyItem . من الغريب أنه قبل هذا الفحص ، يتم تعيين صفيف بايت إلى خاصية
pi.Value ، لأنه إذا كانت
pi خالية ، فسيتم طرح استثناء من النوع
NullReferenceException .
تم رؤية موقف مماثل في مكان آخر:
private static void Task_TaskCompleted(WorkerTask task) { .... task.KeepImage = false; if (task != null) { if (task.RequestSettingUpdate) { Program.MainForm.UpdateCheckStates(); } .... } .... }
تحذير PVS-Studio: V3095 [CWE-476] تم استخدام كائن "المهمة" قبل التحقق من صحته. خطوط التحقق: 268 ، 270. TaskManager.cs 268
عثر PVS-Studio على خطأ مشابه آخر. المعنى لا يزال هو نفسه ، لذلك ليست هناك حاجة كبيرة لإعطاء جزء من الشفرة ، فنحن نقتصر على رسالة المحلل.
تحذير PVS-Studio: V3095 [CWE-476] تم استخدام كائن "Config.PhotobucketAccountInfo" قبل أن يتم التحقق منه مقابل لاغٍ. خطوط التحقق: 216 ، 219. UploadersConfigForm.cs 216
نفس القيمة المرجعةتم اكتشاف جزء مشبوه من التعليمات البرمجية في طريقة
EvalWindows الخاصة بفئة
WindowsList ، والتي تعود بشكل
صحيح تحت أي ظرف من الظروف:
public class WindowsList { public List<IntPtr> IgnoreWindows { get; set; } .... public WindowsList() { IgnoreWindows = new List<IntPtr>(); } public WindowsList(IntPtr ignoreWindow) : this() { IgnoreWindows.Add(ignoreWindow); } .... private bool EvalWindows(IntPtr hWnd, IntPtr lParam) { if (IgnoreWindows.Any(window => hWnd == window)) { return true;
تحذير PVS-Studio: V3009 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة "صواب". WindowsList.cs 82
يبدو من المنطقي أنه إذا تم العثور على مؤشر بنفس قيمة
hWnd في القائمة باسم
IgnoreWindows ، فيجب أن تُرجع الطريقة
خطأ .
يمكن ملؤها قائمة
IgnoreWindows إما عن طريق استدعاء مُنشئ
WindowsList (IntPtr ignoreWindow) ، أو مباشرة من خلال الوصول إلى الخاصية ، لأنها عامة. بطريقة أو بأخرى ، وفقًا لبرنامج Visual Studio ، في الوقت الحالي في الكود لا يتم ملء هذه القائمة بأي طريقة. هذا مكان غريب آخر لهذه الطريقة.
دعوة غير آمنة لمعالجات الأحداث protected void OnNewsLoaded() { if (NewsLoaded != null) { NewsLoaded(this, EventArgs.Empty); } }
تحذير PVS-Studio: V3083 [CWE-367] الاحتجاج غير الآمن للحدث "NewsLoaded" ، NullReferenceException ممكن. النظر في تعيين الحدث إلى متغير محلي قبل استدعاء ذلك. NewsListControl.cs 111
في هذه الحالة ، قد يحدث الموقف غير السار التالي: بعد التحقق من المتغير
NewsLoaded لعدم المساواة
الفارغة ، يمكن إلغاء اشتراك الطريقة التي تعالج الحدث ، على سبيل المثال ، في سلسلة
رسائل أخرى ، وعندما ندخل في نص الشرطي
if statement ، فإن المتغير
NewsLoaded سوف يكون بالفعل يساوي
فارغة . محاولة استدعاء المشتركين في حدث
NewsLoaded الذي يعد
لاغياً سوف يؤدي إلى
NullReferenceException . يعد استخدام عامل التشغيل الشرطي null أكثر أمانًا وإعادة كتابة التعليمات البرمجية أعلاه كما يلي:
protected void OnNewsLoaded() { NewsLoaded?.Invoke(this, EventArgs.Empty); }
وأشار محلل
68 أماكن أكثر مماثلة. لن نصفها هنا - نمط استدعاء الحدث فيها مشابه.
العودة خالية من ToStringمنذ وقت ليس ببعيد ، من
مقالة مثيرة للاهتمام من أحد الزملاء
، اكتشفت أن Microsoft لا توصي بالعودة
خالية من أسلوب
ToString الغالب . تدرك PVS-Studio هذا جيدًا:
public override string ToString() { lock (loggerLock) { if (sbMessages != null && sbMessages.Length > 0) { return sbMessages.ToString(); } return null; } }
تحذير PVS-Studio: V3108 لا يوصى بإرجاع "خالية" من طريقة "ToSting ()". Logger.cs 167
لماذا مناسبة إذا لم تستخدم؟ public SeafileCheckAccInfoResponse GetAccountInfo() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "account/info/?format=json"); .... }
تحذير PVS-Studio: V3008 يتم تعيين قيم "url" للمتغير مرتين على التوالي. ربما هذا خطأ. خطوط الفحص: 197 ، 196. Seafile.cs 197
كما ترى من المثال ، عند الإعلان عن متغير
url ، يتم تعيين بعض القيمة التي يتم إرجاعها من أسلوب
FixPrefix . في السطر التالي ، "نطحن" القيمة الناتجة ، حتى بدون استخدامها في أي مكان. نحصل على شيء مشابه لـ "الكود الميت" - إنه يقوم بالعمل ، ولا يؤثر على النتيجة النهائية. غالبًا ما يكون هذا الخطأ ناتجًا عن نسخ اللصق ، حيث توجد أجزاء التعليمات البرمجية هذه في 9 طرق أخرى.
على سبيل المثال ، نعطي طريقتين بخط أول مشابه:
public bool CheckAuthToken() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "auth/ping/?format=json"); .... } .... public bool CheckAPIURL() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "ping/?format=json"); .... }
في المجموع
كما نرى ، لا يعتمد تعقيد إعداد الفحص التلقائي بواسطة المحلل على نظام CI المحدد - في 15 دقيقة فقط وبضع نقرات من الفأرة ، قمنا بإعداد التحقق من رمز مشروعنا بواسطة محلل ثابت.
في الختام ، نقترح عليك
تنزيل وتجربة المحلل في مشاريعك.

إذا كنت ترغب في مشاركة هذا المقال مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Oleg Andreev، Ilya Gainulin.
PVS-Studio in the Clouds: Azure DevOps .