We bring to your attention a series of articles devoted to recommendations for writing high-quality code using the example of errors found in the Chromium project. This is the fourth part, which will be devoted to the problem of typos and writing code using the "Copy-Paste method."
No one is insured against typos in the code. Mistakes can be found in the code of the most qualified programmers. Qualification and experience, unfortunately, do not protect against confusing the variable name or forgetting to write something. Therefore, typos were, is and will be.
The appearance of additional errors provokes the method of writing code using Copy-Paste. Unfortunately, copying code is an effective method and nothing can be done about it. It is much faster to copy a few lines and fix something in them than to write a code fragment again. I will not even try to dissuade from this method, since I myself am a sinner and use copying code. The unpleasant side effect of copying is that they forget to change something in the copied code snippet. In some sense, these mistakes are also misprints: due to carelessness, they forgot to change something in the text or changed it incorrectly.
Let's see what typos I noticed during the analysis of the report issued by PVS-Studio. As I wrote in the
introductory article , the report was reviewed quite briefly, so there may be other errors that I have not noticed. This article focuses on the most stupid and simple mistakes. But from the fact that mistakes are stupid, they do not cease to be mistakes.
')
Null pointer dereference
According to the Common Weakness Enumeration, null pointer dereference errors can be classified as
CWE-476 .
The following errors refer to the Chromium project code.
class Display : .... { .... std::unique_ptr<FocusController> focus_controller_; .... } Display::~Display() { .... if (!focus_controller_) { focus_controller_->RemoveObserver(this); focus_controller_.reset(); } .... }
Incorrectly written condition. The pointer is dereferenced if it is null. The '!' here is clearly superfluous.
PVS-Studio
warning :
V522 CWE-476 Dereferencing of the null pointer 'focus_controller_' might take place. display.cc 52
The next mistake is worthy of the title of "Classic Classics."
void AppViewGuest::CreateWebContents(....) { .... if (!guest_extension || !guest_extension->is_platform_app() || !embedder_extension | !embedder_extension->is_platform_app()) { callback.Run(nullptr); return; } .... }
A typo. Instead of the operator '||' accidentally wrote the operator '|'. As a result, the
embedder_extension pointer
is dereferenced regardless of whether it is null or not.
Note. If the article is read by someone who is not very familiar with the C ++ language, then I suggest reading the article "
Short-circuit evaluation " to understand what is the matter.
PVS-Studio warning: V522 CWE-476 Dereferencing of the null pointer 'embedder_extension' might take place. Check the bitwise operation. app_view_guest.cc 186
The next error is due to the fact that the code is not added. I think this can also be considered a typo. Should have assigned some value to a smart pointer, but forgot.
std::unique_ptr<base::ListValue> NetworkingPrivateServiceClient::GetEnabledNetworkTypes() { std::unique_ptr<base::ListValue> network_list; network_list->AppendString(::onc::network_type::kWiFi); return network_list; }
The smart pointer defaults to zero. Since this pointer does not change after creation, then the null pointer dereference will occur.
PVS-Studio warning: V522 CWE-476 Dereferencing of the null pointer 'network_list' might take place. networking_private_service_client.cc 351
Let's study some more complicated case now.
Response CSSAgent::getMatchedStylesForNode(int node_id, Maybe<CSS::CSSStyle>* inline_style) { UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id); *inline_style = GetStylesForUIElement(ui_element); if (!inline_style) return NodeNotFoundError(node_id); return Response::OK(); }
PVS-Studio Warning:
V595 CWE-476 The 'inline_style' pointer was used against nullptr. Check lines: 142, 143. css_agent.cc 142
The
inline_style pointer is dereferenceed before checking for equality to
nullptr . It seems to me that this is a consequence of a typo: here we simply missed the asterisk '*' symbol. In this case, the correct code should look like this:
*inline_style = GetStylesForUIElement(ui_element); if (!*inline_style) return NodeNotFoundError(node_id);
However, perhaps they wanted to check the
inline_style pointer. In this case, this is not a typo, but an error in the logic of the function. Then, in order to fix the code, you need to move the check above, before calling the
GetStylesForUIElement function. Then the function should be:
Response CSSAgent::getMatchedStylesForNode(int node_id, Maybe<CSS::CSSStyle>* inline_style) { UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id); if (!inline_style) return NodeNotFoundError(node_id); *inline_style = GetStylesForUIElement(ui_element); return Response::OK(); }
I’ve run out of null pointer dereference errors related to the Chromium code, but there is one more that I noticed in the V8 engine.
bool Object::IsSmi() const { return HAS_SMI_TAG(this); } bool IC::IsHandler(Object* object) { return (object->IsSmi() && (object != nullptr)) || object->IsDataHandler() || object->IsWeakCell() || object->IsCode(); }
The
object pointer is first dereferenced, and only then checked for equality
nullptr . And in general, the expression looks somehow suspicious. Very similar to a typo in hasty programming: first we wrote
object-> IsSmi () , then we remembered that we need to check the
object pointer for equality to
nullptr and add a check. And forget to think :).
The PVS-Studio analyzer generates two warnings here at once:
- V522 CWE-628 Dereferencing of the null pointer 'object' might take place. The null pointer is passed into the 'IsHandler' function. Inspect the first argument. Check lines: 'ic-inl.h: 44', 'stub-cache.cc:19'. ic-inl.h 44
- V713 CWE-476 ic-inl.h 44
Copy paste
It is not possible to classify errors made due to Copy-Paste, according to the Common Weakness Enumeration. There is no defect like Copy-Paste :). Different typos will cause different types of defects. Next, I will show errors that can be classified as:
- CWE-563 : Assignment to Variable without Use
- CWE-570 : Expression is Always False
- CWE-571 : Expression is Always True
- CWE-682 : Incorrect Calculation
- CWE-691 : Insufficient Control Flow Management
Let's start again with a code directly related to the Chromium project.
void ProfileSyncService::OnEngineInitialized(....) { .... std::string signin_scoped_device_id; if (IsLocalSyncEnabled()) { signin_scoped_device_id = "local_device"; } else { SigninClient* signin_client = ....; DCHECK(signin_client); std::string signin_scoped_device_id =
I just feel like the programmer was too lazy to type the variable name
signin_scoped_device_id again . And he decided to copy this name. However, by chance, along with the name, he copied the type
std :: string :
std::string signin_scoped_device_id
The consequence of laziness is that the value returned by the
GetSigninScopedDeviceId function will be placed in a temporary variable, which will immediately be destroyed.
PVS_Studio Warning:
V561 CWE-563 It’s variable to indicate it declare it anew. Previous declaration: profile_sync_service.cc, line 900. profile_sync_service.cc 906
The following error I met in the V8 engine, which is used in Chromium.
static LinkageLocation ForSavedCallerReturnAddress() { return ForCalleeFrameSlot( (StandardFrameConstants::kCallerPCOffset - StandardFrameConstants::kCallerPCOffset) / kPointerSize, MachineType::Pointer()); }
Most likely the programmer copied
StandardFrameConstants :: kCallerPCOffset and wanted to change the name of a constant, but forgot to do it. Therefore, in the code, the constant is subtracted from itself, with the result that it is 0. Then this 0 is divided by
kPointerSize , but it no longer has any meaning. It will still be 0.
PVS-Studio
warning :
V501 There are "standardFrameConstants :: kCallerPCOffset" there are correct. linkage.h 66
Another suspicious code snippet seen in V8:
void JSObject::JSObjectShortPrint(StringStream* accumulator) { .... accumulator->Add(global_object ? "<RemoteObject>" : "<RemoteObject>"); .... }
PVS-Studio
warning :
V583 CWE-783 The '?:' Operator, regardless of its conditional expression, "<RemoteObject>". objects.cc 2993
Now let's take a look at the PDFium project.
inline bool FXSYS_iswalpha(wchar_t wch) { return FXSYS_isupper(wch) || FXSYS_islower(wch); } bool CPDF_TextPage::IsHyphen(wchar_t curChar) const { WideStringView curText = m_TempTextBuf.AsStringView(); .... auto iter = curText.rbegin(); .... if ((iter + 1) != curText.rend()) { iter++; if (FXSYS_iswalpha(*iter) && FXSYS_iswalpha(*iter))
Copied
FXSYS_iswalpha (* iter) . And ... And they forgot to change something in the second part of the conditions.
PVS-Studio warning: V501 CWE-571 There are identical sub-expressions 'FXSYS_iswalpha (* iter)' operator. cpdf_textpage.cpp 1218
A similar mistake when writing an expression can be found in the Protocol buffers library.
bool IsMap(const google::protobuf::Field& field, const google::protobuf::Type& type) { return field.cardinality() == google::protobuf::Field_Cardinality_CARDINALITY_REPEATED && (GetBoolOptionOrDefault(type.options(), "map_entry", false) || GetBoolOptionOrDefault(type.options(), "google.protobuf.MessageOptions.map_entry", false) ||
The code is clearly written using Copy-Paste. No one will re-type such a long line :).
PVS-Studio Warning: V501 CWE-570 ||| operator. utility.cc 351
By the way, there is one more such error next to each other: V501 CWE-570 operator. utility.cc 360
In the following code snippet from the SwiftShader library, my favorite “
last line effect ” is waiting for us. Beautiful bug! I love these.
void TextureCubeMap::updateBorders(int level) { egl::Image *posX = image[CubeFaceIndex(..._POSITIVE_X)][level]; egl::Image *negX = image[CubeFaceIndex(..._NEGATIVE_X)][level]; egl::Image *posY = image[CubeFaceIndex(..._POSITIVE_Y)][level]; egl::Image *negY = image[CubeFaceIndex(..._NEGATIVE_Y)][level]; egl::Image *posZ = image[CubeFaceIndex(..._POSITIVE_Z)][level]; egl::Image *negZ = image[CubeFaceIndex(..._NEGATIVE_Z)][level]; .... if(!posX->hasDirtyContents() || !posY->hasDirtyContents() || !posZ->hasDirtyContents() || !negX->hasDirtyContents() || !negY->hasDirtyContents() ||
At the very end of the condition, it was necessary to use not the
negY pointer, but the
negZ pointer. Obviously, we deal with Copy-Paste lines of code and loss of attention at the very end.
PVS-Studio Warning: V501 CWE-570 There are identical sub-expressions'! NegY-> hasDirtyContents () '||' operator. texture.cpp 1268
The WebKit engine is also pleased with a beautiful bug:
bool IsValid(....) const final { OptionalRotation inherited_rotation = GetRotation(*state.ParentStyle()); if (inherited_rotation_.IsNone() || inherited_rotation.IsNone()) return inherited_rotation.IsNone() == inherited_rotation.IsNone(); .... }
PVS-Studio warning: V501 CWE-571 There are identical sub-expressions' inherited_rotation.IsNone () '==' operator. cssrotateinterpolationtype.cpp 166
Copied
inherited_rotation.IsNone () and forgot to add the underscore character '_'. The correct option is:
return inherited_rotation_.IsNone() == inherited_rotation.IsNone();
Let's look again at the Protocol buffers library.
void SetPrimitiveVariables(...., std::map<string, string>* variables) { .... (*variables)["set_has_field_bit_message"] = ""; (*variables)["set_has_field_bit_message"] = ""; (*variables)["clear_has_field_bit_message"] = ""; .... }
Explanations are superfluous. Copy-Paste in its purest form. PVS-Studio
warning :
V519 CWE-563 The variable is assigned values ​​twice successively. Perhaps this is a mistake. Check lines: 149, 150. java_primitive_field_lite.cc 150
We continue. Programmers need to know how cunning copy-paste is. Read and be terrified. Before us is a function taken from WebRTC.
size_t WebRtcSpl_FilterAR(....) { .... for (i = 0; i < state_length - x_length; i++) { state[i] = state[i + x_length]; state_low[i] = state_low[i + x_length]; } for (i = 0; i < x_length; i++) { state[state_length - x_length + i] = filtered[i]; state[state_length - x_length + i] = filtered_low[i];
Yes, again the effects of Copy-Paste. Copy the line:
state[state_length - x_length + i] = filtered[i];
Replaced it
filtered to
filtered_low . But
they forgot to replace
state with
state_low . As a result, some elements of the
state_low array remain uninitialized.
Are you tired of reading? Imagine how tired I am to write this! Let's take a break and drink coffee.

PAUSE.
I hope you have regained your strength and are ready to continue enjoying the 50 shades of Copy-Paste. Here is what you can see in the PDFium library.
bool CPWL_EditImpl::Backspace(bool bAddUndo, bool bPaint) { .... if (m_wpCaret.nSecIndex != m_wpOldCaret.nSecIndex) { AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>( this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset)); } else { AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>( this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset)); } .... }
Regardless of the condition, the same action is performed. Most likely, there is an unsuccessful Copy-Paste. The programmer copied the fragment, then got distracted and forgot to correct the else-branch.
PVS-Studio
Alerts :
V523 CWE-691 The 'then' statement is equivalent to the 'else' statement. cpwl_edit_impl.cpp 1580
There are three more places like this. I will regret the reader and give only the analyzer messages:
- V523 CWE-691 The 'then' statement is equivalent to the 'else' statement. cpwl_edit_impl.cpp 1616
- V523 CWE-691 The 'then' statement is equivalent to the 'else' statement. cpdf_formfield.cpp 172
- V523 CWE-691 The 'then' statement is equivalent to the 'else' statement. cjs_field.cpp 2323
Skia Library.
bool SkPathRef::isValid() const { .... if (nullptr == fPoints && 0 != fFreeSpace) { return false; } if (nullptr == fPoints && 0 != fFreeSpace) { return false; } .... }
The same check is performed twice. The second check is superfluous. Or forgot to check something else. The analyzer signals the suspicion of this code at once with two warnings:
- V581 The conditional expressions of the statement "if" statements followed alongside each other are identical. Check lines: 758, 761. skpathref.cpp 761
- V649 CWE-561 There are two 'if' statements with identical conditional expressions. The first 'if' statement contains function return. This means that the statement is senseless. Check lines: 758, 761. skpathref.cpp 761
And another mistake in the Skia library.
static inline bool can_blit_framebuffer_for_copy_surface( const GrSurface* dst, GrSurfaceOrigin dstOrigin, const GrSurface* src, GrSurfaceOrigin srcOrigin, ....) { .... const GrGLTexture* dstTex = static_cast<const GrGLTexture*>(dst->asTexture()); const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(dst->asTexture());
PVS-Studio Warning:
V656 Variables 'dstTex', 'srcTex' are initialized through the call to the same function. It's probably not an error or un-optimized code. Check lines: 3312, 3313. grglgpu.cpp 3313
After Copy-Paste, they forgot to replace
dst with
src . It should be written:
const GrGLTexture* srcTex = static_cast<const GrGLTexture*>(src->asTexture());
HarfBuzz Library.
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const { unsigned int l = (this+leftClassTable).get_class (left); unsigned int r = (this+leftClassTable).get_class (left);
PVS-Studio
warning :
V751 Parameter 'right' is not used inside function body. hb-ot-kern-table.hh 115
Beautiful mistake. They copied the line, but forgot to change the
left to
right two times. It should be:
unsigned int l = (this+leftClassTable).get_class (left); unsigned int r = (this+rightClassTable).get_class (right);
SwiftShader library. I am sure these code fragments of the same type were written using Copy-Paste:
class ELFObjectWriter { .... ELFStringTableSection *ShStrTab; ELFSymbolTableSection *SymTab; ELFStringTableSection *StrTab; .... }; void ELFObjectWriter::assignSectionNumbersInfo( SectionList &AllSections) { .... ShStrTab->setNumber(CurSectionNumber++); ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName())); AllSections.push_back(ShStrTab); SymTab->setNumber(CurSectionNumber++); SymTab->setNameStrIndex(ShStrTab->getIndex(SymTab->getName())); AllSections.push_back(SymTab); StrTab->setNumber(CurSectionNumber++); StrTab->setNameStrIndex(ShStrTab->getIndex(StrTab->getName())); AllSections.push_back(StrTab); .... }
The programmer was inattentive. In the second block of text, he forgot to replace
hStrTab-> getIndex with
SymTab-> getIndex , and in the third block he did not replace
hStrTab-> getIndex with
StrTab-> getIndex .
PVS-Studio warning: V778 CWE-682 Two code fragments were found. Perhaps this is a typo and 'SymTab' variable should not be used instead of 'ShStrTab'. iceelfobjectwriter.cpp 194
The following error is due to an incorrect calculation of the size of the rectangle in the WebKit library. Code "tear out the eye." Weak to notice the error?
void NGFragmentBuilder::ComputeInlineContainerFragments(....) { .... value.start_fragment_union_rect.size.width = std::max(descendant.offset_to_container_box.left + descendant.fragment->Size().width - value.start_fragment_union_rect.offset.left, value.start_fragment_union_rect.size.width); value.start_fragment_union_rect.size.height = std::max(descendant.offset_to_container_box.top + descendant.fragment->Size().height - value.start_fragment_union_rect.offset.top, value.start_fragment_union_rect.size.width); .... }
At the very end, they forgot to replace
width with
height in the copied block.
PVS-Studio
warning :
V778 CWE-682 Two code fragments were found. Perhaps this is a typo and 'height' variable should be used instead of 'width'. ng_fragment_builder.cc 326
Whew ... We are finishing. The last code snippet in this section is taken from the PDFium library.
void sycc420_to_rgb(opj_image_t* img) { .... opj_image_data_free(img->comps[0].data); opj_image_data_free(img->comps[1].data); opj_image_data_free(img->comps[2].data); img->comps[0].data = d0; img->comps[1].data = d1; img->comps[2].data = d2; img->comps[1].w = yw;
Duplicate assignment block. PVS-Studio warning: V760 Two blocks of text were found. The second block begins from line 420. fx_codec_jpx_opj.cpp 416
No, I lied to you. Another Copy-Paste from PDFium met. I had to add to the article.
void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const { .... if (*res_x < 0 && *res_x > -kBase) *res_x = kBase + *res_x; if (*res_y < 0 && *res_x > -kBase) *res_y = kBase + *res_y; } }
PVS-Studio warning: V778 CWE-682 Two code fragments were found. Perhaps this is a typo and 'res_y' variable should be used instead of 'res_x'. cfx_imagetransformer.cpp 201
The line was copied:
if (*res_x < 0 && *res_x > -kBase)
One variable name
res_x was replaced by
res_y , and the second was forgotten. As a result, the function
does not check the condition
* res_y> -kBase .
Other typos
If typos like “null pointers” and “Copy-Paste” somehow stood out by themselves, then the remaining errors are quite heterogeneous. I did not try to classify them somehow and just gathered them in this section of the article.
Let's start with a snippet of code related to the Chromium project. I'm not sure this is a mistake. However, this place is clearly worth checking out to developers.
namespace cellular_apn { const char kAccessPointName[] = "AccessPointName"; const char kName[] = "Name"; const char kUsername[] = "Username"; const char kPassword[] = "Password"; const char kLocalizedName[] = "LocalizedName"; const char kLanguage[] = "LocalizedName"; }
Suspiciously, the
kLocalizedName and
kLanguage constants contain the same string. I would venture to suggest that it should be written like this:
const char kLanguage[] = "Language";
However, I am not sure.
The PVS-Studio analyzer issues a warning here:
V691 Empirical analysis. It is possible that a typo is present inside the string literal: “LocalizedName”. The 'Localized' word is suspicious. onc_constants.cc 162
The next mistake in the Skia library is simply beautiful and refers to the article “
Evil lives in comparison functions ”.
inline bool operator==(const SkPDFCanon::BitmapGlyphKey& u, const SkPDFCanon::BitmapGlyphKey& v) { return memcmp(&u, &u, sizeof(SkPDFCanon::BitmapGlyphKey)) == 0; }
Because of a typo, the
u object is compared to itself. It turns out that
operator == considers any two objects to be the same.
PVS-Studio Warning:
V549 CWE-688 The first argument of 'memcmp' function is equal to the second argument. skpdfcanon.h 67
The following typo in the Skia library leads to the fact that the function does not print out all the elements of the array, but repeatedly displays information about the first element. However, the error is not a bad thing, since the function is intended for debugging purposes.
SkString dumpInfo() const override { SkString str; str.appendf("# combined: %d\n", fRects.count()); for (int i = 0; i < fRects.count(); ++i) { const RectInfo& geo = fRects[0]; str.appendf("%d: Color: 0x%08x, " "Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, geo.fRect.fBottom); } str += fHelper.dumpInfo(); str += INHERITED::dumpInfo(); return str; }
Instead of
fRects [0] ,
fRects [i] should be written. PVS-Studio warning: V767 Suspicious access to the element of 'fRects' array by a constant index inside a loop. grnonaafillrectop.cpp 276
A typo in the SwiftShader project causes the
assert macro to check not everything that is required.
static Value *createArithmetic(Ice::InstArithmetic::OpKind op, Value *lhs, Value *rhs) { assert(lhs->getType() == rhs->getType() || (llvm::isa<Ice::Constant>(rhs) && (op == Ice::InstArithmetic::Shl || Ice::InstArithmetic::Lshr || Ice::InstArithmetic::Ashr))); .... }
Two times forgot to write "op ==". As a result, the condition is part of the constants
Ice :: InstArithmetic :: Lshr and
Ice :: InstArithmetic :: Ashr , which are not compared with anything. This is an obvious mistake, because of which part of the condition is always true.
It should actually be written here:
assert(lhs->getType() == rhs->getType() || (llvm::isa<Ice::Constant>(rhs) && (op == Ice::InstArithmetic::Shl || op == Ice::InstArithmetic::Lshr || op == Ice::InstArithmetic::Ashr)));
The PVS-Studio analyzer generates two warnings:
- V768 CWE-571 The enumeration constant 'Lshr' is used as a variable of a Boolean-type. subzeroreactor.cpp 712
- V768 CWE-571 The Enumeration Constant 'Ashr' is used as a variable of a Boolean-type. subzeroreactor.cpp 712
By the way, there are such code fragments that are not in themselves an error or a typo. But they provoke typos in the future. For example, these are unsuccessful names for global variables.
We can observe one of these variables in the Yasm library:
static int i;
PVS-Studio
warning :
V707 Giving short names for global variables. It is suggested to be rename 'i' variable. nasm-eval.c 29
Yes, this is not a mistake. But it is very easy to forget somewhere to declare a local variable
i and use the global one. And the code will be compiled, but how the program will work after that is a big mystery. In general, it is better to give global variables some more unique names.
I hope I was able to convey the seriousness of the errors that occur because of typos!

Finally, I suggest to get acquainted with a beautiful typo in the Protocol buffers library, which I described in a separate note - "
February 31 ".
Recommendations
I apologize, this time I will let you down with the "recommendations" section. It is very difficult to give some clear universal tips to avoid the mistakes discussed in the article. It is human nature to make such mistakes, and that's it.
Nevertheless, I still try to somehow help to reduce the number of typos in the programs.
- Use the "tabular design" of complex conditions. I described this technology in detail in the mini-book “ The Main Question of Programming, Refactoring and All That ”. See tip N13 - Align code of the same type with a “table”. By the way, this year I plan to write an extended version of this book. It will not include 42, but 50 recommendations. Plus, some old chapters need updating and refinement.
- When focusing on Copy-Paste, focus at the end of your work. This is due to the “ last line effect ”, when at the end of writing the same type of text blocks a person relaxes and makes mistakes. If you want to read something more scientific on this topic, then I suggest the article " Explanation of the effect of the last line ."
- Be careful when writing functions that compare objects. These functions are deceptively simple and provoke a large number of typos. Details: " Evil lives in comparison functions ."
- If the code is hard to read, then it is difficult to notice an error in it. So try to write the code as clearly as possible and easily read. Unfortunately, it is difficult to formally formulate, especially briefly. Many wonderful books are devoted to this topic, for example, such as The Perfect Code written by Steve McConnell. From a practical point of view, I recommend paying attention to the coding standard in your company and not be lazy to replenish it with good practitioners that you have learned somewhere. And in general, C ++ is developing rapidly, so it makes sense to regularly audit the standard and update it.
- Regularly use the PVS-Studio static code analyzer. In the end, all the typos described in this article were found precisely with the help of PVS-Studio. Download and try the analyzer here .
Thank you all for your attention, but I'm not saying goodbye yet. Soon there will be another article.If you want to share this article with an English-speaking audience, then please use the link to the translation: Andrey Karpov.
Chromium: Typos .