تحليل كود منصة CUBA باستخدام PVS-Studio


يمكن لمطوري 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 فارغة ، سينتهي البرنامج بتجاوز صفيف.

تحذير 3

V6009 قد تتلقى الدالة "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 .

تحذير 4

V6013 تتم مقارنة الكائنات "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 . سطر واحد في وقت سابق ، تم مقارنة المجموعات بالرجوع إليها ، ولكن ربما قصد المبرمج أن تكون مقارنة بالقيمة.

تحذير 5

V6013 تتم مقارنة الكائنات "القيمة" و "oldValue" بالرجوع إليها. ربما كان المقصود مقارنة المساواة. WebOptionsList.java (278)

 protected boolean isCollectionValuesChanged(Collection<I> value, Collection<I> oldValue) { return value != oldValue; } 

هذه الحالة مماثلة للحالة السابقة. تتم مقارنة المجموعات في دالة isCollectionValuesChanged ، وربما لا تكون المقارنة المرجعية هي المقصود هنا أيضًا.

الظروف الزائدة


تحذير 1

V6007 Expression 'mask.charAt (i + offset)! = PlaceHolder' صحيح دائمًا. DatePickerDocument.java (238)

 private String calculateFormattedString(int offset, String text) .... { .... if ((mask.charAt(i + offset) == placeHolder)) { // <= .... } else if ((mask.charAt(i + offset) != placeHolder) && // <= (Character.isDigit(text.charAt(i)))) { .... } .... } 

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

تعبير 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) { // <= return false; } else if (connector.getWidget() instanceof VDDHasDropHandler) { return false; } return true; } 

بعد مغادرة حلقة الاثناء ، لن تكون قيمة متغير الموصل مساوية للقيمة الخالية ، لذلك يمكن حذف الاختيار المتكرر.

تحذير مشبوه آخر من هذا النوع يحتاج إلى فحص:

  • تعبير 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(); // <= tx1.commit(); // <= } catch (Exception e) { // } finally { tx1.end(); } tx.commit(); } finally { tx.end(); } } 

تقوم دالة 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 ، وكلاهما يتم الكتابة قبل الاستخدام.

تحذير 3

V6022 لا يتم استخدام "اختصار" المعلمة داخل جسم المنشئ. أعلان تراكينج أكشن. جافا (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)

وظائف مختلفة ، نفس الرمز


تحذير 1

V6032 من الغريب أن يكون نص الأسلوب "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.

تحذير 2

V6032 من الغريب أن يكون نص الأسلوب مكافئًا تمامًا لجسم طريقة أخرى. 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 فارغة


تحذير 1

V6060 تم استخدام مرجع '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 ربما يكون زائداً وربما خطير. أعتقد أنه ينتج عن إعادة بيع سيئة.

تحذير 2

V6060 تم استخدام مرجع 'tableModel' قبل التحقق من أنه لاغ. DesktopAbstractTable.java (1580) ، DesktopAbstractTable.java (1564)

 protected Column addRuntimeGeneratedColumn(String columnId) { // store old cell editors / renderers TableCellEditor[] cellEditors = new TableCellEditor[tableModel.getColumnCount() + 1]; // <= TableCellRenderer[] cellRenderers = new TableCellRenderer[tableModel.getColumnCount() + 1]; // <= for (int i = 0; i < tableModel.getColumnCount(); i++) { // <= Column tableModelColumn = tableModel.getColumn(i); if (tableModel.isGeneratedColumn(tableModelColumn)) { // <= TableColumn tableColumn = getColumn(tableModelColumn); cellEditors[i] = tableColumn.getCellEditor(); cellRenderers[i] = tableColumn.getCellRenderer(); } } Column col = new Column(columnId, columnId); col.setEditable(false); columns.put(col.getId(), col); if (tableModel != null) { // <= tableModel.addColumn(col); } .... } 

هذه الحالة مماثلة للحالة السابقة. بحلول الوقت الذي يتم فيه التحقق من كائن tableModel لاغياً ، تم الوصول إليه بالفعل عدة مرات.

مثال آخر:

  • V6060 تم استخدام مرجع 'tableModel' قبل التحقق من أنه لاغ. DesktopAbstractTable.java (596) ، DesktopAbstractTable.java (579)

ربما خطأ منطقي


V6026 تم تعيين هذه القيمة بالفعل للمتغير "sortAscending". CubaScrollTableWidget.java (488)

 @Override protected void sortColumn() { .... if (sortAscending) { if (sortClickCounter < 2) { // special case for initial revert sorting instead of reset sort order if (sortClickCounter == 0) { client.updateVariable(paintableId, "sortascending", false, false); } else { reloadDataFromServer = false; sortClickCounter = 0; sortColumn = null; sortAscending = true; // <= client.updateVariable(paintableId, "resetsortorder", "", true); } } else { client.updateVariable(paintableId, "sortascending", false, false); } } else { if (sortClickCounter < 2) { // special case for initial revert sorting instead of reset sort order if (sortClickCounter == 0) { client.updateVariable(paintableId, "sortascending", true, false); } else { reloadDataFromServer = false; sortClickCounter = 0; sortColumn = null; sortAscending = true; client.updateVariable(paintableId, "resetsortorder", "", true); } } else { reloadDataFromServer = false; sortClickCounter = 0; sortColumn = null; sortAscending = true; client.updateVariable(paintableId, "resetsortorder", "", true); } } .... } 

في الشرط الأول ، تم بالفعل تعيين الفرز المتغير sortAscending على القيمة true ، ولكن لا يزال يتم تعيين نفس القيمة مرة أخرى لاحقًا. يجب أن يكون هذا خطأ ، وربما يعني المؤلف أن القيمة خاطئة .

مثال مشابه من ملف مختلف:

  • V6026 تم تعيين هذه القيمة بالفعل للمتغير "sortAscending". CubaTreeTableWidget.java (444)

قيم عودة غريبة


تحذير 1

V6037 "عودة" غير مشروطة داخل حلقة. 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 .

تحذير 2

V6014 من الغريب أن هذه الطريقة تُرجع دائمًا نفس القيمة. 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 الذي تكون قيمته دائما غير صحيحة . ربما تكون بعض التعليمات البرمجية لتغيير هذه القيمة مفقودة من أحد الشروط.

تحذير 4

V6062 العودية المحتملة لانهائية داخل طريقة 'isFocused'. GwtAceEditor.java (189) ، GwtAceEditor.java (190)

 public final native void focus() /*-{ this.focus(); }-*/; public final boolean isFocused() { return this.isFocused(); } 

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

متنوع


تحذير 1

V6002 لا يغطي بيان التبديل جميع قيم التعداد "التشغيل": ADD. DesktopAbstractTable.java (665)

 /** * Operation which caused the datasource change. */ 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 . إنها القيمة الوحيدة التي لا يتم التحقق منها ، لذلك يجب على المطورين إلقاء نظرة على هذا الرمز.

تحذير 2

V6021 "المصدر" المتغير غير مستخدم. 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(); // <= int idx = (details).getOverIndex(); HorizontalDropLocation loc = (details).getDropLocation(); if (loc == HorizontalDropLocation.CENTER || loc == HorizontalDropLocation.RIGHT) { idx++; } Component comp = resolveComponentFromHTML5Drop(event); if (idx >= 0) { layout.addComponent(comp, idx); } else { layout.addComponent(comp); } if (dropAlignment != null) { layout.setComponentAlignment(comp, dropAlignment); } } 

يتم تعريف المصدر المتغير ولكن غير مستخدم. ربما نسي المؤلفون إضافة مصدر إلى التصميم ، تمامًا كما حدث مع متغير آخر من هذا النوع ، 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 أن يلاحظ أي عبارة "يبدو أننا يجب أن نكتب بعض التشخيصات الإضافية بأنفسنا" ونستغني عن هذه الصورة :)

الصورة 3

استنتاج


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

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


All Articles