A Minecraft modok, a katalán nyelv és a nem nyilvánvaló kölcsönhatások a ternáris operátorral – hány dologgal találkozott elemzőnk! Bevezetés Ebben az évben számos nyílt forráskódú projektet ellenőriztünk és írtunk róluk szól. cikkek Most már kiválasztottuk a tíz legfurcsább hibát és furcsa kódfragmentumot. A kiválasztás és a rangsor természetesen szubjektív és a saját hűvös érzésünkön alapul. Itt az ideje - menjünk el! 10. hely: jobbra balra, balra jobbra Megnyitjuk a csúcsot egy esettel a Bizonyos értelemben a projekt velünk rezonál: a PVS-Studiohoz hasonlóan elemzi a nyelveket – csak a természeteseket, nem a mesterségeseket. nyelvi eszközök A vicces dolog az, hogy ez a hiba az elemzőnkre vonatkozik. Mindazonáltal olyan viccesnek találjuk, hogy egyszerűen nem tudjuk kihagyni. Nézze meg a kód fragmentumát: public String getEnclitic(AnalyzedToken token) { .... if (word.endsWith("ه")) { suffix = "ه"; .... else if ((word.equals("عني") || word.equals("مني")) && word.endsWith("ني") // <= ) { suffix = "ني"; } .... } A PVS-Studio figyelmeztetése: A „word.endsWith” kifejezés mindig hamis. A 6007 ArabTagger.javai 428 Ha megnézzük a sorkonstánsokat, ahogyan a bal-jobb (LTR) nyelvek anyanyelvei – például az angol vagy a francia – általában teszik, az elemző figyelmeztetése igazságosnak tűnik. -Az arab nyelvű beszélők egy szó szerinti végét várják, valójában látni fogják a kezdetét. Beszéljünk arabul non can't Incidentally, if we know that there is an RTL writing, we realize that the condition is actually always És nem hamis. true Ez kíváncsiságot keltett bennünket: hogyan kezeli a Java a jobb-bal oldali nyelveket egyáltalán? Minden olyan egyszerű, mint a sütemény: a Java a szöveges vizualizáció helyett az Unicode-t kezeli. , azaz abban a sorrendben, amelyben az anyanyelvi olvasók olvasnak és írnak. logikai sorrendben tárolják Ez azt jelenti, hogy a Java-nak nem kell speciális átalakításokat végeznie az RTL-szöveg kezeléséhez.A szál az Unicode-kódpontok rendszeres sorozata, és olyan műveletek, mint a hosszának megszerzése, a karakterek elérése, vagy egy alszál kivonása ugyanaz minden nyelven. Ezért egy ilyen ellenőrzéshez: System.out.println("مرحباً بالجميع".endsWith("بالجميع")); Az eredmény lesz , bár ha balról jobbra olvasunk, a szál kezdődik, ahelyett, hogy véget érne, "بالجميع". true Mégis, mivel az elemző technikailag hibás itt, a rangsorban a 10. helyen helyezzük el. Ha kíváncsi vagy többet megtudni, itt olvashatod el a LanguageTool elemzésének teljes történetét. . cikket 9. hely - Katalán nyelv Ismét szerepel a rangsorban! nyelvi eszközök A katalán nyelvű modul a módszer, amely finoman korrigálja az elavult helyesírási formákat és eltávolítja a felesleges diakritikát. Például átalakítja a „adéu” „adieu”, „dóna” „dona”, „vénen” „venen” és így tovább: 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"); } Első pillantásra rendben tűnik: működik, javítja, és nem zavarja senkit. Az elemzőnek azonban van egy másik pontja: A „helyettesítés” függvény furcsa érvet kap. A „véns” argumentumot többször átadták. A 6009 Csehország.java 453 Valószínűleg, amikor a fejlesztők dolgoztak ezzel a blokkkal, egyszerűen másolták az eredeti szót és helyettesítették a betűt, de ebben az esetben elfelejtették megváltoztatni a betűt a második argumentumban: .... .replace("véns", "vens") .... Mivel a mesterséges nyelvek elemzőjének sikerült észlelnie a hibát a természetes nyelvek elemzőjében, ez a figyelmeztetés megérdemli a helyét a legfelső listánkban. 8. Az utolsó vonal hatása Menjünk tovább, most beszélünk a hiba a . Elasztikus keresés Look at the code fragment: @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; // <= } A PVS-Studio figyelmeztetése: V6001 A "==" operátor bal és jobb oldalán azonos alkifejezések vannak "isMissingEventFilter". Kezdőlap > 116 As in the previous example, we're dealing with a typo, but with a slight nuance: the error occurs in the last block line of identical code. Ez a helyzet gyakran előfordul a fejlesztés során, olyan gyakran, hogy a fejlesztők egy nevet adtak neki, Még ebben a projektben is, ez nem egyedi eset: hasonló hiba jelenik meg egy másik blokk utolsó sorában az azonos kódban: 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; // <= } A PVS-Studio figyelmeztetése: V6001 A "==" operátor bal és jobb oldalán azonos "stallTimeSeconds" alkifejezések vannak. IndexError.java 14 Ha többet szeretne megtudni egy ilyen hatásról, meghívjuk Önt, hogy olvassa el a következő cikkeket: Első találkozásunk vele: Az utolsó vonal hatása. A jelenségről részletesebb megbeszélés: Az utolsó vonalhatás magyarázata. Mivel ez a hiba ilyen furcsa jelenséget mutat, helyet adunk neki a felső listánkban. 7. hely: Elveszett a helyszínen Térjünk át a hetedik helyre. We've already looked at three Java errors, but we haven't encountered Minecraft yet. It's time to fix that! Nézze meg a kódrészletet a : Ügyfélszolgálat + 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"); } A PVS-Studio figyelmeztetése: A „később” kijelentés egyenértékű az „else” kijelentéssel. V6004 Személyi Székesfehérvár.hu 76 Itt láthatunk egy olyan betűtípust, amely lokalizációs hibát eredményez. A percek számától függően a „perc” vagy a „perc” jelennek meg. és az ágak azonosak, ami mindig „perceket” ad ki. then else Ennek a szerzőjeként mondja: „Mivel a szöveg forrása a A kompromisszum elküldése hosszabb időt vett igénybe, mint a kód kijavítása.”A fejlesztőknek egy betűt kell törölniük, majd a helyes szóformanyomtatvány a mod által támogatott összes nyelven megjelenik. cikket mailbox.minute Természetesen a hiba nem olyan súlyos, de mivel minden probléma nélkül reprodukálható, hetedik helyet szerez a topunkban: Az 6. Ez volt az utolsó szalma Térjünk át a hiba : Autóbérlés 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]; // <= .... } .... } .... } } A PVS-Studio figyelmeztetése: Lehetséges, hogy az index „típus” határain kívül van. A 6025-es ByteBufAlloc.java 151 Ez egy meglehetősen gyakori hiba ( őszintén szólva, olyan gyakori, hogy a lista szerzője gyakran maga is elkövette). . Due to a minor error in the condition, an előfordulhat az elemző által megjelölt kódvonalon. MAX_TYPE_NUMBER OutOfBoundsException Ez azért történhet, mert Lehetővé teszi azt a helyzetet, amelyben a A szakma, a A paramétereknek meg kell Ebben az esetben egy határon kívüli kivételt dobnak ki, mivel az első sor az array használatával Az index. if (type > MAX_TYPE_NUMBER) else type MAX_TYPE_NUMBER type Ahhoz, hogy ez megtörténjen, csak használni kell Ahelyett, hogy . >= > Mivel ez a hiba közel áll a szerző szívéhez, a listán a hatodik helyen áll. 5. hely: Külső adatok HTML-ben Most már az Nézze meg a kódot: Játé 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>"); // <= .... } } A PVS-Studio figyelmeztetése: . Possible XSS injection. Potentially tainted data in the 'message' variable might be used to execute a malicious script. V5330 KezdőlapHírekJava 70 The analyzer points out that there could be an . XSS injekció A kódban egy szálat kapunk a kérésből, használjuk az üzenetek létrehozásához, majd ezeket az üzeneteket használjuk egy HTML-oldal létrehozására. Mindenesetre az XSS injekció valóban lehetséges. sanitized Amennyiben a külső az A javascript Az oldal megnyitása után végrehajtható, mint bármely más js kód. message <script>alert("XSS Injection")</script> alert("XSS Injection") Ha ez egy valódi biztonsági rés, akkor miért nem rangsorolható magasabbra a listán? Mivel egy demonstrációs példa részét képezi. Más szóval, csak azért mutatjuk meg a kódot, hogy megnézzük, mi történhet a Jetty-ben. Mindazonáltal érdemes megjegyezni, hogy a demonstrációs példa nem másolható a termelésbe. Mint az első -analysis warning in a real project, although it's in a demonstration example, we award it 5th place in the top. Tánc Negyedik hely: csak kettős Lásd a hiba a . Elasztikus keresés 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; // <= } A PVS-Studio figyelmeztetése: Ennek a kifejezésnek az eredménye implicit módon „kettős”. Ellenőrizze, hogy a program logikája helyesen kezeli-e. A 6088-as Jászberényi 122 This method cuts the passed number according to the specified precision. For integer values, it reduces the number of digits, while for fractional values, it removes excess digits after the decimal point. The snippet above shows the logic has applied to fractional numbers. A ternáris operátor alapján ítélve, a frakciók esetében a fejlesztők azt akarják, hogy a módszer azonos számot adjon vissza, amellyel a módszerhez jutott (vagy vagy De egy finom, de fontos részletet figyelmen kívül hagytak. Float Double The ternary operator is an expression, which means its result must have a single and predetermined type. At this point, the rules of Lépjen be a játékba. röviden a következők érvényesek: Numerikus kontextus Ha a kifejezésben különböző típusú objektumok jelennek meg, azokat egyetlen közös típusra alakítják át. Ebben az esetben a közös típus dupla lenne, mivel mindkettőt képviseli, mind a lebegő. Ez azt jelenti, hogy az elvárások ellenére a módszer mindig visszatér a A frakcionális számok feldolgozása. Double A kompakt felvételi formátum valóban leenged minket. A javítás így nézhet ki: public static Number truncate(Number n, Number precision) { .... if (n instanceof Float) { return result.floatValue(); } else { return result; } } Ennek a nem nyilvánvaló nyelvi árnyalatnak köszönhetően a hiba negyedik helyet szerez a top tízben. 3. hely: Kérjük, várja meg a sorát! Megérkezett a bronzérme, a bronzérme is megjelent Nézze meg a kód fragmentumát: Intelligens ötlet 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(....); } } } .... } A PVS-Studio figyelmeztetése: Az „DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED” kezdeményezése a „LOG” kezdeményezése előtt jelenik meg. A 6050-es Használati útmutató: Java 46 Az elemző figyelmeztet a ciklikus függőségre a statikus mező-kezdeményezés során; ez az egyik olyan Java árnyalat, amelyet könnyű kihagyni. Az építőt arra használják, hogy az field. And we access logger in this constructor. DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED LOG mezők Ahogy láthatjuk, Fent található , azaz korábban van beindítva. Ennek eredményeképpen, amikor a konstruktőr fut, , és amikor megpróbálunk hozzáférni, találkozunk egy NPE-vel. are initialized in the order in which they're declared DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED LOG LOG lesz null As in most of the cases above, the fix will be extremely simple. We just need to place the Felső nyilatkozat . LOG DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED 2. Hová tűnt a blokk? Az ezüstérmesre költöztünk. Ismét találkozunk a Minecraft mod, . Ügyfélszolgálat + Nézze meg a kód fragmentumát: 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(); } A PVS-Studio figyelmeztetése: The expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. V6094 megtekintése Munkahelyi.hu 41 Ebben a kódfájlban a játék rendszeresen (a mező) a téglalapon belüli entitások beolvasása olyan sugárral, amelyet Egy NPC-gyógyász köré épült. speed range Azóta a method has the Az elemzőnek abszolút igaza van, hogy figyelmezteti ezt a fragmentumot: A módszert olyan módon használják, amelyben Azt gondoljuk, hogy ez nagyon furcsa, ezért úgy döntöttünk, hogy kivizsgáljuk ezt az ügyet. expand expand(double x, double y, double z) int double Ez a kisebb hiba a következő következményhez vezet: ha a sugár egy furcsa szám, akkor az Y tengely értékelésekor fél blokkot veszítünk a tetején és az alján (azaz egy egész blokk összesen). Az Az NPC orvos ütközése fehér színben van kiemelve. The collision currently implemented (5 / 2) is highlighted in red. Az egész szám megosztása nélküli ütközés (5 / 2.0) zöld színnel van megjelölve. Amint láthatjuk, ha a Szó szerint a Az osztás már nem egész szám, és ennek eredményeképpen az ütközés pontosan a kívánt sugárterületet fedezi. 2.0 double és annak megerősítésére, hogy helyesen működött, a szerző a „Kérdezzük meg az univerzumot”: cikket Figyelemre méltó, hogy a fejlesztők kijavították ezt a hibát az eredeti mod Minecraft 1.12.2-ben. Sajnos nem tudok közvetlen bizonyítékot szolgáltatni, így tegyük fel, hogy megkérdeztem az univerzumot - és ez a kódrészlet az, amit visszaküldött: Figyelemre méltó, hogy a fejlesztők kijavították ezt a hibát az eredeti mod Minecraft 1.12.2-ben. Sajnos nem tudok közvetlen bizonyítékot szolgáltatni, így tegyük fel, hogy megkérdeztem az univerzumot - és ez a kódrészlet az, amit visszaküldött: .... this.npc.world.getEntitiesWithinAABB( EntityLivingBase.class, npc.getEntityBoundingBox().expand(range, range / 2.0, range) ); .... Mivel a fejlesztők az eredeti módosításban kijavították a hibát, itt ugyanezt kell alkalmazni. Since devs have fixed the bug in the original modification, the same should apply here. 1st place. The mystery case of missed bonus A mostani aranyérmét a... Lehet, hogy a Minecraft hibák bőségének oka, hogy csapatunk fele számtalan órát töltött a mod-ok és a bővítmények fejlesztésével? CustomNPC+ Térjünk át a kódra: .... 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; } } .... A PVS-Studio figyelmeztetése: V6001 There are identical sub-expressions 'startIndex' to the left and to the right of the '==' operator. ScriptDBCPlayer.java 289 letöltés V6007 A „!szám” kifejezés mindig igaz. ScriptDBCPlayer.java 289 letöltés V6033 Az ugyanazt a kulcsot tartalmazó elemet már megváltoztatták. ScriptDBCPlayer.java 293 letöltés Do warnings really indicate errors? And if so, what does it lead to? Here, a whole detective investigation with an interesting ending awaits us. Először is, nézzük meg az első figyelmeztetést. A cikk őrült szerzője, nyomozói kalappal és hosszú kabátjával, elindul nyomokat keresni az osztályban. startIndex File: : ScriptDBCPlayer.javai lejátszó(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; } } .... A kód nagyon hasonlít, de nem véletlen, hogy a mi csapatunkban hívjuk a cikk szerzőjét "The Keen Eye" - megtalálta a különbséget! in the block and in the loop: they're and Az első szakaszban először a Ebben a blokkolja, majd átmásolja azt egy A loop. different try for num i startIndex try for Ezért a második töredékben két különböző változót hasonlítunk össze egy azonos ellenőrzésben: és Az első részben azt találjuk, amit az elemző rámutatott: . num i startIndex == startIndex Most már tudjuk, hogy milyen volt az első rész. ? But what does the error impact Ebben a pillanatban a nyomozó egy fontos nyomot vett észre az osztály nevében: a Az előtag azt jelenti, hogy az osztályt a játék scripting mechanizmusában használják. A hibás módszer felelős a bónuszok hozzárendeléséért a játék tulajdonságaihoz, mint például az "Erő", a "Gyorság", stb. Logikusan a módszernek egy adott bónuszindexet kell elolvasnia a játékban lévő scriptből, majd a megfelelő módosítót kell alkalmaznia. Script First, we need to parse the bonus index in the block: try try { num = Integer.parseInt(bonusID); number = true; } .... És akkor, a hurokban, állítsa be az átadott értéket a bónuszra: for (int i = 0; i < bonuses.length; ++i) { .... if (number && i == num || ....) { noNBTText = bonusValues[i][0] + ";" + bonusValueString; bonuses[i] = ""; bonuses[i] = noNBTText; .... break; } } De a hibás töredékben a forgatókönyvből származó attribútumindex nullával van leírva, majd összehasonlítva önmagával: 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; .... } } Ez azt jelenti, hogy a forgatókönyvben átadott bónusz értéke mindig a Bónusz, mert Meg van írva a feltétel és válik nulla, majd összehasonlítja magát. Most a probléma kristálytiszta. Nézzük meg a következményeit a hiba. first startIndex for Először meg kell írnunk egy játékban lévő forgatókönyvet, amely bónuszokat hoz létre az "Erő" számára. First, let's create two bonuses with values of 1, and then set their values to 5 and 15, respectively: Az Így néznek ki a játékban a forgatókönyvek. „Erő” bónuszokat hoztunk létre 0 és 1 indexekkel, majd hozzárendeltük nekik az értékeket. Most elméletben, ha megmutatjuk ezeket a két bónuszt az "Erőből", akkor kapunk 5 és 15? Ha nem volt hiba a kódban, ez történt volna. Ahhoz, hogy megerősítsük ezt a viselkedést, írjunk egy másik játékos forgatókönyvet, amely egyszerűen kinyomtatja a bónusz értékeket: Az Ennek eredményeképpen kapunk: Az Valójában a "Power" második bónuszának is van kezdeti értéke 1, míg az első bónusz most 15. Kiderül, hogy először az első bónuszt 5-re állítjuk, majd 15 értékkel felülírjuk. Figyelembe véve a detektív munka mennyiségét és azt a tényt, hogy ez a hiba megbízhatóan reprodukálható a gyakorlatban, bizalommal ítéljük oda a A top tízben! honorable first place Wrap-up részletek Yay! We've made it through all ten of the most bizarre, heart-warming, and educational bugs, and we hope you enjoyed the journey as much as we did! If you have your own thoughts about this top, or maybe a favorite bug of your own, we'd be happy to see you share them in the comments. Ha többet szeretne olvasni más projektek megtekintéséről, meghívjuk Önt a vagy az . blogot this page Ha arra ösztönöz, hogy ellenőrizze az egyes projekteket, és szeretné kipróbálni a PVS-Studio elemzőt, kövesse a És ha Ön diák, tanár vagy a C, C++, C# vagy Java nyílt forráskódú projektjeinek karbantartója, ingyenesen használhatja a PVS-Studio-t. . linkek Itt Minden, ami maradt, hogy búcsút mondjon most. Boldog ünnepeket!