Примечание: это не письменный отчет онлайн-салона, а мой собственный отчет об исследовании после встречи.
Введение
Недавно Rust официально запустил Клуб чтения исходного кода Rust, см.Rust Code Reading Club.
Цель этого мероприятия — привлечь больше людей к участию в разработке компилятора Rust. Исходный код компилятора Rust поясняет сам руководитель языковой рабочей группы Нико.
В этом событии не будет повторов, только участие в реальном времени.
Условия участия:
- понимать английский
- Некоторое знание Rust, не обязательно все уголки Rust
- Базовые знания компилятора не обязательно требуются, но чтениеRustc Dev guide (Китайская версия также нуждается в вашем вкладе:GitHub.com/ржавчина C/…)
Действия Этапа 1:
- Время: 2021-11-04 12:00 - 13:30 по восточному времени США (see in your time zone)
- способ масштабирования
- содержание:compiler/rustc_resolve/src
- Слайды:взломать MD.IO/@как STC-читать…
Подготовка перед участием:
- Прочитать книгу "мозг программистов"
- Прочитайте руководство разработчика Rustc, чтобы понять процесс компиляции Rust.
- читатьcompiler/rustc_resolve/srcДокументация, связанная с библиотекой
Rust Feishu Group Клуб чтения Rust Source
В ответ на официальное мероприятие я также планирую организовывать онлайн-салон чтения исходного кода каждую субботу или воскресенье в китайском сообществе Feishu Rust.
Содержание заключается в том, чтобы следить за официальным содержанием и учиться вместе.
И будет записывать еженедельное исследование исходного кода, выводить текст поведения и делиться им со всеми.
Как присоединиться к китайскому сообществу Feishu Rust?
Готов к работе
Книга-рекомендация "Мозг программиста"
Rust официально рекомендует книгу «Мозг программиста» вофициальный сайт МэннингаЭлектронная версия книги доступна бесплатно. Если вы хотите перевести название книги, думаю, подойдет название «Программирование мозга»? Я слышал, что эта книга была представлена отечественными издательствами.
Книга разделена на четыре части:
- Как лучше читать код
- Думая о коде
- написать лучший код
- О совместной работе над кодом
Что делает эту книгу особенной, так это то, что в ней представлены связи между различными типами кодов и когнитивным мышлением мозга, а не просто список методов.
Поскольку Rust официально рекомендует эту книгу, это означает, что ее содержание по-прежнему имеет некоторую ценность, и вы можете пойти и прочитать ее, если вам интересно.
О трех видах неприятностей в процессе программирования
В этом выпуске представлены три типа проблем, с которыми сталкиваются разработчики в процессе программирования, упомянутые в первой главе этой книги:
- Недостаток знаний (Недостаток знаний, соответствующий долговременной памяти, LTM). Относится к проблемам, вызванным незнанием базовых языков программирования и неспособностью разработчиков использовать или понимать базовый синтаксис.
- Недостаток информации (Недостаток информации, соответствующий кратковременной памяти, кратковременной памяти, СТМ). Относится к проблеме, вызванной незнанием информации о проблемном домене, с которой имеет дело программа.
- Недостаток вычислительной мощности (Недостаток вычислительной мощности, соответствующей оперативной памяти, оперативной памяти, WM). Это относится к проблемам, вызванным отсутствием у разработчиков возможности обрабатывать весь процесс выполнения программирования.
Эти три типа проблем не только мешают разработчикам писать новые программы, но также мешают разработчикам читать существующие коды.
Поэтому, когда мы читаем исходный код, написанный другими, мы должны убедиться, что у нас есть предварительное дополнение к знаниям, недостающим для этих трех типов задач.
Мои привычки чтения исходного кода
Я читаю исходный код так же, как и читал, от общей структуры к деталям.
Сначала убедитесь, что выrustc_resolve
Контекстная информация этой библиотеки понимается, то есть второй тип проблем в трех типах проблем в процессе программирования, упомянутых выше, необходимо дополнить информацией. Новичкам, не знакомым с Rust, следует избегать проблем первого и третьего типа. Наиболее распространенной проблемой при чтении исходного кода Rust в целом является второй тип проблем, непонимание информации, с которой программа имеет дело в предметной области.
Официально рекомендуемый метод чтения
существуетОфициальный исходный код Rustc читайте в первом выпуске Slides, рекомендуется использовать широкий-глубокий-широкий (Broad - deep - broad
) метода чтения по трем абзацам.
Конкретно:
- широкий(
Broad
): понять модуль в целом. - глубокий(
Deep
): сосредоточьтесь на функции или небольшой области (которая вас интересует или вызывает вопросы). - широкий(
Broad
): вернуться ко всему модулю.
Выполните несколько раундов в соответствии с тремя вышеуказанными методами чтения абзаца, и весь код будет прочитан.
Архитектура компилятора Rustc
существуетRustc Dev GuideОбщая архитектура компилятора Rust (Rustc) описана в .
Архитектура компилятора Rustc отличается от традиционной архитектуры компилятора. Традиционная архитектура компиляторана основе обхода (pass-based
), архитектура компилятора RustПо запросу(demand-driven
) и разработан.
Архитектура компилятора на основе обхода
Так называемый обход (Pass
), то есть для кода /AST
Сканировать и обрабатывать.
Ранние компиляторы обычноSingle Pass
Да и позжеMulti Pass
, и делится на интерфейс компиляции и серверную часть. Интерфейс отвечает за генерациюAST
, а серверная часть используется для генерации машинного кода. Каждый шаг процесса компиляции абстрагируется какPass
, имя впервые было даноLLVM
Принято, а затем распространено на всю область принципов компиляции.
Траверсы делятся на две категории:
- Обход анализа, отвечающий за сбор информации для использования другими проходами, помощь в отладке или визуализации программ.
- Преобразование (transform) обход, используется для изменения потока данных или потока управления программой, например оптимизация и т.п.
Эти два типа процессов обхода также соответствуют двум основным этапам компилятора: этапу анализа и этапу синтеза. Первый создает промежуточное представление из данного исходного текста, а второй создает эквивалентную объектную программу из промежуточного представления.
Интерфейс компилятора обычно соответствует фазе анализа, а серверная часть компилятора соответствует фазе синтеза.
Внешний интерфейс компилятора включает следующие части:
- лексический анализатор
- парсер
- Семантический анализатор
- Генератор промежуточного кода
- оптимизатор кода
Генерация объектного кода выполняется серверной частью.
На этапах лексического анализа, синтаксического анализа и семантического анализа компилятор будет создавать и поддерживать важную структуру данных для отслеживания семантики переменных, то есть он будет хранить связанную информацию и информацию о связывании имен и т. д., называемую таблицей символов (Symbol Table
). Он используется во время генерации промежуточного кода и генерации объектного кода.
Традиционные архитектуры компиляторов на основе обхода, вероятно, таковы.
Архитектура компилятора, управляемая по запросу
Процесс выполнения компилятора Rust:
-
rustc
команда для выполнения компиляции -
rustc_driver
для разбора аргументов командной строки соответствующая конфигурация сборки задокументирована вrustc_interface::Config
-
rustc_lexer
Используется для лексического анализа для вывода текста исходного кода в виде лексического потока (Token Stream
) -
rustc_parse
Подготовьтесь к следующему этапу процесса компиляции. Содержит часть лексического анализа, через встроенныйStringBuffer
Структура проверяет текстовую строку и символизирует строку. Символизация – этоString interning
метод для хранения неизменяемой копии значения строки. -
rustc_parse
Другая часть - синтаксический анализ, который использует метод рекурсивного спуска (сверху вниз) для анализа, преобразуя лексический поток в абстрактное синтаксическое дерево (AST
). Точка входаrustc_parse::parser::Parser
структурныйParser::parse_crate_mod()
иParser::parse_mod()
Ассоциативный метод. Точка входа разрешения внешнего модуляrustc_expand::module::parse_external_mod
. Точка входа парсера макросовParser::parse_nonterminal()
. - расширение макросов,
AST
Проверка правильности, разрешение имен и ранняя проверка выполняются на этапах лексического анализа и синтаксического анализа процесса компиляции. - После этого
AST
Преобразовать вHIR
, использоватьHIR
Вывод типа](как stc-dev-expensive.rust-wolf.org/type-infer E…impl `процесс в паре с каждой ссылкой на трейт) ипроверка типов(процесс преобразования типов). - Впоследствии,будет
HIR
Понижен до промежуточного промежуточного представителя (MIR
). В процессе также построенTHIR
, который является более обессахареннымHIR
.THIR (Typed HIR)
Используется для шаблонной и исчерпывающей проверки. Перевести вMIR
чемHIR
более удобный. -
MIR
используется дляодолжить чек, который в основном представляет собой граф потока управления (CFG
). также ,MIR
Также используется для оптимизации, инкрементной компиляции, проверки Unsafe Rust UB и т. д. - Наконец, генерация кода (
Codegen
). будетMIR
Перевести вLLVM IR
,ПотомLLVM IR
Перейти кLLVM
Сгенерируйте целевой машинный код.
Еще одна вещь, о которой следует знать, это то, что многие значения в компилятореintern
из. Это оптимизация производительности и памяти, которую мы называемArena
Значение размещается в специальном распределителе.
В компиляторе Rust основные этапы описанного выше процесса организованы в виде набора запросов, которые вызывают друг друга.
Компилятор Rust использует систему запросов (Query System
), а не компилятор обхода (архитектура компилятора на основе обхода) большинства учебников по принципам компиляции. Rust использует систему запросов для реализации инкрементной компиляции, то есть компиляции по требованию.
Компилятор Rust изначально не был реализован на основе системы запросов, поэтому весь компилятор все еще находится в процессе преобразования в систему запросов, а весь описанный выше процесс компиляции будет преобразован в систему запросов. Но по состоянию на ноябрь 2021 года в настоящее время только HIR
прибыть LLVM IR
Этот процесс основан на запросе.
Структура исходного кода компилятора
Сам языковой проект Rust состоит из трех основных каталогов:
-
compiler/
, включая исходный кодrustc
. он состоит из многихcrate
состав, этиcrate
Вместе они составляют компилятор. -
library/
, который содержит стандартную библиотеку (core
,alloc
,std
,proc_macro
,test
) и среда выполнения Rust (backtrace
,rtstartup
,lang_start
). -
src/
,Включатьrustdoc
,clippy
,cargo
, исходный код систем сборки, языковая документация и т. д.
Долженcompiler/
Все названия упаковочной коробки начинаются сrustc_*
. Это около 50 взаимозависимыхcrate
коллекции разного размера. иrustc
crate
является фактическим двоичным файлом (т.е.main
функция); в дополнение к вызовуrustc_driver
crate
Кроме того, он фактически ничего не делает.
Почему компилятор Rust так сильно отличаетсяcrate
, в основном с учетом следующих двух факторов:
- Легко организовать код. Компилятор представляет собой огромную кодовую базу, разделенную на несколько частей.
crate
, больше способствует организации. - Ускорить время компиляции. несколько
crate
Подходит для инкрементной и параллельной компиляции.
Но поскольку система запросовrustc_middle
определено в , и многие другиеcrate
все зависит от него, и он огромен, что приводит к длительному времени компиляции. Но работа по его разделению не так проста.
Вершина всего дерева зависимостей компилятораrustc_interface
иrustc_driver
ящик.rustc_interface
представляет собой нестабильную оболочку системы запросов, которая помогает управлять различными этапами компиляции.
Запрос: компиляция драйвера по запросу
Что такое запрос? Например, есть запрос под названиемtype_of(def_id)
, пока определеннаяItem
изdef-id
(значение индекса определяется идентификаторомrustc_middle/src/hir/def_id.rs
), вы можете получитьItem
тип. Выполнение запроса кэшируется, что также является механизмом инкрементной компиляции.
let ty = tcx.type_of(some_def_id);
Однако если запроса нет в кеше, компилятор попытается найти подходящийпровайдер. Поставщик — это функция, которая была определена и связана где-то в компиляторе и содержит код для вычисления результата запроса.
Общая структура инкрементных вычислений по требованию также является производной от системы запросов компилятора Rust.Salsa. ты можешь пройтиSalsa BOOK
Узнайте больше о том, как работает система запросов.
Чтение исходного кода: компонент разрешения именrustc_resolve
Содержание первого выпуска чтения исходного кода сосредоточено наrustc_resolve
Библиотеки, связанные с разрешением имен.
После предыдущего понимания архитектуры компилятора Rust мы знаем, чтоrustc_resolve
Разрешение имен происходит на этапе синтаксического анализа и служит для создания окончательного абстрактного синтаксического дерева, поэтому эта библиотека не использует систему запросов.
Это также является причиной того, что эта библиотека указана в первом выпуске чтения исходного кода, Если она не появится, это будет связано с относительно сложной системой запросов.
crate
Здесь строятся модули, пути к макросам, импорты модулей, выражения, типы, паттерны, метки (label
) и время жизни решаются здесь
Разрешение имен в зависимости от типа (методы, поля, ассоциации) происходит вrustc_typeck
начальство.
Разрешение имен в Rust
Изучив информацию, связанную с разрешением имен, я узнал, что компилятор Rust был представлен в 2016 году.RFC 1560для улучшения процесса разрешения имен.
До этого разрешение имен обрабатывалось компилятором на ранней стадии, после того как AST был понижен до HIR. AST будет пройден три раза, первый проход используется для построения简化图(reduce_graph)
, второй проход для разрешения имен и третий проход для проверки неиспользуемых имен. Упрощенная диаграмма — это запись всех определений и импортов в программе.
RFC 1560 делит разрешение имен на две фазы: первая фаза происходит одновременно с расширением макроса и разрешает импорт для определения сопоставления имени с определением в области действия. Второй этап — поиск определения по имени на всей карте. Целью этого является развязка.
В настоящее время реализован RFC 1560, при расширении макроса не выполняется разрешение полного имени, разрешаются только импорт и макросы. При построении всего AST выполняется разрешение полного имени, чтобы разрешить все имена во всем ящике.
Давайте посмотрим пример:
#![allow(unused)]
fn main() {
type x = u32;
let x: x = 1;
let y: x = 2;
}
Вышеприведенный код допустим для компиляции. вx
То есть имя типа, а также имя переменной. Как Rust выполняет разрешение имен, чтобы разрешить сосуществование двух идентификаторов с одинаковыми именами?
Потому что у Rust разные пространства имен. Различные типы символов существуют в разных пространствах имен, например, типы и переменные не конфликтуют. Каждое пространство имен будет иметь свое собственное независимоеrib
(Концепции абстрактных областей, введенные внутри компилятора, такие как привязка let, область определения фигурных скобок, область определения макроса и т. д., являются стеком rib ).
Теперь мы используем официально рекомендуемый метод чтения из трех абзацев для чтения исходного кода этой библиотеки.
Общая модульная структура rustc_resolve
включено в чтениеrustc_resolve
Когда дело доходит до этой библиотеки, я начинаю с ее документации. Одинcrate
Документация может очень четко показать этоcrate
общая структура.
doc.rust-wolf.org/stable/вы достаточно жестоки…
модуль
-
build_reduced_graph
После получения фрагмента AST из макроса код в этом модуле помогает интегрировать этот фрагмент в уже частично построенную структуру модуля. -
check_unused
, как следует из названия, обнаруживает неиспользуемые структуры, перечисления и функции. -
def_collector
, создайте DefId (идентификационный идентификатор определения) для узлов AST -
diagnostics
, диагностическая информация об отказе -
imports
, методы и структуры, связанные с импортом пакетов и разборов -
late
, «позднее разрешение» — это процесс разрешения по большинству имен, кроме импорта и макроса. Он запускается, когда крейт полностью развернут и модульная структура полностью построена. Таким образом, он просто проходит через крейт и анализирует все выражения, типы и т. д. Почему нет соответсвияearly
, потому что он рассеивается наbuild_reduced_graph.rs
,macros.rs
иimports.rs
середина. -
macros
, пакет методов и структур, связанных с разбором макросов
структура
тип ошибки
-
AmbiguityError
, ошибка неоднозначности -
BindingError
, ошибка привязки -
PrivacyError
, ошибка видимости -
UseError
, используйте ошибку
тип данных
-
DeriveData
, Получить связанные данные -
ExpandHasher
, развернуть Хашер -
ModuleData
, данные узла в дереве модулей -
ExternPreludeEnty
, работа с Extern, связанными с Prelude -
NameBinding
, записывает значения, типы или определения модулей, которые могут быть закрытыми -
UsePlacementFinder
, использовать связанные
пространство имен и область действия
-
PerNS
, отдельные структуры для каждого пространства имен, вспомогательные типы -
ParentScope
, который записывает происхождение посетителя области -
Segment
, сегмент пути отображается минимально
Связанный с парсером
-
Resolver
Основной тип парсера -
ResolverArenas
, предоставляя память для других частей ящика, модель арены
перечислить
Не перечисленные здесь, некоторые типы перечисления похожи на структурную классификацию.
Traits
-
ToNameBinding
, используемый для преобразования ссылок areans в ссылки NameBinding
функция
некоторые вспомогательные функции
введите псевдоним
Некоторые псевдонимы типов задокументированы
зависит от ящика
существуетrustc_resolve
изCargo.toml
Некоторые зависимости можно увидеть вcrate
:
-
rustc_ast
, который определяет структуру данных AST, используемую внутри Rust. -
rustc_arean
, внутренний глобальный пул памяти компилятора, который используется для выделения памяти, и жизненный цикл выделенной памяти'tcx
-
rustc_middle
, основная библиотека компилятора Rust, содержит общие определения типов, используемые в других библиотеках. -
rustc_attr
, что связано со встроенными свойствами компилятора -
rustc_data_structures
, который определяет многие структуры данных, используемые внутри компилятора, включая некоторые потокобезопасные структуры данных, необходимые для параллельной компиляции. -
rustc_errors
, который определяет утилиты, обычно используемые компиляторами для сообщения об ошибках. -
rustc_expand
, для расширения макроса. -
rustc_feature
, который определяет ворота функций в компиляторе -
rustc_hir
, который определяет типы данных, связанные с HIR. -
rustc_index
, правильноusize
Оболочка NewType для внутренней индексации компилятора. -
rustc_metadata
, некоторые ссылки на метаинформацию о статических и динамических библиотеках Rust -
rustc_query_system
, система запросов Rust -
rustc_session
, обработка ошибок в процессе компиляции компилятора связана со встроенным lint -
rustc_span
, который определяет типы данных, связанные с расположением исходного кода, а также информацию, связанную с гигиеной макросов.
Выше приведено лишь перечисление некоторых основных зависимостей. На сегодняшний день (2021.11.13) библиотека разрешения имен также присоединилась к системе запросов.
Далее мы смотрим наlib.rs
Что определено в .
Видно, что вlib.rs
В основном те, которые определены в приведенной выше документации, используются для структур или типов перечисления, используемых в процессе разрешения имен.
Вот несколько наиболее простых для понимания типов:
Scope
Тип перечисления:
// 用于查找名称的特定作用域,只能用于 early 解析过程,比如 导入 和 宏,而不能用于 late 解析。
/// A specific scope in which a name can be looked up.
/// This enum is currently used only for early resolution (imports and macros),
/// but not for late resolution yet.
#[derive(Clone, Copy)]
enum Scope<'a> {
DeriveHelpers(LocalExpnId),
DeriveHelpersCompat,
MacroRules(MacroRulesScopeRef<'a>),
CrateRoot,
// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
// lint if it should be reported.
Module(Module<'a>, Option<NodeId>),
RegisteredAttrs,
MacroUsePrelude,
BuiltinAttrs,
ExternPrelude,
ToolPrelude,
StdLibPrelude,
BuiltinTypes,
}
Segment
Структура:
// path 的最小化呈现 : 段
// 比如 std::sync::Arc 这就是一个 path,其中 `::` 分开的就是段
/// A minimal representation of a path segment. We use this in resolve because we synthesize 'path
/// segments' which don't have the rest of an AST or HIR `PathSegment`.
#[derive(Clone, Copy, Debug)]
pub struct Segment {
ident: Ident,
id: Option<NodeId>,
/// Signals whether this `PathSegment` has generic arguments. Used to avoid providing
/// nonsensical suggestions.
has_generic_args: bool,
}
**LexicalScopeBinding
перечислить:**
// Item,整个块中可见
// Res,只在定义的地方可见
/// An intermediate resolution result.
///
/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
/// items are visible in their whole block, while `Res`es only from the place they are defined
/// forward.
#[derive(Debug)]
enum LexicalScopeBinding<'a> {
Item(&'a NameBinding<'a>),
Res(Res),
}
ModuleKind
перечислить
#[derive(Debug)]
enum ModuleKind {
// 比较有意思的是,我们发现内部模块的分类,还有一种是 匿名模块,一个 block 就是一个匿名模块
/// An anonymous module; e.g., just a block.
///
/// ```
/// fn main() {
/// fn f() {} // (1)
/// { // This is an anonymous module
/// f(); // This resolves to (2) as we are inside the block.
/// fn f() {} // (2)
/// }
/// f(); // Resolves to (1)
/// }
/// ```
Block(NodeId),
/// Any module with a name.
///
/// This could be:
///
/// * A normal module – either `mod from_file;` or `mod from_block { }` –
/// or the crate root (which is conceptually a top-level module).
/// Note that the crate root's [name][Self::name] will be [`kw::Empty`].
/// * A trait or an enum (it implicitly contains associated types, methods and variant
/// constructors).
Def(DefKind, DefId, Symbol),
}
AmbiguityKind
перечислить
// 歧义类型
#[derive(Clone, Copy, PartialEq, Debug)]
enum AmbiguityKind {
Import, // 多个导入源
BuiltinAttr, // 内建属性命名冲突
DeriveHelper, // derive 内命名冲突
MacroRulesVsModularized, // 宏名和非宏名冲突
GlobVsOuter,
GlobVsGlob,
GlobVsExpanded,
MoreExpandedVsOuter,
}
Resolver<'a'>
структура
// 这是主要用于解析的结构体,这是一个很大的结构体,包含了名称解析过程需要的数据信息
/// The main resolver class.
///
/// This is the visitor that walks the whole crate.
pub struct Resolver<'a> {
session: &'a Session,
definitions: Definitions,
graph_root: Module<'a>,
prelude: Option<Module<'a>>,
extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'a>>,
// ...
}
// 用于 Resolver 库里的内存分配
pub struct ResolverArenas<'a> {
modules: TypedArena<ModuleData<'a>>,
local_modules: RefCell<Vec<Module<'a>>>,
imports: TypedArena<Import<'a>>,
name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
ast_paths: TypedArena<ast::Path>,
dropless: DroplessArena,
}
Далее идут некоторые функции, в том числеreport_errors
/ report_conflict
/ add_suggestion_for_rename_of_use
И некоторые другие функции для диагностической информации компилятора.
сосредоточиться на проблеме
Теперь у нас есть достаточное и систематическое понимание предыстории функции разрешения имен. Давайте посмотрим на некоторые детали кода.
Согласно официальному предложению прочитать исходный код, этот шаг должен бытьDeep
, сосредоточив внимание на некоторых интересных или сомнительных функциях.
Меня больше интересует, как Rustc проверяет неиспользуемые переменные, так что давайте сосредоточимсяcheck_unused.rs
соответствующие функции в модуле.
В комментарии к модулю говорится, что проверка неиспользуемого импорта — это трехэтапный процесс:
первый шаг:UnusedImportCheckVisitor
пройти AST, чтобы найтиUseTree
все неиспользованные импорты внутри и регистрируйте ихuse
группировка иNodeId
Информация.
Для неиспользуемых методов признаков, то вrustc_typeck/check_unused.rs
регистрироваться.
Мы уже знаем из предыдущей справочной информации,check_unused
Происходит при третьем обходе AST.После первых двух обходовUseTree
, просто пройдиUnused NodeId
Только что:
struct UnusedImport<'a> {
use_tree: &'a ast::UseTree,
use_tree_id: ast::NodeId,
item_span: Span,
unused: FxHashSet<ast::NodeId>, // 内部的 快速 HashSet 存储 NodeId 信息
}
impl<'a> UnusedImport<'a> {
fn add(&mut self, id: ast::NodeId) {
self.unused.insert(id);
}
}
struct UnusedImportCheckVisitor<'a, 'b> {
r: &'a mut Resolver<'b>,
/// All the (so far) unused imports, grouped path list
unused_imports: NodeMap<UnusedImport<'a>>,
base_use_tree: Option<&'a ast::UseTree>,
base_id: ast::NodeId,
item_span: Span,
}
impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
// We have information about whether `use` (import) items are actually
// used now. If an import is not used at all, we signal a lint error.
fn check_import(&mut self, id: ast::NodeId) {
/* do something */
}
}
// 实现 rustc_ast 中 定义 的 Visitor trait, 这是访问者模式在 Rust 编译器中的应用
// Visitor trait 中定义了 AST Node的访问钩子方法,这样具体的访问者就可以实现 Visitor 的特定方法来进行具体的访问
// 这里具体的访问者就是 UnusedImportCheckVisitor
impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
fn visit_item(&mut self, item: &'a ast::Item) { /* do something */ }
fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) { /* do something */ }
}
Шаг 2:calc_unused_spans
, просмотрите собранные данные на предыдущем шагеNodeId
СвязанныйSpan
fn calc_unused_spans(
unused_import: &UnusedImport<'_>,
use_tree: &ast::UseTree,
use_tree_id: ast::NodeId,
) -> UnusedSpanResult {
/* do something */
match use_tree.kind {
ast::UseTreeKind::Simple(..) | ast::UseTreeKind::Glob => { /* do something */ }
ast::UseTreeKind::Nested(ref nested) => {/* do something */}
}
/* do something */
}
третий шаг:check_crate
, который выдает диагностику на основе сгенерированных данных
impl Resolver<'_> {
// 为 Resolver 实现 check_unused 方法
crate fn check_unused(&mut self, krate: &ast::Crate) {
/* do something */
// 检查导入源
for import in self.potentially_unused_imports.iter() {
match import.kind {
ImportKind::MacroUse => { /* do something */ }
ImportKind::ExternCrate { .. } => { /* do something */ }
}
}
let mut visitor = UnusedImportCheckVisitor {
r: self,
unused_imports: Default::default(),
base_use_tree: None,
base_id: ast::DUMMY_NODE_ID,
item_span: DUMMY_SP,
};
visit::walk_crate(&mut visitor, krate);
for unused in visitor.unused_imports.values() {
let mut fixes = Vec::new(); // 为 cargo fix 记录
/* do something */
// 计算 unused 位置信息
let mut spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) {
/* do something */
}
/* do something */
// 发出诊断消息
visitor.r.lint_buffer.buffer_lint_with_diagnostic(
UNUSED_IMPORTS,
unused.use_tree_id,
ms,
&msg,
BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes),
);
}
}
}
Читая эту часть кода, мы, вероятно, понимаемrustc_resolve
Организация библиотеки:
-
lib.rs
определить основныеResolver
Связанные типы и методы - в различных
Resolver
Реализуйте специальные методы синтаксического анализа в функциональных модулях, таких какcheck_unused
вернуться к общему модулю
Затем мы возвращаемся к общему модулю, чтобы понять другие части кода.
Мы знаем, что первый обход AST строит сокращенный граф (reduced graph
), то этот процесс должен соответствовать build_reduced_graph.rs
модуль.
Мы видим, что модуль импортируетrustc_ast
/ rustc_expand
/ rustc_data_structures::sync::Lrc (等价于 Arc)/ rustc_hir::def_id
и другие связанные компоненты, возможно, что это связано с расширением макросов, а также поддерживает параллельную компиляцию.
impl<'a> Resolver<'a> {
crate fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T) where
T: ToNameBinding<'a>,
{
let binding = def.to_name_binding(self.arenas);
let key = self.new_key(ident, ns);
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_resolve/src/imports.rs#L490
// try_define 定义于 imports 模块,解析导入的时候用于检查绑定的名称
if let Err(old_binding) = self.try_define(parent, key, binding) {
// 如果命名有冲突,这里会调用 report_conflict 来发出错误报告
self.report_conflict(parent, ident, ns, old_binding, &binding);
}
}
fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> {/* do something */}
crate fn get_module(&mut self, def_id: DefId) -> Option<Module<'a>> {/* do something */}
crate fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> {/* do something */}
crate fn build_reduced_graph(
&mut self,
fragment: &AstFragment,
parent_scope: ParentScope<'a>,
) -> MacroRulesScopeRef<'a> {/* do something */}
}
Реализовано то, что нужно для построения упрощенной схемыResolver
сопутствующие методы. Мы не будем рассматривать конкретные детали, просто поймем общий процесс.
Суммировать
Деятельность официального клуба по чтению исходного кода направлена на то, чтобы побудить разработчиков компилятора Rust активно вносить свой вклад в компилятор Rust. Впрочем, для конкретного процесса чтения исходного кода догадка будет не слишком подробной, и есть еще много вещей, которые нужно понять в частном порядке.
Эта статья распространяется как отчет о чтении и изучении исходного кода и направлена на привлечение других.Если в тексте есть ошибки, обратная связь приветствуется.