Как стать автором
Обновить

Комментарии 17

Я когда-то тоже реализовывал авто-подсказки в Visual Studio с помощью libclang. Самым неприятным было столкнуться с недостаточной производительностью для набора текста в реальном времени. Пришлось реализовывать непростой конечный автомат, который асинхронно запускал libclang и сбрасывал результаты при превышении лимита времени или перемене фокуса ввода.
Да. Всё так. :) Приходится извращаться.

В QtCreator это решается (или раньше решалось) переустановкой таймера на каждое изменение текста. Как только пользователь прекращает менять текст — срабатывает таймер и вычисляется подсветка и автодополнение. Также автодополнение вычисляется при явном запросе по хоткею.
В случае с Clang это действительно работает плохо. Хорошо бы сработал запрос разбора в момент, когда начинается написание идентификатора — т.е. пока пользователь вводит только символы, допустимые в идентификаторе, и не использует навигацию, можно вычислять автодополнение так, как будто идентификатор только начали вводить, и затем вручную фильтровать варианты, используя уже введённый текст.
Жаль, мне так и не довелось проверить эту идею на практике.

Смотрю в QtCreator 4.7 — clang backend опционален. Его можно не включать и в этом случае будет использоваться родной анализатор. Дальше уже каждый как ему нравится.
Всё так. Только начиная с 4.7 clang backend по-умолчанию включен. И, насколько я понял из комментариев к релизу, развивать они дальше будут именно эту ветку, а не самописный парсер.
Такое IDE зная нечто, гм, большее о том, что происходит, предлагает вполне ожидаемый вариант: создать функцию из контекста использования.

Есть ещё вариант: если название похоже на другое название, то надо предложить исправить опечатку; clang это умеет делать, и QtCreator ЕМНИП умеет этим пользоваться.
Так что не всё так плохо с clang.

Да. Есть такая фича у clang. А теперь история о том, как я её нашёл. В попытках поднять перформанс работы связки IDE + clang я полез смотреть, а на что же clang тратит процессорное время. И довольно быстро обнаружил в первой двадцатке функций алгоритм подсчёта дистанции Левенштейна. «Что за фигня? — думаю, — Зачем?». Полез копаться дальше и наткнулся как раз на алгоритм подбора подходящего идентификатора к «неправильно написанному» в рамках видимого скоупа. Хорошая фича для компилятора. Но при работе в связке с IDE, когда компиляция запускается буквально на каждый чих — эмм… Не уверен. При условии, что невалидных идентификаторов может быть в достатке по тем или иным причинам. Пробежаться по скоупу может может и код, который автокомплит делает, а не FixIt-хинты для найденных ошибок. При работе в связке с IDE, разумеется.

Как Вам clangd после этого? Нужно/ненужно, поможет/не поможет?

Я не могу сходу ответить на этот вопрос — надо изучать реализацию. Могу лишь сказать, что наличие возможности в рамках одного проекта работать над исходниками с разными опциями компиляции/тулчейнами (что может серьёзно отражаться на AST) наличие единого сервера для моделей кода исходников становится сомнительным бенефитом. В контексте С++, разумеется. Но, возможно, это проблема как-то решена.

Недавно было интересное обсуждение в рассылки clang,
называлось "clangd/libclang: how to emulate other compilers?"


Думаю там можно посмотреть как clangd собирается бороться с этим.


Интересная и уникальная возможность clangd на мой взгляд это индексация
во время компиляции, собрал проект — вот тебе и индекс, а то переключаешься
с проекта на проект и начинается — компиляция того что поменяли, плюс IDE
индексы перестраивает...

Интересная и уникальная возможность clangd на мой взгляд это индексация
во время компиляции

Это работает, если есть возможность пересадить пользователя IDE на нужный компилятор (причем, желательно, свежий, а еще и свой форк, а то иначе функциональность IDE начинает зависить от того, что приняли в upstream, а что еще не успели). Кроме Apple+Xcode (которые, собственно, этот index-while-building и сделали) и in-house разработки в Google (которые в основном вкладываются в clangd), ни у кого такой возможности нет (ну еще MSVC, но там отдельная история).

Мб захотите глянуть в сторону rtags — весьма хороший индексировщик C++, основанный на clang, работает в фоне в виде демона. Я его использую в своей IDE (emacs + cedet + rtags), ошибки подсветки несуществующего foo() у себя не наблюдаю.

Хотя, если честно, я обычно отключаю автодополнение/навигацию полностью, за исключением самых «тупых» функций (базовое текстовое автодополнение по C-/, вызов списка определений ф-й в данном файле, перескочить между cpp/h, и тому подобное). Скорость решает, ради неё можно жертвовать функциональностью. 5 секунд реакции на нажатие хоткея — это вечность (хотя 5 секунд в rtags не видел ни разу), IDE должна реагировать на нажатие клавиши за полсекунды. Замену имён и кодогенерацию можно опять же делать чисто текстовыми средствами (vim :bufdo и аналоги, сниппеты).

Спасибо за статью!


(disclamer: я — разработчик в команде CLion, но я не обязательно сейчас рассказываю про наши планы или транслирую мнение компании, просто абстрактно рассуждаю исходя из своего опыта).


Описанная картина довольно мрачная и примерно соответствует действительности, но некоторые факты позволяют смотреть в будущее более позитивно:


  • Поддержка IDE является одной из целей проекта, и, хотя пока она в достаточно примитивном состоянии, для нее заложен неплохой базис
  • Достаточно много проектов вкладывается в IDE-составляющую clang, и достаточно много людей работает над этим на постоянной основе
  • Если не использовать libclang (и не заботиться о том, чтобы работать со старыми стабильными версиями clang-а из коробки), то можно сделать достаточно много через C++ API уже сейчас.

В CLion 2018.2 мы включили подсветку ошибок через clang (по умолчанию в Linux и Mac, Windows на подходе), ограничимся мы ли только этим, или пойдем дальше — вопрос, требующий исследования, но смотрим мы на это все с осторожным оптимизмом.


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


При этом все те оптимизации, которые позволяют IDE быть быстрее компилятора — это же не магия какая-то, возможная потому, что мы в IDE — надо сделать все ровно то же самое, что и компилятор, только в большем объеме сложнее (всякие ленивости и инкрементальности не достаются бесплатно с точки зрения поддержки и количества багов). Сэкономить негде — это же C++, в нем любой пункт из стандарта может повлиять (и влияет) на что угодно — от индексации до форматирования.


Чуть-чуть по статье:


И если самописная его версия прекрасно "понимает", что имеет дело с полуфабрикатом, то вот clang — нет. И очень сильно удивляется. Что получится в результате такого удивления — зависит "от", как говорится.

Это не принципиальное ограничение компилятора, это вопрос качества восстановления от ошибок. Это больше искусство, чем наука, и во многих случаях clang работает хуже, чем, например, CLion (а в каких-то — лучше). Но "поругаться на ошибку и пойти дальше, предположив, что же имелось в виду" — базовая функциональность компилятора, и в clang тоже потрачено много усилий на это. IDE, конечно, может немножечко пожертвовать корректностью работы на правильном, но экзотическом коде, чтобы лучше восстанавливаться на недописанном, но неправильном, но вообще такие жертвы обычно выходят боком через какое-то время (когда в STL написали как раз то, про что мы год назад подумали "ну, так нормальные люди не пишут").


Первое, очевидное, и, на самом деле, единственное решение — это использовать динамически генерируемые precompiled headers.

И libclang, и clangd умеют работать с т. н. "preambles" — это автоматически генерируемые PCH для всей пачки #include-директив, которые идут в начале файла. Работает очень хорошо — если первая подсветка — примерно как компиляция, то последующее редактирование — гораздо приятнее.


Очевидный недостаток — поменял что-то в хедере — и вся преамбла инвалидировалась, перестраивай заново. В этом месте и находится пока что самый очевидный вектор атаки — сделать так, чтобы можно было переиспользовать куски AST из хедеров, которые были не затронуты изменением. Это сложно, это нужно долго исследовать, но если это сделать — жизнь начнется совсем другая. Благо, работа по реализации модулей — очень связана с этим (грубо говоря, надо динамически понять, можно ли использовать хедер так, как будто он был бы модулем), и тоже активно ведется.


Ну или можно подождать, когда все на модули перейдут, ха-ха.


Да, какие-то идеи про то, как должен работать C++-движок в IDE на основе clang реализовать невозможно, и есть вероятность, что "идеальная C++ IDE" должна работать на других принципах — если это так, то мы туда придем, а по дороге обеспечим (в том числе, и через clang) достойный user experience :)


С удовольствием могу ответить на какие-то более конкретные технические вопросы, если они возникнут.

Вам спасибо за развёрнутый комментарий. Читать было интересно.


Описанная картина довольно мрачная и примерно соответствует действительности, но некоторые факты позволяют смотреть в будущее более позитивно

С моей ТЗ основная проблема в том, что очень сложно (в данном случае) усидеть на двух стульях. Всё-таки задача просто компиляции и задача поддержки IDE — достаточно разные, чтобы можно было эффективно их обьеденить под одной крышкой. Да, интегрируют, да, пользуются, но за прошедшие семь лет развития и того, и другого (а это довольно внушительный срок) — результат не очень впечатляет. По крайней мере меня. :)


Если не использовать libclang (и не заботиться о том, чтобы работать со старыми стабильными версиями clang-а из коробки), то можно сделать достаточно много через C++ API уже сейчас

Именно на этом API и сидел. В том числе для того, чтобы исключать лишние преобразования типов и дополнительного менеджмента.


Зачем — потому что хочется сконцентрироваться на создании крутых фич и полировке функционала, который реально полезен пользователям, а не гнаться за стандартами, творчески перереализовывая огромное количество кода из компилятора.

Хорошая цель. :) Насколько достижимая — вот вопрос. Но это как раз в тему форка кланга и пиления вполне конкретных фичей, специфичных для IDE, без оглядки на функции компиляции. Кстати, хороший вопрос возник: а что IDE должно делать с constexpr-функциями? А если они сложные и ресурсоемкие? :)


И libclang, и clangd умеют работать с т. н. "preambles" — это автоматически генерируемые PCH для всей пачки #include-директив, которые идут в начале файла. Работает очень хорошо — если первая подсветка — примерно как компиляция, то последующее редактирование — гораздо приятнее.

Да. И я даже ходил в эту сторону. Но есть нюанс. Даже два. Первый описан в статье — ошибки в хедере, и создание преамбулы обламывается. А ошибки возникали у меня как-то слишком часто (и в основном в системных заголовках). Второй — pch можно перекомпилировать реже, а польза от него почти такая же. Только в редких проектах исходники включают вязанку заголовков, и все они из project root. Чаще основной объем приходится на библиотеки/фреймворки. А так — да, что pch, что преамбула — это сериализованное AST. Шагов до комбинированния итогового AST из нескольких — совсем чуть чуть осталось. Но для того, чтобы такое комбинированния было возможно — сам clang парсер должен быть глубоко интегрирован в IDE. То есть IDE (та часть, которая занимается обработкой кода — с индексами и прочим) становится как бы фронтендом для библиотек clang'а. Но чтобы так сделать — довольно существенно всё перепиливать надо.

ошибки в хедере, и создание преамбулы обламывается

По крайней мере, сейчас это не так. Там есть всякие досадные упущения (например, include not found считатется fatal error, а во время парсинга по коду расставлены на это оптимизации всякие — мол, после fatal error в режиме компиляции все равно не увидим), но все они решаемые, не принципиальные.

Угу. Да. Вижу. github.com/llvm-mirror/clang/blob/4418c5b56fafe30928f418be4eaee39d275628dc/lib/Frontend/FrontendActions.cpp#L157

На 3.7 мне этот патч приходилось самому класть. А судя по блейму, появилось это не раньше пятой версии.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории