PVS-Studio in the Clouds: Azure DevOps

صورة 9

هذه مقالة ثانية ، تركز على استخدام محلل PVS-Studio في أنظمة CI السحابية. هذه المرة سننظر في النظام الأساسي Azure DevOps - وهو حل سحابة CI \ CD من Microsoft. سنقوم بتحليل مشروع ShareX.

سنحتاج ثلاثة مكونات. الأول هو محلل PVS-Studio. والثاني هو Azure DevOps ، والذي سنقوم بدمج المحلل به. والثالث هو المشروع الذي سنقوم بالتحقق منه لإظهار قدرات PVS-Studio عند العمل في السحابة. لذلك دعونا نذهب.

PVS-Studio هو محلل ثابت للكود للعثور على الأخطاء والعيوب الأمنية. تدعم الأداة تحليل الشفرة C و C ++ و C #.

أزور ديفوبس . تتضمن منصة Azure DevOps أدوات مثل Azure Pipeline ، و Azure Board ، و Azure Artifacts ، وغيرها من الأدوات التي تسرع من عملية إنشاء البرامج وتحسين جودتها.

ShareX هو تطبيق مجاني يتيح لك التقاط وتسجيل أي جزء من الشاشة. المشروع مكتوب بلغة C # وهو مناسب بشكل بارز لإظهار تكوين إطلاق محلل ثابت. رمز مصدر المشروع متاح على جيثب .

مخرجات الأمر cloc لمشروع ShareX:
لغة
ملفات
فراغ
تعليق
قانون
C #
696
20658
24423
102565
برنامج MSBuild النصي
11
1
77
5859
وبعبارة أخرى ، فإن المشروع صغير ، لكنه يكفي لإظهار عمل PVS-Studio مع النظام الأساسي السحابي.

لنبدأ التكوين


لبدء العمل في Azure DevOps ، دعونا نتبع الرابط واضغط على "بدء مجانًا مع GitHub".

الصورة 2

منح تطبيق Microsoft حق الوصول إلى بيانات حساب GitHub.

الصورة 1

يجب عليك إنشاء حساب Microsoft لإكمال التسجيل.

صورة 12

بعد التسجيل ، قم بإنشاء مشروع:

الصورة 5

بعد ذلك ، نحتاج إلى الانتقال إلى "Pipelines" - "Builds" وإنشاء خط أنابيب Build جديد.

الصورة 8

عندما يُطلب منك تحديد مكان رمزنا ، سنجيب - GitHub.

صورة 13

قم بتخويل خطوط أنابيب Azure واختر المستودع بالمشروع ، والذي سنقوم بتكوين تشغيل المحلل الثابت عليه.

الصورة 7

في نافذة تحديد القالب ، اختر "خط أنابيب البدء".

صورة 17

يمكننا تشغيل تحليل الشفرة الثابتة للمشروع بطريقتين: استخدام الوكلاء المستضافة من قبل Microsoft أو المستضافين ذاتيًا.

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

دعنا نستبدل التكوين الافتراضي المقترح للتكوين التالي لاستخدام العوامل التي تستضيفها Microsoft:

# Setting up run triggers # Run only for changes in the master branch trigger: - master # Since the installation of random software in virtual machines # is prohibited, we'll use a Docker container, # launched on a virtual machine with Windows Server 1803 pool: vmImage: 'win1803' container: microsoft/dotnet-framework:4.7.2-sdk-windowsservercore-1803 steps: # Download the analyzer distribution - 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: | # Restore the project and download dependencies nuget restore .\ShareX.sln # Create the directory, where files with analyzer reports will be saved md .\PVSTestResults # Install the analyzer PVS-Studio_setup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /COMPONENTS=Core # Create the file with configuration and license information "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials -u $(PVS_USERNAME) -n $(PVS_KEY) # Run the static analyzer and convert the report in 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 # Save analyzer reports - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults 

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

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

صورة 14

بعد ذلك ، أضف متغيرين - PVS_USERNAME و PVS_KEY ، يحتويان على اسم المستخدم ومفتاح الترخيص على التوالي. عند إنشاء متغير PVS_KEY ، لا تنس تحديد "الحفاظ على سرية هذه القيمة" لتشفير قيم المتغير باستخدام مفتاح RSA 2048 بت ولقمع إخراج القيمة المتغيرة في سجل أداء المهمة.

صورة 15

احفظ المتغيرات وقم بتشغيل خط الأنابيب بالنقر فوق "تشغيل".

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

قبل استخدام مثل هذه العوامل ، يجب عليك تكوينها وفقًا للتعليمات وتثبيت وتكوين محلل ثابت.

لتشغيل المهمة على وكيل مستضاف ذاتيًا ، سنستبدل التكوين المقترح بما يلي:

 # Setting up triggers # Run the analysis for master-branch trigger: - master # The task is run on a self-hosted agent from the pool 'MyPool' pool: 'MyPool' steps: - task: CmdLine@2 inputs: workingDirectory: $(System.DefaultWorkingDirectory) script: | # Restore the project and download dependencies nuget restore .\ShareX.sln # Create the directory where files with analyzer reports will be saved md .\PVSTestResults # Run the static analyzer and convert the report in 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 # Save analyzer reports - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults 

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

صورة 21

نتائج التحليل


الآن دعونا نلقي نظرة على بعض الأخطاء الموجودة في المشروع الذي تم اختباره ، 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 أنه ليس صحيحًا بعد إنشاء نسخة جديدة من فئة الصورة النقطية . الفرق من المثال السابق هنا هو أننا نستخدم أسلوب GetDIBImage بدلاً من المُنشئ لتهيئة متغير img . يقترح مؤلف الكود أنه قد يحدث استثناء في هذه الطريقة ، لكنه يعلن أن الكتل فقط هي المحاولة وأخيراً حذف التجميع. لذلك ، في حالة حدوث استثناء ، لن يحصل أسلوب المتصل GetImageAlternative على مرجع إلى كائن من نوع الصورة النقطية ، ولكن سيتعين عليه معالجة الاستثناء في كتلة catch الخاصة به. في هذه الحالة ، لن يتم تهيئة متغير img ولن يصل مؤشر ترابط التنفيذ إلى img! = Null check ولكنه سيحصل على كتلة 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 أكبر من الصفر. سيكون كل شيء على ما يرام ، ولكن في الخارج إذا تم تحديد عدد العبارة بالفعل 0. لا يمكن أن يحتوي مثيل مجموعة SelectedItems على عدد العناصر التي تقل عن الصفر ، لذلك ، يكون العدد إما مساويًا أو أكبر من 0. نظرًا لأننا لقد تم بالفعل إجراء اختبار Count لعدد 0 في أول عبارة if وكان خطأ ، فلا فائدة من كتابة اختبار Count آخر ليكون أكبر من الصفر في الفرع الآخر.

سيكون المثال الأخير لتحذير 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

لاحظ المحلل أن العنصر itemCount> 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 من العثور عليها.

تستقبل هذه الطريقة معلمتين: كائن من نوع الصورة النقطية وقيمة int int التي تشير إلى حجم البيكسل. خوارزمية العملية بسيطة للغاية:

1) قم بتقسيم جزء الصورة المستلمة إلى مربعات بحيث يكون الجانب مساوياً لحجم البكسل. على سبيل المثال ، إذا كان لدينا حجم البيكسل يساوي 15 ، فسنحصل على مربع ، يحتوي على 15x15 = 225 بكسل.

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

 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) الآن عندما وصلنا إلى اللون النهائي وكتبناه في متغير averageColor ، يمكننا مرة أخرى اجتياز كل بكسل في المربع وتعيين قيمة له من averageColor .

5) عد إلى النقطة 2 بينما لدينا المربعات غير المعالجة.

مرة أخرى ، لا يساوي متغير weightedCount عدد جميع البيكسلات في المربع. على سبيل المثال ، إذا كانت الصورة تحتوي على بكسل شفاف تمامًا (قيمة صفرية في قناة ألفا) ، فإن المتغير pixelWeight سيكون صفراً للبكسل ( 0/255 = 0). لذلك ، لن يؤثر هذا البيكسل في تكوين المتغير المرجح . إنه منطقي تمامًا - لا يوجد أي نقطة يجب أخذها في الاعتبار ألوان بكسل شفافة تمامًا.

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

الصورة 3

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

 float pixelWeight = color.Alpha / 255; 

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

من السهل شرح السبب في أنه من المستحيل تقسيم صور png ببعض الشفافية. نظرًا لأن قيم البكسل لقناة ألفا هذه تقع في النطاق 0 <Alpha <255 ، فإن متغير Alpha مقسومًا على 255 سيؤدي دائمًا إلى 0. وبالتالي ، فإن قيم المتغيرات pixelWeight ، r ، g ، b ، a ، الموزونة Count أيضًا يكون دائمًا 0. ونتيجة لذلك ، سيكون متوسط ​​اللون لدينا قيمًا صفرية في جميع القنوات: أحمر - 0 ، أزرق - 0 ، أخضر - 0 ، ألفا - 0. من خلال رسم مربع في هذا اللون ، لا نغير اللون الأصلي من وحدات البكسل ، حيث يكون متوسط ​​اللون شفافًا تمامًا. لإصلاح هذا الخطأ ، نحتاج فقط إلى إرسال حقل ألفا بشكل صريح إلى نوع التعويم . قد يبدو الإصدار الثابت من سطر الشفرة كما يلي:

 float pixelWeight = (float)color.Alpha / 255; 

حسنًا ، لقد حان الوقت للاستشهاد برسالة PVS-Studio للرمز غير الصحيح:

تحذير PVS-Studio: V3041 [CWE-682] تم نقل التعبير ضمنيًا من نوع "int" إلى "تعويم". النظر في استخدام يلقي نوع واضح لتجنب فقدان جزء كسور. مثال: double A = (double) (X) / Y؛ ImageHelpers.cs 1119

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

صورة 6

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 خاليًا ، ولهذا السبب قبل استدعاء الأسلوب SetPropertyItem ، تحقق pi! = Null . من الغريب أنه قبل هذا التحقق ، يتم تعيين خاصية صفيف من البايتات ، لأنه إذا كانت 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; // <= } windows.Add(new WindowInfo(hWnd)); return true; // <= } } 

تحذير PVS-Studio: V3009 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة "صواب". WindowsList.cs 82

يبدو من المنطقي أنه في القائمة المسمى IgnoreWindows ، يوجد مؤشر يحمل نفس اسم hWnd ، ويجب أن تُرجع الطريقة خطأ .

يمكن ملء قائمة 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 لـ null ، يمكن إلغاء اشتراك الطريقة التي تعالج حدثًا ، على سبيل المثال ، في سلسلة رسائل أخرى. في هذه الحالة ، بحلول الوقت الذي ندخل فيه نص العبارة if ، سيكون NewsLoaded المتغير فارغًا بالفعل. قد يحدث NullReferenceException عند محاولة استدعاء المشتركين من الحدث NewsLoaded ، وهو أمر لاغٍ. يعد استخدام عامل null-conditional أكثر أمانًا وإعادة كتابة التعليمات البرمجية أعلاه كما يلي:

 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 دقيقة حرفيًا وعدة نقرات بالماوس لتهيئة التحقق من رمز مشروعنا باستخدام محلل ثابت.

في الختام ، ندعوك لتنزيل وتجربة المحلل في مشاريعك.

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


All Articles