Как фрагмент CSS разметки сломал C++ компилятор

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Picture 1

В методологии статического анализа применяются разные технологии. Одна из них — препроцессирование файлов непосредственно перед их анализом. Препроцессированные файлы создаёт компилятор, запускаемый в специальном режиме работы. К сожалению, этот режим не очень хорошо тестируется, как показывает наш многолетний опыт разработки статического анализатора кода. В этой заметке приведу пример свеженайденного бага в С++ компиляторе от Microsoft.

Введение


Для демонстрации возможностей статического анализатора PVS-Studio наша команда проверяет исходный код Open Source проектов. Это существенный вклад в качество открытого программного обеспечения, дополнительная реклама и тестирование анализатора. Иногда мы выявляем очень необычные проблемы в компиляторах, с которыми сложно что-то сделать на стороне анализатора. Так, коллега недавно писал статью "Перестал анализироваться файл с директивой 'import' (compiler internal error 'msc1.cpp'). Что делать?", чтобы помочь нашим пользователям в решении «чужой» проблемы.

Причём тут CSS?


Не менее интересный баг был найден мной только что при проверке большого проекта. Компилятор Microsoft для C/C++ версии 19.16.27027.1 (Visual Studio v15.9.9) выдал такую ошибку при анализе нескольких файлов:

fatal error C1021: invalid preprocessor command 'tooltiphint'

Очевидно, это не директива препроцессора, но что же это? Это фрагмент CSS кода:

#tooltiphint {
  position: fixed;
  width: 50em;
  margin-left: -25em;
  left: 50%;
  padding: 10px;
  border: 1px solid #b0b0b0;
  border-radius: 2px;
  box-shadow: 1px 1px 7px black;
  background-color: #c0c0c0;
  z-index: 2;
}

После просмотра фрагмента стало понятно, что компилятор ошибается во время препроцессирования файла, но при этом код компилируется успешно. Фрагмент CSS кода является частью строкового литерала C++ кода. Вот так выглядит пример кода, достаточный для повторения ошибки:

std::string test = R"<<<(
<style type="text/css">
body { color:#000000; background-color:#ffffff }
body { font-family:Helvetica, sans-serif; font-size:10pt }

#tooltiphint {
  position: fixed;
  width: 50em;
  margin-left: -25em;
  left: 50%;
  padding: 10px;
  border: 1px solid #b0b0b0;
  border-radius: 2px;
  box-shadow: 1px 1px 7px black;
  background-color: #c0c0c0;
  z-index: 2;
}
.macro {
  color: darkmagenta;
  background-color:LemonChiffon;
  /* Macros are position: relative to provide base for expansions. */
  position: relative;
}

</style>
</head>
<body>)<<<";

Приведённый фрагмент кода не мешает успешной компиляции, но, в то же время, в режиме препроцессирования (флаг /P) возникнет ошибка.

Вот такая непростая жизнь у разработчиков статических анализаторов :). Вроде виноват не PVS-Studio, но всё равно мы должны заниматься с подобными проблемами. Впрочем, в этом нет чего-то нового. С некоторыми другими подобными случаями можно познакомиться в статье "PVS-Studio и враждебная среда обитания".

Заключение


Эта проблема будет отправлена в официальный баг-трекер, но оперативное решение проблемы вряд ли возможно. Например, проблема с директивой #import, выявленная нами несколько месяцев назад, о которой я писал вначале, будет исправлена только в следующем релизе Visual Studio. Т.к. релиз новой Visual Studio 2019 состоится уже через неделю, скорее всего, этот баг не успеют исправить к этой дате. Пользователям PVS-Studio рекомендуем также воспользоваться макросом PVS_STUDIO.



Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. How the CSS markup fragment broke the C++ compiler

Интересные статьи

Интересные статьи

Пролог: internal is new public Каждый из нас мечтал о проекте, где всё будет сделано правильно. Это кажется вполне естественным. Как только ты узнаёшь о самой возможности писать хороший код, как...
В то время, когда россияне пытаются бороться против изоляции рунета, жители Европейского союза выходят на митинги с требованиями остановить принятие законов, регламентирующие использование платфо...
+БОНУС: как включать классы друг в друга в C++ Привет, Хабр! Эта статья — прямое продолжение статьи Искусство парсинга или DOM собственными руками, где мы разобрали HTML-документ и построили на ...
Самый первый эпик фейл по масштабу — это когда мы ночью перед конференцией внезапно обнаружили на баннере 3 на 10 метров опечатку. Такую, которую не заклеишь. А висел этот баннер над стойкой реги...
Итак, вы решили сделать новый проект. И проект этот — веб-приложение. Сколько времени уйдёт на создание базового прототипа? Насколько это сложно? Что должен уже со старта уметь современный веб-са...