Τα mods του Minecraft, η καταλανική γλώσσα και οι μη προφανείς αλληλεπιδράσεις με τον τρίτο φορέα – πόσα πράγματα έχει συναντήσει ο αναλυτής μας! Εισαγωγή Φέτος, ελέγξαμε πολλά έργα ανοιχτού κώδικα και γράψαμε σχετικά με αυτούς. άρθρα Τώρα, έχουμε επιλέξει δέκα από τα πιο περίεργα σφάλματα και παράξενα κομμάτια κώδικα. Η επιλογή και η κατάταξη είναι, φυσικά, υποκειμενική και βασίζεται στην αίσθηση της δικής μας δροσερότητας. Ήρθε η ώρα - Πάμε! 10η θέση: Δεξιά προς τα αριστερά, αριστερά προς τα δεξιά Ανοίγουμε την κορυφή μας με μια περίπτωση από . In a way, the project resonates with us: just like PVS-Studio, it analyzes languages—only natural ones, not artificial. ΓΛΩΣΣΑ Το αστείο είναι ότι αυτό το σφάλμα σχετίζεται με τον αναλυτή μας. Ωστόσο, το βρίσκουμε τόσο αστείο που απλά δεν μπορούμε να το παραλείψουμε. Δείτε το κομμάτι του κώδικα: public String getEnclitic(AnalyzedToken token) { .... if (word.endsWith("ه")) { suffix = "ه"; .... else if ((word.equals("عني") || word.equals("مني")) && word.endsWith("ني") // <= ) { suffix = "ني"; } .... } Το PVS-Studio προειδοποιεί: Η έκφραση «word.endsWith» είναι πάντα ψευδής. Β6007 ΑραβικάTagger.java 428 Αν κοιτάξουμε τις σταθερές γραμμών όπως κάνουν συνήθως οι μητρικοί ομιλητές των γλωσσών αριστερά-δεξιά (LTR), όπως τα αγγλικά ή τα γαλλικά, η προειδοποίηση του αναλυτή φαίνεται δίκαιη. -Αραβικοί ομιλητές αναμένουν το τέλος ενός κυριολεκτικού, θα δουν στην πραγματικότητα την αρχή του. Μιλάει αραβικά non can't Παρεμπιπτόντως, αν γνωρίζουμε ότι υπάρχει μια γραφή RTL, συνειδητοποιούμε ότι η κατάσταση είναι στην πραγματικότητα πάντα Όχι το ψεύτικο. true Αυτό μας έκανε περίεργους: πώς χειρίζεται η Java τις γλώσσες δεξιάς προς αριστεράς; Τα πάντα είναι εύκολα: η Java χειρίζεται το Unicode αντί για την απεικόνιση κειμένου. , δηλαδή, με τη σειρά με την οποία οι μητρικοί ομιλητές τα διαβάζουν και τα γράφουν. Αποθηκεύονται με λογική σειρά Αυτό σημαίνει ότι η Java δεν χρειάζεται να εκτελέσει ειδικές μετατροπές για να χειριστεί το κείμενο RTL.Η σειρά παραμένει μια κανονική ακολουθία σημείων κώδικα Unicode, και λειτουργίες όπως η απόκτηση του μήκους της, η πρόσβαση σε χαρακτήρες ή η εξαγωγή μιας υποσύνθετης εργασίας είναι η ίδια για όλες τις γλώσσες. Για έναν τέτοιο έλεγχο: System.out.println("مرحباً بالجميع".endsWith("بالجميع")); Το αποτέλεσμα θα είναι , αν και αν διαβάσουμε από αριστερά προς τα δεξιά, η ουρά αρχίζει, αντί να τελειώνει, με "بالجميع". true Ωστόσο, δεδομένου ότι ο αναλυτής είναι τεχνικά λάθος εδώ, το τοποθετούμε στην 10η θέση στην κατάταξή μας. Αν είστε περίεργοι να μάθετε περισσότερα, μπορείτε να διαβάσετε την πλήρη ιστορία του πώς αναλύσαμε το LanguageTool σε αυτό . Άρθρο 9η θέση: Καταλανική γλώσσα appears in our ranking once again! ΓΛΩΣΣΑ Η καταλανική γλώσσα έχει το μέθοδος, η οποία διορθώνει απαλά παρωχημένες μορφές ορθογραφίας και αφαιρεί περιττές διακριτικές. Για παράδειγμα, μετατρέπει το "adéu" σε "adieu", "dóna" σε "dona", "vénen" σε "venen" και ούτω καθεξής: removeOldDiacritics() private String removeOldDiacritics(String s) { return s .replace("contrapèl", "contrapel") .replace("Contrapèl", "Contrapel") .replace("vés", "ves") .replace("féu", "feu") .replace("desféu", "desfeu") .replace("adéu", "adeu") .replace("dóna", "dona") .replace("dónes", "dones") .replace("sóc", "soc") .replace("vénen", "venen") .replace("véns", "véns") // <= .replace("fóra", "fora") .replace("Vés", "Ves") .replace("Féu", "Feu") .replace("Desféu", "Desfeu") .replace("Adéu", "Adeu") .replace("Dóna", "Dona") .replace("Dónes", "Dones") .replace("Sóc", "Soc") .replace("Vénen", "Venen") .replace("Véns", "Vens") .replace("Fóra", "Fora"); } Με την πρώτη ματιά, φαίνεται εντάξει: λειτουργεί, διορθώνει και δεν ενοχλεί κανέναν. Ωστόσο, ο αναλυτής έχει ένα άλλο σημείο: Function 'replace' receives an odd argument. The '" véns " argument was passed several times. Β6009 Καταλανική.java 453 Το πρόβλημα είναι ότι η μέθοδος αντικαθιστά "véns" με "véns", με άλλα λόγια, τίποτα δεν αντικαθίσταται καθόλου. πιθανότατα, όταν οι προγραμματιστές εργάστηκαν με αυτό το μπλοκ, απλά αντιγράφησαν την αρχική λέξη και αντικατέστησαν το γράμμα, αλλά σε αυτή την περίπτωση, ξέχασε να αλλάξει το γράμμα στο δεύτερο επιχείρημα: .... .replace("véns", "vens") .... Δεδομένου ότι ο αναλυτής μας για τις τεχνητές γλώσσες κατάφερε να ανιχνεύσει ένα σφάλμα στον αναλυτή τους για τις φυσικές γλώσσες, αυτή η προειδοποίηση κερδίζει τη θέση της στην κορυφαία λίστα μας. 8th place. Last line effect Ας πάμε πιο πέρα, τώρα μιλάμε για το σφάλμα από . Ελαστικό Δείτε το κομμάτι του κώδικα: @Override public boolean equals(Object obj) { .... KeyedFilter other = (KeyedFilter) obj; return Objects.equals(keys, other.keys) && Objects.equals(timestamp, other.timestamp) && Objects.equals(tiebreaker, other.tiebreaker) && Objects.equals(child(), other.child()) && isMissingEventFilter == isMissingEventFilter; // <= } Η προειδοποίηση PVS-Studio: V6001 Υπάρχουν πανομοιότυπες υποεκφράσεις 'isMissingEventFilter' στα αριστερά και στα δεξιά του χειριστή '=='. ΚΕΦΑΛΑΙΟ 116 Όπως και στο προηγούμενο παράδειγμα, ασχολούμαστε με μια γραφή, αλλά με μια μικρή απόχρωση: το σφάλμα εμφανίζεται στην τελευταία γραμμή μπλοκ του πανομοιότυπου κώδικα. This situation occurs quite often during development, so often that developers have given it a name, Ακόμη και μέσα σε αυτό το έργο, αυτό δεν είναι μια μεμονωμένη περίπτωση: ένα παρόμοιο σφάλμα εμφανίζεται στην τελευταία γραμμή ενός άλλου μπλοκ στον ίδιο κώδικα: the last line effect @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IndexError that = (IndexError) o; return indexName.equals(that.indexName) && Arrays.equals(shardIds, that.shardIds) && errorType == that.errorType && message.equals(that.message) && stallTimeSeconds == stallTimeSeconds; // <= } Η προειδοποίηση PVS-Studio: V6001 Υπάρχουν πανομοιότυπες υπο-εκφράσεις «stallTimeSeconds» στα αριστερά και στα δεξιά του χειριστή «==». Ετικέτα: Ιαπωνία 147 Αν θέλετε να μάθετε περισσότερα για ένα τέτοιο αποτέλεσμα, σας προσκαλούμε να διαβάσετε τα ακόλουθα άρθρα: Η πρώτη μας συνάντηση με αυτό: Το φαινόμενο της τελευταίας γραμμής. μια πιο λεπτομερή συζήτηση για το φαινόμενο: Το φαινόμενο της τελευταίας γραμμής εξήγησε. Δεδομένου ότι αυτό το σφάλμα αποδεικνύει ένα τόσο περίεργο φαινόμενο, του απονέμουμε μια θέση στην κορυφαία λίστα μας. 7η θέση: Χάθηκε στην τοποθεσία Ας περάσουμε στην έβδομη θέση. Έχουμε ήδη εξετάσει τρία σφάλματα Java, αλλά δεν έχουμε συναντήσει το Minecraft ακόμα. Δείτε το κομμάτι κώδικα από : ΚΕΦΑΛΑΙΟ + private String getTimePast() { .... if(selected.timePast > 3600000){ int hours = (int) (selected.timePast / 3600000); if(hours == 1) return hours + " " + StatCollector.translateToLocal("mailbox.hour"); else return hours + " " + StatCollector.translateToLocal("mailbox.hours"); } int minutes = (int) (selected.timePast / 60000); if(minutes == 1) return minutes + " " + StatCollector.translateToLocal("mailbox.minutes"); else return minutes + " " + StatCollector.translateToLocal("mailbox.minutes"); } Το PVS-Studio προειδοποιεί: Η δήλωση «τότε» ισοδυναμεί με τη δήλωση «else». Β6404 Ετικέτα: GuiMailbox.java 76 Εδώ μπορούμε να δούμε ένα τυπογραφικό σφάλμα που οδηγεί σε σφάλμα εντοπισμού. Ανάλογα με τον αριθμό των λεπτών, θα έπρεπε να έχει εμφανιστεί είτε το "λεπτό" είτε το "λεπτό". και Οι κλάδοι είναι πανομοιότυποι, οι οποίοι πάντα παράγουν "λεπτά". then else Ως συγγραφέας αυτού του λέει: «Από τη στιγμή που το κείμενο του Για να το διορθώσετε, οι προγραμματιστές θα πρέπει να διαγράψουν ένα γράμμα. Στη συνέχεια, η σωστή φόρμα λέξης θα εμφανίζεται σε όλες τις γλώσσες που υποστηρίζονται από το mod. Άρθρο mailbox.minute Φυσικά, το λάθος δεν είναι τόσο σοβαρό, αλλά επειδή μπορεί να αναπαραχθεί χωρίς κανένα πρόβλημα, κερδίζει την έβδομη θέση στην κορυφή μας: 6η θέση: Αυτή ήταν η τελευταία λάσπη Ας προχωρήσουμε στο λάθος της : ΑΥΤΟΚΙΝΗΤΟ public static final int MAX_TYPE_NUMBER = 20; private static final LongAdder[] USAGE_STATS = new LongAdder[MAX_TYPE_NUMBER]; .... public static ByteBuf byteBuffer(int initCapacity, int type) { try { if (MEMORY_USAGE_DETECT) { .... if (type > MAX_TYPE_NUMBER) { counter = UNKNOWN_USAGE_STATS; } else { counter = USAGE_STATS[type]; // <= .... } .... } .... } } The PVS-Studio warning: Ίσως ο δείκτης "τύπος" είναι εκτός ορίων. Β6025 ΚΕΦΑΛΑΙΟ ΤΕΧΝΟΛΟΓΙΑΣ 151 Αυτό είναι ένα αρκετά κοινό λάθος (ειλικρινά, τόσο κοινό που ο συγγραφέας αυτής της κορυφαίας λίστας το έχει κάνει πολύ συχνά ο ίδιος). Λόγω ενός μικρού σφάλματος στην κατάσταση, ένα μπορεί να συμβεί στη γραμμή κώδικα που υποδεικνύεται από τον αναλυτή. MAX_TYPE_NUMBER OutOfBoundsException Αυτό μπορεί να συμβεί λόγω Επιτρέπει μια κατάσταση κατά την οποία, στην Ο κλάδος, η Οι παράμετροι θα Σε αυτή την περίπτωση, μια εξαίρεση εκτός ορίων θα ριχτεί, δεδομένου ότι η πρώτη γραμμή αποκτά πρόσβαση στο φάσμα χρησιμοποιώντας το Ο Δείκτης. if (type > MAX_TYPE_NUMBER) else type MAX_TYPE_NUMBER type Για να το διορθώσουμε, πρέπει απλώς να χρησιμοποιήσουμε Αντί να . >= > Δεδομένου ότι αυτό το λάθος είναι κοντά στην καρδιά του συγγραφέα, κερδίζει την έκτη θέση στην κορυφαία λίστα. 5η θέση: Εξωτερικά δεδομένα σε HTML Τώρα είναι Δείτε τον κώδικα: ΤΖΕΤΙ public class HelloSessionServlet extends HttpServlet { .... @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) { .... String greeting = request.getParameter("greeting"); // <= if (greeting != null) { .... message = "New greeting '" + greeting + "' set in session."; // <= .... } .... PrintWriter out = response.getWriter(); out.println("<h1>" + greeting + " from HelloSessionServlet</h1>"); // <= out.println("<p>" + message + "</p>"); // <= .... } } Το PVS-Studio προειδοποιεί: Πιθανή έγχυση XSS. δυνητικά κατεστραμμένα δεδομένα στην μεταβλητή "message" μπορεί να χρησιμοποιηθούν για την εκτέλεση ενός κακόβουλου σεναρίου. Β530 Ετικέτα: Ιαπωνία 70 The analyzer points out that there could be an . XSS ενέσεις Στον κώδικα, λαμβάνουμε μια σειρά από το αίτημα, το χρησιμοποιούμε για να δημιουργήσουμε μηνύματα και στη συνέχεια χρησιμοποιούμε αυτά τα μηνύματα για να δημιουργήσουμε μια σελίδα HTML. Σε κάθε περίπτωση, η ένεση XSS είναι πράγματι δυνατή. Υγεία Αν το εξωτερικό πέρασε είναι Για το JavaScript θα εκτελεστεί όταν ανοίξει η σελίδα. όπως και κάθε άλλος κώδικας js. message <script>alert("XSS Injection")</script> alert("XSS Injection") Εάν πρόκειται για μια πραγματική ευπάθεια, γιατί δεν κατατάσσεται υψηλότερα στη λίστα; Επειδή είναι μέρος ενός παραδείγματος επίδειξης. Με άλλα λόγια, δείχνουμε τον κώδικα μόνο για να δούμε τι μπορεί να συμβεί στο Jetty. Ωστόσο, αξίζει να θυμόμαστε ότι το παράδειγμα επίδειξης δεν μπορούσε να αντιγραφεί στην παραγωγή. Όπως ο πρώτος -προειδοποίηση ανάλυσης σε ένα πραγματικό έργο, αν και είναι σε ένα παράδειγμα επίδειξης, το απονέμουμε 5η θέση στην κορυφή. Τάιντ 4η θέση: Διπλό μόνο Δείτε το λάθος από . Ελαστικό public static Number truncate(Number n, Number precision) { .... Double result = (((n.doubleValue() < 0) ? Math.ceil(g) : Math.floor(g)) / tenAtScale); return n instanceof Float ? result.floatValue() : result; // <= } Το PVS-Studio προειδοποιεί: Το αποτέλεσμα αυτής της έκφρασης θα πέσει σιωπηρά σε "διπλό".Ελέγξτε αν η λογική του προγράμματος το χειρίζεται σωστά. Β6088 Ιαπωνία 122 Αυτή η μέθοδος κόβει τον αριθμό που έχει περάσει σύμφωνα με την καθορισμένη ακρίβεια. Για τις τιμές των ακέραιων αριθμών, μειώνει τον αριθμό των ψηφίων, ενώ για τις κλάσεις τιμές, αφαιρεί τα πλεονάζοντα ψηφία μετά το δεκαδικό σημείο. Κρίνοντας από τον τριμερή χειριστή, στην περίπτωση των κλάσεων, οι προγραμματιστές θέλουν η μέθοδος να επιστρέψει έναν πανομοιότυπο αριθμό με τον οποίο ήρθε στη μέθοδο (είτε ή Αλλά μια λεπτομερής αλλά σημαντική λεπτομέρεια έχει παραβλεφθεί. Float Double Ο τριμερής πράκτορας είναι μια έκφραση, πράγμα που σημαίνει ότι το αποτέλεσμα του πρέπει να έχει έναν ενιαίο και προκαθορισμένο τύπο. Ελάτε να παίξουμε, εν ολίγοις, τα εξής: Αριθμητικό πλαίσιο Εάν αντικείμενα διαφορετικών τύπων εμφανίζονται σε μια έκφραση, μετατρέπονται σε έναν ενιαίο κοινό τύπο. Σε αυτή την περίπτωση, ο κοινός τύπος θα ήταν Διπλός, καθώς αντιπροσωπεύει τόσο το Διπλό όσο και το Πλωτό. Δηλαδή, παρά τις προσδοκίες, η μέθοδος πάντα επιστρέφει ένα Κατά την επεξεργασία των κλασματικών αριθμών. Double The compact recording format really let us down. A fix may look like this: public static Number truncate(Number n, Number precision) { .... if (n instanceof Float) { return result.floatValue(); } else { return result; } } Λόγω αυτής της μη προφανούς γλωσσικής απόχρωσης, το σφάλμα κερδίζει την τέταρτη θέση στην κορυφή δέκα. 3rd place. Please wait your turn! Τώρα πηγαίνουμε στο βραβευμένο βάθρο.Ο χάλκινος μετάλλιος έχει βρεθεί σε Δείτε το κομμάτι του κώδικα: Έξυπνη ιδέα public final class UsageType { .... public static final UsageType DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED = new UsageType( UsageViewBundle.messagePointer( "usage.type.delegate.to.another.instance.method.parameters.changed" ) ); private static final Logger LOG = Logger.getInstance(UsageType.class); public UsageType(@NotNull Supplier<....> nameComputable) { myNameComputable = nameComputable; if (ApplicationManager.getApplication().isUnitTestMode()) { String usageTypeString = myNameComputable.get(); if (usageTypeString.indexOf('{') != -1) { LOG.error(....); } } } .... } Το PVS-Studio προειδοποιεί: Η αρχικοποίηση του 'DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED' εμφανίζεται πριν από την αρχικοποίηση του 'LOG'. Β6050 Τύπος χρήσης.java 46 Ο αναλυτής προειδοποιεί για μια κυκλική εξάρτηση κατά τη διάρκεια της στατικής αρχικοποίησης πεδίου, μία από αυτές τις αποχρώσεις της Java που είναι εύκολο να χάσετε. Ο κατασκευαστής χρησιμοποιείται για να ξεκινήσει το ΠΕΡΙΒΑΛΛΟΝ ΚΑΙ ΕΠΙΒΑΛΛΟΝΤΑΙ καταγραφής σε αυτόν τον κατασκευαστή. DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED LOG πεδία Όπως μπορούμε να δούμε, Βρίσκεται πάνω , δηλ. αρχίζει νωρίτερα. Ως αποτέλεσμα, όταν ο κατασκευαστής τρέχει, Και όταν προσπαθήσουμε να το αποκτήσουμε, θα συναντήσουμε ένα NPE. αρχικοποιούνται με τη σειρά με την οποία δηλώνονται DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED LOG LOG Θα είναι null As in most of the cases above, the fix will be extremely simple. We just need to place the Δήλωση πάνω . LOG DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED 2η θέση: Πού πήγε το μπλοκ; Πήγαμε στο ασημένιο μετάλλιο.Συναντήστε ξανά το Minecraft mod, . ΚΕΦΑΛΑΙΟ + Δείτε το κομμάτι του κώδικα: public int range = 5; public int speed = 5; .... public boolean aiShouldExecute() { healTicks++; if (healTicks < speed * 10) return false; for (Object plObj : npc.worldObj.getEntitiesWithinAABB( EntityLivingBase.class, npc.boundingBox.expand( range, range / 2, // <= range)) ) { .... } healTicks = 0; return !toHeal.isEmpty(); } Το PVS-Studio προειδοποιεί: Η έκφραση χύθηκε σιωπηρά από τον τύπο "int" στον τύπο "διπλό".Σκεφτείτε να χρησιμοποιήσετε έναν ρητό τύπο χύσης για να αποφύγετε την απώλεια ενός κλάσματος. Β6094 Ιαπωνία 41 Σε αυτό το κομμάτι κώδικα, το παιχνίδι περιοδικά (με βάση το σάρωση για οντότητες μέσα σε μια ορθογώνια περιοχή με ακτίνα που ορίζεται από Κεντρικά γύρω από έναν γιατρό NPC. speed range Από την Η μέθοδος έχει το Η υπογραφή, ο αναλυτής έχει απόλυτο δίκιο να προειδοποιήσει για αυτό το κομμάτι: Μεταφέρεται στη μέθοδο που «Τι;» μπορεί να ρωτήσετε.Νομίζουμε ότι αυτό είναι πραγματικά παράξενο, οπότε αποφασίσαμε να διερευνήσουμε αυτή την υπόθεση. expand expand(double x, double y, double z) int double Αυτό το μικρό ελάττωμα οδηγεί στην ακόλουθη συνέπεια: αν η ακτίνα είναι ένας παράξενος αριθμός, όταν αξιολογούμε τον άξονα Y, χάνουμε μισό μπλοκ στην κορυφή και στο κάτω μέρος (δηλαδή, ένα ολόκληρο μπλοκ συνολικά). Η σύγκρουση του γιατρού NPC επισημαίνεται σε λευκό. Η σύγκρουση που εφαρμόζεται επί του παρόντος (5 / 2) επισημαίνεται με κόκκινο. Η σύγκρουση χωρίς διαίρεση ολόκληρων αριθμών (5 / 2.0) επισημαίνεται με πράσινο. Όπως μπορούμε να δούμε, αν χρησιμοποιήσουμε literal of the Η διαίρεση δεν είναι πλέον ένας ολόκληρος αριθμός, και ως εκ τούτου, η σύγκρουση θα καλύψει ακριβώς την ακτίνα που θέλουμε. 2.0 double Και για να επιβεβαιώσει ότι λειτουργούσε σωστά, ο συγγραφέας του had to "ask the universe": Άρθρο Σημαντικά, οι προγραμματιστές έχουν διορθώσει αυτό το σφάλμα στο αρχικό mod για το Minecraft 1.12.2. Δυστυχώς, δεν μπορώ να δώσω καμία άμεση απόδειξη, οπότε ας υποθέσουμε ότι ρώτησα το σύμπαν - και αυτό το κομμάτι κώδικα είναι αυτό που έστειλε πίσω: Σημαντικά, οι προγραμματιστές έχουν διορθώσει αυτό το σφάλμα στο αρχικό mod για το Minecraft 1.12.2. Δυστυχώς, δεν μπορώ να δώσω καμία άμεση απόδειξη, οπότε ας υποθέσουμε ότι ρώτησα το σύμπαν - και αυτό το κομμάτι κώδικα είναι αυτό που έστειλε πίσω: .... this.npc.world.getEntitiesWithinAABB( EntityLivingBase.class, npc.getEntityBoundingBox().expand(range, range / 2.0, range) ); .... Δεδομένου ότι οι προγραμματιστές έχουν διορθώσει το σφάλμα στην αρχική τροποποίηση, το ίδιο θα πρέπει να ισχύει εδώ. Since devs have fixed the bug in the original modification, the same should apply here. 1η θέση: Η μυστηριώδης περίπτωση του χαμένου μπόνους And now, the gold medal goes to... Μπορεί η αφθονία των σφαλμάτων του Minecraft να οφείλεται στο γεγονός ότι το ήμισυ της ομάδας μας πέρασε αμέτρητες ώρες δουλεύοντας σε mods και plugins γι 'αυτό; Ποιος ξέρει. ΚΕΦΑΛΑΙΟ + Ας περάσουμε στον κώδικα: .... int startIndex = -1; boolean number = false; try { startIndex = Integer.parseInt(bonusID); number = true; } catch (Exception var34) { number = false; } for (startIndex = 0; startIndex < bonuses.length; ++startIndex) { .... if (number && startIndex == startIndex || // <= !number && bonusValues[startIndex][0].equals(bonusID) ) { noNBTText = bonusValues[startIndex][0] + ";" + bonusValueString; bonuses[startIndex] = ""; bonuses[startIndex] = noNBTText; .... break; } } .... Το PVS-Studio προειδοποιεί: V6001 There are identical sub-expressions 'startIndex' to the left and to the right of the '==' operator. ΣκηνοθεσίαDBCPlayer.java 289 V6007 Η έκφραση «!αριθμός» είναι πάντα αληθινή. ΣκηνοθεσίαDBCPlayer.java 289 V6033 Ένα στοιχείο με το ίδιο κλειδί "startIndex" έχει ήδη αλλάξει. ΣκηνοθεσίαDBCPlayer.java 293 Οι προειδοποιήσεις δείχνουν πραγματικά λάθη; Και αν ναι, σε τι οδηγεί; Εδώ, μας περιμένει μια ολόκληρη ντετέκτιβ έρευνα με ενδιαφέρον τέλος. Ας ρίξουμε μια ματιά στην πρώτη προειδοποίηση. Ο ατρόμητος συγγραφέας του άρθρου, φορώντας το καπέλο του ντετέκτιβ και το μακρύ παλτό του, πηγαίνει να ψάξει για ενδείξεις στην τάξη. startIndex Αρχείο : : ΣκηνοθεσίαDBCPlayer.java(224) .... int num = -1; boolean number; try { num = Integer.parseInt(bonusID); number = true; } catch (Exception var33) { number = false; } for (int i = 0; i < bonuses.length; ++i) { .... if (number && i == num || !number && bonusValues[i][0].equals(bonusID) ) { bonuses[i] = ""; break; } } .... The code looks very similar, but it's no coincidence that in our team, we call the author of the article "The Keen Eye"—he found the difference! In the second case, the variables are in the μπλοκ και στο loop: they're και . In the first fragment, we first initialize Στην μπλοκ, και στη συνέχεια να επαναλάβετε μέσα από αυτό σε ένα loop. different try for num i startIndex try for Ως εκ τούτου, στο δεύτερο απόσπασμα, δύο διαφορετικές μεταβλητές συγκρίνονται σε ένα πανομοιότυπο έλεγχο: και Στο πρώτο, έχουμε αυτό που υπογράμμισε ο αναλυτής - . num i startIndex == startIndex Έτσι, τώρα ξέρουμε πώς έπρεπε να μοιάζει το πρώτο κομμάτι. ? But what does the error impact Σε αυτό το σημείο, ο ντετέκτιβ παρατήρησε ένα σημαντικό ίχνος στο όνομα της ίδιας της τάξης: το prefix means that the class is used in the game scripting mechanism. The method with error is responsible for assigning bonuses to game attributes such as "Strength", "Agility", etc. Logically, the method is supposed to read a specific bonus index from the in-game script and then apply the corresponding modifier. So how should this have worked? Script Πρώτον, πρέπει να αναλύσουμε τον δείκτη μπόνους στο Το μπλοκ: try try { num = Integer.parseInt(bonusID); number = true; } .... Και στη συνέχεια, στον κύκλο, ορίστε την τιμή που έχει περάσει στο μπόνους: for (int i = 0; i < bonuses.length; ++i) { .... if (number && i == num || ....) { noNBTText = bonusValues[i][0] + ";" + bonusValueString; bonuses[i] = ""; bonuses[i] = noNBTText; .... break; } } Αλλά στο κομμάτι με το σφάλμα, ο δείκτης ιδιοτήτων που λαμβάνεται από το σενάριο υπεργράφεται με το μηδέν και στη συνέχεια συγκρίνεται με τον εαυτό του: try { startIndex = Integer.parseInt(bonusID); number = true; } catch (Exception var34) { number = false; } for (startIndex = 0; startIndex < bonuses.length; ++startIndex) { .... if (number && startIndex == startIndex || // <= !number && bonusValues[startIndex][0].equals(bonusID) ) { noNBTText = bonusValues[startIndex][0] + ";" + bonusValueString; .... } } Δηλαδή, η αξία μπόνους που μεταδίδεται στο σενάριο εκχωρείται πάντα στο bonus because Είναι γραμμένο στο κατάσταση και γίνεται μηδέν, και στη συνέχεια συγκρίνεται με τον εαυτό του. Τώρα το πρόβλημα είναι κρυστάλλινο. Ας δούμε τις συνέπειες του σφάλματος. first startIndex for First, we need to write an in-game script that will create bonuses for "Strength". First, let's create two bonuses with values of 1, and then set their values to 5 and 15, respectively: Δημιουργήσαμε μπόνους για το "Strength" με δείκτες 0 και 1 και στη συνέχεια τους αναθέσαμε τις τιμές. Now, in theory, if we display these two bonuses from "Strength", we will obtain 5 and 15? If there had been no error in the code, that's what would have happened. However, as we understood from the block above, the error leads to the value of the first bonus being overwritten. To confirm this behavior, let's write another in-game script that simply prints the bonus values: As a result, we get: Πράγματι, το δεύτερο μπόνους στο "Power" έχει επίσης μια αρχική αξία του 1, ενώ το πρώτο μπόνους είναι τώρα ίσο με 15. Δεδομένου του ποσού της ερευνητικής εργασίας που εμπλέκεται και του γεγονότος ότι αυτό το σφάλμα μπορεί να αναπαραχθεί αξιόπιστα στην πράξη, το απονέμουμε με αυτοπεποίθηση. Στο top 10 μας! honorable first place Επανασύνδεση Yay! Το έχουμε κάνει μέσα από όλα τα δέκα από τα πιο παράξενα, ζεστά και εκπαιδευτικά σφάλματα, και ελπίζουμε να απολαύσετε το ταξίδι όσο και εμείς! Αν έχετε τις δικές σας σκέψεις για αυτό το top, ή ίσως ένα αγαπημένο σφάλμα του δικού σας, θα χαρούμε να σας δούμε να τα μοιραστείτε στα σχόλια. If you want to read more about checking other projects, we invite you on our or on . μπλογκ Αυτή η σελίδα Εάν αισθάνεστε εμπνευσμένοι να ελέγξετε τα μεμονωμένα έργα σας και θέλετε να δοκιμάσετε τον αναλυτή PVS-Studio μόνοι σας, ακολουθήστε το Και αν είστε φοιτητής, δάσκαλος ή διαχειριστής έργων ανοιχτού κώδικα σε C, C++, C# ή Java, μπορείτε να χρησιμοποιήσετε το PVS-Studio δωρεάν. . λινκ ΕΔΩ All that's left is to say goodbye for now. Happy Holidays! See ya soon!