يمكن لمطوري Java الوصول إلى عدد من الأدوات المفيدة التي تساعد على كتابة التعليمات البرمجية عالية الجودة مثل IDE IntelliJ IDEA القوي ، والمحللات المجانية SpotBugs ، PMD ، وما شابه ذلك. يستخدم المطورون الذين يعملون على CUBA Platform بالفعل كل هذه الأشياء ، وسيوضح هذا الاستعراض كيف يمكن للمشروع الاستفادة بشكل أكبر من استخدام محلل الكود الثابت PVS-Studio.
بضع كلمات عن المشروع والمحلل
يعد CUBA Platform إطارًا عالي المستوى لتطوير تطبيقات المؤسسات. يستخلص النظام الأساسي المطورين من التقنيات الأساسية حتى يتمكنوا من التركيز على مهام العمل ، مع الحفاظ على المرونة الكاملة من خلال توفير وصول غير مقيد إلى رمز منخفض المستوى. تم تنزيل الكود المصدري من
جيثب .
يعد PVS-Studio أداة للكشف عن الأخطاء ونقاط الضعف الأمنية المحتملة في التعليمات البرمجية المصدر للبرامج المكتوبة بلغات C و C ++ و C # و Java. يعمل المحلل على أنظمة تشغيل Windows و Linux و macOS 64 بت. لتسهيل الأمور على مبرمجي Java ، قمنا بتطوير مكونات إضافية لـ Maven و Gradle و IntelliJ IDEA. راجعت المشروع باستخدام البرنامج المساعد Gradle ، وانفجرت دون عقبة.
أخطاء في الظروف
تحذير 1تعبير
V6007 'StringUtils.isNotEmpty ("handleTabKey")' صحيح دائمًا. SourceCodeEditorLoader.java (60)
@Override public void loadComponent() { .... String handleTabKey = element.attributeValue("handleTabKey"); if (StringUtils.isNotEmpty("handleTabKey")) { resultComponent.setHandleTabKey(Boolean.parseBoolean(handleTabKey)); } .... }
لم يتم تحديد قيمة السمة المستخرجة من العنصر. بدلاً من ذلك ، تحصل الدالة
isNotEmpty على سلسلة حرفية كوسيطة لها بدلاً من المتغير
handleTabKey .
يوجد خطأ مشابه موجود في ملف AbstractTableLoader.java:
- تعبير V6007 'StringUtils.isNotEmpty ("editable")' صحيح دائمًا. AbstractTableLoader.java (596)
تحذير 2تعبير
V6007 'previousMenuItemFlatIndex> = 0' صحيح دائمًا. CubaSideMenuWidget.java (328)
protected MenuItemWidget findNextMenuItem(MenuItemWidget currentItem) { List<MenuTreeNode> menuTree = buildVisibleTree(this); List<MenuItemWidget> menuItemWidgets = menuTreeToList(menuTree); int menuItemFlatIndex = menuItemWidgets.indexOf(currentItem); int previousMenuItemFlatIndex = menuItemFlatIndex + 1; if (previousMenuItemFlatIndex >= 0) { return menuItemWidgets.get(previousMenuItemFlatIndex); } return null; }
ترجع الدالة
indexOf -1 إذا لم يتم العثور على العنصر في القائمة. ثم تضاف القيمة
1 إلى الفهرس ، الذي يخفي المشكلة مع العنصر الغائب. هناك مشكلة محتملة أخرى لها علاقة بحقيقة أن المتغير
السابق MenuItemFlatIndex سيكون دائمًا أكبر من أو يساوي الصفر. على سبيل المثال ، إذا تم العثور على قائمة
menuItemWidgets فارغة ، سينتهي البرنامج بتجاوز صفيف.
تحذير 3V6009 قد تتلقى الدالة "delete" القيمة "-1" بينما من المتوقع أن تكون القيمة غير السالبة. تفقد الوسيطة: 1. AbstractCollectionDatasource.java (556)
protected DataLoadContextQuery createDataQuery(....) { .... StringBuilder orderBy = new StringBuilder(); .... if (orderBy.length() > 0) { orderBy.delete(orderBy.length() - 2, orderBy.length()); orderBy.insert(0, " order by "); } .... }
يتم حذف آخر حرفين من المخزن المؤقت
orderBy إذا كان العدد الإجمالي للعناصر أكبر من الصفر ، أي إذا كانت السلسلة تحتوي على حرف واحد على الأقل. ومع ذلك ، يتم تعويض موضع البدء من حيث يبدأ الحذف بمقدار 2. لذلك ، إذا حدث أن
orderBy يحتوي على حرف واحد ، فإن محاولة حذفه سترفع
StringIndexOutOfBoundsException .
تحذير 4V6013 تتم مقارنة الكائنات "masterCollection" و "الكيانات" بالرجوع إليها. ربما كان المقصود مقارنة المساواة. CollectionPropertyContainerImpl.java (81)
@Override public void setItems(@Nullable Collection<E> entities) { super.setItems(entities); Entity masterItem = master.getItemOrNull(); if (masterItem != null) { MetaProperty masterProperty = getMasterProperty(); Collection masterCollection = masterItem.getValue(masterProperty.getName()); if (masterCollection != entities) { updateMasterCollection(masterProperty, masterCollection, entities); } } }
في وظيفة
updateMasterCollection ، يتم نسخ القيم من
الكيانات إلى
masterCollection . سطر واحد في وقت سابق ، تم مقارنة المجموعات بالرجوع إليها ، ولكن ربما قصد المبرمج أن تكون مقارنة بالقيمة.
تحذير 5V6013 تتم مقارنة الكائنات "القيمة" و "oldValue" بالرجوع إليها. ربما كان المقصود مقارنة المساواة. WebOptionsList.java (278)
protected boolean isCollectionValuesChanged(Collection<I> value, Collection<I> oldValue) { return value != oldValue; }
هذه الحالة مماثلة للحالة السابقة. تتم مقارنة المجموعات في دالة
isCollectionValuesChanged ، وربما لا تكون المقارنة المرجعية هي المقصود هنا أيضًا.
الظروف الزائدة
تحذير 1V6007 Expression 'mask.charAt (i + offset)! = PlaceHolder' صحيح دائمًا. DatePickerDocument.java (238)
private String calculateFormattedString(int offset, String text) .... { .... if ((mask.charAt(i + offset) == placeHolder)) {
يتحقق الشرط الثاني من التعبير المقابل للتعبير المحدد في الشرط الأول. وبالتالي ، يمكن إزالة هذا الأخير بأمان لتقصير الرمز.
تعبير
V6007 'connector == null' خاطئ دائمًا. HTML5Support.java (169)
private boolean validate(NativeEvent event) { .... while (connector == null) { widget = widget.getParent(); connector = Util.findConnectorFor(widget); } if (this.connector == connector) { return true; } else if (connector == null) {
بعد مغادرة حلقة الاثناء ، لن تكون قيمة متغير
الموصل مساوية للقيمة
الخالية ، لذلك يمكن حذف الاختيار المتكرر.
تحذير مشبوه آخر من هذا النوع يحتاج إلى فحص:
- تعبير V6007 'StringUtils.isBlank (strValue)' صحيح دائمًا. بارام.جافا (818)
رمز غير قابل للوصول في الاختبارات
V6019 تم اكتشاف كود غير
قابل للوصول. من الممكن وجود خطأ. TransactionTest.java (283)
private void throwException() { throw new RuntimeException(TEST_EXCEPTION_MSG); } @Test public void testSuspendRollback() { Transaction tx = cont.persistence().createTransaction(); try { .... Transaction tx1 = cont.persistence().createTransaction(); try { EntityManager em1 = cont.persistence().getEntityManager(); assertTrue(em != em1); Server server1 = em1.find(Server.class, server.getId()); assertNull(server1); throwException();
تقوم دالة
thrException بطرح استثناء يمنع تنفيذ استدعاء
tx1.com . يجب تبديل هذين الخطين حتى يعمل الرمز بشكل صحيح.
كانت هناك بعض المشاكل المشابهة في اختبارات أخرى أيضًا:
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. TransactionTest.java (218)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. TransactionTest.java (163)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. TransactionTest.java (203)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. TransactionTest.java (137)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. UpdateDetachedTest.java (153)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. EclipseLinkDetachedTest.java (132)
- V6019 تم اكتشاف كود غير قابل للوصول. من الممكن وجود خطأ. PersistenceTest.java (223)
الحجج المشبوهة
تحذير 1يتم إعادة كتابة المعلمة
V6023 "ملح" دائمًا في
هيكل الطريقة قبل استخدامها. BCryptEncryptionModule.java (47)
@Override public String getHash(String content, String salt) { salt = BCrypt.gensalt(); return BCrypt.hashpw(content, salt); }
في التشفير ،
الملح عبارة عن سلسلة بيانات تقوم بتمريرها مع كلمة المرور إلى دالة التجزئة. يستخدم بشكل رئيسي لحماية البرنامج من هجمات القاموس وهجمات جدول قوس قزح ، وكذلك لإخفاء كلمات المرور المتطابقة. المزيد هنا:
الملح (التشفير) .
في هذه الوظيفة ، يتم الكتابة على السلسلة التي تم تمريرها مباشرة بعد الدخول. تجاهل القيمة التي تم تمريرها إلى الوظيفة مشكلة عدم حصانة محتملة.
تحذير 2تسببت هذه الوظيفة في تحذيرين في آن واحد:
- تتم إعادة كتابة المعلمة V6023 "offsetWidth" دائمًا في نص الطريقة قبل استخدامها. CubaSuggestionFieldWidget.java (433)
- تتم إعادة كتابة المعلمة V6023 "offsetHeight" دائمًا في نص الطريقة قبل استخدامها. CubaSuggestionFieldWidget.java (433)
@Override public void setPosition(int offsetWidth, int offsetHeight) { offsetHeight = getOffsetHeight(); .... if (offsetHeight + getPopupTop() > ....)) { .... } .... offsetWidth = containerFirstChild.getOffsetWidth(); if (offsetWidth + getPopupLeft() > ....)) { .... } else { left = getPopupLeft(); } setPopupPosition(left, top); }
هذا مقتطف فضولي. يتم استدعاء الدالة مع اثنين فقط من المتغيرات
كوسائط ،
offsetWidth و
offsetHeight ، وكلاهما يتم الكتابة قبل الاستخدام.
تحذير 3V6022 لا يتم استخدام "اختصار" المعلمة داخل جسم المنشئ. أعلان تراكينج أكشن. جافا (47)
public DeclarativeTrackingAction(String id, String caption, String description, String icon, String enable, String visible, String methodName, @Nullable String shortcut, ActionsHolder holder) { super(id); this.caption = caption; this.description = description; this.icon = icon; setEnabled(enable == null || Boolean.parseBoolean(enable)); setVisible(visible == null || Boolean.parseBoolean(visible)); this.methodName = methodName; checkActionsHolder(holder); }
لا تستخدم الدالة القيمة التي تم تمريرها كمعلمة
اختصار . ربما أصبحت واجهة الوظيفة قديمة أو أن هذا التحذير هو مجرد خطأ إيجابي.
بعض العيوب الأخرى من هذا النوع:
- V6022 لا يتم استخدام المعلمة "type" داخل هيكل المنشئ. QueryNode.java (36)
- V6022 لا يتم استخدام المعلمة 'text2' داخل هيكل المنشئ. MarkerAddition.java (22)
- لا يتم استخدام "اختيار" المعلمة V6022 داخل جسم المنشئ. AceEditor.java (114)
- لا يتم استخدام "خيارات" المعلمة V6022 داخل جسم المنشئ. EntitySerialization.java (379)
وظائف مختلفة ، نفس الرمز
تحذير 1V6032 من الغريب أن يكون نص الأسلوب "firstItemId" مكافئًا تمامًا لنص طريقة أخرى "lastItemId". ContainerTableItems.java (213) ، ContainerTableItems.java (219)
@Override public Object firstItemId() { List<E> items = container.getItems(); return items.isEmpty() ? null : items.get(0).getId(); } @Override public Object lastItemId() { List<E> items = container.getItems(); return items.isEmpty() ? null : items.get(0).getId(); }
الدالتان firstItemId و
lastItemId لهاما نفس التطبيقات. ربما كان المقصود بالأخير هو الحصول على فهرس العنصر الأخير بدلاً من الحصول على العنصر في الفهرس 0.
تحذير 2V6032 من الغريب أن يكون نص الأسلوب مكافئًا تمامًا لجسم طريقة أخرى. SearchComboBoxPainter.java (495) ، SearchComboBoxPainter.java (501)
private void paintBackgroundDisabledAndEditable(Graphics2D g) { rect = decodeRect1(); g.setPaint(color53); g.fill(rect); } private void paintBackgroundEnabledAndEditable(Graphics2D g) { rect = decodeRect1(); g.setPaint(color53); g.fill(rect); }
وظيفتان أخريان بهيئات متطابقة بشكل مثير للريبة. أعتقد أن أحدهم كان يهدف إلى العمل مع بعض الألوان الأخرى بدلاً من
color53 .
dereference فارغة
تحذير 1V6060 تم استخدام مرجع 'descriptionPopup' قبل أن يتم التحقق منه ضد قيمة خالية. SuggestPopup.java (252) ، SuggestPopup.java (251)
protected void updateDescriptionPopupPosition() { int x = getAbsoluteLeft() + WIDTH; int y = getAbsoluteTop(); descriptionPopup.setPopupPosition(x, y); if (descriptionPopup!=null) { descriptionPopup.setPopupPosition(x, y); } }
في سطرين فقط ، تمكن المبرمج من كتابة شفرة مشبوهة للغاية. أولاً ، يتم
استدعاء الأسلوب
setPopupPosition الخاص بالكائن
descriptionPopup ، ثم يتم التحقق من أن الكائن
لاغٍ . الاستدعاء الأول ل
setPopupPosition ربما يكون زائداً وربما خطير. أعتقد أنه ينتج عن إعادة بيع سيئة.
تحذير 2V6060 تم استخدام مرجع 'tableModel' قبل التحقق من أنه لاغ. DesktopAbstractTable.java (1580) ، DesktopAbstractTable.java (1564)
protected Column addRuntimeGeneratedColumn(String columnId) {
هذه الحالة مماثلة للحالة السابقة. بحلول الوقت الذي يتم فيه التحقق من كائن
tableModel لاغياً ، تم الوصول إليه بالفعل عدة مرات.
مثال آخر:
- V6060 تم استخدام مرجع 'tableModel' قبل التحقق من أنه لاغ. DesktopAbstractTable.java (596) ، DesktopAbstractTable.java (579)
ربما خطأ منطقي
V6026 تم تعيين هذه القيمة بالفعل للمتغير "sortAscending". CubaScrollTableWidget.java (488)
@Override protected void sortColumn() { .... if (sortAscending) { if (sortClickCounter < 2) {
في الشرط الأول ، تم بالفعل تعيين
الفرز المتغير
sortAscending على القيمة
true ، ولكن لا يزال يتم تعيين نفس القيمة مرة أخرى لاحقًا. يجب أن يكون هذا خطأ ، وربما يعني المؤلف أن القيمة
خاطئة .
مثال مشابه من ملف مختلف:
- V6026 تم تعيين هذه القيمة بالفعل للمتغير "sortAscending". CubaTreeTableWidget.java (444)
قيم عودة غريبة
تحذير 1V6037 "عودة" غير مشروطة داخل حلقة. QueryCacheManager.java (128)
public <T> T getSingleResultFromCache(QueryKey queryKey, List<View> views) { .... for (Object id : queryResult.getResult()) { return (T) em.find(metaClass.getJavaClass(), id, views.toArray(....)); } .... }
اكتشف المحلل دعوة غير مشروطة
للعودة عند التكرار الأول للحلقة. إما أن السطر غير صحيح أو يجب إعادة كتابة الحلقة كعبارة
if .
تحذير 2V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. DefaultExceptionHandler.java (40)
@Override public boolean handle(ErrorEvent event, App app) { Throwable t = event.getThrowable(); if (t instanceof SocketException || ExceptionUtils.getRootCause(t) instanceof SocketException) { return true; } if (ExceptionUtils.getThrowableList(t).stream() .anyMatch(o -> o.getClass().getName().equals("...."))) { return true; } if (StringUtils.contains(ExceptionUtils.getMessage(t), "....")) { return true; } AppUI ui = AppUI.getCurrent(); if (ui == null) { return true; } if (t != null) { if (app.getConnection().getSession() != null) { showDialog(app, t); } else { showNotification(app, t); } } return true; }
ترجع هذه الوظيفة إلى
حقيقة في كل حالة ، في حين أن السطر الأخير يستدعي
خطأً . يبدو وكأنه خطأ.
فيما يلي قائمة كاملة بالوظائف المشبوهة الأخرى:
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. ErrorNodesFinder.java (31)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. FileDownloadController.java (69)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. IdVarSelector.java (73)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. IdVarSelector.java (48)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. IdVarSelector.java (67)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. IdVarSelector.java (46)
- V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. JoinVariableNode.java (57)
تحذير 3تعبير
V6007 "needReload" خطأ دائمًا. WebAbstractTable.java (2702)
protected boolean handleSpecificVariables(Map<String, Object> variables) { boolean needReload = false; if (isUsePresentations() && presentations != null) { Presentations p = getPresentations(); if (p.getCurrent() != null && p.isAutoSave(p.getCurrent()) && needUpdatePresentation(variables)) { Element e = p.getSettings(p.getCurrent()); saveSettings(e); p.setSettings(p.getCurrent(), e); } } return needReload; }
تقوم الدالة
بارجاع متغير
needReload الذي تكون قيمته دائما غير
صحيحة . ربما تكون بعض التعليمات البرمجية لتغيير هذه القيمة مفقودة من أحد الشروط.
تحذير 4V6062 العودية المحتملة لانهائية داخل طريقة 'isFocused'. GwtAceEditor.java (189) ، GwtAceEditor.java (190)
public final native void focus() ; public final boolean isFocused() { return this.isFocused(); }
اكتشف المحلل وظيفة تكرارية بدون أي شرط توقف. يحتوي هذا الملف على الكثير من الوظائف المميزة بالكلمة الرئيسية
الأصلية والتي تحتوي على كود تعليق. ربما يقوم المطورون بإعادة كتابة هذا الملف الآن وسوف يلاحظون قريبًا الوظيفة
isFocused أيضًا.
متنوع
تحذير 1V6002 لا يغطي بيان التبديل جميع قيم التعداد "التشغيل": ADD. DesktopAbstractTable.java (665)
enum Operation { REFRESH, CLEAR, ADD, REMOVE, UPDATE } @Override public void setDatasource(final CollectionDatasource datasource) { .... collectionChangeListener = e -> { switch (e.getOperation()) { case CLEAR: case REFRESH: fieldDatasources.clear(); break; case UPDATE: case REMOVE: for (Object entity : e.getItems()) { fieldDatasources.remove(entity); } break; } }; .... }
بيان
التبديل لا يوجد لديه حالة للقيمة
ADD . إنها القيمة الوحيدة التي لا يتم التحقق منها ، لذلك يجب على المطورين إلقاء نظرة على هذا الرمز.
تحذير 2V6021 "المصدر" المتغير غير مستخدم. DefaultHorizontalLayoutDropHandler.java (177)
@Override protected void handleHTML5Drop(DragAndDropEvent event) { LayoutBoundTransferable transferable = (LayoutBoundTransferable) event .getTransferable(); HorizontalLayoutTargetDetails details = (HorizontalLayoutTargetDetails) event .getTargetDetails(); AbstractOrderedLayout layout = (AbstractOrderedLayout) details .getTarget(); Component source = event.getTransferable().getSourceComponent();
يتم تعريف
المصدر المتغير ولكن غير مستخدم. ربما نسي المؤلفون إضافة
مصدر إلى
التصميم ، تمامًا كما حدث مع متغير آخر من هذا النوع ،
comp .
وظائف أخرى مع المتغيرات غير المستخدمة:
- V6021 "المصدر" المتغير غير مستخدم. DefaultHorizontalLayoutDropHandler.java (175)
- V6021 يتم تخصيص القيمة للمتغير 'r' لكن لا يتم استخدامها. ExcelExporter.java (262)
- V6021 متغير "انتهى" لا يستخدم. DefaultCssLayoutDropHandler.java (49)
- V6021 المتغير "القابل للتحويل" لا يستخدم. DefaultHorizontalLayoutDropHandler.java (171)
- V6021 المتغير "القابل للتحويل" لا يستخدم. DefaultHorizontalLayoutDropHandler.java (169)
- V6021 المتغير 'beanLocator' لا يستخدم. ScreenEventMixin.java (28)
تحذير 3لا ينبغي مقارنة الفصول الدراسية
V6054 باسمها. MessageTools.java (283)
public boolean hasPropertyCaption(MetaProperty property) { Class<?> declaringClass = property.getDeclaringClass(); if (declaringClass == null) return false; String caption = getPropertyCaption(property); int i = caption.indexOf('.'); if (i > 0 && declaringClass.getSimpleName().equals(caption.substring(0, i))) return false; else return true; }
كشف المحلل عن مقارنة صفية بالاسم. من الخطأ مقارنة الفئات بالاسم ، وفقًا للمواصفات ، يجب أن تكون أسماء فئات JVM فريدة من نوعها فقط داخل الحزمة. تعطي هذه المقارنة نتائج غير صحيحة وتؤدي إلى تنفيذ التعليمات البرمجية غير الصحيحة.
تعليق من قبل مطوري منصة CUBA
أي مشروع كبير بالتأكيد يحتوي على أخطاء فيه. مع العلم أنه تم الاتفاق علينا بكل سرور عندما عرض فريق PVS-Studio فحص مشروعنا. يحتوي مستودع CUBA على شوكات لبعض مكتبات OSS التابعة لجهات أخرى المرخصة بموجب Apache 2 ، ويبدو أننا يجب أن نولي المزيد من الاهتمام لتلك الشفرة لأن المحلل وجد عددًا كبيرًا من المشكلات في تلك المصادر. نستخدم حاليًا SpotBugs كمحلل أساسي لدينا ، ويفشل في ملاحظة بعض الأخطاء الكبيرة التي أبلغ عنها PVS-Studio. يبدو أننا يجب أن نكتب بعض التشخيصات الإضافية بأنفسنا. شكرا جزيلا لفريق PVS-Studio لهذا المنصب.أخبرنا المطورين أيضًا أن التحذيرات V6013 و V6054 كانت إيجابية. كان قرارهم الواعي لكتابة هذا الرمز بالطريقة التي فعلوا بها. تم تصميم المحلل للكشف عن أجزاء التعليمات البرمجية المشبوهة ، واحتمال العثور على أخطاء حقيقية يختلف عبر التشخيصات المختلفة. ومع ذلك ، يمكن التعامل بسهولة مع هذه التحذيرات باستخدام آلية
منع التحذير الشامل دون الحاجة إلى تعديل الملفات المصدر.
أيضًا ، لا يمكن لفريق PVS-Studio أن يلاحظ أي عبارة "يبدو أننا يجب أن نكتب بعض التشخيصات الإضافية بأنفسنا" ونستغني عن هذه الصورة :)
استنتاج
يمكن أن يكون PVS-Studio مكملاً مثالياً لأدوات مراقبة الجودة الحالية المستخدمة في عملية التطوير. هذا ينطبق بشكل خاص على الشركات التي لديها العشرات أو المئات أو الآلاف من المطورين. تم تصميم PVS-Studio ليس فقط للكشف عن الأخطاء ولكن أيضًا لمساعدتك في إصلاحها ، وما أقصده ليس تحرير الشفرة تلقائيًا ولكن وسيلة موثوقة لمراقبة جودة الشفرة. في شركة كبيرة ، يستحيل على كل مطور التحقق من الأجزاء الخاصة به من الشفرة باستخدام أدوات مختلفة ، لذلك فإن الحل الأفضل لهذه الشركات هو اعتماد أدوات مثل PVS-Studio ، التي توفر مراقبة جودة الرمز في كل مرحلة تطوير ، وليس فقط على جانب مبرمج.