﻿# Какие ошибки есть в коде LLVM?

LLVM — open\-source проект с огромной кодовой базой\. Лучший из лучших, если говорить о качестве кода, учитывая его размеры и открытость\. Ведь кому, как не разработчикам инструментов для компиляторов, лучше знать о возможностях языка и правильном их использовании\. Их код всегда на высоте, а найти ошибки в нём всегда вызов для нашего анализатора, который мы принимаем\.

![1126_LLVM_part1_ru/image1.png](https://import.viva64.com/docx/blog/1126_LLVM_part1_ru/image1.png)

Пару месяцев назад у LLVM вышла в релиз 18\-я версия, поэтому настало время в очередной раз убедиться в качестве их кода\. Для тех, кому интересно, наши предыдущие статьи про проверку можно прочитать [тут](https://pvs-studio.ru/ru/blog/posts/cpp/0871/) и [тут](https://pvs-studio.ru/ru/blog/posts/cpp/1003/)\.

Для [нас](https://pvs-studio.ru/ru/pvs-studio/) это всегда особые проверки, ведь, по сути, статические анализаторы выполняют схожую с компиляторами работу для проведения анализа\. А компиляторы также проводят статический анализ для выдачи предупреждений\. Практически двоюродные братья\. Но каждый из них хорош в своём деле\. Доказательством этого как раз служит эта статья\. Компилятор скомпилировал наш анализатор, причём так, чтобы тот работал \(кстати, именно Clang, входящий в состав LLVM, и у нас даже есть [статья](https://pvs-studio.ru/ru/blog/posts/cpp/0830/) о переходе на него с MSVC\)\. Ну а наш анализатор нашёл ошибки в компиляторе\. Чем не доказательство синергии?

Версия LLVM, на которой производилась проверка — [LLVM 18\.1\.0](https://github.com/llvm/llvm-project/tree/llvmorg-18.1.0)\. 

**Фрагмент N1**

Пример, как в простыне из условий может появиться логическая ошибка, приводящая к недостижимому коду\.

```cpp
if (Tok->is(tok::hash)) {
  // Start of a macro expansion.
  First = Tok;
  Tok = Next;
  if (Tok)
    Tok = Tok->getNextNonComment();
} else if (Tok->is(tok::hashhash)) {
  // Concatenation. Skip.
  Tok = Next;
  if (Tok)
    Tok = Tok->getNextNonComment();
} else if (Keywords.isVerilogQualifier(*Tok) ||
           Keywords.isVerilogIdentifier(*Tok)) {
  First = Tok;
  Tok = Next;
  // The name may have dots like `interface_foo.modport_foo`.
  while (Tok && Tok->isOneOf(tok::period, tok::coloncolon) &&
         (Tok = Tok->getNextNonComment())) {
    if (Keywords.isVerilogIdentifier(*Tok))
      Tok = Tok->getNextNonComment();
  }
} else if (!Next) {
  Tok = nullptr;
} else if (Tok->is(tok::l_paren)) {
  // Make sure the parenthesized list is a drive strength. Otherwise the
  // statement may be a module instantiation in which case we have already
  // found the instance name.
  if (Next->isOneOf(
          Keywords.kw_highz0, Keywords.kw_highz1, Keywords.kw_large,
          Keywords.kw_medium, Keywords.kw_pull0, Keywords.kw_pull1,
          Keywords.kw_small, Keywords.kw_strong0, Keywords.kw_strong1,
          Keywords.kw_supply0, Keywords.kw_supply1, Keywords.kw_weak0,
          Keywords.kw_weak1)) {
    Tok->setType(TT_VerilogStrength);
    Tok = Tok->MatchingParen;
    if (Tok) {
      Tok->setType(TT_VerilogStrength);
      Tok = Tok->getNextNonComment();
    }
  } else {
    break;
  }
} else if (Tok->is(tok::hash)) {
  if (Next->is(tok::l_paren))
    Next = Next->MatchingParen;
  if (Next)
    Tok = Next->getNextNonComment();
}
```

Предупреждение анализатора: 

[V517](https://pvs-studio.ru/ru/docs/warnings/v517/) The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: [3016](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Format/TokenAnnotator.cpp#L3016), [3058](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Format/TokenAnnotator.cpp#L3058)\. TokenAnnotator\.cpp

Давайте посмотрим внимательнее\. В первом условии стоит проверка _Tok\-\>is\(tok::hash\)_\. В последнем условии в конструкции _else if_ стоит аналогичная проверка\. При этом токен, с которым работают, не меняется\. Таким образом, код в последнем _else if_ никогда не выполнится\. В этом случае это критично, так как код под этими условиями разный\.

```cpp
if (Tok->is(tok::hash)) {
  // Start of a macro expansion.
  First = Tok;
  Tok = Next;
  if (Tok)
    Tok = Tok->getNextNonComment();
} else
....
else if (Tok->is(tok::hash)) {
  if (Next->is(tok::l_paren))
    Next = Next->MatchingParen;
  if (Next)
    Tok = Next->getNextNonComment();
}
```

Возможно, такую конструкцию стоило бы заменить на _switch_, и тогда проще было бы заметить ошибку, но это уже дело вкуса\.

**Фрагмент N2**

Интересный фрагмент кода, которым я бы хотел с вами поделиться:

```cpp
assert(bArgs.size() == reduc.size() + needsUniv ? 1 : 0);
```

Предупреждения анализатора: 

* [V502](https://pvs-studio.ru/ru/docs/warnings/v502/) Perhaps the '?:' operator works in a different way than it was expected\. The '?:' operator has a lower priority than the '\+' operator\. LoopEmitter\.cpp [983](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/mlir/lib/Dialect/SparseTensor/Transforms/Utils/LoopEmitter.cpp#L983)
* [V502](https://pvs-studio.ru/ru/docs/warnings/v502/) Perhaps the '?:' operator works in a different way than it was expected\. The '?:' operator has a lower priority than the '\+' operator\. LoopEmitter\.cpp [1039](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/mlir/lib/Dialect/SparseTensor/Transforms/Utils/LoopEmitter.cpp#L1039)

Предупреждения два, потому что этот фрагмент встречается в двух разных местах\.

Анализатор говорит, что здесь возможно неправильное использование тернарного оператора\. Давайте разбираться\.

Для начала вспомним приоритет операторов\. Приоритет в порядке убывания следующий: оператор _\+_, оператор _\=\=_, тернарный оператор\. Также необходимо отметить, что переменная _needsUniv_ имеет тип _bool_\.

Получается, что сначала произойдёт сложение результата работы _reduc\.size\(\)_ и _needsUniv_\. При этом _needsUniv_ неявно кастанётся к типу _size\_t_\. Далее результат сложения сравнится с результатом работы _bArgs\.size\(\)_\. Затем выполнится тернарный оператор, который вернёт либо 1, либо 0\. 

Согласитесь, выглядит немного странновато\. Вероятнее всего, хотели написать такой код:

```cpp
assert(bArgs.size() == reduc.size() + (needsUniv ? 1 : 0));
```

В таком случае сначала выполнится тернарный оператор, который вернёт либо 1, либо 0\. Затем это значение сложится с результатом работы _reduc\.size\(\), _а затем произойдёт сравнение с результатом работы _bArgs\.size\(\)_\.

Занимательно, что результат работы и в первом, и во втором случае один и тот же\.

Ещё занимательнее, что можно было написать так:

```cpp
assert(bArgs.size() == reduc.size() + needsUniv)
```

Результат и в этом случае был бы тот же, но уже без лишнего выполнения тернарного оператора\.

В общем, интересный случай, когда код написан неправильно, но работает правильно\.

**Фрагмент N3**

Довольно любопытное использование постфиксного инкремента\. Внимание на второй аргумент кастомной функции _Printf_, вызываемой у объекта _strm_:

```cpp
static void DumpTargetInfo(uint32_t target_idx, Target *target,
                           const char *prefix_cstr,
                           bool show_stopped_process_status, Stream &strm) 
{
  ....
  uint32_t properties = 0;
  if (target_arch.IsValid()) 
  {
    strm.Printf("%sarch=", properties++ > 0 ? ", " : " ( ");
    target_arch.DumpTriple(strm.AsRawOstream());
    properties++;
  }
}
```

Предупреждение анализатора: 

[V547](https://pvs-studio.ru/ru/docs/warnings/v547/) Expression 'properties \+\+ \> 0' is always false\. [CommandObjectTarget\.cpp:100](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Commands/CommandObjectTarget.cpp#L100)

На мой взгляд, здесь сначала хотели провести сравнение _properties_ c нулём, и, чтобы дальше не писать лишний код с инкрементацией переменной _properties_, решили тут же воспользоваться постфиксным инкрементом\.

Таким образом, для сравнения бы использовалось старое значение переменной, но при этом оно всё равно увеличилось бы на единицу\. Но тогда непонятно, зачем дальше её ещё раз проинкрементировали\.

А затем приходит понимание, что тернарный оператор здесь вообще не нужен\. Возможно, когда\-то этот фрагмент был в цикле\. Напишите ваши варианты в комментариях\.

**Фрагменты N4\-8**

Рассмотрим следующий код и срабатывание PVS\-Studio:

```cpp
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
                            const ASTContext &Context, bool Canonical) 
{
  ....
 
  if (FirstStmt->getStmtClass() != FirstStmt->getStmtClass())
    return false;
  ....
}
```

Предупреждение анализатора: 

[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'FirstStmt\-\>getStmtClass\(\)' to the left and to the right of the '\!\=' operator\. [ASTUtils\.cpp:99](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp#L99)

Казалось бы, мелочь\. Ну перепутали _FirstStmt_ и _SecondStmt,_ чего бухтеть\-то?

А потом встречаем такой код:

```cpp
static bool sameFunctionParameterTypeLists(Sema &S,
                                           const OverloadCandidate &Cand1,
                                           const OverloadCandidate &Cand2) {
  if (!Cand1.Function || !Cand2.Function)
    return false;

  FunctionDecl *Fn1 = Cand1.Function;
  FunctionDecl *Fn2 = Cand2.Function;

  if (Fn1->isVariadic() != Fn1->isVariadic())
    return false;
  ....
}
```

Предупреждение анализатора: 

[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions to the left and to the right of the '\!\=' operator: Fn1\-\>isVariadic\(\) \!\= Fn1\-\>isVariadic\(\)\. [SemaOverload\.cpp:10190](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaOverload.cpp#L10190)

Думаешь: "Ну ошиблись второй раз, с кем не бывает"\.

А потом видишь такой код:

```cpp
if (G1->Rank < G1->Rank)
  G1->Group = G2;
else {
  G2->Group = G1;
}
```

Предупреждение анализатора: 

[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions to the left and to the right of the '<' operator: G1\-\>Rank < G1\-\>Rank\. [SCCIterator\.h:285](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/include/llvm/ADT/SCCIterator.h#L285)

Начинают закрадываться сомнения\.

А потом появляется такой фрагмент:

```cpp
ValueBoundsConstraintSet::areOverlappingSlices(MLIRContext *ctx,
                                               HyperrectangularSlice slice1,
                                               HyperrectangularSlice slice2) {
  assert(slice1.getMixedOffsets().size() == slice1.getMixedOffsets().size()
      && "expected slices of same rank");
  assert(slice1.getMixedSizes().size()   == slice1.getMixedSizes().size() 
      && "expected slices of same rank");
  assert(slice1.getMixedStrides().size() == slice1.getMixedStrides().size() 
      && "expected slices of same rank");
  ....
}
```

Предупреждения анализатора: 

* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedOffsets\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. [ValueBoundsOpInterface\.cpp:581](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp#L581)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedSizes\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. ValueBoundsOpInterface\.cpp:583
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedStrides\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. ValueBoundsOpInterface\.cpp:585

И ещё один почти такой же: 

```cpp
ValueBoundsConstraintSet::areEquivalentSlices(MLIRContext *ctx,
                                              HyperrectangularSlice slice1,
                                              HyperrectangularSlice slice2) {
  assert(slice1.getMixedOffsets().size() == slice1.getMixedOffsets().size() 
      && "expected slices of same rank");
  assert(slice1.getMixedSizes().size()   == slice1.getMixedSizes().size() 
      && "expected slices of same rank");
  assert(slice1.getMixedStrides().size() == slice1.getMixedStrides().size() 
      && "expected slices of same rank");
  ....
}
```

Предупреждения анализатора: 

* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedOffsets\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. [ValueBoundsOpInterface\.cpp:646](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp#L646)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedSizes\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. ValueBoundsOpInterface\.cpp:648
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'slice1\.getMixedStrides\(\)\.size\(\)' to the left and to the right of the '\=\=' operator\. ValueBoundsOpInterface\.cpp:650

И удивляешься: неужели так много ошибок может тихо жить в коде? И ведь из\-за каждого из таких мест что\-то может отвалиться или работать не так, как задумано\.

Кстати, видя все эти _Fn1_, _G1_, _slice1_, вспоминается статья коллеги "[Ноль, один, два, Фредди заберёт тебя](https://pvs-studio.ru/ru/blog/posts/cpp/0713/)"\.

Ну и на закуску аналогичные предупреждения:

* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'EltRange\.getEnd\(\) \>\= Range\.getEnd\(\)' to the left and to the right of the '\|\|' operator\. [HTMLLogger\.cpp:421](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp#L421)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'SrcExpr\.get\(\)\-\>containsErrors\(\)' to the left and to the right of the '\|\|' operator\. [SemaCast\.cpp:2938](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaCast.cpp#L2938)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'ND\-\>getDeclContext\(\)' to the left and to the right of the '\!\=' operator\. [SemaDeclCXX\.cpp:4391](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaDeclCXX.cpp#L4391)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'DepType \!\= OMPC\_DOACROSS\_source' to the left and to the right of the '&&' operator\. [SemaOpenMP\.cpp:24348](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaOpenMP.cpp#L24348)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions '\!OldMethod\-\>isStatic\(\)' to the left and to the right of the '&&' operator\. [SemaOverload\.cpp:1425](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaOverload.cpp#L1425)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'lldb::eTypeClassUnion' to the left and to the right of the '\|' operator\. [JSONUtils\.cpp:139](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/tools/lldb-dap/JSONUtils.cpp#L139)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions to the left and to the right of the '&&' operator: \!BFI &&\!BFI\. [JumpThreading\.cpp:2531](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/Transforms/Scalar/JumpThreading.cpp#L2531)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'BI\-\>isConditional\(\)' to the left and to the right of the '&&' operator\. [VPlanHCFGBuilder\.cpp:401](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/Transforms/Vectorize/VPlanHCFGBuilder.cpp#L401)
* [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions to the left and to the right of the '\=\=' operator: getNumRows\(\) \=\= getNumRows\(\)\. [Simplex\.cpp:108](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/mlir/lib/Analysis/Presburger/Simplex.cpp#L108)



**Фрагмент N9**

А вот ещё интересный фрагмент кода\. Попробуйте самостоятельно разобраться, где тут ошибка:

```cpp
const Expr *CGOpenMPRuntime::getNumTeamsExprForTargetDirective(
    CodeGenFunction &CGF, const OMPExecutableDirective &D, int32_t &MinTeamsVal,
    int32_t &MaxTeamsVal) 
{
  ....
  if (isOpenMPParallelDirective(NestedDir->getDirectiveKind()) ||
          isOpenMPSimdDirective(NestedDir->getDirectiveKind())) {
    MinTeamsVal = MaxTeamsVal = 1;
    return nullptr;
  }
  MinTeamsVal = MaxTeamsVal = 1;
  return nullptr;
  ....
}
```

Давайте посмотрим, что говорит анализатор\.

Предупреждение анализатора: 

[V523](https://pvs-studio.ru/ru/docs/warnings/v523/) The 'then' statement is equivalent to the subsequent code fragment\. [CGOpenMPRuntime\.cpp:6040](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/CodeGen/CGOpenMPRuntime.cpp#L6040), [6036](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/CodeGen/CGOpenMPRuntime.cpp#L6036)

Он говорит, что код после _if_ точно такой же, как и в _then_\-ветке\. Соответственно, здесь лишняя либо проверка, либо же часть кода после неё\.

**Фрагмент N10**

Следующий фрагмент выглядит весьма странно:

```cpp
explicit MapLattice(Container C) { C = std::move(C); }
```

Предупреждение анализатора: 

[V570](https://pvs-studio.ru/ru/docs/warnings/v570/) The 'C' variable is assigned to itself\. [MapLattice\.h:52](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/include/clang/Analysis/FlowSensitive/MapLattice.h#L52)

И неспроста\. Внутри тела конструктора класса MapLattice произошло [сокрытие](https://en.wikipedia.org/wiki/Variable_shadowing) нестатического поля с тем же именем, что и у параметра\. И в этом фрагменте забыли явно указать _this_ слева от оператора присваивания\.

Небольшая правка, и код заработает так, как и ожидалось:

```cpp
explicit MapLattice(Container C) { this->C = std::move(C); }
```

Хотя, на мой взгляд, куда изящнее было бы воспользоваться списком инициализации конструктора:

```cpp
explicit MapLattice(Container C) : C { std::move(C) } {};
```

В таком случае сокрытия не происходит из\-за правил поиска имён \([тык](https://timsong-cpp.github.io/cppwp/n4950/class.init#class.base.init-2), [тык](https://timsong-cpp.github.io/cppwp/n4950/class.init#class.base.init-15)\)\.

Ну а вообще, в имена приватных полей можно добавлять префикс или постфикс, чтобы было легче отличать их в коде от параметров\.

**Фрагмент N11**

У вас когда\-нибудь возникало чувство, будто вы что\-то забыли? Например, вы не помните, взяли ли вы с собой ключи\. Начинаете ощупывать карманы и не можете найти их\.

Так вот, в следующем фрагменте как будто забыли использовать результат работы функции:

```cpp
ScalarEvolution::getRangeRefIter(const SCEV *S,
                                 ScalarEvolution::RangeSignHint SignHint) 
{
  ....
  for (const SCEV *P : reverse(drop_begin(WorkList))) {
    getRangeRef(P, SignHint);
    ....
  }
  ....
}
```

Предупреждение анализатора: 

[V530](https://pvs-studio.ru/ru/docs/warnings/v530/) The return value of function 'getRangeRef' is required to be utilized\. [ScalarEvolution\.cpp:6587](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/Analysis/ScalarEvolution.cpp#L6587)

Можно заметить, что результат работы _getRangeRef_ никак не используется\. 

А вот и сигнатура этой функции, которая подтверждает, что результат работы есть:

```cpp
const ConstantRange &getRangeRef(const SCEV *S, RangeSignHint Hint,
                                 unsigned Depth = 0);
```

Да и далее по коду видно, что в других местах её результат используется\.

Но не всё так просто\. Иногда наши чувства нас подводят и оказывается, что ключи всё это время были прямо у нас в руке\.

Над самим фрагментом есть такой комментарий:

```cpp
// Use getRangeRef to compute ranges for items in the worklist in reverse
// order. This will force ranges for earlier operands to be computed before
// their users in most cases.
```

Вероятнее всего, разработчики осознанно написали этот код\. Другой вопрос в том, что использовать таким образом функцию с названием _get_ и, возможно, заставлять её выполнять несколько дел одновременно — плохая практика, которая может ввести в заблуждение других разработчиков\.

Таким образом, формально анализатор оказался прав, но фактически ошибки здесь нет\. Для таких случаев у нас есть способы подавления отдельных срабатываний\. Например, с помощью комментария в коде:

```cpp
for (const SCEV *P : reverse(drop_begin(WorkList))) {
    getRangeRef(P, SignHint); //-V530
```

**Фрагмент N12**

Для самых внимательных\. Найдите три отличия кода в _if_ и _else_ ветках\. Ладно, хотя бы одно:

```cpp
case OptionParser::eOptionalArgument:
  if (OptionParser::GetOptionArgument() != nullptr) {
    option_element_vector.push_back(OptionArgElement(
        opt_defs_index,
        FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
                          args),
        FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
                          args)));
  } else {
    option_element_vector.push_back(OptionArgElement(
        opt_defs_index,
        FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
                          args),
        FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
                          args)));
  }
```

И вы абсолютно правы\. Их нет\.

Предупреждение анализатора: 

[V523](https://pvs-studio.ru/ru/docs/warnings/v523/) The 'then' statement is equivalent to the 'else' statement\. Options\.cpp [1212](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Interpreter/Options.cpp#L1212)

**Заключение**

Всё хорошее имеет свойство заканчиваться\. Но к статье это не относится\. Большому проекту — большая статья, а ещё лучше две\. В этот раз я решил придерживаться второго варианта\. В следующей части будут рассмотрены ошибки гораздо серьёзнее, чем в этой \(спойлер: связанные с UB\)\.

Даже лучшие из нас совершают ошибки \(и не только в коде\)\. Что уж говорить про рядовых пользователей\. Но ошибки это не повод расстраиваться\. Ошибки — это повод становиться лучше и расти \(можете говорить это вашему тимлиду каждый раз, когда что\-то отваливается на проде\)\. 

Ну а чтобы избегать ошибок в коде, нужно пробовать разные способы их поиска\. Например, помимо покрытия кода тестами и code review можно использовать инструменты динамического и статического анализа кода\.

Интересно узнать, какие ошибки есть в вашем коде? Тогда вы можете проверить это [бесплатно](https://pvs-studio.ru/ru/blog/posts/0614/)\.